indexkit
Offline hybrid retrieval: build a local semantic + lexical index over your files
and query it. Chunks and embeds a corpus through ollama,
indexes it with turbovec (a quantized
vector index), and fuses that with SQLite FTS5/BM25 for opt-in hybrid search.
No API key and no network egress by default — the embedding endpoint is
localhost. A configured remote CONTEXT_KIT_OLLAMA_HOST receives corpus chunks
and queries, so that is the one setting that sends data off the machine.
Notes-first but corpus-agnostic: loaders are pluggable, so the same engine can index Markdown notes, code, or any text corpus.
Formerly
local-rag. The old name claimed a deployment property that a supported setting falsifies, and understated an engine that also does lexical retrieval. Pre-rename environment variables are still honored.
Install
Not on PyPI yet. The package is publish-ready but the name has not been claimed. Until the release workflow runs, install from source; the
pip installline below is what will work once it ships.
# From a clone of https://github.com/mbeacom/context-kit
pip install ./plugins/indexkit # or: uv tool install ./plugins/indexkit
ollama pull nomic-embed-text # once
Once published:
pip install indexkit # or: uv tool install indexkit
Either way that is the whole setup — no plugin host, no bootstrap step. Indexes
default to ${XDG_DATA_HOME:-~/.local/share}/indexkit.
indexkit index ~/notes --name notes
indexkit query "how did we handle retry backoff" --name notes --k 8
As a context-kit plugin
Claude Code and GitHub Copilot CLI auto-bootstrap the bundled bin/indexkit
launcher from the plugin's SessionStart hook, into ${CLAUDE_PLUGIN_DATA}/venv
— for Copilot, ~/.copilot/plugin-data/<marketplace>/indexkit/venv. In that
mode the host controls where indexes live.
The launcher prefers that bootstrapped venv, then falls back to an indexkit
already on your PATH, then to any importable indexkit module. So a
pip-installed copy satisfies the plugin too, and a missing venv is not fatal.
Requirements
-
ollamarunning with an embedding model pulled:ollama pull nomic-embed-text
-
uv— only for the plugin bootstrap path. Apip installneeds nothing beyond Python 3.10+.
For APM or manual plugin usage — or on any host where the venv is missing or stale — bootstrap it yourself into a neutral data location:
export CONTEXT_KIT_DATA="$HOME/.local/share/context-kit"
bash scripts/bootstrap.sh
export PATH="$PWD/bin:$PATH"
To check readiness without installing anything:
bash scripts/bootstrap.sh --check # exit 0 ready, 3 needs bootstrap
It prints KEY=VALUE lines: status (ready, missing, stale,
uv-missing), the raw venv_status and uv availability as separate fields,
the resolved home/venv paths, and the exact bootstrap_command. A stale
venv was built from different pyproject.toml metadata and would run outdated
code, so it is reported as clearly as a missing one. Because uv is only
needed to build the venv, an already-usable venv reports ready even when
uv is absent. Dependent tooling uses this to detect an unusable runtime on
hosts that do not deploy plugin hooks, such as APM. (Claude Code and GitHub
Copilot CLI both run this plugin's SessionStart bootstrap.)
Usage
Index a corpus, then query it:
indexkit index <path> --name X
indexkit query "your question" --name X
indexkit query "exact terms and intent" --name X --hybrid
indexkit status --name X
indexkit list
indexkit remove --name X --yes
Each named index is persisted under <data-dir>/indexes/<name>/, so queries are
fast and survive across sessions. The data directory resolves in this order:
CONTEXT_KIT_DATA(or thePRODUCTIVITY_SKILLS_DATAalias)CLAUDE_PLUGIN_DATA, set by a plugin host${XDG_DATA_HOME:-~/.local/share}/indexkit— the standalone default
An existing ~/.claude/plugins/data/indexkit directory still wins over the
standalone default while that default has not been created, so upgrading a
plugin install does not orphan indexes you already built.
Index lifecycle
Index names remain backward-compatible with earlier releases: any non-empty
single path component except . or .. is accepted, including names with
spaces or more than 80 characters. Path separators (/ and \) and NUL are
rejected. These containment rules apply consistently to index, query,
status, and remove.
indexkit remove --name X --yes permanently removes one named index. The command is
non-interactive and refuses to run without --yes; missing indexes fail clearly.
Indexing, querying, status inspection, and removal share a per-index process
lock, so removal fails clearly while that index is in use. Once locked, removal
moves only the selected index out of the active namespace, then unlinks its flat
artifact files without recursive directory deletion. Other indexes are
untouched, and incomplete cleanup is reported with the quarantined artifact
location rather than silently ignored.
Portable environment variables:
| Variable | Purpose | Claude fallback |
|---|---|---|
CONTEXT_KIT_DATA |
venv and index storage | CLAUDE_PLUGIN_DATA |
CONTEXT_KIT_INDEXKIT_HOME |
venv location only, when it must differ from index storage | — (defaults to CONTEXT_KIT_DATA) |
CONTEXT_KIT_EMBED_MODEL |
ollama embedding model | CLAUDE_PLUGIN_OPTION_EMBED_MODEL |
CONTEXT_KIT_OLLAMA_HOST |
ollama base URL | CLAUDE_PLUGIN_OPTION_OLLAMA_HOST |
XDG_DATA_HOME |
relocates the standalone default data directory | — |
None of these are required for a standalone install; all have defaults.
The pre-rename CONTEXT_KIT_LOCAL_RAG_HOME is still read as a fallback for
CONTEXT_KIT_INDEXKIT_HOME.
CONTEXT_KIT_DATA normally holds both the venv and the indexes. Set
CONTEXT_KIT_INDEXKIT_HOME only when a caller needs to redirect index data
to an isolated store while still using one shared bootstrapped venv — the
memory plugin does this to keep each project's index inside its own
project-isolated provider directory. When it is unset, behavior is unchanged.
The pre-rename PRODUCTIVITY_SKILLS_* names still resolve as a deprecated alias
(CONTEXT_KIT_* → PRODUCTIVITY_SKILLS_* → Claude fallback).
Hybrid retrieval
Semantic-only retrieval remains the default. --hybrid adds SQLite FTS5 lexical
BM25 candidates and fuses them with turbovec semantic candidates using deterministic
reciprocal-rank fusion: 1.0 / (60 + semantic_rank) + 1.0 / (60 + lexical_rank).
Each source retrieves 3 × k candidates before fusion, so the candidate depth is
greater than the requested final result count. JSON results include source offsets
and per-source rank/score metadata; text output remains compact.
indexkit query also accepts an --allowlist of candidate documents (read from a file,
or - for stdin), which applies to both semantic and lexical candidates. For
example, feeding Obsidian backlinks into a hybrid query:
obsidian backlinks file="X" | indexkit query "..." --hybrid --allowlist -
FTS5 is detected and backfilled automatically for existing indexes. indexkit status
reports its fts5 capability. If the SQLite build lacks FTS5, semantic retrieval
continues to work but --hybrid exits with a clear error.
MIT © Mark Beacom.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file indexkit-0.6.1.tar.gz.
File metadata
- Download URL: indexkit-0.6.1.tar.gz
- Upload date:
- Size: 60.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2de3ee61ce540f9fbe7da7942622486ce9b662006737ad2d20fae51389f22fd8
|
|
| MD5 |
f10db9ad2b80a4d44229feee1ff808e0
|
|
| BLAKE2b-256 |
0f06ec348e7768858474c3b8a9fd82812e2412480cfa96ddc26a5e38d60a0c0b
|
Provenance
The following attestation bundles were made for indexkit-0.6.1.tar.gz:
Publisher:
release-indexkit.yml on mbeacom/context-kit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
indexkit-0.6.1.tar.gz -
Subject digest:
2de3ee61ce540f9fbe7da7942622486ce9b662006737ad2d20fae51389f22fd8 - Sigstore transparency entry: 2396508482
- Sigstore integration time:
-
Permalink:
mbeacom/context-kit@f57340d91b9a098fb1a6ce47175c21c0962911b9 -
Branch / Tag:
refs/tags/indexkit/v0.6.1 - Owner: https://github.com/mbeacom
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-indexkit.yml@f57340d91b9a098fb1a6ce47175c21c0962911b9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file indexkit-0.6.1-py3-none-any.whl.
File metadata
- Download URL: indexkit-0.6.1-py3-none-any.whl
- Upload date:
- Size: 21.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dcf532842b5105ec563d5604b77961310c71f1ee0b286d3d83810198abd8fad9
|
|
| MD5 |
afaf43fabfed21ece07d7abe61151397
|
|
| BLAKE2b-256 |
bdc221af8c785da815de08db226a4c89d501c7a6001cf000011e23940f5cef49
|
Provenance
The following attestation bundles were made for indexkit-0.6.1-py3-none-any.whl:
Publisher:
release-indexkit.yml on mbeacom/context-kit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
indexkit-0.6.1-py3-none-any.whl -
Subject digest:
dcf532842b5105ec563d5604b77961310c71f1ee0b286d3d83810198abd8fad9 - Sigstore transparency entry: 2396508780
- Sigstore integration time:
-
Permalink:
mbeacom/context-kit@f57340d91b9a098fb1a6ce47175c21c0962911b9 -
Branch / Tag:
refs/tags/indexkit/v0.6.1 - Owner: https://github.com/mbeacom
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-indexkit.yml@f57340d91b9a098fb1a6ce47175c21c0962911b9 -
Trigger Event:
push
-
Statement type: