kosha (कोश) — a treasury of your repo and environment context for humans and coding agents. FTS5 + vector search + call graph, no LLMs required.
kosha
Find the code you need before you write it.
Kosha keeps a searchable memory of your repository and installed packages. Start with semantic search; add call-graph context when you need to understand the impact of a change. It works locally, uses no LLM, and returns code you can inspect.
Install
kosha is a dev dependency — it indexes at development time so AI coding assistants can search it.
uv add --dev kosha
One-time project setup — installs SKILL.md so every agent picks up the skill automatically:
Kosha(install_skill=True) # writes .agents/skills/kosha/ and .claude/skills/kosha/
Start a session
Create one index for the repository and the packages it uses. Later syncs compare source fingerprints and skip unchanged files.
k = Kosha()k.sync()
k.sync(graph=False) is the shortest refresh when you only need semantic search. graph_mode='full' asks for pyan3’s broader static analysis. Pass graph_metrics=False when you want to defer PageRank for a large graph refresh.
k = Kosha()k.sync(pkgs=['fastcore', 'litesearch'])
/Users/71293/code/personal/orgs/kosha/.venv/lib/python3.13/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
Package names in the query (package:fastcore, or a bare package word) are soft-boosted — matching results rank higher but other packages still appear. Use package!:fastcore to hard-filter to a single package. path:, lang:, type: tokens are hard filters that narrow further:
k.env_context('package:fastcore path:xtras atomic save', limit=8) # boost fastcore, keep othersk.context('atomic save package!:fastcore', limit=8) # fastcore only
Need more info on a package? Call pkg_url to get its repo/docs URL, then use websearch for changelogs, API docs, or migration guides:
from kosha.core import pkg_urlpkg_url('litesearch')
'https://github.com/Karthik777/litesearch'
Find a local pattern
Use repository and package search together when the task changes existing behaviour.
results = k.context('search code embeddings', limit=6, graph=True)for r in results: m = r['metadata']print(f"{m['mod_name']} L{m.get('lineno','?')} "f"pr={r.get('pagerank',0):.4f} callers={list(r.get('callers',[]))[:2]}")
compact=True strips full code bodies and returns slim dicts for fast scanning.
hits = k.context('database search filter package:litesearch', limit=2,repo=False, compact=True)for r in hits: sig = r.get('sig', '') doc = (r.get('docstring') or'')[:60]print(f"{r['mod_name']} L{r.get('lineno','?')}") if sig: print(f' {sig}')if doc: print(f' # {doc}')
litesearch.api.search L100
def search(self:Index,
# Hybrid keyword + vector search over the chunk store.
litesearch.core.database L397
def database(pth_or_uri:str=':memory:', # the database name or URL
# Set up a database connection and load usearch extensions.
Public API surface
api = k.public_api('fastcore', limit=12)for e in api: name = e.get('mod_name', '') doc = (e.get('docstring') or'')[:55]print(f"{name}"+ (f' # {doc}'if doc else''))
fastcore.aio.run_sync # Run coroutine `coro` to completion from sync code and r
fastcore.aio.iter_sync # Iterate async generator `agen` from sync code
fastcore.aio.ctx_sync # Use async context manager `acm` in a plain `with` block
fastcore.aio.athreaded # Run `f` in a worker thread, awaitably; use as `@athread
fastcore.aio.then # Pipe `x` through each of `fs`, awaiting values as neede
fastcore.aio.acache # Cache results of async function `f`
fastcore.aio.CachedAwaitable # Cache the result from an awaitable
fastcore.aio.reawaitable # Wraps the result of an asynchronous function into an ob
fastcore.aio.is_async_callable # Check if `obj` is an async callable, handling `partial`
fastcore.aio.to_aiter # Async yield each item in `items` with `asyncio.sleep(0)
fastcore.aio.maybe_aiter # If `items` already async, return it; otherwise to_aiter
fastcore.aio.mapa # Async `map`; apply `f` (sync or async) to `items` (sync
Trace a call path
Use these graph queries after you have a symbol or package in hand. They show a shortest call chain, public API paths, dependency layers, and the most connected nodes.
from fastcore.foundation import L
k.graphdb.t.graph_edges(where='callee like "%litesearch%"')[:2]
The first kosha call in a process pays a 3–5s embedder cold-start. kosha daemon keeps a warm process running and routes JSON requests over stdin/stdout, so subsequent calls are immediate.
kosha daemon &# start once per session
Then send newline-delimited JSON requests:
→ {"cmd":"context","args":{"q":"embed a query","limit":10}}
← {"ok":true,"result":[…]}
→ {"cmd":"short_path","args":{"src":"kosha.core.Kosha.sync","tgt":"litesearch.core.search"}}
← {"ok":true,"result":[…]}
Re-index the repo incrementally on every file change (blocking — Ctrl-C to stop):
kosha watch
or programmatically:
k.watch_repo()
CLI
Shell access to everything. Markdown by default; --as_json pipes into jq.
kosha install # install SKILL.md to .agents/ and .claude/kosha sync # index repo + env + call graphkosha status # check index freshnesskosha context "embed a query"--as_json|jq'.[].metadata.mod_name'kosha ni "fastcore.basics.merge"# node infokosha where-to-add "new route handler"kosha public-api fastcorekosha api-paths kosha litesearchkosha daemon # persistent kernel — warm for all session calls
Harness install
Kosha(install_skill=True) # installs to .agents/ and .claude/
Commit .agents/skills/kosha/SKILL.md so every contributor picks up the skill automatically.
pyskills
kosha registers as a pyskill (kosha.skill) for Python-native LLM hosts.
MCP server
kosha-mcp exposes the index over the Model Context Protocol, so Claude Code, Claude Desktop, Codex, and any other MCP client can query it directly — status/sync, context/repo_context/env_context, node_info/short_path/api_paths, where_to_add, and more.
The MCP server ships with kosha (no extra needed). kosha indexes the current repo and its venv, so the server must launch from the project root with the project’s environment — uv run does both:
uv add --dev koshas
Claude Code (run inside the project)
claude mcp add kosha -- uv run kosha-mcp
Codex (~/.codex/config.toml; Codex launches servers from your session’s working directory, so start it at the project root)