kosha (कोश) — a treasury of your repo and environment context for coding agents. FTS5 + vector search + call graph, no LLMs required.
Project description
kosha
kosha (कोश) — a treasury of repo and installed-package context for humans and coding assistants.
Persistent knowledge of your codebase and installed packages — FTS5 + vector search + call graph, merged with Reciprocal Rank Fusion. Each result includes the code snippet, callers, callees, and PageRank. No LLMs required.
Install
kosha is a dev dependency — it indexes code at development time so AI coding assistants can search it. It does not ship with your application.
uv add --dev koshas # uv (recommended)
pip install --group dev koshas
Setup (one-time per project)
Kosha(install_skill=True) # writes .agents/skills/kosha/SKILL.md
Commit .agents/skills/kosha/SKILL.md so every contributor — human and
AI — picks up the skill automatically.
Sync (once per session, re-run when things change)
k = Kosha() # auto-detects git repo root
k.sync(pkgs=['fasthtml', 'fastcore', 'litesearch'])
Subsequent calls are incremental — only changed files and new package
versions are re-indexed. Re-run k.sync() whenever the env or repo
changes materially: after uv add / pip install / version bumps,
after pulling or merging significant code changes, or when results look
stale. Calling it too often is harmless; calling it too rarely silently
feeds the agent stale context. Indexes:
.kosha/code.db— repo chunks + embeddings (project-local).kosha/graph.db— call graph (project-local)$XDG_DATA_HOME/kosha/env.db— installed packages (shared across repos)
The four-step workflow
The high-value pattern: inventory → disambiguate → narrow → trace. Skip steps that don’t apply (table at the end of this section).
Step 1 — Inventory
Query the indexes directly to see what’s installed and what each package
exposes. pkgs_in_env, dep_stack, and public_api are all indexed
SQLite reads, sub-second on typical envs — no cache file is needed. (A
markdown cache without invalidation goes silently stale when packages
change, and the reads are already fast enough that no cache is earning
its keep.)
pkgs = k.pkgs_in_env(pyproject=True) # [{name, version}, ...]
layers = k.dep_stack(seeds=[p['name'] for p in pkgs], depth=2) # BFS, by coupling
for p in pkgs:
api = k.public_api(p['name'], limit=30) # public surface + docstrings
# use `pkgs`, `layers`, `api` directly — no intermediate file needed
Step 2 — Disambiguate
If the task names a domain (“payments”, “ui”, “http client”) and several
installed packages could plausibly serve it, ask the user which to use
before searching deeper. env_context() auto-detects package names in
plain query tokens, so this call is cheap.
hits = k.env_context('toast notification', limit=30) # cheap, no graph enrichment
by_pkg = {}
for r in hits: by_pkg.setdefault(r['metadata'].get('package'), []).append(r)
candidates = sorted(by_pkg, key=lambda p: -len(by_pkg[p]))[:4]
# If multiple candidates have comparable hit counts, present them and wait for the user.
Step 3 — Narrow
Once the package set is known, run context() once with a
package: filter. The result is already enriched with pagerank,
callers, callees, co_dispatched — do not loop and call ni()
afterwards.
results = k.context('toast notification package:monsterui', limit=10)
for r in results:
m = r['metadata']
print(f"{m['mod_name']} L{m.get('lineno','?')} pr={r.get('pagerank',0):.4f} "
f"callers={list(r['callers'])[:2]} callees={list(r['callees'])[:2]}")
Step 4 — Trace
Reach for these only when the task spans packages or you need entry points.
k.api_call_paths('myapp', 'fasthtml', k=15) # how myapp reaches into fasthtml
k.short_path('myapp.routes.checkout', 'stripe.Webhook.construct_event')
k.top_nodes('fasthtml', k=5) # entry points to read first
k.neighbors('myapp.payments.verify', depth=2)
When to skip steps
| Situation | Steps |
|---|---|
| Trivial “how does X work” lookup in a known package | 3 only |
| First time working in this repo / env | 1 → 3 |
| Task names a domain, multiple packages could fit | 1 → 2 → 3 |
| Task spans packages, or you need entry points | 1 → 3 → 4 |
context() reference
The main entry point. Parses key:value filters, auto-detects package
names, fans out repo + env searches in parallel, and merges with chained
RRF. With graph=True (default) each result is enriched from
.kosha/graph.db.
Each result is a dict with:
content— code snippetmetadata—{mod_name, path, lineno, type, package?, docstring?}- graph fields —
pagerank,in_degree,out_degree,callers,callees,co_dispatched
(co_dispatched lists siblings registered together at module level —
route groups, plugin tables — the pattern to follow when adding a new
handler. Inspect any result with dir(r) or r.keys().)
Filters
| Token | Example | Effect |
|---|---|---|
package:name |
package:fasthtml |
Env search restricted to one package |
file:glob |
file:routes* |
Repo results by filename |
path:pattern |
path:api/* |
Repo results by path |
lang:ext |
lang:py |
By language |
type:node |
type:FunctionDef |
By AST node type |
Plurals (packages:, paths:) and comma-separated values are
supported.
# Combined: functions in payments/, restricted to one package
k.context('handle stripe webhook type:FunctionDef path:payments/ package:fasthtml', limit=5)
Graph API
| Call | Returns |
|---|---|
k.ni(node) |
Node row + callers + callees + co_dispatched + pagerank |
k.short_path(a, b) |
Shortest call chain between two nodes |
k.neighbors(node, depth=2) |
All nodes within N hops |
k.graph.ranked(k=10, module='pkg') |
Top nodes by PageRank |
k.public_api(pkg, limit=200) |
Public entries (__all__ + @patch) with docstrings |
k.gn(where=...) / k.ge(where=...) |
Direct graph_nodes / graph_edges queries |
CLI
For shell harnesses (GitHub Copilot CLI, Claude Code hooks, scripts).
Markdown by default; --as_json pipes cleanly into jq. Run kosha
with no args to see all subcommands.
kosha sync # once per checkout
kosha context "embed a query" --as_json | jq '.[].metadata.mod_name'
kosha public-api fastcore
kosha api-paths kosha litesearch --k 10
kosha ni "fastcore.basics.merge"
Harness install
# Project-local (default; auto-discovered by Claude Code, Continue.dev, Cursor, Copilot)
# .agents/skills/kosha/SKILL.md ← written by Kosha(install_skill=True); commit it
# Claude Code, global across all projects on this machine:
mkdir -p ~/.claude/skills/kosha && cp .agents/skills/kosha/SKILL.md ~/.claude/skills/kosha/
# Other harnesses: place SKILL.md at whatever path the harness scans (e.g. .continue/skills/kosha/).
pyskills
kosha is registered as a
pyskill (entry point
kosha.skill). LLM hosts can list_pyskills() to discover it without
importing, then doc(kosha.skill) for the full surface and instantiate
Kosha directly.
Project details
Release history Release notifications | RSS feed
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 koshas-0.0.9.tar.gz.
File metadata
- Download URL: koshas-0.0.9.tar.gz
- Upload date:
- Size: 34.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79fee3d4d05dbe6043de057f98cb8969a3913afeafcfd477d167708143a083f7
|
|
| MD5 |
b2a3310ca8984a1961f4dad6af2583db
|
|
| BLAKE2b-256 |
95ce2d761f6ebea8f3249dc3f6f6c1f803da90e2d91333760f0851aa58cb6ab8
|
File details
Details for the file koshas-0.0.9-py3-none-any.whl.
File metadata
- Download URL: koshas-0.0.9-py3-none-any.whl
- Upload date:
- Size: 37.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2108eb6d1782529e4d3153c8065244dea1b9f8a15088ecb4fdfbe5dbfffaa47
|
|
| MD5 |
a9aa27be69344cb53be5ccee30661d9e
|
|
| BLAKE2b-256 |
80234259b88eae7c7e74b72c13568c3a1b711a570eed0f587713a66f8c3aa10b
|