Skip to main content

ravenclaw

Deterministic code navigation and refactoring for coding agents: find a symbol's definition and every real use of it, rename it, move it, or rename the file that holds it, and have every import follow. It resolves bindings (which name refers to which definition), not text.

This README is written for an agent that has never seen the repository. Each section stands on its own.

When to use it

Use ravenclaw instead of grep, sed or a hand edit when the job is "every place this particular thing is used". Text search cannot tell two unrelated functions that share a name apart, matches the name inside comments and strings, and misses uses written differently (from .area import compute_area as area_of, a relative import, a re-export). ravenclaw parses the code with tree-sitter, builds the scopes, and follows the binding, so it touches exactly the occurrences that are the same thing, and nothing else. It follows an aliased import to the definition (a rename edits the import and the alias keeps working), but find_references lists the import itself, not the calls made through the alias (see find_references).

You want to Use
Go from a use to where it is defined find_definition
Know who uses a symbol, or size a change before making it find_references
Rename a function, class, method, variable or constant everywhere rename_symbol
Rename or move a source file and fix every import of it rename_file
Move a function or class to another file and fix every import move_symbol
Check nothing is left dangling after one of the above find_unresolved_references

Do not use it for text that is not code (a README, a JSON file: use git mv), for a one-off edit inside one function, or for a language it does not resolve yet (see Languages).

Every operation takes a position: a file, a 1-indexed line and a 0-indexed column that points at the identifier itself, at any of its occurrences (its definition or any use: the same set comes back either way).

Install

Needs Python 3.10 or newer. Create a clean environment first if your default Python is older:

uv venv --python 3.12 .venv && uv pip install --python .venv/bin/python ravenclaw
# or, without uv:
python3.12 -m venv .venv && .venv/bin/pip install ravenclaw

then pip install ravenclaw as below, inside it. On Python 3.9 the failure does not name the Python version, and one form is worse than a failure: pip install ravenclaw there silently installs 0.1.0, the older unrelated project (it pulls scikit-learn and has none of the operations below), and installing the 0.2.0 wheel fails with ERROR: No matching distribution found for tree-sitter-javascript>=0.25.0. After installing, check python -c "import importlib.metadata as m; print(m.version('ravenclaw'))" prints 0.2.0 or newer.

pip install ravenclaw

or, as a command-line tool in its own environment:

uv tool install ravenclaw
uvx --from ravenclaw ravenclaw --help   # one-off run, nothing installed

The PyPI project ravenclaw is this one from version 0.2.0 (published 2026-10-17 from github.com/idin/ravenclaw). An older, unrelated project released earlier under the same name (0.1.0, and a yanked 2020 release) is still listed there; the newest version is the one you get. Check which you have with python -c "import importlib.metadata as m; print(m.version('ravenclaw'))".

The operations are a Python library; ravenclaw run also exposes the three read-only ones as a command line (below). The package depends on pandas, tree-sitter (pinned below 0.26.0, see Known limitations), tree-sitter-python, tree-sitter-javascript and tree-sitter-typescript.

Upgrade and uninstall

# Upgrade the library, then refresh the installed skills to match it.
pip install -U ravenclaw
ravenclaw install-skills --overwrite
# Remove the library.
pip uninstall ravenclaw

The skills install-skills copied stay where they are; delete the ravenclaw-* folders in the skills folder (see Install the skills) to remove them.

Installing without PyPI

ravenclaw makes no network calls when it runs; only installing needs a package index. On a machine that has one, collect the files, then install from the folder on the target machine. Installing a ravenclaw wheel by itself is not enough: it still downloads its dependencies from an index (offline, uv pip install ravenclaw-0.2.0-py3-none-any.whl stops with "Packages were unavailable because the network was disabled") unless they are in the same folder and you pass --no-index --find-links (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 download ravenclaw -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. To build from a source tree, pip wheel --no-deps git+https://github.com/idin/ravenclaw -w wheels/ (the build needs hatchling, which pip wheel fetches itself).

Install 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 that tell it to call ravenclaw instead. Copy them into your skills folder:

ravenclaw install-skills
  • Where: $CLAUDE_CONFIG_DIR/skills when that variable is set to a non-empty value, otherwise ~/.claude/skills. --destination <folder> installs into another skills folder. The folder is created when missing.
  • What: one folder per bundled skill, each holding a SKILL.md. The command prints installed <skill> into <folder> per skill.
  • Safe by default: if any skill folder already exists, the command installs nothing, prints which folder is in the way, and exits 1. --overwrite replaces the existing copies wholesale (edits you made to them are lost); folders that are not ravenclaw's skills are never touched.
  • Exit codes: 0 installed; 1 refused (a skill folder exists and --overwrite was not given, or the destination exists and is not a folder); 2 a usage error (an unknown option).

Inside Claude Code, the same skills can instead be installed 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.

The seven bundled skills

Skill Use it when
ravenclaw-find-definition You have a use of a name and want the place it is defined ("go to definition", "which X is this").
ravenclaw-find-references You want every use of a symbol, to answer "who uses this" or to size a change before making it.
ravenclaw-find-unresolved-references You finished a rename, move or deletion and want to check no import or name was left dangling.
ravenclaw-rename-symbol You rename a function, class, method, variable or constant, however small the rename looks.
ravenclaw-rename-file You rename or move a source file (a module) that other files import.
ravenclaw-move-symbol You move a function or class to another file.
ravenclaw-settings The user wants to configure ravenclaw: which folders it searches, which interpreter it checks imports against, preview before writing, or how much to report.

Each skill's Requirements section says pip install ravenclaw and that it needs Python 3.10 or newer. A skill is the instruction an agent reads; the code it calls is in the operations below. The skills use the library's functions, so a skill for an operation the ravenclaw run command does not offer (rename, move) calls the Python function.

A skill named ravenclaw-install is not installed: it is a placeholder, marked "NOT READY", for a future skill that installs ravenclaw into an environment of its own (uv, conda, pipx or a virtual environment). Its prototype, the script that picks the manager, lives with its plan in playbook/plans/environment_install/prototype/ravenclaw_install/, and a finished skill will replace it in a later release.

The operations, one call each

Every example below was run against a throwaway project with the installed wheel (python -I from another directory). The project:

shapes/
  pyproject.toml
  src/shapes/__init__.py        (empty)
  src/shapes/area.py
  src/shapes/report.py
  tests/test_area.py
# src/shapes/area.py
def compute_area(width, height):
    return width * height


def describe(width, height):
    return f"area is {compute_area(width, height)}"
# src/shapes/report.py
from .area import compute_area, describe


def report():
    total = compute_area(2, 3)
    return describe(2, 3), total
# tests/test_area.py
from shapes.area import compute_area


def test_area():
    assert compute_area(2, 3) == 6

Rules that hold for every operation below:

  • Relative paths are read from the working directory, in the Python API as well as in ravenclaw run: file="src/shop/pricing.py" from another directory raises FileNotFoundError. Run from the project root, or pass absolute paths.
  • Positions: line is 1-based and column is 0-based, pointing at the first character of the identifier. For a top-level def compute_total, the line comes from grep -n "def compute_total" src/shop/pricing.py and the column is the index of the name in that line, 4 after def : python3 -c "print(open('src/shop/pricing.py').read().splitlines()[4 - 1].index('compute_total'))". A wrong column (on the def keyword, on whitespace, past the name) raises UnresolvedBindingError: None at <file>:4:0 - no enclosing scope binds this name (:4:0 is the line and column you gave).
  • An empty scan is not an all-clear unless the scope covers the code. With no scope, an operation uses the project of the current working directory (see Settings); find_unresolved_references returns an empty result both for a clean project and for a scope that scanned nothing (a mistyped folder, a working directory with no Python code). GitHub issue #357 tracks making that loud; until then this README documents the behaviour.

In the outputs, <project> stands for the project's absolute path: positions carry absolute paths.

The command line: ravenclaw run <operation>

ravenclaw run takes the operation's keyword arguments as one JSON object on standard input and prints one JSON document on standard output. Only the read-only operations are offered: find-definition, find-references and find-unresolved-references. The editing operations (rename, move) are Python functions only (ravenclaw run rename-symbol is refused as an unknown operation).

# find_definition from a use (report.py line 5, column 12 is the call to compute_area)
echo '{"file": "src/shapes/report.py", "line": 5, "column": 12}' | ravenclaw run find-definition
{"result": "definition", "value": {"type": "Reference", "position": {"type": "Position", "file": "<project>/src/shapes/area.py", "line": 1, "column": 4}, "name": "compute_area", "is_definition": true, "kind": "function", "module_segments": null, "relative_level": null, "imported_name_position": null, "is_from_import": false, "is_aliased_module_import": false, "is_order_dependent": false, "is_type_only": false}}

find-references returns {"result": "references", "value": [...]} with one object of that same shape per occurrence; find-unresolved-references returns {"result": "unresolved_references", "value": [...]}. A relative file is read from the working directory. Failures print one error document on standard error and exit 1 (see Errors).

find_definition

from ravenclaw.operations import find_definition

definition = find_definition(file="src/shapes/report.py", line=5, column=12)
# definition.name == "compute_area", definition.kind == BindingKind.FUNCTION
# definition.position -> <project>/src/shapes/area.py line 1, column 4
# definition.is_definition is True

Returns one Reference. Raises UnresolvedBindingError when the position has no resolvable binding (whitespace, a keyword, the wrong column, or a file the scope leaves out).

find_references

from ravenclaw.operations import find_references

for reference in find_references(file="src/shapes/area.py", line=1, column=4):
    print(reference.position.file, reference.position.line, reference.position.column,
          reference.is_definition, reference.kind)

Real output (paths shortened):

src/shapes/area.py   1 4  True   BindingKind.FUNCTION   the definition
src/shapes/area.py   6 22 False  BindingKind.FUNCTION   the call inside describe
src/shapes/report.py 5 12 False  BindingKind.FUNCTION   the call in report
src/shapes/report.py 1 18 True   BindingKind.MODULE     the name in `from .area import ...`
tests/test_area.py   5 11 False  BindingKind.FUNCTION   the call in the test
tests/test_area.py   1 24 True   BindingKind.MODULE     the name in `from shapes.area import ...`

An import that names the symbol is listed too (kind module, with its module_segments), so the list is exactly what a rename would edit. A call made through an alias is not listed: with from .pricing import compute_total as total_of in a file, find_references on compute_total lists that import (kind module) but not the total_of(...) calls (checked against the built wheel). A use through a module alias (pr.tax(5) after from . import pricing as pr) is listed. Leaving the calls out is right for a rename, where the alias stays and keeps working, but it undercounts "who uses this": search the alias names too.

find_unresolved_references

from ravenclaw.operations import find_unresolved_references

unresolved = find_unresolved_references(scope=("src", "tests"))
# () on the project above. With `from .area import missing_thing` and a use of
# `undefined_name` added, it holds a Reference for each (name, file, line, column).

A scope-wide scan. It never raises for an unresolved name: listing them is its purpose. It is also how you confirm a rename, a move or a deletion left nothing dangling. An empty result means "nothing found", and is only an all-clear if the scope covers the code: a scope naming a folder that does not exist returns an empty result too. With no scope it scans the project of the current working directory (its src and tests, or the folder itself when it is in no project): from a folder with no Python code, or with a mistyped scope=("srcc",), it returns () exactly as a clean project does, so an empty result from outside the project root means nothing was scanned. Run it from the project root or pass absolute paths. (#357 tracks making this loud.)

rename_symbol

from ravenclaw.operations import apply_edit_set, rename_symbol

# Preview: nothing is written, the EditSet comes back.
edit_set = rename_symbol(file="src/shapes/area.py", line=1, column=4,
                         new_name="calculate_area", auto_apply=False)
# EditSet with 6 edits, no file renames, no created files; edit_set.edits[0] is
# TextEdit(position=Position(file='<project>/src/shapes/area.py', line=1, column=4),
#          old_text='compute_area', new_text='calculate_area')
print(apply_edit_set(edit_set=edit_set))
# {'num_files_changed': 3, 'num_edits_applied': 6, 'num_files_renamed': 0, 'num_files_created': 0}

# Direct: with auto_apply left at its default (true) the same call writes and reports.
rename_symbol(file="src/shapes/area.py", line=1, column=4, new_name="compute_area")
# {'num_files_changed': 3, 'num_edits_applied': 6, 'num_files_renamed': 0, 'num_files_created': 0}

It renames the definition, every use, and every import that names the symbol, from any of those positions. A module that lists the name as a string in its __all__ has that string renamed too. A report whose num_edits_applied is 1 for a symbol with obvious callers is a red flag: the scope probably leaves the callers out. As with every operation below, an empty or too-small result can mean the working directory or scope is not the project (see the rules above).

Strings, comments and docstrings that mention the old name are not changed ("compute_total" in a string and # compute_total sums in a comment stayed as they were in a checked run). After a rename, re-read the docstrings and comments, and grep for the old name as text.

verbosity="files" adds edits_by_file (an edit count per absolute path); verbosity="edits" adds edits (every {file, line, column, old_text, new_text}).

rename_file

from ravenclaw.operations import rename_file

rename_file(file="src/shapes/area.py", new_path="src/shapes/geometry.py")
# {'num_files_changed': 2, 'num_edits_applied': 2, 'num_files_renamed': 1, 'num_files_created': 0}

Moves the file on disk and rewrites every import of it (absolute, relative, dotted, and from-imports of its package) and every relative import inside it. A new path in a folder that does not exist creates the folder and its __init__.py.

move_symbol

from ravenclaw.operations import move_symbol

move_symbol(file="src/shapes/geometry.py", line=1, column=4,
            destination_file="src/shapes/maths.py")
# {'num_files_changed': 4, 'num_edits_applied': 5, 'num_files_renamed': 0, 'num_files_created': 1}

Takes a module-level function or class (decorators included) out of its file, puts it in the destination (created when missing) with the imports it needs, rewrites every consumer's import, and keeps an import in the old file when code left there still uses the name. Afterwards report.py read from .geometry import describe and from .maths import compute_area, and the test read from shapes.maths import compute_area.

An attribute use through a module alias is rewritten to an absolute import, even in a file that otherwise uses relative imports: with from . import pricing as pr and pr.tax(5), moving tax to src/shop/taxes.py added import shop.taxes to that file and changed the call to shop.taxes.tax(5) (checked against the built wheel). Expect that in the diff. Like the other editing operations, it resolves relative paths from the working directory.

apply_edit_set

Shown above: writes an EditSet returned by a preview. The three editing operations call it themselves unless auto_apply is false.

The ravenclaw.settings functions

See Settings for the examples.

Settings

An agent standing in a project should not have to restate where the code is on every call, so each operation reads the arguments you leave out from settings. 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.

Setting Applies to Meaning Fallback
scope every operation The paths to search and edit. A relative path in a settings file is read from the folder that holds its .ravenclaw folder (the project root, or the home folder for the user's file). The project the target file belongs to (root found by pyproject.toml or .git): its src folder and, when it exists, tests; the whole root for a flat layout or a target outside src and tests.
python_executable every operation The interpreter whose environment decides which imports are standard-library or third-party. The running interpreter.
auto_apply rename_symbol, rename_file, move_symbol Whether to write the edits in the same call. true: mechanical edits are the point. Set false to get the EditSet back for review.
verbosity the same three and apply_edit_set What to report: totals, files or edits. totals, the cheapest.

A target file, or move_symbol's destination, or rename_file's new path, outside the given or stored scope raises ScopeError and writes nothing.

Write and read them with the functions of ravenclaw.settings. By convention scope and python_executable describe the codebase (location project); auto_apply and verbosity are one person's preferences (location user).

from ravenclaw.settings import (
    read_all_settings, read_shared_setting, remove_shared_setting,
    write_function_setting, write_shared_setting,
)

path = write_shared_setting(name="scope", value=("src", "tests"),
                            location="project", start_directory="/path/to/shapes")
# '/path/to/shapes/.ravenclaw/settings.json', containing {"scope": ["src", "tests"]}

write_function_setting(name="auto_apply", value=False, function_name="rename_symbol",
                       location="user", start_directory="/path/to/shapes")
write_function_setting(name="verbosity", value="files", function_name="rename_symbol",
                       location="user", start_directory="/path/to/shapes")
# ~/.ravenclaw/settings.json now holds {"rename_symbol": {"auto_apply": false, "verbosity": "files"}}

read_all_settings(start_directory="/path/to/shapes")
# {'shared': {'scope': ('/path/to/shapes/src', '/path/to/shapes/tests'),
#             'python_executable': '/path/to/venv/bin/python'},
#  'functions': {'apply_edit_set': {'verbosity': 'totals', 'auto_apply': True},
#                'move_symbol':    {'verbosity': 'totals', 'auto_apply': True},
#                'rename_file':    {'verbosity': 'totals', 'auto_apply': True},
#                'rename_symbol':  {'verbosity': 'files',  'auto_apply': False}}}

read_shared_setting(name="scope", start_directory="/path/to/shapes")   # the stored scope: a tuple of absolute paths
remove_shared_setting(name="scope", location="project", start_directory="/path/to/shapes")
# back to the fallback; returns the path of the settings file

read_all_settings shows the effective value of each setting, fallbacks applied, not whether it is stored. read_function_setting reads one function's setting. A write checks the shape of a value, not that it works: a scope naming a folder that does not exist is stored, and then the position-based operations raise ScopeError. A write for an operation that has no settings (find_references, say) raises KeyError; an invalid value (verbosity="loud") raises ValueError naming what is allowed. Always pass start_directory: it decides which project file a project write goes to. Do not edit the settings files by hand.

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.

Catalog and hygiene checks

Separate from navigation and rename, for exploring a codebase and judging its conventions. Both are Python-only library calls (no ravenclaw run command).

Catalog parses a codebase once and exposes pandas.DataFrame summaries: .classes, .functions (columns such as name, file, start_line, end_line, kind, num_parameters, parameter_names, return_type, has_docstring, is_recursive), .variables, .files (per-file counts), .symbol_dependencies (source_name, source_file, source_line, target_name, target_file, target_line, target_kind) and .file_dependencies. Missing values are None, never NaN. .symbol_graph and .file_graph each have __graph__(), returning a plain {"nodes": {...}, "edges": {...}} dict for any graph-drawing tool (rendering is out of scope).

import sys
from ravenclaw.python.catalog import Catalog

catalog = Catalog(scope=("/path/to/shapes/src",), python_executable=sys.executable)
catalog.functions[["name", "start_line", "end_line", "num_parameters"]]
#            name  start_line  end_line  num_parameters
# 0  compute_area           1         2               2
# 1      describe           5         6               2
# 2        report           4         6               0
len(catalog.symbol_dependencies)             # 3
list(catalog.file_graph.__graph__()["nodes"])  # ['shapes', 'shapes.area', 'shapes.report']

find_docstring_issues(scope=...) scans for docstrings that are missing, whose Args: section does not match the real parameters, or that are not Google style; check_docstring checks one definition. The checks are deliberately strict, with no built-in exceptions.

from ravenclaw.python.hygiene import find_docstring_issues

for issue in find_docstring_issues(scope=("/path/to/shapes/src",)):
    print(issue.name, issue.kind, issue.position.line)
# DocstringIssue(position=Position(file='<project>/src/shapes/area.py', line=1, column=0),
#                name='compute_area', kind=<DocstringIssueKind.MISSING: 'missing'>,
#                missing_parameter_names=(), stale_parameter_names=())

Languages, and TypeScript today

Python files only. What happens with anything else:

You do What happens
Give a .ts .tsx .mts .cts .js .jsx .mjs .cjs file to a position-based operation (find_definition, find_references, rename_symbol, rename_file, move_symbol) UnsupportedLanguageError: TypeScript and JavaScript files are not supported yet: <file>. ravenclaw resolves Python only. Nothing is written.
Run a scope-wide scan (find_unresolved_references) over a folder holding such files The files are skipped, silently.
ravenclaw run ... on such a file An error document on standard error, {"error": "UnsupportedLanguageError", "message": ..., "version": ...}, and exit 1.

So a TypeScript project is not refactored by ravenclaw yet: use the editor's or tsc's rename there, and do not fall back to grep-and-replace for a Python name that also appears in a TypeScript file (the scan will not look at it).

TypeScript and JavaScript are in progress on main, not usable. The package ravenclaw.typescript already holds the grammar and parsing, the scope tree (bindings and scopes), module records (imports and exports) and module resolution (specifier to file, from tsconfig.json, jsconfig.json and package.json). None of it is wired into the operations, so the behaviour in the table above holds. The first release that includes TypeScript support is planned for the next minor release. One resolver will serve all eight file kinds. The plan and its milestones are in playbook/plans/typescript_javascript/; the order of future languages is issue #43.

A language's parsing and scope resolution lives in its own subpackage behind the shared Resolver protocol, since each language needs its own grammar and scoping rules.

Known limitations

Honest and current as of this README; each item is an open issue or a stated design limit.

Python resolution

  • PEP 695/696 type parameters (T in def f[T], class A[T]) are never bindings: find_definition, find_references and rename_symbol raise UnresolvedBindingError on them (#345).
  • A PEP 695 type X = ... statement never binds the alias name X (#353).
  • Type parameter defaults (PEP 696) are not parsed by the pinned tree-sitter-python 0.25.0, so a comprehension in one binds the outer name (#346).
  • A lambda or comprehension in a decorator, type alias value, type parameter bound or class attribute annotation binds the outer name (#344).
  • Scope spans are tracked by line, so on one line a use after a comprehension or lambda that binds the same name resolves to the inner variable (#155).
  • find_definition on a method name defined in a class returns no resolved definition position in some cases (#355).
  • Undecided whether the module-path segments of an import statement belong in find_references of a module (#172).
  • Documented, deliberately deferred, in the module docstring of ravenclaw.python.scope_tree: a binding written after a star import under an if or try, or changed by del or global, is judged by where it is written; dynamic getattr/setattr attribute access; decorators that rewrite the decorated function's effective scope; PEP 420 namespace packages (a directory without __init__.py is not a package).

Edits

  • move_symbol does not refuse a destination that already binds the dependency's alias to a different definition; it can silently rebind the name (#178).
  • __all__ edits: a removal silently leaves the name listed when the statement is a bare tuple the grammar reports as an error (#200); cosmetic leftovers remain after some edits (#199). An __all__ built by concatenation, comprehension or with an escape is not edited: search the old name as a string and change each export list by hand.
  • Import root of a package with no __init__.py files is a guess about sys.path (#112).

TypeScript (the in-progress work, not reachable through any operation yet)

  • Two block scopes on one line are not told apart (#289); a redeclared name reports the first declaration (#247); for-in/for-of heads, function names and parameters are skipped until their milestones (#250); escaped string names are kept raw in some imports and exports (#332); a raw CR or CRLF in a template literal name keeps the CR (#354); the parse time limit is not yet a setting (#349); .jsx, .mjs and .cjs declaration mapping is unconfirmed (#234).

Environment

  • tree-sitter must stay pinned below 0.26.0: that release has a confirmed upstream use-after-free bug affecting node positions past line 256 of a file (playbook/bugs/resolved/2026-08-21_tree_sitter_026_start_point_use_after_free.md).
  • There is no skill yet that installs ravenclaw itself; the prototype is not shipped (see the skills).

The full list is gh issue list --repo idin/ravenclaw --state open.

Errors you can see

Every error the library raises is a RavenclawError (importable from ravenclaw.core.errors), except the plain ValueError, KeyError, TypeError, FileNotFoundError and FileExistsError that argument checks raise. No operation writes anything when it raises.

Error Meaning and what to do
UnresolvedBindingError The identifier at that position has no binding in the scope: wrong position, a keyword, or the defining file is outside scope. Fix the position or widen the scope.
ScopeError The scope (given or stored) is invalid, or the target file, destination or new path lies outside it. Pass a scope that includes it, or fix the stored one.
UnsupportedLanguageError (also a ValueError) The file is TypeScript or JavaScript (or another language with no resolver). See Languages.
ParseTimeoutError A file's parse did not finish within the time limit, so the file is refused.
StarImportOrderError A rename is refused because a use inside a function body may run before the from x import * that would carry the name. Move the star import above the function, or rename the earlier binding too.
UnmovableDefinitionError move_symbol was asked to move something that is not a module-level function or class.
DestinationDefinesNameError The destination already defines a module-level function or class of that name.
DestinationBindsDependencyNameError The destination binds a name the moved definition's import would bind, to something else; writing the import would silently rebind it.
DestinationIsSourceError (also a ValueError) The destination is the file the definition is moving out of.
QualifiedUseNotRewritableError A dotted use of a moved module or symbol (pkg.mod.name) cannot follow the move; the message names the use and the advice.
CircularImportError The operation would leave two files importing each other where that cannot work.
UnsupportedDunderAllError A file's __all__ is built in a form that moving a name cannot keep true; the message names the statement.

Through ravenclaw run, any failure (these, a bad argument, malformed JSON, a missing file, an unknown operation) prints one JSON document to standard error, {"error": "<class name>", "message": "...", "version": "<ravenclaw version>"}, and exits 1. Standard input that is empty or not a JSON object is a ValueError.

For contributors

Needs uv. The suite needs no node, npm or network once the dependencies are installed (the TypeScript and JavaScript tests ship their fixtures):

uv sync
uv run pytest

Tests mirror the source under tests/, and everything they write goes under tests/_output/ (git-ignored). Plans, designs and the bug logs are in playbook/: plans/ for what is being built and in what order, bugs/ for the logs of bugs found and fixed.

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 do not need to rely on an agent's judgement or guesswork.

Metadata

Release files for ravenclaw 0.3.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.3.0
File Size Uploaded Provenance Actions
ravenclaw-0.3.0.tar.gz 1.4 MB Without provenance Details for ravenclaw-0.3.0.tar.gz

Built distribution (wheel)

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

Total release size: 1.8 MB

Release files / ravenclaw-0.3.0.tar.gz

Download URL ravenclaw-0.3.0.tar.gz
Size 1.4 MB
Tags Source
SHA-256 checksum
How to use checksums
2651cfeb18caf7fa5c185499a200fb7ac97b93b9e2d602b0d38c57a24af87b07
BLAKE2b-256 checksum
How to use checksums
61c4ae9aab993b056a23e91a4ddf8ac8d7664e93e5112b654d7baf2ea230449b
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.3.0-py3-none-any.whl

Download URL ravenclaw-0.3.0-py3-none-any.whl
Size 464.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b02b6552a16c3aab1fd44910ff768c791cb4e294693ed8600a05de11c6890fd7
BLAKE2b-256 checksum
How to use checksums
db1c1498453b9923718d111870ba9704a0bd8c79a719dbffbb77cbf37db76b19
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.3.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.3.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