opencode-codebase-index 0.15.0 → 0.16.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/README.md +33 -8
- package/dist/cli.cjs +144 -51
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +144 -51
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +13 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +92 -9
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +92 -9
- package/dist/pi-extension.js.map +1 -1
- package/hooks/hooks.json +2 -2
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
- package/skills/codebase-search/SKILL.md +9 -7
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codebase-index",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Semantic code search and codebase graph tools for Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Kenneth",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"termsOfServiceURL": "https://github.com/Helweg/opencode-codebase-index/blob/main/LICENSE",
|
|
38
38
|
"brandColor": "#3B82F6",
|
|
39
39
|
"defaultPrompt": [
|
|
40
|
-
"
|
|
40
|
+
"For repository questions, use index_status when readiness is unknown, then call codebase_context before shell search or broad file reads."
|
|
41
41
|
]
|
|
42
42
|
}
|
|
43
43
|
}
|
package/README.md
CHANGED
|
@@ -115,6 +115,8 @@ pi install ./path/to/opencode-codebase-index
|
|
|
115
115
|
|
|
116
116
|
Pi uses the neutral `.codebase-index/` project storage and falls back to existing OpenCode state when present.
|
|
117
117
|
|
|
118
|
+
The Pi extension injects lightweight routing guidance and exposes `codebase_context` as its preferred first repository tool. It routes conceptual discovery, known-symbol definitions, and dependency paths before broad shell search or file reads.
|
|
119
|
+
|
|
118
120
|
## 🧩 Codex Plugin
|
|
119
121
|
Install once for Codex threads and get skill guidance plus MCP tools in one manifest.
|
|
120
122
|
|
|
@@ -144,6 +146,8 @@ npm run dev:link-mcp
|
|
|
144
146
|
|
|
145
147
|
After that, the normal `.mcp.json` command also works when Codex starts the plugin from this repository.
|
|
146
148
|
|
|
149
|
+
The native Codex plugin is important: its session hook and `codebase-search` skill tell Codex to use `index_status` and `codebase_context` before shell exploration. A bare MCP configuration does not provide the same selection reliability. Current Codex `exec` releases may cancel MCP calls in non-interactive mode while waiting for an app-tool approval; use an interactive Codex thread to approve the tool call. This is a Codex client limitation, not an MCP server failure.
|
|
150
|
+
|
|
147
151
|
## 🧩 Claude Code Plugin
|
|
148
152
|
Install once for Claude Code sessions and get skill guidance plus MCP tools in one manifest.
|
|
149
153
|
|
|
@@ -225,7 +229,19 @@ Use the same semantic search from any MCP-compatible client. Index once, search
|
|
|
225
229
|
npx -y --package opencode-codebase-index opencode-codebase-index-mcp
|
|
226
230
|
```
|
|
227
231
|
|
|
228
|
-
The MCP server exposes all
|
|
232
|
+
The MCP server exposes all 13 tools (`codebase_context`, `codebase_search`, `codebase_peek`, `find_similar`, `implementation_lookup`, `call_graph`, `call_graph_path`, `pr_impact`, `index_codebase`, `index_status`, `index_health_check`, `index_metrics`, `index_logs`) and 5 prompts (`search`, `find`, `definition`, `index`, `status`).
|
|
233
|
+
|
|
234
|
+
The tools carry self-routing descriptions so clients can choose the lightweight path without relying on separate documentation:
|
|
235
|
+
|
|
236
|
+
1. `codebase_context` as the preferred single entry point for repository questions
|
|
237
|
+
2. `index_status` when index readiness is unknown
|
|
238
|
+
3. `codebase_peek` for direct low-token conceptual discovery
|
|
239
|
+
4. `implementation_lookup` for direct known-symbol definition lookup
|
|
240
|
+
5. `codebase_search` only when full semantic content is needed
|
|
241
|
+
6. `grep` for exact identifiers or exhaustive matches
|
|
242
|
+
7. `call_graph` / `call_graph_path` for direct graph queries
|
|
243
|
+
|
|
244
|
+
The server also publishes this workflow through the standard MCP initialization `instructions` field. Client behavior remains client-controlled: an MCP server can describe and recommend its tools, but cannot force an agent host to read server instructions or invoke a tool before filesystem search. Clients that ignore MCP instructions still receive the routing guidance in each tool description.
|
|
229
245
|
|
|
230
246
|
The MCP dependencies (`@modelcontextprotocol/sdk`, `zod`) ship with the package so published `npx --package opencode-codebase-index` launches work in clean MCP clients.
|
|
231
247
|
|
|
@@ -254,9 +270,9 @@ src/api/checkout.ts:89 (Route handler for /pay)
|
|
|
254
270
|
|
|
255
271
|
| Scenario | Tool | Why |
|
|
256
272
|
|----------|------|-----|
|
|
257
|
-
| Don't know the function name | `
|
|
258
|
-
| Exploring unfamiliar codebase | `
|
|
259
|
-
| Just need to find locations | `codebase_peek` | Returns metadata only, saves ~90% tokens |
|
|
273
|
+
| Don't know the function name | `codebase_context` (MCP) | Routes conceptual questions to low-token discovery first |
|
|
274
|
+
| Exploring unfamiliar codebase | `codebase_context` (MCP) | Discovers related code and then guides to definitions or graph queries |
|
|
275
|
+
| Just need to find locations | `codebase_peek` (or `codebase_context`) | Returns metadata only, saves ~90% tokens |
|
|
260
276
|
| Need the authoritative definition site | `implementation_lookup` | Prioritizes real implementation definitions over docs/tests |
|
|
261
277
|
| Understand code flow | `call_graph` | Find callers/callees of any function |
|
|
262
278
|
| Trace dependency paths | `call_graph_path` | Find the shortest known call path between two symbols |
|
|
@@ -264,7 +280,7 @@ src/api/checkout.ts:89 (Route handler for /pay)
|
|
|
264
280
|
| Need ALL matches | `grep` | Semantic returns top N only |
|
|
265
281
|
| Mixed discovery + precision | `/find` (hybrid) | Best of both worlds |
|
|
266
282
|
|
|
267
|
-
**Rule of thumb**: `
|
|
283
|
+
**Rule of thumb**: `codebase_context` to route discovery first. Then `Read` to examine exact content and `grep` for precision. For symbol-definition questions, use `implementation_lookup` first.
|
|
268
284
|
|
|
269
285
|
## 🧭 OMO CodeGraph Compatibility
|
|
270
286
|
|
|
@@ -281,7 +297,7 @@ Recent OMO releases include a built-in CodeGraph MCP and make it part of the def
|
|
|
281
297
|
|
|
282
298
|
Recommended OMO workflow:
|
|
283
299
|
|
|
284
|
-
1. Start broad with `
|
|
300
|
+
1. Start broad with `codebase_context` when the prompt is conceptual, such as "where is auth enforced?" or "payment validation flow".
|
|
285
301
|
2. Use `implementation_lookup` once you have a symbol or concept that should resolve to a definition.
|
|
286
302
|
3. Use OMO CodeGraph, `call_graph`, or `call_graph_path` after locating the relevant symbol to check blast radius and dependency flow.
|
|
287
303
|
4. Keep `grep` for exact identifiers and exhaustive text matches.
|
|
@@ -424,9 +440,18 @@ The following files/folders are excluded from indexing by default:
|
|
|
424
440
|
|
|
425
441
|
The plugin exposes these tools to the OpenCode agent:
|
|
426
442
|
|
|
443
|
+
`codebase_context` is MCP-server-only.
|
|
444
|
+
|
|
445
|
+
### `codebase_context`
|
|
446
|
+
*MCP-only entrypoint for combined routing*
|
|
447
|
+
**Preferred first tool for repository questions.** Routes to the lowest-token indexed operation that matches the query: conceptual discovery, definition lookup, callers/callees, or symbol-to-symbol paths.
|
|
448
|
+
- **Use for**: New questions about behavior, locating symbols, or tracing direct call relationships.
|
|
449
|
+
- **Example**: `"Where is the payment validation logic?"`
|
|
450
|
+
- **Workflow**: If the query is conceptual, it may return locations first. For exact behavior text, follow with `codebase_search`.
|
|
451
|
+
|
|
427
452
|
### `codebase_search`
|
|
428
|
-
**
|
|
429
|
-
- **Use for**: Discovery
|
|
453
|
+
**Behavioral semantic retrieval with full content.** Searches code by describing behavior.
|
|
454
|
+
- **Use for**: Discovery when you already want full matching snippets and are ready to inspect implementation text.
|
|
430
455
|
- **Example**: `"find the middleware that sanitizes input"`
|
|
431
456
|
- **Ranking path**: hybrid retrieval → fusion (`search.fusionStrategy`) → deterministic rerank (`search.rerankTopN`) → filters
|
|
432
457
|
- **Blame filters**: when `indexing.gitBlame.enabled` is `true`, filter with `blameAuthor`, `blameSha`, or `blameSince`.
|
package/dist/cli.cjs
CHANGED
|
@@ -8998,10 +8998,19 @@ var Indexer = class {
|
|
|
8998
8998
|
});
|
|
8999
8999
|
const embeddingStartTime = import_perf_hooks.performance.now();
|
|
9000
9000
|
const embeddingQuery = stripFilePathHint(query);
|
|
9001
|
-
|
|
9001
|
+
let embedding;
|
|
9002
|
+
try {
|
|
9003
|
+
embedding = await this.getQueryEmbedding(embeddingQuery, provider);
|
|
9004
|
+
} catch (error) {
|
|
9005
|
+
this.logger.warn("Query embedding failed; falling back to keyword-only search", {
|
|
9006
|
+
query,
|
|
9007
|
+
error: getErrorMessage2(error),
|
|
9008
|
+
action: "Check the embedding provider configuration and retry search after restoring provider health."
|
|
9009
|
+
});
|
|
9010
|
+
}
|
|
9002
9011
|
const embeddingMs = import_perf_hooks.performance.now() - embeddingStartTime;
|
|
9003
9012
|
const vectorStartTime = import_perf_hooks.performance.now();
|
|
9004
|
-
const semanticResults = store.search(embedding, maxResults * 4);
|
|
9013
|
+
const semanticResults = embedding ? store.search(embedding, maxResults * 4) : [];
|
|
9005
9014
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
9006
9015
|
const keywordStartTime = import_perf_hooks.performance.now();
|
|
9007
9016
|
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
@@ -9036,12 +9045,13 @@ var Indexer = class {
|
|
|
9036
9045
|
});
|
|
9037
9046
|
}
|
|
9038
9047
|
const fusionStartTime = import_perf_hooks.performance.now();
|
|
9048
|
+
const rankingHybridWeight = embedding === void 0 && fusionStrategy === "weighted" ? 1 : effectiveHybridWeight;
|
|
9039
9049
|
const combined = rankHybridResults(query, semanticCandidates, keywordCandidates, {
|
|
9040
9050
|
fusionStrategy,
|
|
9041
9051
|
rrfK,
|
|
9042
9052
|
rerankTopN,
|
|
9043
9053
|
limit: maxResults,
|
|
9044
|
-
hybridWeight:
|
|
9054
|
+
hybridWeight: rankingHybridWeight,
|
|
9045
9055
|
prioritizeSourcePaths: sourceIntent
|
|
9046
9056
|
});
|
|
9047
9057
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
@@ -11942,20 +11952,87 @@ var CHUNK_TYPE_ENUM = [
|
|
|
11942
11952
|
];
|
|
11943
11953
|
|
|
11944
11954
|
// src/mcp-server/register-tools.ts
|
|
11955
|
+
function allowNullAsUndefined(schema) {
|
|
11956
|
+
return import_zod2.z.preprocess((value) => value === null ? void 0 : value, schema);
|
|
11957
|
+
}
|
|
11945
11958
|
function registerMcpTools(server, runtime) {
|
|
11959
|
+
server.tool(
|
|
11960
|
+
"codebase_context",
|
|
11961
|
+
"PREFERRED FIRST TOOL for any question about this repository. Use before built-in code search, grep, shell search, or broad file reads. Provide from+to for a dependency path, symbol for a definition, or only query for low-token conceptual discovery. Use call_graph directly for callers or callees.",
|
|
11962
|
+
{
|
|
11963
|
+
query: import_zod2.z.string().describe("The codebase question or behavior to locate. Always provide the user's repository question here."),
|
|
11964
|
+
from: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Source symbol. For dependency-path questions, extract the first endpoint and provide it here."),
|
|
11965
|
+
to: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Target symbol. For dependency-path questions, extract the second endpoint and provide it here."),
|
|
11966
|
+
symbol: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Exact symbol for an authoritative definition lookup. Omit when from and to are supplied."),
|
|
11967
|
+
limit: allowNullAsUndefined(import_zod2.z.number().optional().default(10)).describe("Maximum number of search or definition results"),
|
|
11968
|
+
maxDepth: allowNullAsUndefined(import_zod2.z.number().optional().default(10)).describe("Maximum call-graph traversal depth for from/to path lookup"),
|
|
11969
|
+
fileType: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
11970
|
+
directory: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by directory path (e.g., 'src/utils', 'lib')")
|
|
11971
|
+
},
|
|
11972
|
+
async (args) => {
|
|
11973
|
+
if (args.from && args.to) {
|
|
11974
|
+
const path27 = await getCallGraphPath(runtime.projectRoot, runtime.host, args.from, args.to, args.maxDepth);
|
|
11975
|
+
if (path27.length > 0) {
|
|
11976
|
+
return { content: [{ type: "text", text: formatCallGraphPath(args.from, args.to, path27) }] };
|
|
11977
|
+
}
|
|
11978
|
+
const { callers } = await getCallGraphData(runtime.projectRoot, runtime.host, {
|
|
11979
|
+
name: args.to,
|
|
11980
|
+
direction: "callers"
|
|
11981
|
+
});
|
|
11982
|
+
const directEdge = callers.find((edge) => edge.fromSymbolName === args.from);
|
|
11983
|
+
if (directEdge) {
|
|
11984
|
+
const location = directEdge.fromSymbolFilePath ? ` at ${directEdge.fromSymbolFilePath}:${directEdge.line}` : "";
|
|
11985
|
+
return {
|
|
11986
|
+
content: [{
|
|
11987
|
+
type: "text",
|
|
11988
|
+
text: `Direct path: ${args.from} --${directEdge.callType}--> ${args.to}${location} (edge is ${directEdge.isResolved ? "resolved" : "unresolved"}).`
|
|
11989
|
+
}]
|
|
11990
|
+
};
|
|
11991
|
+
}
|
|
11992
|
+
return { content: [{ type: "text", text: formatCallGraphPath(args.from, args.to, path27) }] };
|
|
11993
|
+
}
|
|
11994
|
+
if (args.symbol) {
|
|
11995
|
+
const results2 = await implementationLookup(runtime.projectRoot, runtime.host, args.symbol, {
|
|
11996
|
+
limit: args.limit ?? 10,
|
|
11997
|
+
fileType: args.fileType,
|
|
11998
|
+
directory: args.directory
|
|
11999
|
+
});
|
|
12000
|
+
return { content: [{ type: "text", text: formatDefinitionLookup(results2, args.symbol) }] };
|
|
12001
|
+
}
|
|
12002
|
+
const results = await searchCodebase(runtime.projectRoot, runtime.host, args.query, {
|
|
12003
|
+
limit: args.limit ?? 10,
|
|
12004
|
+
fileType: args.fileType,
|
|
12005
|
+
directory: args.directory,
|
|
12006
|
+
metadataOnly: true
|
|
12007
|
+
});
|
|
12008
|
+
if (results.length === 0) {
|
|
12009
|
+
return { content: [{ type: "text", text: "No matching code found. Try a different query or run index_codebase first." }] };
|
|
12010
|
+
}
|
|
12011
|
+
return {
|
|
12012
|
+
content: [{
|
|
12013
|
+
type: "text",
|
|
12014
|
+
text: `Found ${results.length} locations for "${args.query}":
|
|
12015
|
+
|
|
12016
|
+
${formatCodebasePeek(results)}
|
|
12017
|
+
|
|
12018
|
+
Use implementation_lookup for an authoritative definition, or call call_graph/call_graph_path once you have a symbol.`
|
|
12019
|
+
}]
|
|
12020
|
+
};
|
|
12021
|
+
}
|
|
12022
|
+
);
|
|
11946
12023
|
server.tool(
|
|
11947
12024
|
"codebase_search",
|
|
11948
|
-
"
|
|
12025
|
+
"FULL-CONTENT semantic retrieval. Use after codebase_peek when you need implementation text, not as the default first step. For exact identifiers or exhaustive matches use grep instead.",
|
|
11949
12026
|
{
|
|
11950
12027
|
query: import_zod2.z.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
|
|
11951
|
-
limit: import_zod2.z.number().optional().default(5).describe("Maximum number of results to return"),
|
|
11952
|
-
fileType: import_zod2.z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
11953
|
-
directory: import_zod2.z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
11954
|
-
chunkType: import_zod2.z.enum(CHUNK_TYPE_ENUM).optional().describe("Filter by code chunk type"),
|
|
11955
|
-
contextLines: import_zod2.z.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
|
|
11956
|
-
blameAuthor: import_zod2.z.string().optional().describe("Filter by git blame author name or email"),
|
|
11957
|
-
blameSha: import_zod2.z.string().optional().describe("Filter by git blame commit SHA or prefix"),
|
|
11958
|
-
blameSince: import_zod2.z.string().optional().describe("Filter to chunks last changed on or after this date")
|
|
12028
|
+
limit: allowNullAsUndefined(import_zod2.z.number().optional().default(5)).describe("Maximum number of results to return"),
|
|
12029
|
+
fileType: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
12030
|
+
directory: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
12031
|
+
chunkType: allowNullAsUndefined(import_zod2.z.enum(CHUNK_TYPE_ENUM).optional()).describe("Filter by code chunk type"),
|
|
12032
|
+
contextLines: allowNullAsUndefined(import_zod2.z.number().optional()).describe("Number of extra lines to include before/after each match (default: 0)"),
|
|
12033
|
+
blameAuthor: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by git blame author name or email"),
|
|
12034
|
+
blameSha: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by git blame commit SHA or prefix"),
|
|
12035
|
+
blameSince: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter to chunks last changed on or after this date")
|
|
11959
12036
|
},
|
|
11960
12037
|
async (args) => {
|
|
11961
12038
|
const results = await searchCodebase(runtime.projectRoot, runtime.host, args.query, {
|
|
@@ -11978,16 +12055,16 @@ ${formatSearchResults(results, "score")}` }] };
|
|
|
11978
12055
|
);
|
|
11979
12056
|
server.tool(
|
|
11980
12057
|
"codebase_peek",
|
|
11981
|
-
"
|
|
12058
|
+
"DIRECT LOW-TOKEN semantic location lookup for unfamiliar-code discovery. Prefer codebase_context when the request may involve definitions or graph navigation; use this specialized tool when you only need conceptual locations.",
|
|
11982
12059
|
{
|
|
11983
12060
|
query: import_zod2.z.string().describe("Natural language description of what code you're looking for."),
|
|
11984
|
-
limit: import_zod2.z.number().optional().default(10).describe("Maximum number of results to return"),
|
|
11985
|
-
fileType: import_zod2.z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
11986
|
-
directory: import_zod2.z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
11987
|
-
chunkType: import_zod2.z.enum(CHUNK_TYPE_ENUM).optional().describe("Filter by code chunk type"),
|
|
11988
|
-
blameAuthor: import_zod2.z.string().optional().describe("Filter by git blame author name or email"),
|
|
11989
|
-
blameSha: import_zod2.z.string().optional().describe("Filter by git blame commit SHA or prefix"),
|
|
11990
|
-
blameSince: import_zod2.z.string().optional().describe("Filter to chunks last changed on or after this date")
|
|
12061
|
+
limit: allowNullAsUndefined(import_zod2.z.number().optional().default(10)).describe("Maximum number of results to return"),
|
|
12062
|
+
fileType: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
12063
|
+
directory: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
12064
|
+
chunkType: allowNullAsUndefined(import_zod2.z.enum(CHUNK_TYPE_ENUM).optional()).describe("Filter by code chunk type"),
|
|
12065
|
+
blameAuthor: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by git blame author name or email"),
|
|
12066
|
+
blameSha: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by git blame commit SHA or prefix"),
|
|
12067
|
+
blameSince: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter to chunks last changed on or after this date")
|
|
11991
12068
|
},
|
|
11992
12069
|
async (args) => {
|
|
11993
12070
|
const results = await searchCodebase(runtime.projectRoot, runtime.host, args.query, {
|
|
@@ -12012,11 +12089,11 @@ Use Read tool to examine specific files.` }] };
|
|
|
12012
12089
|
);
|
|
12013
12090
|
server.tool(
|
|
12014
12091
|
"index_codebase",
|
|
12015
|
-
"
|
|
12092
|
+
"Create or refresh the semantic index. Call index_status first when readiness is unknown, then use this tool only if the index is missing, stale, or incompatible. Incremental by default; force=true rebuilds everything.",
|
|
12016
12093
|
{
|
|
12017
|
-
force: import_zod2.z.boolean().optional().default(false).describe("Force reindex even if already indexed"),
|
|
12018
|
-
estimateOnly: import_zod2.z.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
|
|
12019
|
-
verbose: import_zod2.z.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
|
|
12094
|
+
force: allowNullAsUndefined(import_zod2.z.boolean().optional().default(false)).describe("Force reindex even if already indexed"),
|
|
12095
|
+
estimateOnly: allowNullAsUndefined(import_zod2.z.boolean().optional().default(false)).describe("Only show cost estimate without indexing"),
|
|
12096
|
+
verbose: allowNullAsUndefined(import_zod2.z.boolean().optional().default(false)).describe("Show detailed info about skipped files and parsing failures")
|
|
12020
12097
|
},
|
|
12021
12098
|
async (args) => {
|
|
12022
12099
|
const result = await runIndexCodebase(runtime.projectRoot, runtime.host, args);
|
|
@@ -12031,7 +12108,7 @@ Use Read tool to examine specific files.` }] };
|
|
|
12031
12108
|
);
|
|
12032
12109
|
server.tool(
|
|
12033
12110
|
"index_status",
|
|
12034
|
-
"
|
|
12111
|
+
"START HERE once per repository task when index readiness or freshness is unknown. Reports whether semantic retrieval is ready, chunk counts, compatibility, and the embedding provider. If ready, continue with codebase_peek or implementation_lookup; otherwise run index_codebase.",
|
|
12035
12112
|
{},
|
|
12036
12113
|
async () => {
|
|
12037
12114
|
const status = await getIndexStatus(runtime.projectRoot, runtime.host);
|
|
@@ -12063,9 +12140,13 @@ Use Read tool to examine specific files.` }] };
|
|
|
12063
12140
|
"index_logs",
|
|
12064
12141
|
"Get recent debug logs from the codebase indexer. Requires debug.enabled=true in config.",
|
|
12065
12142
|
{
|
|
12066
|
-
limit: import_zod2.z.number().optional().default(20).describe("Maximum number of log entries to return"),
|
|
12067
|
-
category:
|
|
12068
|
-
|
|
12143
|
+
limit: allowNullAsUndefined(import_zod2.z.number().optional().default(20)).describe("Maximum number of log entries to return"),
|
|
12144
|
+
category: allowNullAsUndefined(
|
|
12145
|
+
import_zod2.z.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional()
|
|
12146
|
+
).describe("Filter by log category"),
|
|
12147
|
+
level: allowNullAsUndefined(
|
|
12148
|
+
import_zod2.z.enum(["error", "warn", "info", "debug"]).optional()
|
|
12149
|
+
).describe("Filter by minimum log level")
|
|
12069
12150
|
},
|
|
12070
12151
|
async (args) => {
|
|
12071
12152
|
const result = await getIndexLogs(runtime.projectRoot, runtime.host, args);
|
|
@@ -12074,14 +12155,14 @@ Use Read tool to examine specific files.` }] };
|
|
|
12074
12155
|
);
|
|
12075
12156
|
server.tool(
|
|
12076
12157
|
"find_similar",
|
|
12077
|
-
"
|
|
12158
|
+
"Use when you already have a code snippet and need analogous implementations, duplicates, patterns, or refactoring candidates. For a natural-language concept without example code, start with codebase_peek instead.",
|
|
12078
12159
|
{
|
|
12079
12160
|
code: import_zod2.z.string().describe("The code snippet to find similar code for"),
|
|
12080
|
-
limit: import_zod2.z.number().optional().default(10).describe("Maximum number of results to return"),
|
|
12081
|
-
fileType: import_zod2.z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
12082
|
-
directory: import_zod2.z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
12083
|
-
chunkType: import_zod2.z.enum(CHUNK_TYPE_ENUM).optional().describe("Filter by code chunk type"),
|
|
12084
|
-
excludeFile: import_zod2.z.string().optional().describe("Exclude results from this file path")
|
|
12161
|
+
limit: allowNullAsUndefined(import_zod2.z.number().optional().default(10)).describe("Maximum number of results to return"),
|
|
12162
|
+
fileType: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
12163
|
+
directory: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
12164
|
+
chunkType: allowNullAsUndefined(import_zod2.z.enum(CHUNK_TYPE_ENUM).optional()).describe("Filter by code chunk type"),
|
|
12165
|
+
excludeFile: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Exclude results from this file path")
|
|
12085
12166
|
},
|
|
12086
12167
|
async (args) => {
|
|
12087
12168
|
const results = await findSimilarCode(runtime.projectRoot, runtime.host, args.code, {
|
|
@@ -12101,12 +12182,12 @@ ${formatSearchResults(results)}` }] };
|
|
|
12101
12182
|
);
|
|
12102
12183
|
server.tool(
|
|
12103
12184
|
"implementation_lookup",
|
|
12104
|
-
"
|
|
12185
|
+
"FIRST TOOL only for known-symbol definition questions. Returns authoritative source locations and prefers implementations over tests, docs, examples, and fixtures. Do not use for callers, callees, dependency paths, or code flow; use codebase_context with direction or from/to for those questions.",
|
|
12105
12186
|
{
|
|
12106
12187
|
query: import_zod2.z.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
|
|
12107
|
-
limit: import_zod2.z.number().optional().default(5).describe("Maximum number of results"),
|
|
12108
|
-
fileType: import_zod2.z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
|
|
12109
|
-
directory: import_zod2.z.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
|
|
12188
|
+
limit: allowNullAsUndefined(import_zod2.z.number().optional().default(5)).describe("Maximum number of results"),
|
|
12189
|
+
fileType: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by file extension (e.g., 'ts', 'py')"),
|
|
12190
|
+
directory: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by directory path (e.g., 'src/utils')")
|
|
12110
12191
|
},
|
|
12111
12192
|
async (args) => {
|
|
12112
12193
|
const results = await implementationLookup(runtime.projectRoot, runtime.host, args.query, {
|
|
@@ -12119,12 +12200,16 @@ ${formatSearchResults(results)}` }] };
|
|
|
12119
12200
|
);
|
|
12120
12201
|
server.tool(
|
|
12121
12202
|
"call_graph",
|
|
12122
|
-
"
|
|
12203
|
+
"Use after identifying a symbol to find its direct callers or callees and understand code flow. Use implementation_lookup first if the symbol or definition is still ambiguous. Supports relationship types: Call, MethodCall, Constructor, Import, Inherits, Implements.",
|
|
12123
12204
|
{
|
|
12124
12205
|
name: import_zod2.z.string().describe("Function or method name to query"),
|
|
12125
|
-
direction:
|
|
12126
|
-
|
|
12127
|
-
|
|
12206
|
+
direction: allowNullAsUndefined(
|
|
12207
|
+
import_zod2.z.enum(["callers", "callees"]).default("callers")
|
|
12208
|
+
).describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
|
|
12209
|
+
symbolId: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Symbol ID (required for 'callees' direction)"),
|
|
12210
|
+
relationshipType: allowNullAsUndefined(
|
|
12211
|
+
import_zod2.z.enum(["Call", "MethodCall", "Constructor", "Import", "Inherits", "Implements"]).optional()
|
|
12212
|
+
).describe("Filter by relationship type. Omit to show all.")
|
|
12128
12213
|
},
|
|
12129
12214
|
async (args) => {
|
|
12130
12215
|
if (args.direction === "callees") {
|
|
@@ -12140,11 +12225,11 @@ ${formatSearchResults(results)}` }] };
|
|
|
12140
12225
|
);
|
|
12141
12226
|
server.tool(
|
|
12142
12227
|
"call_graph_path",
|
|
12143
|
-
"
|
|
12228
|
+
"Use after identifying both endpoint symbols to find the shortest known call path between them. Use codebase_peek or implementation_lookup first when either endpoint is unknown.",
|
|
12144
12229
|
{
|
|
12145
12230
|
from: import_zod2.z.string().describe("Source function/method name (starting point)"),
|
|
12146
12231
|
to: import_zod2.z.string().describe("Target function/method name (destination)"),
|
|
12147
|
-
maxDepth: import_zod2.z.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
|
|
12232
|
+
maxDepth: allowNullAsUndefined(import_zod2.z.number().optional().default(10)).describe("Maximum traversal depth (default: 10)")
|
|
12148
12233
|
},
|
|
12149
12234
|
async (args) => {
|
|
12150
12235
|
const path27 = await getCallGraphPath(runtime.projectRoot, runtime.host, args.from, args.to, args.maxDepth);
|
|
@@ -12153,14 +12238,16 @@ ${formatSearchResults(results)}` }] };
|
|
|
12153
12238
|
);
|
|
12154
12239
|
server.tool(
|
|
12155
12240
|
"pr_impact",
|
|
12156
|
-
"
|
|
12241
|
+
"FIRST TOOL for pull-request or branch blast-radius questions. Analyzes changed files, affected symbols, transitive dependencies, communities, hub nodes, conflicts, and risk before merging.",
|
|
12157
12242
|
{
|
|
12158
|
-
pr: import_zod2.z.number().optional().describe("Pull request number to analyze"),
|
|
12159
|
-
branch: import_zod2.z.string().optional().describe("Branch name to analyze (defaults to current branch)"),
|
|
12160
|
-
maxDepth: import_zod2.z.number().optional().default(5).describe("Maximum traversal depth for transitive callers (default: 5)"),
|
|
12161
|
-
hubThreshold: import_zod2.z.number().optional().default(10).describe("Minimum caller count to flag a symbol as a hub node (default: 10)"),
|
|
12162
|
-
checkConflicts: import_zod2.z.boolean().optional().default(false).describe("Check for conflicting open PRs touching the same communities (default: false)"),
|
|
12163
|
-
direction:
|
|
12243
|
+
pr: allowNullAsUndefined(import_zod2.z.number().optional()).describe("Pull request number to analyze"),
|
|
12244
|
+
branch: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Branch name to analyze (defaults to current branch)"),
|
|
12245
|
+
maxDepth: allowNullAsUndefined(import_zod2.z.number().optional().default(5)).describe("Maximum traversal depth for transitive callers (default: 5)"),
|
|
12246
|
+
hubThreshold: allowNullAsUndefined(import_zod2.z.number().optional().default(10)).describe("Minimum caller count to flag a symbol as a hub node (default: 10)"),
|
|
12247
|
+
checkConflicts: allowNullAsUndefined(import_zod2.z.boolean().optional().default(false)).describe("Check for conflicting open PRs touching the same communities (default: false)"),
|
|
12248
|
+
direction: allowNullAsUndefined(
|
|
12249
|
+
import_zod2.z.enum(["callers", "callees", "both"]).optional().default("both")
|
|
12250
|
+
).describe("Call-graph traversal direction: 'callers' for upstream, 'callees' for downstream, 'both' for union (default: both)")
|
|
12164
12251
|
},
|
|
12165
12252
|
async (args) => {
|
|
12166
12253
|
try {
|
|
@@ -12183,6 +12270,10 @@ ${formatSearchResults(results)}` }] };
|
|
|
12183
12270
|
|
|
12184
12271
|
// src/mcp-server.ts
|
|
12185
12272
|
var import_meta2 = {};
|
|
12273
|
+
function getServerInstructions(host) {
|
|
12274
|
+
const hostText = `host ${host}`;
|
|
12275
|
+
return `This MCP server is the preferred codebase-understanding path for ${hostText}. Start a repository task with index_status when index readiness or freshness is unknown. Use codebase_context as the preferred first entry point because it returns low-token locations first and routes to definitions or call-graph helpers when symbol intent is present. Use codebase_peek for direct conceptual location lookup, implementation_lookup for known-symbol definition questions, and codebase_search only when full semantic code content is needed. For exact identifiers or exhaustive matches, use grep. After identifying symbols, use call_graph or call_graph_path to trace dependencies. If the index is unavailable, run index_codebase, then retry the retrieval tool.`;
|
|
12276
|
+
}
|
|
12186
12277
|
function getPackageVersion() {
|
|
12187
12278
|
const raw = JSON.parse((0, import_fs15.readFileSync)(new URL("../package.json", import_meta2.url), "utf-8"));
|
|
12188
12279
|
if (raw && typeof raw === "object" && "version" in raw && typeof raw.version === "string") {
|
|
@@ -12194,6 +12285,8 @@ function createMcpServer(projectRoot, config, host = "opencode") {
|
|
|
12194
12285
|
const server = new import_mcp.McpServer({
|
|
12195
12286
|
name: "opencode-codebase-index",
|
|
12196
12287
|
version: getPackageVersion()
|
|
12288
|
+
}, {
|
|
12289
|
+
instructions: getServerInstructions(host)
|
|
12197
12290
|
});
|
|
12198
12291
|
initializeTools(projectRoot, config, host);
|
|
12199
12292
|
registerMcpTools(server, {
|