Standalone real-time code index — live local directories and registered git repos
Project description
eulerian
Real-time code navigation for AI agents and editors. eulerian keeps a live
symbol index — files, classes, functions, methods, constants, with line
ranges — so an agent can jump straight to a definition instead of grepping
or reading whole files.
Two ways to point it at code:
- Local mode — watch a real working directory. A filesystem watcher
re-parses files the moment they're saved; the index never goes stale and
there's no explicit re-index step. Every query also runs a synchronous,
cheap mtime-diff catch-up first (
LocalWatcher.sync()), so a query issued immediately after a save — with no delay — never races ahead of the watcher's debounce window and returns pre-edit state. - Repo mode — register a git remote, check out a
(user, repo, ref)worktree, and browse it without touching your actual working tree. Freshness is checked against the remote on each call instead of pushed live, since nothing is editing the worktree directly.
Both modes share one in-memory symbol index and the same tree-sitter parser.
Python only for now (multi-language grammars are straightforward to add —
see parser.py).
Install
uv tool install eulerian
# or
pip install eulerian
No other runtime dependencies beyond tree-sitter, tree-sitter-python,
rapidfuzz, watchfiles, fastmcp, and typer — all pulled in
automatically.
CLI
# Local mode: one-shot queries against a directory, no persistent watcher
eulerian status [PATH]
eulerian outline QUERY [PATH] [-k N]
eulerian find SYMBOL [PATH]
# Local mode: live watch + MCP server (stdio by default, for an MCP client)
eulerian watch [PATH] [--http --host HOST --port PORT]
# Repo mode: register, checkout, browse
eulerian repo add URL
eulerian repo list
eulerian repo fetch REPO
eulerian repo checkout REPO REF [--user NAME]
eulerian repo outline REPO REF QUERY [--user NAME] [-k N]
MCP server
eulerian watch [PATH] starts a FastMCP server (stdio or --http) exposing:
Local mode — watch(path), unwatch(path), outline(query, paths, root, k, format),
find(symbol, root), references(symbol, root, regex, max_results),
callers(symbol, root, depth), callees(symbol, root, depth),
call_path(source, target, root, max_depth), imports(file, root),
read(path, start, end, root, segments), status(root).
Repo mode — repo_add(url), repo_fetch(repo), repo_list(), repo_pubkey(),
checkout(repo, ref, user), checkout_close(repo, ref, user), git_log(repo, ref, user, n),
repo_outline(repo, ref, query, paths, user, k, format, reset_head),
repo_read(repo, ref, path, user, start, end, segments, reset_head),
repo_references(repo, ref, symbol, user, regex, max_results, reset_head),
repo_callers(repo, ref, symbol, user, depth, reset_head),
repo_callees(repo, ref, symbol, user, depth, reset_head),
repo_call_path(repo, ref, source, target, user, max_depth, reset_head),
repo_imports(repo, ref, file, user, reset_head).
Point any MCP-capable client (Claude Code, etc.) at eulerian watch <path>
to get instant, line-precise code navigation without burning tool calls on
grep/read round-trips.
references/repo_references complement outline/find, which only match
definitions in the symbol index: they grep the watched directory's or
worktree's file contents directly for call sites, imports, type hints,
decorators, and any other textual mention of a symbol, defaulting to
whole-word matching (pass regex=True for a raw pattern instead).
callers/callees/call_path/imports (and their repo_* counterparts)
walk a structural call/import graph instead, built from the CallEdge/
ImportEdge records the parser resolves during parsing — same-file calls
and calls through a tracked import alias. They're precise where they
resolve at all, but resolve less than references() finds textually:
instance-method calls on untracked objects, decorators, and other dynamic
call forms aren't in the graph. depth/max_depth walk multiple hops
(depth=1 is direct callers/callees only); call_path finds the shortest
path between two symbols, one per matching start node.
Indexing scope and .gitignore
Every file walk — the initial full scan, the local watcher's live filter,
and repo_references's grep — honors .gitignore recursively (nested
.gitignore files and global excludes included) whenever the root is a git
working tree, via git ls-files/git check-ignore. Registered-repo
worktrees always qualify; a locally watched directory only gets this
treatment if it's itself a git repo. On top of that, a fixed set of noise
directories (.venv, node_modules, __pycache__, build, dist, etc. —
see parser.SKIP_DIRS) is always excluded, gitignored or not.
Repo-mode configuration
Bare clones and worktrees live under ~/.eulerian/ by default:
| Env var | Default | Purpose |
|---|---|---|
EULERIAN_GIT_REPOS_PATH |
~/.eulerian/repos/ |
Bare clone + worktree storage |
EULERIAN_GIT_SSH_KEY |
~/.eulerian/ssh/id_ed25519 |
Deploy key for private remotes |
EULERIAN_GIT_WORKTREE_MAX_IDLE_SEC |
3600 |
Idle worktree eviction threshold |
Architecture
parser.py tree-sitter Python parser -> Symbol / CallEdge / ImportEdge
index.py in-memory Index, keyed by an opaque handle, rapidfuzz search
sources/local.py watchfiles watcher -> handle = resolved root Path
sources/repo.py bare clone + worktree lifecycle -> handle = (user, repo, ref)
mcp/ FastMCP server exposing both modes as tools
cli.py typer CLI
Index doesn't care what a handle is — local mode and repo mode just pick
different shapes for it. Both feed the same outline/find query surface.
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 eulerian-0.2.2.tar.gz.
File metadata
- Download URL: eulerian-0.2.2.tar.gz
- Upload date:
- Size: 30.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d9de646d54d372fff9b49309cff3b85e18dfbd949d1b8f36796bcdcf0c504cb0
|
|
| MD5 |
3975ad0bba6e164e484fd043826b3faa
|
|
| BLAKE2b-256 |
0b7139232fe019c779abd17bc7fbab8b9aba5e3b8aa99c2877622b383d7012c8
|
File details
Details for the file eulerian-0.2.2-py3-none-any.whl.
File metadata
- Download URL: eulerian-0.2.2-py3-none-any.whl
- Upload date:
- Size: 36.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
166ad5256ea08555352f4f210a18a342bf832043ee4c8f4c6b06ca8695c7ab8f
|
|
| MD5 |
f2c923c8388767e36d1d5fd5561fb793
|
|
| BLAKE2b-256 |
040dd3e39927819354463d24be76ee0306f5a5b2e51f1e7a0d35da8a43e64fce
|