opencode-codebase-index

内容来源:README.md(说明文档) · 原始地址 · 查看安装指南

原始内容

opencode-codebase-index

npm version License: MIT Downloads Build Status Node.js

Stop grepping for concepts. Start searching for meaning.

opencode-codebase-index brings semantic understanding to your OpenCode, Pi, Codex, and MCP-compatible workflows. Instead of guessing function names or grepping for keywords, ask your codebase questions in plain English.

📌 Quick Navigation

👋 Choose Your Path

🚀 Why Use This?

  • 🧠 Semantic Search: Finds "user authentication" logic even if the function is named check_creds.
  • Blazing Fast Indexing: Powered by a Rust native module using tree-sitter and usearch. Incremental updates take milliseconds.
  • 🌿 Branch-Aware: Seamlessly handles git branch switches — reuses embeddings, filters stale results.
  • 🔒 Privacy Focused: Your vector index is stored locally in your project.
  • 🔌 Model Agnostic: Works out-of-the-box with GitHub Copilot, OpenAI, Gemini, or local Ollama models.
  • 🥧 Pi Package: First-class Pi extension and skill package with native tools.
  • 🌐 MCP Server: Use with Cursor, Claude Code, Windsurf, or any MCP-compatible client — index once, search from anywhere.

⚡ Quick Start

  1. Install the plugin

    npm install opencode-codebase-index
    
  2. Add to opencode.json

    {
      "plugin": ["opencode-codebase-index"]
    }
    
  3. Index your codebase Run /index or ask the agent to index your codebase. This only needs to be done once — subsequent updates are incremental.

    Recommended check: run /status after the first index so you can confirm the detected provider/model before you start searching.

  4. Start Searching Ask:

    "Find the function that handles credit card validation errors"

🥧 Pi Package

Install as a Pi package to get first-class codebase_search, index_codebase, call graph, PR impact, and knowledge-base tools plus the codebase-search skill.

pi install npm:opencode-codebase-index
# or, for local development:
pi install ./path/to/opencode-codebase-index

Pi uses the neutral .codebase-index/ project storage and falls back to existing OpenCode state when present.

🧩 Codex Plugin

Install once for Codex threads and get skill guidance plus MCP tools in one manifest.

  1. Add this repo as a marketplace source
    codex plugin marketplace add Helweg/opencode-codebase-index
    
  2. Install the plugin
    codex plugin add codebase-index@helweg-plugins
    
  3. Restart or open a new thread in the target workspace.
  4. Use MCP tools (index_codebase, index_status, codebase_search, etc.) and the codebase-search skill guidance.

The plugin includes:

  • skills/ guidance for local workflows
  • hooks/hooks.json lightweight session-start guidance
  • .mcp.json running the published opencode-codebase-index CLI via npx … --host codex, so a git marketplace install works without a local build
  • .agents/plugins/marketplace.json so this repo can act as a Codex marketplace source

For local plugin development from this checkout, build and link the local MCP bin once:

npm run build:ts
npm run dev:link-mcp

After that, the normal .mcp.json command also works when Codex starts the plugin from this repository.

🧩 Claude Code Plugin

Install once for Claude Code sessions and get skill guidance plus MCP tools in one manifest.

  1. Add this repo as a marketplace source
    /plugin marketplace add Helweg/opencode-codebase-index
    
  2. Install the plugin
    /plugin install codebase-index@helweg-plugins
    
  3. Restart or open a new session in the target workspace.
  4. Use MCP tools (index_codebase, index_status, codebase_search, etc.) and the codebase-search skill guidance.

The plugin includes:

  • skills/ guidance for local workflows
  • inline mcpServers (in .claude-plugin/plugin.json) running the published opencode-codebase-index CLI via npx … --host claude, so a git marketplace install works without a local build
  • .claude-plugin/marketplace.json so this repo can act as a Claude Code marketplace source

Provider selection notes

  • Default auto-detect order: Ollama → GitHub Copilot → OpenAI → Google

  • Ollama is the preferred zero-cost local option and works especially well for large repos:

    ollama pull nomic-embed-text
    
    {
      "embeddingProvider": "ollama"
    }
    
  • GitHub Copilot is a good default if OpenCode already has Copilot auth and you prefer hosted embeddings.

  • OpenAI is a good hosted option when you want predictable API behavior and standard cloud setup.

  • Google is available if you prefer Gemini-hosted embeddings.

  • If /status reports provider or compatibility problems, follow that guidance before using /index force.

🌐 MCP Server (Cursor, Claude Code, Windsurf, etc.)

Use the same semantic search from any MCP-compatible client. Index once, search from anywhere.

  1. Install dependencies

    npm install opencode-codebase-index @modelcontextprotocol/sdk zod
    
  2. Configure your MCP client

    Cursor (.cursor/mcp.json):

    {
      "mcpServers": {
        "codebase-index": {
          "command": "npx",
          "args": ["-y", "--package", "opencode-codebase-index", "opencode-codebase-index-mcp", "--project", "/path/to/your/project"]
        }
      }
    }
    

    Claude Code (claude_desktop_config.json):

    {
      "mcpServers": {
        "codebase-index": {
          "command": "npx",
          "args": ["-y", "--package", "opencode-codebase-index", "opencode-codebase-index-mcp", "--project", "/path/to/your/project"]
        }
      }
    }
    
  3. CLI options

    npx -y --package opencode-codebase-index opencode-codebase-index-mcp --project /path/to/repo
    npx -y --package opencode-codebase-index opencode-codebase-index-mcp --config /path/to/config
    npx -y --package opencode-codebase-index opencode-codebase-index-mcp
    

The MCP server exposes all 12 tools (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).

The MCP dependencies (@modelcontextprotocol/sdk, zod) ship with the package so published npx --package opencode-codebase-index launches work in clean MCP clients.

If you are testing the MCP command from inside this repository checkout and see opencode-codebase-index-mcp: command not found, run npm run build:ts && npm run dev:link-mcp. That adds the local bin shim expected by npx without changing the published MCP config.

🔍 See It In Action

Scenario: You're new to a codebase and need to fix a bug in the payment flow.

Without Plugin (grep):

  • grep "payment" . → 500 results (too many)
  • grep "card" . → 200 results (mostly UI)
  • grep "stripe" . → 50 results (maybe?)

With opencode-codebase-index: You ask: "Where is the payment validation logic?"

Plugin returns:

src/services/billing.ts:45  (Class PaymentValidator)
src/utils/stripe.ts:12      (Function validateCardToken)
src/api/checkout.ts:89      (Route handler for /pay)

🎯 When to Use What

Scenario Tool Why
Don't know the function name codebase_search Semantic search finds by meaning
Exploring unfamiliar codebase codebase_search Discovers related code across files
Just need to find locations codebase_peek Returns metadata only, saves ~90% tokens
Need the authoritative definition site implementation_lookup Prioritizes real implementation definitions over docs/tests
Understand code flow call_graph Find callers/callees of any function
Trace dependency paths call_graph_path Find the shortest known call path between two symbols
Know exact identifier grep Faster, finds all occurrences
Need ALL matches grep Semantic returns top N only
Mixed discovery + precision /find (hybrid) Best of both worlds

Rule of thumb: codebase_peek to find locations → Read to examine → grep for precision. For symbol-definition questions, use implementation_lookup first.

🧭 OMO CodeGraph Compatibility

Recent OMO releases include a built-in CodeGraph MCP and make it part of the default agent workflow. This does not replace opencode-codebase-index; the two tools answer different first questions.

Need Prefer Why
Find code by intent, behavior, or natural language codebase_peek / codebase_search Semantic + hybrid retrieval works when you do not know exact names
Jump to the likely implementation site implementation_lookup Definition-oriented ranking prefers source over tests/docs
Find similar implementations or duplicate patterns find_similar Embedding similarity compares code shape and meaning
Follow callers, callees, imports, inheritance, or implementations OMO CodeGraph or call_graph Structural graph tools are best for dependency topology
Find a shortest known relationship chain call_graph_path Uses this plugin's indexed call edges to connect two symbols
Include external docs, examples, or API references in discovery add_knowledge_base + codebase_search Knowledge bases are indexed into the same retrieval store

Recommended OMO workflow:

  1. Start broad with codebase_peek when the prompt is conceptual, such as "where is auth enforced?" or "payment validation flow".
  2. Use implementation_lookup once you have a symbol or concept that should resolve to a definition.
  3. Use OMO CodeGraph, call_graph, or call_graph_path after locating the relevant symbol to check blast radius and dependency flow.
  4. Keep grep for exact identifiers and exhaustive text matches.

If OMO reports an uninitialized CodeGraph workspace, follow its codegraph init guidance. That setup is independent from this plugin's index under .opencode/index/, so /index and codegraph init may both be useful in the same repository.

📊 Token Usage

In our testing across open-source codebases (axios, express), we observed up to 90% reduction in token usage for conceptual queries like "find the error handling middleware".

Why It Saves Tokens

  • Without plugin: Agent explores files, reads code, backtracks, explores more
  • With plugin: Semantic search returns relevant code immediately → less exploration

Key Takeaways

  1. Significant savings possible: Up to 90% reduction in the best cases
  2. Results vary: Savings depend on query type, codebase structure, and agent behavior
  3. Best for discovery: Conceptual queries benefit most; exact identifier lookups should use grep
  4. Complements existing tools: Provides a faster initial signal, doesn't replace grep/explore

When the Plugin Helps Most

  • Conceptual queries: "Where is the authentication logic?" (no keywords to grep for)
  • Unfamiliar codebases: You don't know what to search for yet
  • Large codebases: Semantic search scales better than exhaustive exploration

🛠️ How It Works

graph TD
    subgraph Indexing
    A[Source Code] -->|Tree-sitter| B[Semantic Chunks]
    B -->|Embedding Model| C[Vectors]
    C -->|uSearch| D[(Vector Store)]
    C -->|SQLite| G[(Embeddings DB)]
    B -->|BM25| E[(Inverted Index)]
    B -->|Branch Catalog| G
    end

    subgraph Searching
    Q[User Query] -->|Embedding Model| V[Query Vector]
    V -->|Cosine Similarity| D
    Q -->|BM25| E
    D --> F[Hybrid Fusion RRF/Weighted]
    E --> F
    F --> X[Deterministic Rerank]
    G -->|Branch + Metadata Filters| X
    X --> R[Ranked Results]
    end
  1. Parsing: We use tree-sitter to intelligently parse your code into meaningful blocks (functions, classes, interfaces). JSDoc comments and docstrings are automatically included with their associated code.

Supported Languages (Tree-sitter semantic parsing): TypeScript, JavaScript, Python, Rust, Go, Java, C#, Ruby, PHP, Apex, Bash, C, C++, JSON, TOML, YAML, Zig, GDScript, MATLAB†

† MATLAB (.m) is opt-in — see below.

Additional Supported Formats (line-based chunking): TXT, HTML, HTM, Markdown, Shell scripts

Default File Patterns:

**/*.{ts,tsx,js,jsx,mjs,cjs}    **/*.{py,pyi}
**/*.{go,rs,java,kt,scala}      **/*.{c,cpp,cc,h,hpp}
**/*.{rb,php,inc,swift}         **/*.{vue,svelte,astro}
**/*.{sql,graphql,proto}        **/*.{yaml,yml,toml}
**/*.{md,mdx}                   **/*.{sh,bash,zsh}
**/*.{txt,html,htm}              **/*.{cls,trigger}
**/*.zig                         **/*.gd

Use include to replace defaults, or additionalInclude to extend (e.g. "**/*.pdf", "**/*.csv").

MATLAB opt-in: .m is excluded from defaults because it conflicts with the Objective-C extension used on Apple codebases. To enable MATLAB discovery, add to your global config (~/.config/opencode/codebase-index.json):

{ "additionalInclude": ["**/*.m"] }

Max File Size: Default 1MB (1048576 bytes). Configure via indexing.maxFileSize (bytes). 2. Chunking: Large blocks are split with overlapping windows to preserve context across chunk boundaries. 3. Embedding: These blocks are converted into vector representations using your configured AI provider. 4. Storage: Embeddings are stored in SQLite (deduplicated by content hash) and vectors in usearch with F16 quantization for 50% memory savings. A branch catalog tracks which chunks exist on each branch. 5. Hybrid Search: Combines semantic similarity (vectors) with BM25 keyword matching, fuses (rrf default, weighted fallback), applies deterministic rerank, then filters by current branch/metadata.

Performance characteristics:

  • Incremental indexing: ~50ms check time — only re-embeds changed files
  • Smart chunking: Understands code structure to keep functions whole, with overlap for context
  • Native speed: Core logic written in Rust for maximum performance
  • Memory efficient: F16 vector quantization reduces index size by 50%
  • Branch-aware: Automatically tracks which chunks exist on each git branch
  • Provider validation: Detects embedding provider/model changes and requires rebuild to prevent garbage results

🌿 Branch-Aware Indexing

The plugin automatically detects git branches and optimizes indexing across branch switches.

How It Works

When you switch branches, code changes but embeddings for unchanged content remain the same. The plugin:

  1. Stores embeddings by content hash: Embeddings are deduplicated across branches
  2. Tracks branch membership: A lightweight catalog tracks which chunks exist on each branch
  3. Filters search results: Queries only return results relevant to the current branch

Benefits

Scenario Without Branch Awareness With Branch Awareness
Switch to feature branch Re-index everything Instant — reuse existing embeddings
Return to main Re-index everything Instant — catalog already exists
Search on branch May return stale results Only returns current branch's code

Automatic Behavior

  • Branch detection: Automatically reads from .git/HEAD
  • Re-indexing on switch: Triggers when you switch branches (via file watcher)
  • Legacy migration: Automatically migrates old indexes on first run
  • Garbage collection: Health check removes orphaned embeddings and chunks

Storage Structure

.opencode/index/
├── codebase.db           # SQLite: embeddings, chunks, branch catalog, symbols, call edges
├── vectors.usearch       # Vector index (uSearch)
├── inverted-index.json   # BM25 keyword index
└── file-hashes.json      # File change detection

File Exclusions

The following files/folders are excluded from indexing by default:

  • Hidden files/folders: Files starting with . (e.g., .github, .vscode, .env)
  • Build folders: Folders containing "build" in their name (e.g., build, mingwBuildDebug, cmake-build-debug)
  • Default excludes: node_modules, dist, vendor, __pycache__, target, coverage, etc.

🧰 Tools Available

The plugin exposes these tools to the OpenCode agent:

codebase_search

The primary tool. Searches code by describing behavior.

  • Use for: Discovery, understanding flows, finding logic when you don't know the names.
  • Example: "find the middleware that sanitizes input"
  • Ranking path: hybrid retrieval → fusion (search.fusionStrategy) → deterministic rerank (search.rerankTopN) → filters
  • Blame filters: when indexing.gitBlame.enabled is true, filter with blameAuthor, blameSha, or blameSince.

Writing good queries:

✅ Good queries (describe behavior) ❌ Bad queries (too vague)
"function that validates email format" "email"
"error handling for failed API calls" "error"
"middleware that checks authentication" "auth middleware"
"code that calculates shipping costs" "shipping"
"where user permissions are checked" "permissions"

codebase_peek

Token-efficient discovery. Returns only metadata (file, line, name, type) without code content.

  • Use for: Finding WHERE code is before deciding what to read. Saves ~90% tokens vs codebase_search.
  • Ranking path: same hybrid ranking path as codebase_search (metadata-only output)
  • Example output:
    [1] function "validatePayment" at src/billing.ts:45-67 (score: 0.92)
        abc1234 | Jane Doe | 2025-03-14 | billing: validate payment state
    [2] class "PaymentProcessor" at src/processor.ts:12-89 (score: 0.87)
    
    Use Read tool to examine specific files.
    
  • Workflow: codebase_peek → find locations → Read specific files

implementation_lookup

Definition-first lookup. Jumps to the authoritative definition site for a symbol or natural-language definition query.

  • Use for: "Where is X defined?", symbol-definition requests, and cases where you want the implementation site rather than all usages.
  • Behavior: Prefers real implementation files over tests, docs, examples, and fixtures.
  • Fallback: If nothing authoritative is found, use codebase_search for broader discovery.

find_similar

Find code similar to a provided snippet.

  • Use for: Duplicate detection, refactor prep, pattern mining.
  • Ranking path: semantic retrieval only + deterministic rerank (no BM25, no RRF).

index_codebase

Manually trigger indexing.

  • Use for: Forcing a re-index or checking stats.
  • Parameters: force (rebuild all), estimateOnly (check costs), verbose (show skipped files and parse failures).

index_status

Checks if the index is ready and healthy.

  • Recommended workflow: run this after /index to confirm the detected provider/model and whether the index is ready to search.

index_health_check

Maintenance tool to remove stale entries from deleted files and orphaned embeddings/chunks from the database.

index_metrics

Returns collected metrics about indexing and search performance. Requires debug.enabled and debug.metrics to be true.

  • Metrics include: Files indexed, chunks created, cache hit rate, search timing breakdown, GC stats, embedding API call stats.

index_logs

Returns recent debug logs with optional filtering.

  • Parameters: category (optional: search, embedding, cache, gc, branch), level (optional: error, warn, info, debug), limit (default: 50).

call_graph

Query the call graph to find callers or callees of a function/method. Automatically built during indexing for TypeScript, JavaScript, Python, Go, Rust, PHP, Apex, Zig, GDScript, MATLAB, and Bash.

  • Use for: Understanding code flow, tracing dependencies, impact analysis.
  • Parameters: name (function name), direction (callers or callees), symbolId (required for callees, returned by previous queries), relationshipType (optional: Call, MethodCall, Constructor, Import, Inherits, Implements).
  • Example: Find who calls validateTokencall_graph(name="validateToken", direction="callers")

call_graph_path

Find the shortest known call-graph path between two symbols. Use it after codebase_peek, implementation_lookup, or call_graph identifies the important source and target names.

  • Use for: Blast-radius checks, dependency-chain discovery, explaining how one subsystem reaches another.
  • Parameters: from (source symbol name), to (target symbol name), maxDepth (optional, default 10).
  • Example: Trace how createOrder reaches chargeCardcall_graph_path(from="createOrder", to="chargeCard")

pr_impact

Analyzes a PR's changed files to determine impact scope within the codebase.

  • Use for: Understanding which symbols are affected by a PR, their call-graph reach, risk level, and community/cluster detection.
  • Parameters: checkConflicts (optional, default false) — when true, detects overlapping concurrent PRs sharing affected symbols and returns conflictingPRs.

index_visualize

Generate a self-contained temporal call graph view for browser-based exploration.

  • Use for: Onboarding, architecture walkthroughs, and understanding what moved recently before drilling into call relationships.
  • What it shows: Recent change lenses, module overview, symbol exploration, hotspots, and cycles.
  • Concepts: Modules are path-based code areas such as src/tools or native; symbols are indexed functions, classes, methods, or similar named code units; edges are caller/callee relationships.
  • Parameters: directory (optional folder filter), maxNodes (default 5000), includeOrphans (include disconnected symbols).
  • Output: Writes a temporary HTML file you can open in any browser.
  • Example: index_visualize(directory="src/tools", maxNodes=1500)

CLI shortcut after building locally:

npm run build
npm run visualize
npm run visualize -- native
npm run visualize -- src/tools max=1000
npm run visualize -- src/indexer orphans

add_knowledge_base

Add a folder as a knowledge base to be indexed alongside project code.

  • Use for: Indexing external documentation, API references, example programs.
  • Parameters: path (folder path, absolute or relative), reindex (optional, default true).
  • Restrictions: System directories (/etc, /proc, /sys, /dev) and sensitive home directories (.ssh, .gnupg, .aws, .docker, .kube) are blocked. Symlinks are resolved before validation.
  • Example: add_knowledge_base(path="/path/to/docs")

list_knowledge_bases

List all configured knowledge base folders and their status.

remove_knowledge_base

Remove a knowledge base folder from the index.

  • Parameters: path (folder path to remove), reindex (optional, default false).
  • Example: remove_knowledge_base(path="/path/to/docs")

🎮 Slash Commands

The plugin automatically registers these slash commands:

Command Description
/definition <query> Definition Lookup. Finds the authoritative implementation site for a symbol or concept.
/peek <query> Quick Semantic Lookup. Returns likely locations only, without full code content.
/reindex Full Rebuild. Rebuilds the codebase index from scratch.
/search <query> Pure Semantic Search. Best for "How does X work?"
/find <query> Hybrid Search. Combines semantic search + grep. Best for "Find usage of X".
/call-graph <query> Call Graph Trace. Find callers/callees to understand execution flow.
/pr-impact <PR number or branch> PR Impact Analysis. Analyze changed files, affected symbols, communities, hub nodes, and risk.
`/visualize [directory max=N
/index Update Index. Runs incremental indexing by default; use /index force for a full rebuild.
/status Check Status. Shows if indexed, chunk count, and provider info.

📚 Knowledge Base

The plugin can index external documentation alongside your project code. The indexed codebase includes:

  • Project Source Code — all code files in the current workspace
  • API References — hardware API docs, library documentation
  • Usage Guides — tutorials, how-to guides
  • Example Programs — code samples, demo projects

Adding Knowledge Base Folders

Use the built-in tools to add documentation folders:

add_knowledge_base(path="/path/to/api-docs")
add_knowledge_base(path="/path/to/examples")

The folder will be indexed into the same database as your project code. All searches automatically include both sources.

Managing Knowledge Bases

list_knowledge_bases          # Show configured knowledge bases
remove_knowledge_base(path="/path/to/api-docs")  # Remove a knowledge base

Configuration Example

Project-level config (.opencode/codebase-index.json):

{
  "knowledgeBases": [
    "/home/user/docs/esp-idf",
    "/home/user/docs/arduino"
  ]
}

Global-level config (~/.config/opencode/codebase-index.json):

{
  "embeddingProvider": "custom",
  "customProvider": {
    "baseUrl": "{env:EMBED_BASE_URL}",
    "model": "BAAI/bge-m3",
    "dimensions": 1024,
    "apiKey": "{env:EMBED_API_KEY}"
  }
}

Config merging: Global config is the base, project config overrides. Knowledge bases from both levels are merged.

Syncing Changes

  • Project code: Auto-synced via file watcher (real-time)
  • Knowledge base folders: Manual sync — run /index force after changes

🔄 Reranking

The plugin supports API-based reranking for improved search result quality. Reranking uses a cross-encoder model to rescore the top search results.

Enable Reranking

Add to your config (.opencode/codebase-index.json or global config):

{
  "reranker": {
    "enabled": true,
    "baseUrl": "https://api.cohere.ai/v1",
    "model": "rerank-v3.5",
    "apiKey": "{env:RERANK_API_KEY}",
    "topN": 20
  }
}

Reranker Options

Option Default Description
enabled false Enable reranking
baseUrl - Rerank API endpoint
model - Reranking model name
apiKey - API key (use {env:VAR} for security)
topN 20 Number of top results to rerank
timeoutMs 30000 Request timeout

How It Works

Query → Embedding Search → BM25 Search → Fusion → Reranking → Results
  1. Embedding Search: Semantic similarity via vector search
  2. BM25 Search: Keyword matching via inverted index
  3. Fusion: Combine semantic + keyword results (RRF or weighted)
  4. Reranking: Cross-encoder rescores top N results via API
  5. Results: Final ranked results

Supported Reranking APIs

Any OpenAI-compatible reranking endpoint. Examples:

  • SiliconFlow: BAAI/bge-reranker-v2-m3
  • Cohere: rerank-english-v3.0
  • Local models: Any server implementing /v1/rerank format

⚙️ Configuration

Storage Paths (OpenCode + Codex + Claude + Pi)

OpenCode default (existing behavior):

  • project config: .opencode/codebase-index.json
  • project index: .opencode/index
  • global config: ~/.config/opencode/codebase-index.json
  • global index: ~/.opencode/global-index

Codex/Pi host mode (neutral default):

  • project config: .codebase-index/config.json
  • project index: .codebase-index/index
  • global config: ~/.config/codebase-index/config.json
  • global index: ~/.codebase-index/global-index

Claude Code host mode (--host claude):

  • project config: .claude/codebase-index.json
  • project index: .claude/index
  • global config: ~/.claude/codebase-index.json
  • global index: ~/.claude/global-index

Codex, Claude Code, and Pi read legacy OpenCode paths when host-native paths are absent, so existing state continues to work.

Zero-config by default (uses auto mode). Customize in .opencode/codebase-index.json:

Full Configuration Example

{
  // === Embedding Provider ===
  "embeddingProvider": "custom",              // auto | github-copilot | openai | google | ollama | custom
  "scope": "project",                         // project (per-repo) | global (shared)

  // === Custom Embedding API (when embeddingProvider is "custom") ===
  "customProvider": {
    "baseUrl": "{env:EMBED_BASE_URL}",
    "model": "BAAI/bge-m3",
    "dimensions": 1024,
    "apiKey": "{env:EMBED_API_KEY}",
    "maxTokens": 8192,                        // Max tokens per input text
    "timeoutMs": 30000,                       // Request timeout (ms)
    "concurrency": 3,                         // Max concurrent requests
    "requestIntervalMs": 1000,                // Min delay between requests (ms)
    "maxBatchSize": 64                        // Max inputs per /embeddings request
  },

  // === File Patterns ===
  "include": [                                // Override default include patterns
    "**/*.{ts,js,py,go,rs}"
  ],
  "exclude": [                                // Override default exclude patterns
    "**/node_modules/**"
  ],
  "additionalInclude": [                      // Extend defaults (not replace)
    "**/*.{txt,html,htm}",
    "**/*.pdf"
  ],

  // === Knowledge Bases ===
  "knowledgeBases": [                         // External docs to index alongside code
    "/home/user/docs/esp-idf",
    "/home/user/docs/arduino"
  ],

  // === Indexing ===
  "indexing": {
    "autoIndex": false,                       // Auto-index on plugin load
    "watchFiles": true,                       // Re-index on file changes
    "maxFileSize": 1048576,                   // Max file size in bytes (default: 1MB)
    "maxChunksPerFile": 100,                  // Max chunks per file
    "semanticOnly": false,                    // Only index functions/classes (skip blocks)
    "retries": 3,                             // Embedding API retry attempts
    "retryDelayMs": 1000,                     // Delay between retries (ms)
    "autoGc": true,                           // Auto garbage collection
    "gcIntervalDays": 7,                      // GC interval (days)
    "gcOrphanThreshold": 100,                 // GC trigger threshold
    "requireProjectMarker": true,             // Require .git/package.json to index
    "maxDepth": 5,                            // Max directory depth (-1=unlimited, 0=root only)
    "maxFilesPerDirectory": 100,              // Max files per directory (smallest first)
    "fallbackToTextOnMaxChunks": true         // Fallback to text chunking on maxChunksPerFile
  },

  // === Search ===
  "search": {
    "maxResults": 20,                         // Max results to return
    "minScore": 0.1,                          // Min similarity score (0-1)
    "hybridWeight": 0.5,                      // Keyword (1.0) vs semantic (0.0)
    "fusionStrategy": "rrf",                  // rrf | weighted
    "rrfK": 60,                               // RRF smoothing constant
    "rerankTopN": 20,                         // Deterministic rerank depth
    "contextLines": 0,                        // Extra lines before/after match
    "routingHints": true,                     // Runtime nudges for local discovery/definition queries
    "routingGraphHandoffHints": false,        // Add opt-in graph/OMO CodeGraph handoff wording
    "routingHintRole": "system"              // system | developer (message role used for hints)
  },
  "reranker": {
    "enabled": false,
    "provider": "cohere",
    "model": "rerank-v3.5",
    "apiKey": "{env:RERANK_API_KEY}",
    "topN": 15,
    "timeoutMs": 10000
  },
  "debug": {
    "enabled": false,                         // Enable debug logging
    "logLevel": "info",                       // error | warn | info | debug
    "logSearch": true,                        // Log search operations
    "logEmbedding": true,                     // Log embedding API calls
    "logCache": true,                         // Log cache hits/misses
    "logGc": true,                            // Log garbage collection
    "logBranch": true,                        // Log branch detection
    "metrics": false                          // Enable metrics collection
  }
}

String values in codebase-index.json can reference environment variables with {env:VAR_NAME} when the placeholder is the entire string value. Variable names must match [A-Z_][A-Z0-9_]*. This is useful for secrets such as custom provider API keys so they do not need to be committed to the config file.

{
  "embeddingProvider": "custom",
  "customProvider": {
    "baseUrl": "{env:EMBED_BASE_URL}",
    "model": "nomic-embed-text",
    "dimensions": 768,
    "apiKey": "{env:EMBED_API_KEY}"
  }
}

Options Reference

Option Default Description
embeddingProvider "auto" Which AI to use: auto, github-copilot, openai, google, ollama, custom
scope "project" project = index per repo, global = shared index across repos
include (defaults) Override the default include patterns (replaces defaults)
exclude (defaults) Override the default exclude patterns (replaces defaults)
additionalInclude [] Additional file patterns to include (extends defaults, e.g. "**/*.txt", "**/*.html")
knowledgeBases [] External directories to index as knowledge bases (absolute or relative paths)
indexing
autoIndex false Automatically index on plugin load
watchFiles true Re-index when files change
maxFileSize 1048576 Skip files larger than this (bytes). Default: 1MB
maxChunksPerFile 100 Maximum chunks to index per file (controls token costs for large files)
semanticOnly false When true, only index semantic nodes (functions, classes) and skip generic blocks
retries 3 Number of retry attempts for failed embedding API calls
retryDelayMs 1000 Delay between retries in milliseconds
autoGc true Automatically run garbage collection to remove orphaned embeddings/chunks
gcIntervalDays 7 Run GC on initialization if last GC was more than N days ago
gcOrphanThreshold 100 Run GC after indexing if orphan count exceeds this threshold
requireProjectMarker true Require a project marker (.git, package.json, etc.) to enable file watching and auto-indexing. Prevents accidentally indexing large directories like home. Set to false to index any directory.
maxDepth 5 Max directory traversal depth. -1 = unlimited, 0 = only files in root dir, 1 = one level of subdirectories, etc.
maxFilesPerDirectory 100 Max files to index per directory. Always picks the smallest files first.
fallbackToTextOnMaxChunks true When a file exceeds maxChunksPerFile, fallback to text-based (line-by-line) chunking instead of skipping the rest of the file.
gitBlame.enabled false Annotate changed chunks with git blame commit SHA, author, author email, commit timestamp, and summary. Enables blameAuthor, blameSha, and blameSince filters.
search
maxResults 20 Maximum results to return
minScore 0.1 Minimum similarity score (0-1). Lower = more results
hybridWeight 0.5 Balance between keyword (1.0) and semantic (0.0) search
fusionStrategy "rrf" Hybrid fusion mode: "rrf" (rank-based reciprocal rank fusion) or "weighted" (legacy score blending fallback)
rrfK 60 RRF smoothing constant. Higher values flatten rank impact, lower values prioritize top-ranked candidates more strongly
rerankTopN 20 Deterministic rerank depth cap. Applies lightweight name/path/chunk-type rerank to top-N only
contextLines 0 Extra lines to include before/after each match
routingHints true Inject lightweight runtime hints for local conceptual discovery and definition lookups. Set to false to disable plugin-side routing nudges.
routingGraphHandoffHints false When true, conceptual discovery hints also say to use graph tools (including OMO CodeGraph) after semantic discovery identifies relevant symbols.
routingHintRole "system" Message role used when injecting routing hints: "system" (default) or "developer".
reranker Optional second-stage model reranker for the top candidate pool
enabled false Turn external reranking on/off
provider "custom" Hosted shortcuts: cohere, jina, or custom
model Reranker model name required when enabled
baseUrl provider default Override reranker endpoint base URL. coherehttps://api.cohere.ai/v1, jinahttps://api.jina.ai/v1
apiKey API key for hosted reranker providers
topN 15 Number of top candidates to send to the external reranker
timeoutMs 10000 Timeout for external rerank requests
debug
enabled false Enable debug logging and metrics collection
logLevel "info" Log level: error, warn, info, debug
logSearch true Log search operations with timing breakdown
logEmbedding true Log embedding API calls (success, error, rate-limit)
logCache true Log cache hits and misses
logGc true Log garbage collection operations
logBranch true Log branch detection and switches
metrics false Enable metrics collection (indexing stats, search timing, cache performance)

Recovery warnings in debug logs

When debug logging is enabled, the indexer now emits warn-level recovery messages if persisted cache state cannot be read safely.

  • Corrupted or unreadable file-hashes.json causes the in-memory file hash cache to be reset.
  • Corrupted or unreadable failed-batches.json causes persisted retry batches to be skipped for that run.

These warnings improve observability but do not change the recovery behavior: the indexer still falls back to a safe reset/skip path instead of crashing. If these warnings recur, remove the affected file under .opencode/index/ (or the global index directory) and rebuild with /index force.

Retrieval ranking behavior

  • codebase_search and codebase_peek use the hybrid path: semantic + keyword retrieval → fusion (fusionStrategy) → deterministic rerank (rerankTopN) → optional external reranker (reranker) → filtering.
  • 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.routingGraphHandoffHints to true to add opt-in graph/OMO CodeGraph handoff wording, and set search.routingHintRole to "developer" if your client/runtime expects developer-role guidance instead of system-role guidance.
  • find_similar stays semantic-only: semantic retrieval + deterministic rerank only (no keyword retrieval, no RRF).
  • For compatibility rollbacks, set search.fusionStrategy to "weighted" to use the legacy weighted fusion path.
  • 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.
  • Retrieval benchmark artifacts are separated by role:
    • baseline (versioned): benchmarks/baselines/retrieval-baseline.json
    • latest candidate run (generated): benchmark-results/retrieval-candidate.json

📏 Evaluation Harness

This repository includes a first-class eval system for retrieval quality with versioned golden sets, compare mode, parameter sweeps, CI budgets, and run artifacts.

Commands

npm run eval
npm run eval:ci
npm run eval:ci:ollama
npm run eval:compare -- --against benchmarks/baselines/eval-baseline-summary.json

CI usage split:

  • npm run eval:smoke: harness smoke check with local mock embeddings (used in main CI)
  • npm run eval:ci: real quality gate against baseline/budget (for scheduled/manual quality workflow)

For eval-quality.yml, the default CI path uses GitHub Models with the workflow GITHUB_TOKEN plus models: read, so you do not need a separate OpenAI API key just to run the scheduled gate.

That default GitHub Models path uses benchmarks/budgets/github-models.json, which applies stable absolute thresholds instead of the stricter baseline-regression budget used for explicit external providers.

Optional override secrets for another OpenAI-compatible endpoint:

  • EVAL_EMBED_BASE_URL
  • EVAL_EMBED_API_KEY
  • EVAL_EMBED_MODEL (optional, default text-embedding-3-small)
  • EVAL_EMBED_DIMENSIONS (optional, default 1536)

If you override the provider, set both EVAL_EMBED_BASE_URL and EVAL_EMBED_API_KEY. Otherwise the workflow falls back to GitHub Models automatically. Override providers continue to use the baseline-driven budget in benchmarks/budgets/default.json.

No OpenAI API access? Use Ollama quality gate locally:

  • Config: .github/eval-ollama-config.json
  • Script: npm run eval:ci:ollama

Prerequisites: Ollama installed, ollama serve running on 127.0.0.1:11434, and nomic-embed-text pulled.

Examples:

# Run against small golden set
npm run eval -- --dataset benchmarks/golden/small.json

# Compare against baseline
npm run eval:compare -- --against benchmarks/baselines/eval-baseline-summary.json --dataset benchmarks/golden/medium.json

# Sweep retrieval parameters
npm run eval -- --dataset benchmarks/golden/small.json --sweepFusionStrategy rrf,weighted --sweepHybridWeight 0.3,0.5,0.7 --sweepRrfK 30,60 --sweepRerankTopN 10,20

What it reports

  • Hit@1, Hit@3, Hit@5, Hit@10
  • MRR@10, nDCG@10
  • Latency p50/p95/p99
  • Token estimates, embedding call counts, estimated embedding cost
  • Failure buckets (wrong-file, wrong-symbol, docs-tests-outranking-source, no-relevant-hit-top-k)

Artifacts

Each run writes:

benchmarks/results/<timestamp>/

  • summary.json
  • summary.md
  • per-query.json
  • compare.json (when baseline/sweep used)

Golden sets and budgets

  • Golden datasets:
    • benchmarks/golden/small.json
    • benchmarks/golden/medium.json
    • benchmarks/golden/large.json
  • CI budgets:
    • benchmarks/budgets/github-models.json for the default GitHub Models workflow path
    • benchmarks/budgets/default.json for explicit external provider overrides with baseline comparison

Full docs: docs/evaluation.md

Cross-repo benchmark results snapshot

Recent representative runs (plugin vs ripgrep vs ast-grep) on two medium repos:

Methodology for the snapshot below:

  • Dataset: auto-generated cross-repo golden sets for axios + express
  • Repeats: 20 per mode
  • Aggregation: median metric per tool (then averaged across repos)
  • Reindex behavior: when enabled, index reset applies on repeat #1 only; subsequent repeats measure warm-index query behavior
  • Sampling note: repository parsing can be capped; benchmark reports include truncation metadata
  • ast-grep scope note: sg metrics are computed on its compatible query subset (definition, keyword-heavy) with scoped denominators shown in run reports

Without reindex (--no-reindex, default)

Metric Plugin ripgrep ast-grep (5/10 queries)
Hit@5 50% 5% 100%
MRR@10 0.48 0.04 0.90
nDCG@10 0.48 0.08 0.93
Latency p50 (ms) 17.5 36.9 66.6
Latency p95 (ms) 30.9 44.1 70.7

With reindex (--reindex)

Metric Plugin ripgrep ast-grep (5/10 queries)
Hit@5 50% 5% 100%
MRR@10 0.48 0.04 0.98
nDCG@10 0.48 0.07 0.98
Latency p50 (ms) 17.1 35.9 69.1
Latency p95 (ms) 30.4 43.7 75.1

ast-grep metrics are computed on its compatible query subset only (definition + keyword-heavy, 5/10 queries per repo). Plugin and ripgrep are scored on all 10 queries.

Interpretation:

  • ast-grep dominates on its scoped subset (structural definition queries), but only handles 50% of query types. Plugin handles all query types including natural language.
  • Plugin leads on rank-sensitive quality (MRR/nDCG) vs ripgrep across all query types.
  • ripgrep remains a useful speed-oriented lexical baseline but has significantly lower retrieval relevance for intent-style queries.
  • Plugin is the fastest tool at p50 (~17ms), ahead of ripgrep (~36ms) and ast-grep (~67ms).
  • Reported numbers are rounded to avoid false precision; use report artifacts for full per-repeat audit trails.

For reproducible setup and commands (including with/without reindex), see:

  • docs/benchmarking-cross-repo.md

Embedding Providers

The plugin automatically detects available credentials in this order:

  1. GitHub Copilot (Free if you have it)
  2. OpenAI (Standard Embeddings)
  3. Google (Gemini Embeddings)
  4. Ollama (Local/Private - requires nomic-embed-text)

You can also use Custom to connect any OpenAI-compatible embedding endpoint (llama.cpp, vLLM, text-embeddings-inference, LiteLLM, etc.).

Rate Limits by Provider

Each provider has different rate limits. The plugin automatically adjusts concurrency and delays:

Provider Concurrency Delay Best For
GitHub Copilot 1 4s Small codebases (<1k files)
OpenAI 3 500ms Medium codebases
Google 5 200ms Medium-large codebases
Ollama 5 None Large codebases (10k+ files)
Custom 3 1s Any OpenAI-compatible endpoint

For large codebases, use Ollama locally to avoid rate limits:

# Install the embedding model
ollama pull nomic-embed-text
// .opencode/codebase-index.json
{
  "embeddingProvider": "ollama"
}

The built-in ollama provider uses Ollama's native /api/embeddings endpoint and is the simplest setup when you want to use nomic-embed-text.

For the built-in Ollama path, the plugin budgets nomic-embed-text against an observed effective input limit of about 2048 tokens, not the model's higher advertised theoretical context. This keeps batching and chunk text generation aligned with real Ollama embedding runtime behavior.

If you want to use a different Ollama embedding model through its OpenAI-compatible API, use the custom provider instead and set customProvider.baseUrl to http://127.0.0.1:11434/v1 so the plugin calls .../v1/embeddings.

📈 Performance

The plugin is built for speed with a Rust native module (tree-sitter, usearch, SQLite). In practice, indexing and retrieval remain fast enough for interactive use on medium/large repositories.

  • Typical query latency: ~800-1000ms (mostly embedding API time)
  • Incremental indexing: only changed files are re-embedded
  • Batch DB operations: significant write-speed improvements for large indexes

For reproducible measurements on your machine, run: npx tsx benchmarks/run.ts.

🎯 Choosing a Provider

Quick recommendation:

  • Want local + private + fast indexing → use Ollama
  • Already have Copilot and a smaller repo → use GitHub Copilot
  • General cloud setup → use OpenAI or Google
  • Custom/OpenAI-compatible endpoint → use custom provider

Provider Comparison

Provider Speed Cost Privacy Best For
Ollama Fastest Free Full Large codebases, privacy-sensitive
GitHub Copilot Slow (rate limited) Free* Cloud Small codebases, existing subscribers
OpenAI Medium ~$0.0001/1K tokens Cloud General use
Google Fast Free tier available Cloud Medium-large codebases
Custom Varies Varies Varies Self-hosted or third-party endpoints

*Requires active Copilot subscription

Setup by Provider

Set the provider in .opencode/codebase-index.json:

{ "embeddingProvider": "ollama" }

Credentials (if required) are read from environment variables (for example OPENAI_API_KEY or GOOGLE_API_KEY).

Custom (OpenAI-compatible) Works with any server that implements the OpenAI /v1/embeddings API format (llama.cpp, vLLM, text-embeddings-inference, LiteLLM, etc.).

{
  "embeddingProvider": "custom",
  "customProvider": {
    "baseUrl": "{env:EMBED_BASE_URL}",
    "model": "nomic-embed-text",
    "dimensions": 768,
    "apiKey": "{env:EMBED_API_KEY}",
    "maxTokens": 8192,
    "timeoutMs": 30000,
    "maxBatchSize": 64
  }
}

Required fields: baseUrl, model, dimensions (positive integer). Optional: apiKey, maxTokens, timeoutMs (default: 30000), maxBatchSize (or max_batch_size) to cap inputs per /embeddings request for servers like text-embeddings-inference. {env:VAR_NAME} placeholders are resolved before config validation for fields that are actually used and throw if the referenced environment variable is missing or malformed.

Custom Ollama models via OpenAI-compatible API If you are running Ollama locally and want to use an embedding model other than the built-in ollama setup, point the custom provider at Ollama's OpenAI-compatible base URL with the /v1 suffix:

{
  "embeddingProvider": "custom",
  "customProvider": {
    "baseUrl": "http://127.0.0.1:11434/v1",
    "model": "qwen3-embedding:0.6b",
    "dimensions": 1024,
    "apiKey": "ollama"
  }
}

Notes:

  • The plugin appends /embeddings, so baseUrl should be http://127.0.0.1:11434/v1, not just http://127.0.0.1:11434.
  • Ollama ignores the API key, but some OpenAI-compatible clients expect one, so a placeholder like "ollama" is fine.
  • Make sure dimensions matches the actual output size of the model you pulled locally.

⚠️ Tradeoffs

Be aware of these characteristics:

Aspect Reality
Search latency ~800-1000ms per query (embedding API call)
First index Takes time depending on codebase size (e.g., ~30s for 500 chunks)
Requires API Needs an embedding provider (Copilot, OpenAI, Google, or local Ollama)
Token costs Uses embedding tokens (free with Copilot, minimal with others)
Best for Discovery and exploration, not exhaustive matching

💻 Local Development

  1. Build:

    npm run build
    
  2. Register in Test Project (use file:// URL in opencode.json):

    {
      "plugin": [
        "file:///path/to/opencode-codebase-index"
      ]
    }
    

    This loads directly from your source directory, so changes take effect after rebuilding.

🤝 Contributing

For contribution workflow, standards, and release-label requirements, see CONTRIBUTING.md.

If you want to add support for a new language, see docs/adding-language-support.md for the full Rust + TypeScript checklist.

Quick path:

  1. Fork + branch
  2. Implement + tests
  3. Run checks: npm run build && npm run typecheck && npm run lint && npm run test:run
  4. Open PR with a release category label

Release process (structured + complete notes)

To ensure release notes reflect all merged work, this repo uses a draft-release workflow.

  1. Label every PR with at least one semantic label:
    • feature, bug, performance, documentation, dependencies, refactor, test, chore
    • and (when relevant) semver:major, semver:minor, or semver:patch
    • PRs are validated by CI (Release Label Check) and fail if no release category label is present
  2. Let Release Drafter build the draft notes automatically from merged PRs on main.
  3. Before publishing:
    • compare git log --oneline vX.Y.Z..HEAD (or the previous release tag range) against the draft release notes so the release summary covers the full shipped delta, not just the current CHANGELOG.md Unreleased section
    • copy/finalize relevant highlights into CHANGELOG.md
    • bump package.json version
    • run: npm run build && npm run typecheck && npm run lint && npm run test:run
  4. Publish release from the draft (or via gh release create after reviewing draft content).

PRs labeled skip-changelog are intentionally excluded from release notes.

Project Structure

├── src/
│   ├── index.ts              # Plugin entry point
│   ├── mcp-server.ts         # MCP server (Cursor, Claude Code, Windsurf)
│   ├── cli.ts                # CLI entry for MCP stdio transport
│   ├── config/               # Configuration schema
│   ├── embeddings/           # Provider detection and API calls
│   ├── indexer/              # Core indexing logic + inverted index
│   ├── git/                  # Git utilities (branch detection)
│   ├── tools/                # OpenCode tool definitions
│   ├── utils/                # File collection, cost estimation
│   ├── native/               # Rust native module wrapper
│   └── watcher/              # File/git change watcher
├── native/
│   └── src/                  # Rust: tree-sitter, usearch, xxhash, SQLite
├── tests/                    # Unit tests (vitest)
├── commands/                 # Slash command definitions
├── skill/                    # Agent skill guidance
└── .github/workflows/        # CI/CD (test, build, publish)

Native Module

The Rust native module handles performance-critical operations:

  • tree-sitter: Language-aware code parsing with JSDoc/docstring extraction
  • usearch: High-performance vector similarity search with F16 quantization
  • SQLite: Persistent storage for embeddings, chunks, branch catalog, symbols, and call edges
  • BM25 inverted index: Fast keyword search for hybrid retrieval
  • Call graph extraction: Tree-sitter query-based extraction of function calls, method calls, constructors, and imports (TypeScript/JavaScript, Python, Go, Rust, PHP, Apex, Zig, GDScript, MATLAB, Bash)
  • xxhash: Fast content hashing for change detection

Rebuild with: npm run build:native (requires Rust toolchain)

Platform Support

Pre-built native binaries are published for:

Platform Architecture SIMD Acceleration
macOS x86_64 ✅ simsimd
macOS ARM64 (Apple Silicon) ✅ simsimd
Linux x86_64 (GNU) ✅ simsimd
Linux ARM64 (GNU) ✅ simsimd
Windows x86_64 (MSVC) ❌ scalar fallback

Windows builds use scalar distance functions instead of SIMD — functionally identical, marginally slower for very large indexes. This is due to MSVC lacking support for certain AVX-512 intrinsics used by simsimd.

License

MIT