{
  "markdown": "<p align=\"center\">\n  <img src=\"docs/assets/arbor-logo.svg\" alt=\"Arbor logo\" width=\"120\" height=\"120\" />\n</p>\n\n<h1 align=\"center\">Arbor</h1>\n\n<p align=\"center\">\n  <strong>Graph-native intelligence for codebases.</strong><br>\n  Know what breaks <em>before</em> you break it.\n</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/Anandb71/arbor/actions\"><img src=\"https://img.shields.io/github/actions/workflow/status/Anandb71/arbor/rust.yml?style=flat-square&label=Rust%20CI\" alt=\"Rust CI\" /></a>\n  <a href=\"https://crates.io/crates/arbor-graph-cli\"><img src=\"https://img.shields.io/crates/v/arbor-graph-cli?style=flat-square&label=crates.io\" alt=\"Crates.io\" /></a>\n  <a href=\"https://github.com/Anandb71/arbor/releases\"><img src=\"https://img.shields.io/github/v/release/Anandb71/arbor?style=flat-square&label=release\" alt=\"Latest release\" /></a>\n  <a href=\"https://github.com/Anandb71/arbor/pkgs/container/arbor\"><img src=\"https://img.shields.io/badge/GHCR-container-blue?style=flat-square\" alt=\"GHCR\" /></a>\n  <a href=\"https://glama.ai/mcp/servers/@Anandb71/arbor\"><img src=\"https://img.shields.io/badge/MCP%20Directory-Glama-6f42c1?style=flat-square\" alt=\"Glama MCP Directory\" /></a>\n  <img src=\"https://img.shields.io/badge/license-MIT-green?style=flat-square\" alt=\"MIT License\" />\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/assets/arbor-demo.gif\" alt=\"Side-by-side: an agent navigating tokio with grep-and-read (47 tool calls, still searching) vs the same agent with arbor's code graph (4 graph calls + 1 read, done)\" width=\"900\" />\n</p>\n<p align=\"center\">\n  <sub>Simulated replay — the <code>arbor</code> commands and their output are real (tokio @ 178k LOC). Methodology: <a href=\"docs/BENCHMARKS.md\">BENCHMARKS.md</a></sub>\n</p>\n\n> **v3.0.0 — The Right Node** · v2.6.0 stopped *dropping* colliding symbols. It did not stop resolving them to the wrong one. When a bare name matched several modules, resolution fell through to \"same directory\" and confidently attached the edge to whichever definition happened to sit next to the caller. On a graded fixture the three largest hubs reported **zero downstream impact** while unrelated siblings inherited their centrality. A file's own imports now settle it. Reproduce it yourself: [getArbor-dev/arbor-torture](https://github.com/getArbor-dev/arbor-torture)\n\n---\n\n## Why Arbor\n\nMost AI coding tools treat code as text. Arbor builds a **semantic dependency graph** — functions, classes, and modules as nodes; calls, imports, and inheritance as edges — then answers execution-aware questions with deterministic precision:\n\n| Question | Arbor answer |\n|----------|--------------|\n| *If I change this symbol, what breaks?* | Blast radius with depth, confidence, and risk level |\n| *Who calls this — directly and transitively?* | Caller/callee traversal on the call graph |\n| *What's the shortest path between A and B?* | A* path through real dependencies |\n| *Is this PR too risky to merge?* | CI gate on blast-radius thresholds |\n\nNo keyword guessing. No embedding hallucinations. One graph, every interface.\n\nWhere the graph is *unsure*, it says so — edges carry a confidence, and ambiguous resolutions are labelled rather than hidden. An honest unknown beats a confident wrong answer.\n\n---\n\n## What's new in v3.0.0\n\nOne fix, measured.\n\n**Symbol resolution consults the importing file.** When a bare name matched\ndefinitions in several modules, `resolve_ref` fell through to `SameDir` and\nattached the edge to whichever definition sat in the caller's own directory —\nnot a dropped edge, a confidently misrouted one, stamped at 0.55 confidence.\n\n`GraphBuilder` already kept a per-file import map, but only\n`apply_import_validation` read it, and that scores an edge *after* one has been\nchosen. It never saw the references going to the wrong node. Consulting it\nbetween the same-file and same-directory checks keeps a local definition\nshadowing an import, while letting a written import beat mere adjacency.\n`Resolution::ViaImport` scores 0.93, above `SameDir`'s 0.55.\n\n### Measured\n\nA fixture of 260 modules across 10 layers, each layer defining the same 26\nfunction names. Ground truth is derived from the generator's own edge list, so\nthe expected answer is exact rather than estimated.\n\n| True downstream | v2.6.0 | v3.0.0 |\n|---|---|---|\n| 179 | 0 | **163** |\n| 178 | 0 | **161** |\n| 161 | 0 | **133** |\n| 143 | 22 | **133** |\n| 122 | 22 | **119** |\n| 36 | 22 | 61 |\n| 16 | 22 | 46 |\n\nPreviously flat at about 22 regardless of the real answer. Now it tracks. Risk\non the largest hub moves from `LOW` to `CRITICAL`.\n\nTotal edge count barely moves (1335 → 1334). That is the signature of\nmisrouting rather than loss: the edges were always there, pointing at the wrong\nnodes.\n\n### Breaking\n\n- `Resolution` gains a `ViaImport` variant — an exhaustive match will not compile\n- Edges land on different nodes, so cached graphs, stored node ids, and\n  centrality baselines from 2.6.0 will differ\n\n### Known and still open\n\nWritten down rather than left to be discovered:\n\n- Small targets now **over**-report (36 → 61, 16 → 46). Safer direction than\n  silence, but not yet correct.\n- PageRank has no escape from a closed cycle. Every member of a 500-function\n  ring scores above 90% centrality on one caller each, so mutually recursive\n  clusters — parsers, tree walkers, state machines — crowd the top of any\n  ranking.\n- Inheritance produces no edges. `class Middle(Base)` is invisible, so changing\n  a base class shows zero blast radius.\n- Dynamic and reflective imports (`importlib`, `__import__`, `import()`,\n  `eval(require(...))`) are unresolvable by construction and are documented as\n  expected misses in the fixture rather than counted as defects.\n\n<details>\n<summary><strong>v2.6.0 — Ground Truth</strong> (colliding symbols kept, deterministic resolution, edge confidence, percentile centrality)</summary>\n\nCorrectness, not speed. Each of these was silently wrong before.\n\n| Fix | Why it mattered |\n|-----|-----------------|\n| **Colliding symbols are kept** | `SymbolTable` used `HashMap::insert`, so a second `handler`, `new`, or `process` replaced the first. The loser had zero callers and was invisible to blast radius. |\n| **Resolution is deterministic** | Same-directory locality was decided by iterating a `HashMap`. Rust seeds `RandomState` per process, so the same binary on the same input could build different edges between runs. Now asserted across eight fresh processes. |\n| **Edges carry confidence** | A proven same-file call and a same-directory guess were identical evidence. Each edge now scores `[0,1]` by how it resolved. |\n| **Exported TS symbols indexed once** | `export_statement` recursed into its children, then the generic loop recursed again — every exported symbol became two vertices sharing one node id. **133 phantom nodes on a 149-file app, 25% of the graph.** |\n| **Method calls on untyped receivers resolve** | `obj.method()` was dropped outright, leaving the graph nearly edgeless on TS/JS — and an empty graph reports a blast radius of zero, which reads as \"safe\" rather than \"unknown\". |\n| **Centrality is a percentile rank** | Scores were divided by the graph maximum, so the top node was `1.0` by construction and a `0.6` threshold meant nothing consistent between repos. Adding one hub rescaled every other node. |\n| **Resolution is O(1), not O(refs × nodes × files)** | Unresolvable references — stdlib and third-party calls, most call sites in real code — paid the worst case. Suffixes are now indexed. |\n\n**New capability — concept search.** Substring matching cannot find `get_authenticated` from `login`; they share no substring. Identifiers are now tokenized and expanded through curated concept clusters, and docstrings, signatures, and paths are indexed alongside names. Deterministic, offline, no model. Available on the library as `ArborGraph::search_ranked` (`arbor query` remains literal-substring for now).\n\n**New capability — hunk-level impact.** `changed_node_ids_for_ranges` keeps only symbols whose lines actually changed, instead of every symbol in a touched file.\n\nMeasured on identical node sets, after the duplicate-extraction fix:\n\n| Codebase | Before | After |\n|----------|--------|-------|\n| TypeScript (149 files) | 172 edges | **196** (+14%) |\n| Rust (arbor-graph) | 116 edges | **167** (+44%) |\n\nGraph caches from earlier versions are invalidated — centrality now means something different, so a stale cache would be read wrong.\n\n</details>\n\n<details>\n<summary><strong>v2.5.0 — The Last Excuse</strong> (PageRank 23x, parallel indexing, warm-start centrality)</summary>\n\n| Change | Measured |\n|--------|----------|\n| **PageRank rewrite** — flat call-graph adjacency replaces per-iteration traversal | 149.8ms → **6.6ms** on a 10k-node graph (**23x**), verified side-by-side vs the old implementation |\n| **Parallel indexing** — parse fans out across all cores, deterministic assembly | Arbor: 253ms → **95ms** · tokio (178k LOC): 2.7s → **1.6s** |\n| **Warm-start centrality** — watcher recomputes seed from previous scores | Converges in ~2 rounds after a one-file patch instead of the full 20-iteration budget |\n| **Convergence early-exit** | Iteration stops at 1e-9 max delta — the budget is a ceiling, not a sentence |\n\nThink a number is wrong? `cargo bench -p arbor-graph` and prove it: [BENCHMARKS.md](docs/BENCHMARKS.md).\n\n</details>\n\n<details>\n<summary><strong>v2.4.0 — The Agent-Native Leap</strong> (MCP <code>2026-07-28</code>, HTTP transport, Tasks, MCP Apps)</summary>\n\n| Feature | What it does |\n|---------|--------------|\n| **MCP `2026-07-28`** | Stateless `server/discover`, response caching (`ttlMs`/`cacheScope`), dual-version fallback for `2025-03-26` clients |\n| **Tasks extension** | `tasks/get` · `tasks/update` · `tasks/cancel` — cold-start indexing returns task handles, not errors |\n| **MCP Apps** | Interactive blast-radius graph (`ui://arbor/blast-radius`) and architecture map (`ui://arbor/architecture-map`) inside agent hosts |\n| **HTTP transport** | `arbor bridge --http --port 3333` — stateless MCP behind load balancers |\n| **Real `get_blast_radius`** | Git-diff-aware impact analysis via shared `arbor-graph::compute_blast_radius` |\n| **Pagination** | `offset` / `limit` / `hasMore` on `search_symbols` and `get_map` |\n| **Benchmarks** | Criterion suite + CI regression gate — see [BENCHMARKS.md](docs/BENCHMARKS.md) |\n\n</details>\n\n---\n\n## Quickstart\n\n```bash\n# Install\ncargo install arbor-graph-cli\n\n# Index your project (one command)\ncd your-project && arbor setup\n\n# Explore before you edit\narbor map . --exclude-test          # ranked project skeleton (~1k tokens)\narbor refactor parse_file           # blast radius of changing a symbol\narbor diff                          # impact of uncommitted git changes\n\n# Wire up your AI agent\nclaude mcp add --transport stdio --scope project arbor -- arbor bridge\n```\n\n**Agent workflow:** call `get_map` first → `search_symbols` / `get_file_graph` to locate code → `Read` only the target file. [Full MCP guide →](docs/MCP_INTEGRATION.md)\n\n---\n\n## For AI agents (MCP)\n\nArbor ships a production MCP server via `arbor bridge`. Stdio is the default; HTTP is opt-in for remote/enterprise.\n\n```bash\n# Stdio (Claude, Cursor, VS Code)\narbor bridge\n\n# HTTP (MCP 2026-07-28)\narbor bridge --http --port 3333\n```\n\n### Cursor / VS Code\n\n```json\n{\n  \"mcpServers\": {\n    \"arbor\": {\n      \"type\": \"stdio\",\n      \"command\": \"arbor\",\n      \"args\": [\"bridge\"]\n    }\n  }\n}\n```\n\nTemplates: [`templates/mcp/`](templates/mcp/) · Setup scripts: `scripts/setup-mcp.sh` · `scripts/setup-mcp.ps1`\n\n### 16 MCP tools\n\n| Tier | Tools | Use when |\n|------|-------|----------|\n| **Orientation** | `get_map` | First call — token-budgeted project skeleton ranked by PageRank |\n| **Surgical** | `list_entry_points` · `get_callers` · `get_callees` · `search_symbols` · `get_file_graph` · `get_node_detail` | Navigate to a specific symbol or file |\n| **Broad** | `get_logic_path` · `analyze_impact` · `find_path` · `get_knowledge_path` | Trace dependencies, blast radius, paths |\n| **Agent-native** | `get_blast_radius` · `explain_symbol` · `audit_security` · `get_architecture_overview` · `batch_query` | PR impact, onboarding, security audit, bulk lookup |\n\nEvery tool returns `{ ok, tool, data, meta: { suggested_next_tool, suggested_next_args } }` so agents chain calls without re-prompting.\n\n**Registry:** `io.github.Anandb71/arbor` · [Official API lookup](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.Anandb71/arbor) · [Glama listing](https://glama.ai/mcp/servers/@Anandb71/arbor)\n\n---\n\n## CLI reference\n\n| Command | Description |\n|---------|-------------|\n| `arbor setup` | One-shot init + index |\n| `arbor map` | Ranked, token-budgeted project skeleton |\n| `arbor query <term>` | Fuzzy symbol search (supports `\\|` OR) |\n| `arbor callers / callees <sym>` | One-hop graph traversal |\n| `arbor entry-points` | HTTP handlers, main, jobs, webhooks |\n| `arbor file-graph <path>` | Symbols + edges in one file |\n| `arbor inspect <sym>` | Full symbol detail |\n| `arbor path <a> <b>` | Shortest call-graph path |\n| `arbor refactor <sym>` | Blast radius before refactoring |\n| `arbor diff` | Git-change impact report |\n| `arbor check` | CI safety gate (`--max-blast-radius N`) |\n| `arbor summary` | Auto-generate PR description |\n| `arbor agent review` | Autonomous PR architecture review |\n| `arbor agent onboard` | Codebase onboarding guide |\n| `arbor agent guard` | Real-time architectural safety gate |\n| `arbor bridge` | MCP server (add `--http` for HTTP transport) |\n| `arbor watch` | Live re-index on file changes |\n| `arbor gui` | Native desktop UI |\n\nAll query commands support `--json`. `map` additionally supports `--tokens N`, `--focus \"pattern\"`, `--focus-changed`.\n\n---\n\n## Visual tour\n\n<p align=\"center\">\n  <img src=\"docs/assets/visualizer-screenshot.png\" alt=\"Arbor visualizer screenshot\" width=\"760\" />\n</p>\n\nFull recording: [media/recording-2026-01-13.mp4](media/recording-2026-01-13.mp4)\n\n---\n\n## Installation\n\n```bash\n# Rust / Cargo\ncargo install arbor-graph-cli\n\n# Homebrew (macOS/Linux)\nbrew install Anandb71/tap/arbor\n\n# Scoop (Windows)\nscoop bucket add arbor https://github.com/Anandb71/arbor && scoop install arbor\n\n# npm wrapper (cross-platform)\nnpx @anandb71/arbor-cli\n\n# Docker\ndocker pull ghcr.io/anandb71/arbor:latest\n```\n\nNo-Rust installers:\n\n- macOS/Linux: `curl -fsSL https://raw.githubusercontent.com/Anandb71/arbor/main/scripts/install.sh | bash`\n- Windows: `irm https://raw.githubusercontent.com/Anandb71/arbor/main/scripts/install.ps1 | iex`\n\nPinned installs: [docs/INSTALL.md](docs/INSTALL.md)\n\n---\n\n## Language support\n\n**Production parsers:** Rust · TypeScript / JavaScript · Python · Go · Java · C / C++ · C# · Dart\n\n**Fallback parsers:** Kotlin · Swift · Ruby · PHP · Shell\n\n[Adding languages →](docs/ADDING_LANGUAGES.md)\n\n---\n\n## CI & pull requests\n\n```bash\narbor diff --markdown\narbor check --max-blast-radius 30 --markdown\narbor summary\n```\n\nGitHub Action (pre-built binary, ~5s vs ~3–5min compile):\n\n```yaml\nname: Arbor Check\non: [pull_request]\n\njobs:\n  arbor:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - uses: getArbor-dev/arbor@v3.0.0\n        with:\n          command: check . --max-blast-radius 30 --markdown\n          comment-on-pr: true\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n```\n\n---\n\n## Architecture\n\n```\narbor-core (Tree-sitter parsing)\n    └── arbor-graph (petgraph + PageRank + impact analysis)\n            ├── arbor-cli      — CLI + MCP bridge\n            ├── arbor-mcp      — MCP protocol server\n            ├── arbor-server   — WebSocket JSON-RPC\n            ├── arbor-watcher  — incremental file watcher\n            └── arbor-gui      — desktop UI\n```\n\n**Docs:** [Quickstart](docs/QUICKSTART.md) · [Architecture](docs/ARCHITECTURE.md) · [Graph schema](docs/GRAPH_SCHEMA.md) · [MCP integration](docs/MCP_INTEGRATION.md) · [Benchmarks](docs/BENCHMARKS.md) · [Roadmap](docs/ROADMAP.md) · [Philosophy](PHILOSOPHY.md)\n\n**Release channels:** GitHub Releases · crates.io · GHCR · npm · VS Code / Open VSX · Homebrew · Scoop — [Releasing guide](docs/RELEASING.md)\n\n---\n\n## Philosophy\n\n1. **Consumer first** — beautiful, intuitive, instantly useful\n2. **Accessibility second** — works across ecosystems, runs anywhere\n3. **Affordability next** — minimal overhead, from laptops to monoliths\n\nArbor is **local-first**: no mandatory data exfiltration, offline-capable, open source. [Security policy →](SECURITY.md)\n\n---\n\n## Contributing\n\n```bash\ncargo build --workspace\ncargo test --workspace\ncargo clippy --workspace --all-targets --all-features\n```\n\n[CONTRIBUTING.md](CONTRIBUTING.md) · [Good first issues](docs/GOOD_FIRST_ISSUES.md) · [Code of conduct](CODE_OF_CONDUCT.md)\n\n---\n\n## Contributors\n\n<!-- CONTRIBUTORS:START -->\n<p align=\"center\">\n    <a href=\"https://github.com/Anandb71\" title=\"Anandb71\" style=\"text-decoration:none; margin:6px; display:inline-block;\">\n        <img src=\"https://avatars.githubusercontent.com/u/169837340?v=4\" alt=\"Anandb71\" width=\"72\" height=\"72\" loading=\"lazy\" style=\"border-radius:50%; border:2px solid #30363d; box-sizing:border-box;\" />\n  </a>\n    <a href=\"https://github.com/holg\" title=\"holg\" style=\"text-decoration:none; margin:6px; display:inline-block;\">\n        <img src=\"https://avatars.githubusercontent.com/u/1383439?v=4\" alt=\"holg\" width=\"72\" height=\"72\" loading=\"lazy\" style=\"border-radius:50%; border:2px solid #30363d; box-sizing:border-box;\" />\n  </a>\n    <a href=\"https://github.com/cabinlab\" title=\"cabinlab\" style=\"text-decoration:none; margin:6px; display:inline-block;\">\n        <img src=\"https://avatars.githubusercontent.com/u/66889299?v=4\" alt=\"cabinlab\" width=\"72\" height=\"72\" loading=\"lazy\" style=\"border-radius:50%; border:2px solid #30363d; box-sizing:border-box;\" />\n  </a>\n    <a href=\"https://github.com/Karthiksenthilkumar1\" title=\"Karthiksenthilkumar1\" style=\"text-decoration:none; margin:6px; display:inline-block;\">\n        <img src=\"https://avatars.githubusercontent.com/u/182195883?v=4\" alt=\"Karthiksenthilkumar1\" width=\"72\" height=\"72\" loading=\"lazy\" style=\"border-radius:50%; border:2px solid #30363d; box-sizing:border-box;\" />\n  </a>\n    <a href=\"https://github.com/zacwolfe\" title=\"zacwolfe\" style=\"text-decoration:none; margin:6px; display:inline-block;\">\n        <img src=\"https://avatars.githubusercontent.com/u/2164736?v=4\" alt=\"zacwolfe\" width=\"72\" height=\"72\" loading=\"lazy\" style=\"border-radius:50%; border:2px solid #30363d; box-sizing:border-box;\" />\n  </a>\n    <a href=\"https://github.com/sanjayy-j\" title=\"sanjayy-j\" style=\"text-decoration:none; margin:6px; display:inline-block;\">\n        <img src=\"https://avatars.githubusercontent.com/u/178475117?v=4\" alt=\"sanjayy-j\" width=\"72\" height=\"72\" loading=\"lazy\" style=\"border-radius:50%; border:2px solid #30363d; box-sizing:border-box;\" />\n  </a>\n    <a href=\"https://github.com/sathguru07\" title=\"sathguru07\" style=\"text-decoration:none; margin:6px; display:inline-block;\">\n        <img src=\"https://avatars.githubusercontent.com/u/182798669?v=4\" alt=\"sathguru07\" width=\"72\" height=\"72\" loading=\"lazy\" style=\"border-radius:50%; border:2px solid #30363d; box-sizing:border-box;\" />\n  </a>\n</p>\n<p align=\"center\"><sub><strong>7 contributors</strong> | <a href=\"https://github.com/Anandb71/arbor/graphs/contributors\">View all</a></sub></p>\n\n<!-- CONTRIBUTORS:END -->\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n",
  "bytes": 19473,
  "sha": "4a32750cb5e1eb76c17cfeccc033c6bbd11ee1dede0993a00bc8a76aedfdeb2c",
  "repo_slug": "anandb71/arbor",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_anandb71_arbor_068c2cb1/readme"
}