Semantic code search plugin for Claude Code using hybrid vector search + BM25. 98% accuracy, 5x faster than grep.
> /plugin marketplace add sagarmk/beacon-plugin> /plugin install beacon@claude-code-beacon-plugin
Repo: sagarmk/beacon-plugin
What's inside
brew install ollama
ollama serve &
ollama pull qwen3-embedding:0.6b
claude plugin marketplace add sagarmk/beacon-plugin
claude plugin install beacon@beacon-plugin
claude
That's it. On first session start, Beacon will:
better-sqlite3 — takes a few seconds)No npm install, no manual setup. Just install and go.
After installing, Beacon indexes automatically on session start. Here's the essentials:
> /reindex
Deletes existing embeddings and rebuilds from scratch — useful after switching models or if the index gets stale.
> /index
Beacon Index
● ● ● ● ● qwen3-embedding:0.6b · Ollama (local)
● ● ● ● ● 1024 dims · 3.8 MB
● ● ● ● ●
● ● ● ● ● Coverage: 100% (38/38 files)
Indexed by extension
● .js 25 files
● .md 13 files
Statistics
Indexed files 38
Total chunks 109
Avg chunks/file 2.9
Last sync 2 minutes ago
For a quick numeric summary:
> /index-status
{
"files_indexed": 38,
"total_chunks": 114,
"last_sync": "2026-03-01T04:30:21.453Z",
"embedding_model": "qwen3-embedding:0.6b",
"embedding_endpoint": "http://localhost:11434/v1"
}
> /search-code "authentication flow"
[
{
"file": "src/middleware/auth.ts",
"lines": "12-45",
"similarity": "0.82",
"score": "0.74",
"preview": "export async function verifyAuth(req, res, next) {\n const token = req.headers.authorization?.split(' ')[1];\n ..."
},
{
"file": "src/routes/login.ts",
"lines": "8-32",
"similarity": "0.78",
"score": "0.65",
"preview": "router.post('/login', async (req, res) => {\n const { email, password } = req.body;\n ..."
}
]
Hybrid search combines semantic similarity (understands meaning), BM25 keyword matching, identifier boosting, and a symbol reference graph — so searching "auth flow" finds code about authentication even if it never uses the word "auth".
Options: --top-k N (results count), --threshold F (min score), --path <dir> (scope to directory), --no-hybrid (pure vector search).
Beacon ships an MCP server, so Claude picks the right tool for the question instead of grepping and hoping. Three of these never run the embedding model at all — they are indexed lookups over the symbol tables.
| Tool | For | Cost |
|---|---|---|
find_symbol(name) | Where a symbol is defined | ~0.01 ms, exact |
find_references(name) | Every call site — exhaustive, not a ranked sample | ~0.01 ms |
outline(file) | A file's symbols with line numbers | ~0.01 ms |
search_code(query) | Code matching a description you can't name | ~200 ms, ranked |
index_status() | Index health, when results look stale | ~0.01 ms |
find_references is the one worth knowing about: it is exhaustive over indexed
files, so it is the only correct tool for "what breaks if I change this".
Ranked search returns a sample and will quietly miss call sites.
search_code also takes mode: "lexical" (keyword only, skips the embedding
model entirely) and mode: "semantic" (embeddings only).
Grep is still the right tool for literal text, regex, case-sensitive
matches, and anything outside the index — node_modules, dist, lockfiles,
.env. Beacon leaves those alone.
The symbol graph exists because embeddings are blind to structure. A function with no doc comment has almost no natural-language surface, so no phrasing reaches it — but whatever calls it usually is retrievable, one reference edge away. Personalized PageRank seeded on the current best matches walks that edge.
Measured on this repo, 12 natural-language queries with no lexical overlap with their targets:
| top-1 | MRR | |
|---|---|---|
| Vector only | 5/12 | 0.552 |
| Hybrid, no graph | 7/12 | 0.722 |
| Hybrid + symbol graph | 9/12 | 0.813 |
Two caveats worth stating plainly:
.4 / .3 / .3 split is the only
configuration that holds up on both.Run search.js with search.hybrid.debug: true in .claude/beacon.json to see
every scoring component per result.
Beacon runs on open-source models by default — no API keys, no cloud costs, fully local via Ollama.
| Model | Dims | Speed (M2, measured) | Best for |
|---|---|---|---|
| qwen3-embedding:0.6b (default) | 1024 | 24 chunks/s | Strong code retrieval, instruction-aware queries |
| nomic-embed-text | 768 | 43 chunks/s | Fastest indexing; set dimensions: 768 |
| qwen3-embedding:4b | 1024 | 4 chunks/s | Highest quality, ~10x slower to index |
| all-minilm | 384 | very fast | Lightweight, low resource usage |
Throughput measured warm on an M2/16GB — a cold model load makes the first run look far slower than steady state.
Changing model or dimensions invalidates the index. Beacon detects the
mismatch, refuses to search rather than returning nonsense, and tells you to run
/reindex.
To switch models, pull with Ollama and update your config:
ollama pull mxbai-embed-large
// .claude/beacon.json
{
"embedding": {
"model": "mxbai-embed-large",
"dimensions": 1024,
"query_prefix": ""
}
}
Then run /reindex to rebuild with the new model.
For cloud-hosted embeddings, create .claude/beacon.json in your repo:
export OPENAI_API_KEY="sk-..."
{
"embedding": {
"api_base": "https://api.openai.com/v1",
"model": "text-embedding-3-small",
"api_key_env": "OPENAI_API_KEY",
"dimensions": 1536,
"batch_size": 100,
"query_prefix": ""
}
}
export VOYAGE_API_KEY="pa-..."
{
"embedding": {
"api_base": "https://api.voyageai.com/v1",
"model": "voyage-code-3",
"api_key_env": "VOYAGE_API_KEY",
"dimensions": 1024,
"batch_size": 50,
"query_prefix": ""
}
}
pip install litellm
litellm --model vertex_ai/text-embedding-004 --port 4000
{
"embedding": {
"api_base": "http://localhost:4000/v1",
"model": "vertex_ai/text-embedding-004",
"api_key_env": "LITELLM_API_KEY",
"dimensions": 1024,
"batch_size": 50,
"query_prefix": ""
}
}
Any server implementing the OpenAI /v1/embeddings API will work. Set api_base, model, dimensions, and optionally api_key_env in .claude/beacon.json.
Beacon indexes your codebase automatically on session start and re-embeds files as you edit — no manual steps needed.
| Command | Description |
|---|---|
/search-code | Hybrid code search — semantic + keyword + BM25 matching. Supports --path <dir> to scope results |
| Command | Description |
|---|---|
/index | Visual overview — files, chunks, coverage, provider |
/index-status | Quick health check — file count, chunk count, last sync |
/reindex | Force full re-index from scratch |
/run-indexer | Manually trigger indexing |
/terminate-indexer | Kill a running sync process |
| Command | Description |
|---|---|
/config | View and modify Beacon configuration |
/blacklist | Prevent indexing of specific directories |
/whitelist | Allow indexing in otherwise-blacklisted directories |
Beacon also provides a code-explorer agent and a semantic-search skill that Claude can invoke automatically.
lib/auth.ts, not every file containing "auth"Beacon uses Claude Code hooks to stay in sync with your codebase:
| Hook | Trigger | What it does |
|---|---|---|
| SessionStart | Every session | Ensures npm deps are installed (first run only), then full index or diff-based catch-up |
| PostToolUse | Write, Edit, MultiEdit | Re-embeds the changed file |
| PostToolUse | Bash | Garbage collects embeddings for deleted files |
| PreCompact | Before context compaction | Injects index status so search capability survives compaction |
| PreToolUse | Grep, Bash | Redirects a codebase search to the matching Beacon tool. Covers shell grep/rg too, since that is what agents actually run. Skips regex, literals, and anything outside the index. |
Default configuration (config/beacon.default.json):
FAQ
beacon is a Claude Code plugin with 1 hand-picked skill for development work, indexed on Flowy. Install it with the command on its page. It includes semantic-search. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.
Is this plugin yours?
Claim it with GitHubSubmit a pluginPromote it