edhindex
v3.4.0
Published
Local-first hybrid code search engine — 42 adapters, BM25 + vector + rerank, binary doc/image indexing, lazy background mode, MCP server
Maintainers
Readme
EDHIndex
Local-first hybrid code search engine with Knowledge Graph — zero telemetry, fully offline.
npm install -g edhindex
# or
npx edhindex <command>Quick start
cd ~/your-project
edhindex init # initialize index (pick model, index mode + MCP client)
edhindex start # build index + start MCP server + watch for changesThen ask any MCP client (OpenCode, Claude Code, Cline, Cursor, etc.) to search your codebase.
Supported file types
EDHIndex uses a pluggable Language Adapter architecture. Every language has its own adapter file. To add a new language, just create one adapter — no changes to the core engine.
42 adapters supporting 100+ file extensions — from programming languages to config files, documentation, documents, images, data formats, and infrastructure.
Tier 1 — Full symbol extraction (18 adapters)
| Language | Key | Extensions | Symbols indexed |
|----------|-----|-----------|----------------|
| TypeScript | ts | .ts, .tsx, .mts, .cts | functions, methods, classes, interfaces, enums |
| JavaScript | js | .js, .jsx, .mjs, .cjs | functions, methods, classes |
| Python | py | .py, .pyw | functions, classes |
| Go | go | .go | functions, methods |
| Rust | rs | .rs | functions, structs, traits, enums |
| Java | java | .java | methods, classes, interfaces, enums |
| Ruby | rb | .rb | methods, classes, modules |
| C | c | .c, .h | functions, structs |
| C++ | cpp | .cpp, .cxx, .cc, .c++, .hpp, .hxx, .hh | functions, classes, structs, enums |
| C# | csharp | .cs | methods, classes, interfaces, structs, enums |
| PHP | php | .php | functions, methods, classes, interfaces |
| Swift | swift | .swift | classes, structs, enums, protocols, extensions, actors, functions, methods |
| Kotlin | kotlin | .kt, .kts | classes, interfaces, objects, enums, functions, properties |
| Dart | dart | .dart | classes, mixins, extensions, enums, functions, methods |
| Scala | scala | .scala, .sc | classes, traits, objects, enums, functions, methods |
| Haskell | haskell | .hs, .lhs | data types, type classes, instances, functions |
| Solidity | solidity | .sol | contracts, interfaces, libraries, structs, enums, functions |
| Lua | lua | .lua | functions, methods, tables, variables |
Note: Tier 1 adapters use tree-sitter WASM for full AST parsing. In runtimes where WASM loading fails, they gracefully fall back to paragraph-based text indexing so every file still gets indexed.
Tier 2 — Structural support (24 adapters)
| Language | Key | Extensions | What gets extracted |
|----------|-----|-----------|-------------------|
| HTML | html | .html, .htm | significant elements (section, nav, header, etc.) |
| CSS | css | .css, .scss, .less | rule selectors, variables, media queries |
| JSON | json | .json | object keys, nested structures |
| JSON Variants | json-variants | .jsonc, .json5 | object keys, comments, nested structures |
| YAML | yaml | .yaml, .yml | mappings, sequences, keys, anchors |
| TOML | toml | .toml | tables, arrays, keys, values |
| Markdown | markdown | .md, .markdown | headings, paragraphs, blockquotes, tables, lists, code blocks, frontmatter |
| SQL | sql | .sql | tables, views, indexes, functions, foreign keys |
| Vue | vue | .vue | components, template, script, style sections |
| Svelte | svelte | .svelte | components, script, style, Svelte blocks |
| Astro | astro | .astro | frontmatter, components, style, script |
| Terraform | terraform | .tf, .tfvars | blocks, attributes, resources, variables |
| Dockerfile | dockerfile | Dockerfile* | FROM, RUN, CMD, COPY, ENV, EXPOSE |
| Salesforce | salesforce | .cls, .trigger, .page, .component, etc. | classes, triggers, components, metadata |
| CSV | csv | .csv | headers, columns, data structure |
| Config | config | .ini, .properties, .env, .conf, .cfg, .gitignore, .editorconfig | sections, key-value pairs |
| Documentation | documentation | .rst, .adoc | sections, headings, directives, code blocks |
| Documents | document | .pdf, .docx, .xlsx, .pptx | text extraction — paragraphs, sections, tables, sheets, slides |
| SVG | svg | .svg | elements, IDs, classes, paths |
| Images | image | .png, .jpg, .jpeg, .gif, .ico, .webp, .bmp, .tiff | metadata (format, dimensions, size) |
| Text | text | .txt, .log | sections, paragraphs, log entries |
Generic fallback
Any file with an unrecognized (non-binary) extension goes through the GenericAdapter — splits content by paragraphs, indexes as plain text. Never crashes.
What gets skipped
- Binary/archive/media files (
.exe,.dll,.so,.zip,.tar,.mp3,.mp4, etc.) - Files larger than 10 MB
- Generated/minified files (
*.min.js,*.min.css,*.generated.ts,*.g.ts,package.json,tsconfig.json, lockfiles) - macOS system files (
.DS_Store) - Language artifacts (
.pyc,.class,.jar)
.edhindexignore
Add a .edhindexignore file at your project root (created automatically on edhindex init) with gitignore-compatible glob patterns to skip additional files. Supports # comments and ! negation.
# ignore generated code
src/generated/
*.log.gitignore
edhindex init automatically adds .edhindex/ to your project's .gitignore (or creates one if none exists).
Indexing modes
EDHIndex supports two indexing modes, chosen during edhindex init (or via edhindex config indexMode):
Lazy (default — Recommended)
- Server starts immediately — no waiting for indexing to complete
- Files are indexed in small batches (3 files per batch, 500ms delay) in the background
- Already-indexed files are skipped — only new/changed files get processed
- Keeps the system responsive — never blocks CPU/IO
Full
- Indexes everything at once before starting the server
- Use when you want the full index available immediately after startup
Switch anytime:
edhindex config indexMode lazy
edhindex config indexMode fullLive file watcher
When edhindex start runs (and watch: true in config, which is the default), chokidar watches the filesystem. On every save — removes old chunks for the file, re-parses, re-indexes. Debounced at 300ms.
Search pipeline
User query
↓
┌────────────────┐
│ Keyword (BM25) │── SQLite FTS5 → top 30
└────────────────┘
┌────────────────┐
│ Vector (ANN) │── transformers.js → LanceDB → top 30
└────────────────┘
↓
┌────────────────┐
│ Deduplicate │── max score for duplicates
└────────────────┘
↓
┌────────────────┐
│ Reranker │── cross-encoder → final top N
└────────────────┘
↓
Results with matchType: keyword | vector | hybrid- Keyword: SQLite FTS5 with BM25 scoring (porter + unicode61 tokenizer)
- Semantic:
@huggingface/transformersembeddings → LanceDB ANN search - Hybrid: top 30 BM25 + top 30 vectors → deduplicate → rerank → top 10
- Reranker:
Xenova/ms-marco-MiniLM-L-6-v2cross-encoder (configurable viaedhindex config)
Embedding model tiers
| Tier | Model | Dimensions | Download size |
|------|-------|-----------|--------------|
| fast (default) | GTE-Small | 384 | ~50 MB |
| balanced | bge-base-en-v1.5 | 768 | ~109 MB |
| max | BGE-M3 | 1024 | ~1.1 GB |
Switch with edhindex config model <tier>.
Error logging
All indexing errors and per-file status are written to .edhindex/edhindex.log — not printed to console during indexing. Each file gets a trace:
[2026-07-28T06:18:00.571Z] [INDEXED] Sample.java
[2026-07-28T06:18:09.713Z] [FAILED] sample.dart
Error: ... (stack trace)
...
[2026-07-28T06:18:32.088Z] [INFO] Index summary: 80 files indexed, 11 failed, 2919 chunks totalKnowledge Graph
Every symbol becomes a node. Imports, exports, and hierarchy become edges. Four graph modes (switch in the UI):
| View | Description | |------|-------------| | Force | Physics-based node layout | | Hierarchy | Directory tree structure | | Circle | Circular arrangement | | Grid | Grid layout |
Commands
edhindex kg --rebuild Build the knowledge graph from the index
edhindex kg --stats Node/edge counts by type
edhindex kg --serve Open interactive browser at localhost
edhindex kg --write Write standalone HTML → .edhindex/knowledge-graph.htmlBuilt on tree-sitter + SQLite + Cytoscape.js — all local, no telemetry, no cloud.
Node/edge types
Node kinds: workspace, folder, file, module, class, interface, enum, struct, trait, function, method, variable, constant, property, import, export, namespace, package, constructor, extension, field, type_alias, document, section, table, selector, rule, actor, object, protocol
Edge kinds: contains, imports, exports, inherits, implements, calls, references, defines, belongs_to
Memory Graph
Persistent, queryable agent memory graph — stores entity facts, observations, decisions, tasks, questions, and notes across sessions, linked directly to code symbols in the Knowledge Graph.
Commands
edhindex memory add --label "Auth Decision" --type decision --content "Use JWT for session auth"
edhindex memory search "JWT"
edhindex memory list --type observation
edhindex memory get <id>
edhindex memory delete <id>
edhindex memory stats
edhindex memory prune
edhindex memory clear
edhindex memory export -o memories.json
edhindex memory import memories.jsonMemory Node Types
entity: modules, services, APIs, peopleobservation: performance findings, bugs, quirksdecision: architectural choices, trade-offstask: active or past goalsfact: verified codebase factsquestion: open questions or TODOsnote: general notes and context
Commands
| Command | What it does |
|---------|-------------|
| edhindex init | Initialize .edhindex/ — pick model, index mode + MCP client |
| edhindex start | Build/update index + start MCP server + file watcher |
| edhindex index | Just build/update the index (no server) |
| edhindex search <query> | CLI hybrid search |
| edhindex status | Index stats |
| edhindex config | View/update settings |
| edhindex models | List available embedding models |
| edhindex kg ... | Knowledge graph operations |
| edhindex memory ... | Persistent agent memory graph operations |
| edhindex graph | Static SVG dependency graph |
| edhindex doctor | Diagnostics |
| edhindex reset | Delete the entire .edhindex/ |
MCP Tools
When edhindex start is running, any MCP client can call:
| Tool | What it does |
|------|-------------|
| search_codebase | Hybrid search with file/language filters |
| get_graph | Full knowledge graph (nodes + edges) |
| get_node | Node details + neighbors + linked memories |
| search_graph | Search nodes by name |
| get_graph_stats | Graph statistics |
| memory_add | Record persistent memory (entity, decision, fact, etc.) |
| memory_get | Retrieve memory by ID with code links |
| memory_search | Full-text & tag search over memories |
| memory_link | Link memory to code symbol in Knowledge Graph |
| memory_update | Update memory content, tags, or confidence |
| memory_list | List memories by type/recency |
Client setup
During edhindex init, pick your MCP client and the config file is created automatically:
- OpenCode →
opencode.json - Cline →
.cline/mcp.json - Roo Code →
.roo/mcp.json - Claude Code →
claude.jsonc - Cursor →
.cursor/mcp.json - Windsurf →
.windsurf/mcp_config.json - GitHub Copilot →
.vscode/settings.json - Continue →
.continue/config.json
Requirements
- Node.js 18+
License
MIT
