Skip to main content

ravenclaw

Deterministic code navigation and refactoring tools for coding agents.

When an agent needs to find every reference to a symbol or rename it, text search over-matches and under-matches: it can't tell two identically-named but unrelated bindings apart, and it misses references written differently than the search text. ravenclaw resolves real semantic bindings instead, using tree-sitter to parse source into an AST and a scope-resolution pass on top, so operations touch exactly the occurrences that are actually the same binding.

Installing

pip install git+https://github.com/idin/ravenclaw

The PyPI project named ravenclaw is an older, unrelated project until a new release replaces it.

Installing without PyPI

ravenclaw makes no network calls when it runs; only installing needs a package index, for pandas>=2.0.0, tree-sitter>=0.25.0,<0.26.0, tree-sitter-javascript>=0.25.0, tree-sitter-python>=0.25.0 and tree-sitter-typescript>=0.23.2 (pandas also pulls numpy, python-dateutil, pytz, tzdata and six), on Python 3.10 or newer. Building the wheel from a source tree also needs hatchling, which pip wheel fetches itself. Either bring the files across, or point at a mirror.

On a machine that has PyPI, collect the files, then install from the folder on the target machine (the wheels must match the target's Python version and platform; if it differs, add --only-binary=:all: --python-version <3.x> --platform <tag> to pip download):

pip wheel --no-deps git+https://github.com/idin/ravenclaw -w wheels/
pip download pandas tree-sitter tree-sitter-javascript tree-sitter-python \
  tree-sitter-typescript -d wheels/
pip install --no-index --find-links wheels/ ravenclaw

With an internal mirror, pip install --index-url <mirror> ravenclaw, or set PIP_INDEX_URL (uv: UV_DEFAULT_INDEX) once.

The plugin route (/plugin marketplace add idin/ravenclaw) clones from GitHub, so it needs the network; ravenclaw install-skills does not, once the package is installed.

Installing the skills

The library alone does nothing for an agent that does not know to call it: Claude Code still reaches for grep and hand edits. ravenclaw ships Claude Code skills (such as ravenclaw-rename-symbol) that tell it to use ravenclaw instead. Install them one of two ways.

With the package installed, copy them into your skills folder ($CLAUDE_CONFIG_DIR/skills when that is set, else ~/.claude/skills; --destination picks another, --overwrite replaces an older copy):

ravenclaw install-skills

Or, inside Claude Code, install them as a plugin:

/plugin marketplace add idin/ravenclaw
/plugin install ravenclaw@ravenclaw

Claude Code does not update a plugin from this marketplace on its own: run claude plugin update ravenclaw@ravenclaw in your shell, or turn on auto-update for the ravenclaw marketplace in the Marketplaces tab of /plugin.

Either way the skills call the ravenclaw package, so it must be installed in the environment that runs them.

Operations

  • find_references — every real occurrence of an identifier's binding, including the definition, every usage, and every import that names it
  • find_definition — the definition site of an identifier's binding
  • rename_symbol — rename a binding from any site (its definition, any usage, or an import statement that names it)
  • rename_file — rename a file and update every import/reference to it
  • move_symbol — move a function/class definition to a different file, auto-detecting its free-variable dependencies (adding whatever new imports the destination needs, or reusing what it already has), removing it from its old location, and rewriting every consumer that previously imported it from there
  • find_unresolved_references — a scope-wide diagnostic scan for every identifier usage or import with no resolvable binding; unlike the operations above, this never raises — enumerating every unresolved occurrence is its entire purpose
  • apply_edit_set — write an EditSet to disk; the three editing operations call it themselves unless auto_apply is false, so it is needed only to write a set held back for review

Which files a scan reads

A scope-wide scan should see the project's own source, not everything that happens to sit under its folder, or it would resolve against (and rename inside) installed copies of the same code. So a folder in scope is walked skipping .git, __pycache__ and node_modules, any virtual environment (a folder holding pyvenv.cfg, whatever it is called), any conda environment (holding conda-meta), any folder of installed packages (holding a *.dist-info folder), and every path git ignores. A file named in scope directly is read even if git ignores it. Inside a git work tree the ignore rules come from git itself, so git must be runnable there: if it is not, the scan raises rather than silently reading ignored files.

Settings

An agent standing in a project should not have to restate where the code is on every call, so the operations read their arguments from settings when the caller leaves them out. An argument passed explicitly always wins. Otherwise the value comes from the project's .ravenclaw/settings.json (found by walking up from the folder of the file the operation acts on, or from the working directory for an operation on no one file), then the user's ~/.ravenclaw/settings.json, then a fallback:

  • scope (every operation): the paths to search and edit. A relative path in a settings file is taken from the folder that holds its .ravenclaw folder: the project root, or the home folder for the user's file. Falls back to the project the target file belongs to (its root found by pyproject.toml or .git): its src folder and, when it exists, its tests folder; the whole root for a flat layout with no src, or when the target file lies outside src and tests. A target file, or move_symbol's destination or rename_file's new path, outside a given or stored scope raises ScopeError and nothing is written.
  • python_executable (every operation): the interpreter whose environment external (stdlib/third-party) imports are checked against. Falls back to the running interpreter.
  • auto_apply (rename_symbol, rename_file, move_symbol): whether to write the edits in the same call. Falls back to true, since mechanical edits are the point; set it false to get the EditSet back unapplied, for review or a dry run, and write it later with apply_edit_set.
  • verbosity (the same three, and apply_edit_set): how much of what was written to report: totals, files or edits. Falls back to totals, the cheapest.

ravenclaw.settings reads and writes these files (write_shared_setting, write_function_setting, read_all_settings).

Catalog

ravenclaw.python.catalog.Catalog(scope=..., python_executable=...) parses a codebase once and exposes pandas.DataFrame summaries of its structure, for exploration and dependency visualization rather than navigation/rename: .classes (inheritance, method-kind counts, decorators), .functions (parameters, return type, decorators, whether a function calls itself), .variables (every variable/constant/attribute/parameter binding, with type annotations and enclosing class/function), .files (per-file counts, aggregated from the properties above), and two dependency tables — .symbol_dependencies (one row per "this symbol references that symbol" edge) and .file_dependencies (a groupby of .symbol_dependencies by file, so the two can never disagree). Every property is computed once and cached; accessing several never re-parses the same files. Every table preserves None for missing values rather than pandas' usual NaN coercion (convert_rows_to_dataframe builds each column as an explicit dtype=object pd.Series).

.symbol_graph and .file_graph (the folder tree plus .file_dependencies) each have a __graph__() method returning a plain {"nodes": {...}, "edges": {...}} dict — a data-only interchange contract with no import in either direction between ravenclaw and any graph-visualization consumer (e.g. coralreef). Both sides only need to agree on the dict's shape:

{
    "nodes": {node_id: {...attrs}},
    "edges": {(source_id, target_id): {edge_id: {...attrs}}},
}

Graph rendering itself is intentionally out of scope for this library — see a separate package for that.

Hygiene checks

Separate from navigation/rename, ravenclaw.python.hygiene judges whether existing, correctly-resolved code follows conventions — starting with find_docstring_issues (a scope-wide scan) and check_docstring (a single definition), which detect missing docstrings, Args: sections that don't match a function's real parameters, and docstrings that don't follow Google style (identifying NumPy, reST/Sphinx, or plain style instead). Hygiene checks are deliberately strict with no built-in exceptions — see playbook/bugs/ for the project's own dogfooding history.

Languages

Python only, for now. A language's parsing and scope resolution lives in its own subpackage (ravenclaw.python) behind the shared Resolver protocol, since each language needs its own grammar and scoping rules; a new language is added the same way. Files in a language without a resolver are skipped by scope-wide scans and refused by position-based operations.

Testing

The test suite needs no node, npm or network: the TypeScript and JavaScript tests ship their fixtures instead of installing packages to resolve against. After the dependencies are installed (uv sync needs the network once), the suite runs offline:

uv sync
uv run pytest

Where ravenclaw cannot guarantee a result without an external tool, its error names the tool to install.

Known limitations

The Python resolver's known, deliberately deferred gaps (documented, not silently broken) are listed in ravenclaw.python.scope_tree's module docstring: the star-import limits (#88, #89, #90), dynamic getattr/setattr attribute access, decorators that rewrite a function's effective scope, and PEP 420 namespace packages.

tree-sitter must stay pinned below 0.26.0 — that release has a confirmed upstream use-after-free bug affecting node position data past line 256 of a file. See playbook/bugs/resolved/2026-08-21_tree_sitter_026_start_point_use_after_free.md.

Scope of the project

Not limited to the operations above — the goal is the full category of code-development-lifecycle operations that have a deterministic correct answer, so they don't need to rely on an agent's judgment or guesswork.

Metadata

Release files for ravenclaw 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ravenclaw 0.2.0
File Size Uploaded Provenance Actions
ravenclaw-0.2.0.tar.gz 1.1 MB Without provenance Details for ravenclaw-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ravenclaw 0.2.0
File Interpreter ABI Platform Actions
ravenclaw-0.2.0-py3-none-any.whl Python 3 none any Details for ravenclaw-0.2.0-py3-none-any.whl

Total release size: 1.5 MB

Release files / ravenclaw-0.2.0.tar.gz

Download URL ravenclaw-0.2.0.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
3df7b135d774911b541d164245216e8251dfb2777f21e7eeac946d507f60b1d6
BLAKE2b-256 checksum
How to use checksums
6e08468526130f9f68abf3141d63cd5bfe6659f550f97933a924bb5136a1bc21
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / ravenclaw-0.2.0-py3-none-any.whl

Download URL ravenclaw-0.2.0-py3-none-any.whl
Size 397.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ff04516e505633130b389ca499c45703c821f77565603573c085c40fb3531743
BLAKE2b-256 checksum
How to use checksums
e3051f59bc0d0fab45074486441dd880ecc5374e0c78137ff8bdc9f45aeb9db8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for ravenclaw 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page