The Arbor engine
Who calls it? What does it reach?
Arbor reads your source into a graph of functions, classes and modules, joined by the calls and inheritance it resolves. Query it from the arbor CLI or over MCP. It runs on your machine.
arbor setup indexes the project into .arbor/ at its root; add that folder to .gitignore. Callers and callees come back with the file and line to check. Tree-sitter parsers cover Rust, TypeScript/JavaScript, Python, Go, Java, C/C++, C# and Dart; Kotlin, Swift, Ruby, PHP and Shell use a simpler line-based fallback.
$ arbor callers your_functionWho calls this function?
Replace your_function with a symbol from your project. If several files define it, narrow with src/parser.py:your_function.Follow the calls.
arbor callers and callees go one hop. arbor path finds the shortest call path between two functions. arbor refactor --why lists what a change to one symbol reaches, to a depth you set, and why.
Review a branch.
arbor diff --base main reports a branch the way its pull request shows it. Each modified symbol gets its own blast radius; new ones are listed separately. arbor check can fail a CI job when a change reaches more than you allow or touches an entry point. A passing check isn’t proof the change is safe.
Bring your assistant.
arbor bridge gives Claude Code, Cursor, VS Code or another stdio MCP client the engine’s tools. v3.0.5 has 16, including get_callers and analyze_impact. Your assistant, and its model provider if it uses one, can receive what those tools return.
A static graph is a map, not proof that a change is safe. Arbor reads source; it doesn’t run your program or see production traffic. Dynamic imports and reflection can be missing and inheritance is only partly followed, so an empty answer doesn’t prove that nothing calls a function. Some impact counts also run high. Review the documented limitations and keep running your tests.