{"slug": "graft", "title": "Graft", "summary": "Graft, a tool from Nanonets, reduces coding agent tool calls by 46%, tokens by 42%, and time by 60% in a 162-run controlled benchmark by building a persistent graph of codebase context as linked markdown files, eliminating repeated exploration overhead. The tool integrates with Claude Code, Cursor, Codex, and Gemini, and can be installed via npm and initialized with a single command.", "body_md": "### Turbocharge Claude Code, Cursor, Codex, Gemini & every coding agent: faster, cheaper, with contextual understanding specific to your codebase.\n\n| vs. a standard session, no Graft | With Graft |\n|---|---|\n| Tool calls | 46% fewer |\n| Tokens | 42% fewer |\n| Time | 60% less |\n| Correctness | equal |\n\nThe table is a 162-run controlled benchmark (same agent, same file tools, only the context differs). The \"up to 4× cheaper / 3× faster\" figures are the biggest single-task wins from a separate real-repo sweep (PocketBase, ollama, Excalidraw). Full method & per-repo numbers ↓\n\n[Quick start](#quick-start)[The problem](#the-problem)[What Graft does](#what-graft-does)[How the graph gets built](#how-the-graph-gets-built)[What's in a node](#whats-in-a-node)[What runs where](#what-runs-where)[Agent integration](#agent-integration)—[MCP server](#mcp-server)·[Claude Code (deep integration)](#claude-code-deep-integration)[CLI](#cli)[Search & orient](#search--orient-graft-grep--graft-map)(`graft grep`\n\n/`graft map`\n\n)[Monorepos & multi-repo folders](#monorepos--multi-repo-folders)[Visualize it](#visualize-it-graft-viz)(`graft viz`\n\n)[Benchmark](#benchmark)[Tested on your popular repos](#tested-on-your-popular-repos)[Development](#development)[License](#license)\n\n```\nnpm install -g @nanonets/graft   # install the CLI, once\ngraft init                       # build the graph + wire it into Claude Code\n```\n\nThat is the whole setup. `graft init`\n\nasks which of your coding agents to wire up, builds `graft/`\n\nfrom your code, and drops a statusline and hooks into `.claude/`\n\n, so from the next session on Graft rides along in Claude Code: it pulls the matching nodes into each prompt and rebuilds the graph in the background after every turn. No daemon, no re-indexing to remember, nothing to run or maintain by default — the graph is just files.\n\nNothing is written until you pick. Run `graft init --dry-run`\n\nto see every file it would touch first, or `graft init --agents claude`\n\nto skip the prompt and wire Claude Code alone.\n\n`graft build`\n\nadds `graft/`\n\nto your `.gitignore`\n\nautomatically — the graph is a local, regenerable cache (like `node_modules`\n\n), not something you commit. What you share is the wiring `init`\n\ndropped into `.claude/`\n\n; each teammate runs `graft build`\n\nto generate their own graph:\n\n```\ngit add .claude && git commit -m \"wire in graft\"\n```\n\nPrefer not to install globally? `npx @nanonets/graft init`\n\nworks the same way.\n\nEvery task, your coding agent starts blind. Before it changes anything, it re-explores the repo: grep a term, open a file, follow an import, back out, try again. It is rebuilding a picture of a codebase it mapped an hour ago and threw away. That rediscovery burns most of a run's tool calls, tokens, and latency, and it is pure overhead:\n\n**Repeated.** Every task pays the exploration cost again, from zero.**Discarded.** Whatever the agent figured out dies with the session.**Unshared.** The next teammate, and their agent, start from scratch too.\n\nHumans onboard to a codebase once. Agents onboard every single time.\n\nGraft builds that understanding **once** and writes it into your repo as a folder of linked markdown files, one node per system, API, or concept.\n\n**Real explanations, not a list of symbols.** Each node says, in plain English, what a part of the system does and how it connects to the rest, the way a senior engineer would explain it. That is the part an agent actually needs so it can skip the exploration. It is not a dump of function names.**A real graph you can read.** No embeddings, no similarity search, no index to keep warm. The graph is a set of linked files your agent opens, greps, and follows, exactly the way it reads any other file in the repo.**Grafted into git.** The graph is just files in`graft/`\n\n. Commit it, and anyone who clones the repo has it. No database, no server, no setup. Git does the syncing, and a stale graph shows up as a diff in review instead of rotting in some external store.**The diff lives with the code.** When a change moves things around, you see it in the graph diff in the same pull request, right next to the code that caused it.**Your provider, your key, your model.** Summaries are written by any provider you choose — OpenAI, Anthropic (native), OpenRouter, Fireworks, Groq, a LiteLLM proxy, or a local model — under your own key. The structural code graph (`graft build`\n\n,`graft check`\n\n) is deterministic tree-sitter and never calls a model at all.\n\nGraft builds the graph in two passes, both powered by a language model:\n\n**Read each file.** Every source file is summarized once into a short description of what it does.**Group into nodes.** Those summaries are grouped into a curated set of nodes (subsystems, key files, and concepts) with typed links between them. Graft chooses the right level of detail for you instead of making one node per file, so a big repo becomes a few dozen readable nodes.\n\n``` php\nflowchart LR\n    S[Source files] --> T[\"Tier 1 — tree-sitter<br/>no model, no key\"]\n    S --> P1[\"Pass 1 — LLM summarizes<br/>each file (--deep)\"]\n    T --> W[\"graft/.graph/wiring.json<br/>per-symbol code graph\"]\n    P1 --> P2[\"Pass 2 — group into nodes<br/>+ typed links\"]\n    P2 --> N[\"graft/*.md<br/>markdown node graph\"]\n```\n\nEvery pass is cached by content hash — the LLM ones and the tree-sitter parse alike. Re-running only touches the files that changed, so the second build is fast and cheap (on this repo, 124 files: 0.74s cold, 0.18s after one edited file, 0.18s with nothing changed). `graft build --no-reuse`\n\nforces a cold re-parse.\n\nThat cheapness is what lets **every query refresh the graph before it answers**. A retrieval call stats the tree against the last build's fingerprint (~3ms), and rebuilds only if something moved — so `ask`\n\n/`grep`\n\n/`callers`\n\n/`skeleton`\n\n/`map`\n\ndescribe the code as it is right now, including edits that are unsaved to git: uncommitted, unstaged, or staged all look the same to graft, which never reads git at all. The refresh is structural and `$0`\n\n; it never calls the LLM. Turn it off per-command with `--no-refresh`\n\n, or everywhere with `GRAFT_NO_REFRESH=1`\n\n.\n\nAlongside the markdown graph, `graft build`\n\nbuilds `graft/.graph/wiring.json`\n\n— a per-symbol code graph — plus a per-file wiring card mirroring your source tree. Tier 1 is pure tree-sitter (every function, class, and call edge; deterministic, no model, no network), which is why plain `graft build`\n\nneeds no key. The `--deep`\n\npass adds a one-line summary and a crux excerpt per symbol, cached by body hash.\n\nA node is a single markdown file. Most code maps stop at an address: this thing lives in that file, on that line. That tells an agent where to look, not what it will find, so it still has to open the source and read. A Graft node holds the meaning inline, so the agent learns what it needs up front and opens the file only when it wants more.\n\nEach node holds:\n\n| Part | What it holds |\n|---|---|\nSummary |\nA plain-English explanation of what the code does, written by the model and cached. It is there whether or not the code was ever documented, and it is regenerated when the source changes. |\nCrux |\nThe handful of lines that actually carry the logic: the guard, the skip condition, the state change. Lifted straight from the source and stored inline, so the agent sees how it works, not just what. |\nSources |\nThe exact files the node is built from, each tracked by a content hash, so Graft can tell precisely when a node has gone stale. |\nLinks |\nTyped connections to other nodes (`depends_on` , `part_of` , `uses` , `implements` , `produces` ), written as `[[wikilinks]]` your agent can follow. |\nNotes |\nAnything you write below the generated block. It is preserved across regenerations, so your own context is never overwritten. |\n\nThat is three depths in one file: the summary says *what* the code does, the crux shows *how*, and the sources point to the rest if the agent needs it. A plain index makes it read a whole file to learn one thing. A Graft node hands it the answer inline, and the follow-up read often never happens.\n\nThe crux is stored as the code itself, not as a line range, on purpose. Line numbers drift whenever unrelated code above them shifts, but the lines that matter do not. Keeping the text, not the numbers, means the crux stays correct even as the file around it moves.\n\n*Summary, sources, links, and notes ship today in markdown nodes. The crux ships per-symbol in the code graph ( graft build --deep); inlining it into markdown nodes is next.*\n\n**On your machine, no key, no network:** the structural code graph.`graft build`\n\n(wiring graph + per-file cards),`graft check`\n\n, and`graft ask`\n\nare deterministic tree-sitter — they never call a model.**Through your provider key:** the LLM-written parts —`graft build --deep`\n\nadds the concept nodes (file summaries + node synthesis) and the per-symbol summaries and cruxes. graft is vendor-neutral: set`GRAFT_PROVIDER`\n\n(`openai`\n\nfor any OpenAI-compatible endpoint, or`anthropic`\n\nfor the native API), your`GRAFT_API_KEY`\n\n,`GRAFT_MODEL`\n\n, and — for the`openai`\n\nwire format —`GRAFT_BASE_URL`\n\nto point at OpenRouter, Fireworks, Groq, a LiteLLM proxy, a local server, or OpenAI itself. Or pass`--provider/--model/--api-key/--base-url`\n\non the command line. (`OPENROUTER_API_KEY`\n\nstill works as a deprecated fallback.)**No telemetry** and no analytics — the only network calls are the LLM requests you configured.\n\nSee [ .env.example](/NanoNets/Graft/blob/main/.env.example) for the full list of settings (model, base URL, graph directory).\n\nOne command wires Graft into the coding agents you use:\n\n```\nnpx @nanonets/graft init\n# detects your agents and writes each one's native instruction file;\n# Claude Code additionally gets the live statusline + hooks below\n```\n\nOn a terminal, `init`\n\nshows you every agent it knows about — flagging the ones it detected (via their config directories) and listing the exact files each would write — and wires only the ones you select. Claude Code is pre-selected; nothing else is. Selected agents get a marker-fenced Graft section in their shared instruction file — `AGENTS.md`\n\n(Codex, OpenCode and other CLIs that read it), `GEMINI.md`\n\n, `.github/copilot-instructions.md`\n\n— or a wholly-owned rule/skill file for the agents that use one — `.cursor/rules/graft.mdc`\n\n, `.kiro/steering/graft.md`\n\n, `.windsurf/rules/graft.md`\n\n, `.adal/skills/graft/SKILL.md`\n\nfor [AdaL](https://adal.sylph.ai) (progressive-disclosure skill, same shape as the Claude Code skill below). Re-running only updates Graft's own section (or replaces the owned file) and never touches the rest of your content.\n\nWith no TTY to prompt on — CI, a Dockerfile, a piped shell — `init`\n\nwrites **nothing** and prints the command to run instead. Pass `--agents <ids>`\n\nor `--yes`\n\nto make a scripted run explicit.\n\n| Flag | Effect |\n|---|---|\n`--agents <ids...>` |\nwire only these, no prompt — ids: `agents` , `cursor` , `gemini` , `copilot` , `kiro` , `windsurf` , `adal` , `claude` |\n`--yes` , `-y` |\nskip the prompt and wire every detected agent |\n`--dry-run` |\nprint every file `init` would touch, then exit without writing |\n`--all-agents` |\nwrite instruction files for every known agent, detected or not |\n`--no-agents` |\nClaude Code wiring only; skip other agents |\n`--list-agents` |\nprint the known agent ids and exit |\n`--no-mcp` |\nskip MCP server registration |\n`--no-hooks` |\nskip hook installation |\n`--no-global` |\nskip writes outside this repo (the `~/.codex/` entries below) |\n\nSelecting the `agents`\n\nhost also touches your **user-level** Codex config, when `~/.codex/`\n\nexists:\n\n| Path | What changes |\n|---|---|\n`~/.codex/config.toml` |\nregisters the Graft MCP server (`[mcp_servers.graft]` ) |\n`~/.codex/hooks/graft/graft-hooks.cjs` |\nthe post-edit hook shim |\n`~/.codex/hooks.json` |\na `PostToolUse` entry matching `Write|Edit|MultiEdit` |\n\nBoth configs are user-level, so they apply to **every** repo you open with Codex, not just this one. The picker labels these `machine-wide`\n\n, `--dry-run`\n\nlists them in their own section, and `--no-global`\n\nskips them while still wiring `AGENTS.md`\n\n.\n\n`graft init`\n\nalso registers Graft's MCP server with agents that support it, so these six tools appear natively, no shell required. Claude Code gets this too: `graft init`\n\nwrites the server into the project's `.mcp.json`\n\n(restart Claude Code to load it). Skip with `--no-mcp`\n\n; run it manually with `graft mcp [dir]`\n\n.\n\n| Tool | Takes | What it's for |\n|---|---|---|\n`graft_ask` |\na question | Ranked nodes with file:line, source inlined — usually the full answer, no follow-up read needed. |\n`graft_skeleton` |\na file path | Every signature in that file, no bodies — the API surface for a tenth of the tokens. |\n`graft_callers` |\na symbol | Who depends on it, or what it depends on with `direction: out` , N levels deep for blast radius. |\n`graft_grep` |\na regex | Every hit, grouped by enclosing symbol, ranked by how coupled that symbol is. |\n`graft_map` |\nnothing | A first look at an unfamiliar repo: directory clusters, hubs, hotspots. |\n`graft_check` |\nnothing | Whether the local graph has drifted from the code. |\n\nRegister it by hand if your agent needs it explicit:\n\n```\n{ \"mcpServers\": { \"graft\": { \"command\": \"npx\", \"args\": [\"-y\", \"@nanonets/graft\", \"mcp\"] } } }\n```\n\nWhere a CLI agent supports user-level `hooks.json`\n\n, `init`\n\nalso installs Graft's post-edit hook — blast-radius warnings and automatic `$0`\n\ngraph re-sync after edits (skip with `--no-hooks`\n\n).\n\n`graft init`\n\nalways wires up Claude Code, and Claude Code gets more than an instruction file. From then on, any Claude Code session opened in the repo gets:\n\n**a live statusline**— graph size, % enriched, and a`⚠ N stale`\n\nwarning when the code has moved ahead of the graph**auto-sync**— every graft query brings the graph up to date first, so an answer always describes the code as it is right now, uncommitted edits included. A query refreshes only what it reads; the markdown under`graft/`\n\nis refreshed by the background rebuild at the end of a turn that touched code. Both are structural and`$0`\n\n— auto-sync never calls the LLM on its own**context on tap**— each prompt pulls the matching nodes into the session; editing a file surfaces what depends on it (\"blast radius\"); new sessions start with the repo map\n\n[\n](/NanoNets/Graft/blob/main/assets/graft-hook-blast-radius-demo.gif)edit a file → blast radius appears inline → graph auto-resyncs → confirmed in graft viz\n\n`graft init`\n\nis idempotent and never clobbers your existing `.claude/settings.json`\n\n— it merges its blocks and leaves the rest alone. Want the LLM summaries too? Run `graft build --deep`\n\n(with a key) whenever you like; auto-sync will never do it for you.\n\n```\ngraft build [dir]                    # build graft/ from the code at [dir]: wiring graph + per-file cards (no LLM, no key)\ngraft build --deep                   # add the LLM layer: concept nodes + per-symbol summary/crux (cached)\ngraft build --extensions .ts .py     # only include these code extensions\ngraft build --no-reuse               # re-parse every file instead of replaying unchanged ones from cache\n\ngraft ask \"<task>\" [dir]             # query the graph — ranked nodes + exact file:line (no LLM, no key)\ngraft ask \"<task>\" --json            # machine-readable result\ngraft ask \"<task>\" --in <scope>      # narrow to one sub-project of a monorepo/multi-repo folder (see below)\n\ngraft skeleton <file> [dir]          # every signature in one file, no bodies — the API surface for ~1/10th the tokens (no LLM, no key)\n\ngraft callers <symbol> [dir]         # who calls/references/imports/implements/extends a symbol (no LLM, no key)\ngraft callers <symbol> --direction out  # the reverse: what the symbol itself calls/references (was `graft callees`)\ngraft callers <symbol> -d N          # walk transitively out to depth N — full blast radius (was `graft impact`)\n\ngraft grep \"<regex>\" [dir]           # exhaustive regex search over indexed files, grouped by enclosing symbol (no LLM, no key)\ngraft grep \"<regex>\" --in <path>     # narrow to files whose path contains this substring\ngraft grep \"<regex>\" -i --fixed      # case-insensitive; treat the pattern as a literal string, not a regex\n\ngraft map [dir]                      # token-budgeted repo orientation — dir clusters, hubs, hotspots (no LLM, no key)\ngraft map --max-dirs N               # raise/lower the number of directories shown\n\ngraft check [dir]                    # fail (exit 1) if graft/ has drifted from the code (never auto-refreshes — it's the drift report)\ngraft check --json                   # print the drift report as JSON\n\n# ask / skeleton / callers / grep / map all refresh the graph first if the working tree moved:\n#   --no-refresh                     # answer from the graph exactly as it is on disk\n#   GRAFT_NO_REFRESH=1               # same, for every command\n#   GRAFT_REFRESH=hash               # hash every file instead of trusting size+mtime\n\ngraft viz [dir]                      # see the graph: serves an interactive viewer on localhost\ngraft viz --port 5000 --no-open      # pick a port; don't auto-open the browser\n\ngraft init [dir]                     # pick which agents to wire (prompts on a terminal; writes nothing until you choose)\ngraft init --dry-run                 # list every file it would touch, then exit\ngraft init --agents cursor kiro      # wire only these agents, no prompt (ids: agents, cursor, gemini, copilot, kiro, windsurf, adal, claude)\ngraft init --yes                     # no prompt; wire every detected agent\ngraft init --no-global               # skip writes outside this repo (~/.codex/ config + hooks)\ngraft init --no-build                # wire the files only; don't build the graph\ngraft init --all-agents              # wire every known agent, detected or not\ngraft init --list-agents             # list known agent ids and exit\n\ngraft version                        # print the installed + latest published npm version\ngraft upgrade                        # npm install -g the latest published version\n\n# global\ngraft --dir <path>                   # use a context dir other than <repo>/graft\ngraft --version, -v                  # print the installed version and exit\n```\n\nMethod calls resolve through the receiver's type — constructor assignments\n(`self.router = APIRouter()`\n\n) and type annotations, not just the call-site\nname — so `callers`\n\n/`grep --in`\n\nreturn calls bound to the right\ntype on method-heavy code, not every method anywhere with that name.\n\n`graft grep \"<regex>\"`\n\nis exhaustive over every indexed file and groups hits\nby enclosing symbol, ranked by the same in-edge coupling `graft map`\n\nuses —\nbuilt for \"every occurrence of this pattern\" tasks where `graft ask`\n\n's\nranked top-N isn't enough:\n\n```\n\"NEEDLE\" — 2 hits in 2 symbols across 1 files (searched 1 indexed files)\n\nheavilyCalled · function · src/a.ts:L1-L3 · 3 in-edges\n  L2: console.log(\"NEEDLE hit in heavilyCalled\");\n\nrarelyCalled · function · src/a.ts:L4-L6 · 0 in-edges\n  L5: console.log(\"NEEDLE hit in rarelyCalled\");\n```\n\n`graft map`\n\nis a token-budgeted first look at a repo — directory clusters\nwith file/symbol counts, each dir's local hubs, and the global hotspots —\nall ranked by in-degree, no LLM, no key:\n\n```\nrepo map — 113 files · 687 symbols · 2186 edges · typescript\n\nsrc/                63 files · 527 symbols   hubs: contextDirFor (node-file.ts, 21←), wiringPath (write.ts, 14←), buildGraph (build.ts, 11←)\ntest/               43 files · 102 symbols   hubs: edge (graph-traverse.test.ts, 4←), graphOf (graph-traverse.test.ts, 4←), fileNode (graph-map.test.ts, 3←)\nviewer/             5 files · 58 symbols   hubs: $ (main.ts, 9←), activeGraph (main.ts, 5←), cvar (data.ts, 5←)\nscripts/            2 files · 0 symbols\n\nhotspots: contextDirFor · function · src/context/node-file.ts:L100-L103 · 21←  wiringPath · function · src/graph/write.ts:L20-L22 · 14←  buildGraph · function · src/graph/build.ts:L104-L218 · 11←  ...\n```\n\nGraft handles two shapes without any config:\n\n**A monorepo with one**(a`.git`\n\n`pnpm-workspace.yaml`\n\n/`package.json`\n\n`workspaces`\n\n, or per-package`go.mod`\n\n/`pyproject.toml`\n\n/`Cargo.toml`\n\n) —`graft build`\n\ndiscovers each sub-project as a ranking scope.`ask`\n\n/`map`\n\nrank every scope on its own terms and fuse the results, so the biggest sub-project can't drown a small one; hits carry`[scope/]`\n\nlabels, and`graft map`\n\ngroups its directory clusters by scope first.**A folder of separate git repos**(no`.git`\n\nat the top) —`graft build`\n\nauto-splits: each child gets its own (git-ignored)`graft/`\n\n, and the parent gets a`graft/workspace.json`\n\nindex. Queries from the parent federate across every child, always labeled`<child>/`\n\n. Run`graft build`\n\ninside a child to work on just that repo.\n\nEither way, narrow to one sub-project with `graft ask \"<task>\" --in <scope>/`\n\nonce you know where you're working.\n\n`graft viz`\n\nopens a local, interactive view of both graphs — no install, no dev\nserver; the viewer ships prebuilt inside the package.\n\n[\n](/NanoNets/Graft/blob/main/assets/graft-viz-demo.gif)search → jump to a node → dependency graph lights up\n\n**Context** tab — the architecture graph from`graft/*.md`\n\n. Nodes colored by type, sized by connectedness.**Code** tab — the per-symbol graph from`graft/.graph/wiring.json`\n\n(run`graft build`\n\nfirst).**Outline** tab — the file → class → method hierarchy as a collapsible tree.\n\nEdges speak the code's language. Every link is one of a closed set of verbs, each answering a question someone building or reviewing code actually asks:\n\n| Verb | The question it answers |\n|---|---|\n`part_of` / `contains` |\nwhere does this live? |\n`uses` / `calls` / `imports` / `depends_on` |\nwhat breaks if I change this? |\n`produces` |\nwhere does this output come from? |\n`configures` |\nwhat changes its behavior without a code change? |\n`validates` |\nwhat checks or judges this? (tests, drift checks, scoring) |\n`extends` / `implements` |\nwhat contract must this honor? |\n\nSelect a node and its edges take on direction: **amber = what it depends on,\nteal = what depends on it**, with the verb written on each highlighted edge.\nChips above the canvas filter by verb; tree-sitter-extracted edges draw solid\nwhile LLM-inferred ones draw dashed. The viewer live-reloads when `graft/`\n\nchanges on disk. Older graphs with vague verbs (`influences`\n\n, `supports`\n\n) are\nnormalized on load — no regeneration needed.\n\nAn agent that reads the graph should be cheaper and faster without getting more answers wrong. That's the whole claim, so we measured it instead of asserting it.\n\nThe harness ran three variants of the same Claude Sonnet 5 agent with the same file tools: **cold** (explores from zero), **Graft** (a `graft ask --source`\n\nbundle pushed up front), and **pull** (graft_ask/graft_skeleton tools, nothing injected — context paid for only when asked). An Opus 4.8 judge scored correctness with a required-keyword floor, so a fast-but-wrong answer couldn't win by being fast. Cost is cache-aware: reads ≈0.1×, writes 1.25×, the billing model agents actually run under.\n\n162 runs, two repos (graft itself and a real Node/Express auth service), 3 trials each, tasks split between single-file and multi-file questions.\n\n| Metric (mean/task) | Cold | Graft |\n|---|---|---|\n| Cost ($) | 0.0429 | 0.0292 (−32%) |\n| Uncached input tokens | 8,070 | 4,650 (−42%) |\n| Tool calls | 4.2 | 2.3 (−46%) |\n| Latency (s) | 39.8 | 15.8 (−60%) |\n| Correctness | 93% | 93% (equal) |\n\nGraft never answered worse than cold, on any corpus. The pull variant gave up most of that speed for something bigger: correctness jumped to 98%, +5 points over cold, the strongest single result in the sweep. Push when speed is what you need; pull when being right matters more.\n\nThe sweep above measures the mechanism. The real test is whether graft helps an agent **ship real changes** on code people actually run, not just answer questions. So we benchmark it on popular open-source repos: **15 tasks each**, 10 real developer questions plus **5 actual implementation tasks** (real merged pull requests, each re-implemented from its base commit and scored against the files the maintainers actually changed). Same agent (Claude Opus), same file tools; the only difference is whether graft is wired in.\n\nAcross these repos graft runs **up to 4× cheaper and 3× faster**, with better or no loss of correctness: it reproduces the real merged PRs by touching the same files the maintainers did. Per-repo detail below.\n\n| Aggregate over 15 tasks | Standard Claude Code | With graft |\n|---|---|---|\n| Cost | $13.91 | $11.02 (−21%) |\n| Wall-clock | 2,044s | 1,762s (−14%) |\n| PRs reproduced | 5 / 5 | 5 / 5 (same files as the maintainers) |\n\nCheaper and faster with no loss of correctness: graft reproduced all five merged PRs, touching the same files the maintainers did. The gap is widest on cross-file understanding — \"how does auth work across OAuth2 providers\" dropped from $2.19 to $0.84.\n\n**The 10 questions we asked**\n\n**Orientation**— Give me a map of PocketBase's architecture: the main subsystems and how an HTTP request flows through to the database.** Entry-point trace**— Trace end-to-end what happens when a client creates a record via the REST API, from route handler to database write.** Feature location**— I want to add a brand-new collection field type. Where do I hook it in, and which pieces must change?** Bug localization**— Realtime subscriptions silently stop delivering events after a while. Where would you start looking, and why?** Blast radius**— If I change the signature of the record-validation logic, what depends on it and what could break?** Cross-file synthesis**— How does auth work across OAuth2 providers: where are tokens issued, validated, stored, and refreshed?** Extensibility**— How do I use PocketBase as a Go framework to register a custom route plus an on-record-create hook?** Security discovery**— Where is user input validated, and where are collection API access rules enforced before a query runs?** Public API**— As an external app, how do I authenticate and then list and filter records over the REST API?** Test verification**— Where are the tests for the record CRUD API, and what do they assert about access rules?\n\n**The 5 merged PRs we re-implemented**\n\nEach PR was reset to its base commit; graft's diff was scored against the files the merged PR changed.\n\n| PR | Type | What it does | Files the maintainers touched |\n|---|---|---|---|\n|\n\n`apis/file.go`\n\n, `tools/filesystem/filesystem.go`\n\n[#6947](https://github.com/pocketbase/pocketbase/pull/6947)`tools/security/random_by_regex.go`\n\n[#6690](https://github.com/pocketbase/pocketbase/pull/6690)`x/oauth2/endpoints`\n\n`tools/auth/patreon.go`\n\n[#2726](https://github.com/pocketbase/pocketbase/pull/2726)`apis/middlewares.go`\n\n[#3192](https://github.com/pocketbase/pocketbase/pull/3192)`plugins/migratecmd/templates.go`\n\n**Method**\n\nTwo clones of PocketBase at the same commit: one wired with `graft init`\n\n, one untouched and verified graft-free. Each task run headless (`claude -p`\n\n, Claude Opus) with an empty MCP config. Understanding questions were graded by whether the answer pointed to the right files and functions; PR tasks were scored on whether the agent's diff touched the same files as the merged PR. Every transcript was audited to confirm graft was actually used in the graft arm and absent from the standard arm.\n\n```\ngit clone https://github.com/NanoNets/context-graph-engine.git && cd context-graph-engine\nnpm install\nnpm run build\nnpm test\n\nnpm run cli -- build --deep .      # run the CLI from source\n```\n\nMIT. See [LICENSE](/NanoNets/Graft/blob/main/LICENSE).", "url": "https://wpnews.pro/news/graft", "canonical_source": "https://github.com/NanoNets/Graft", "published_at": "2026-07-30 07:42:13+00:00", "updated_at": "2026-07-30 07:52:20.062802+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "large-language-models", "ai-tools"], "entities": ["Nanonets", "Claude Code", "Cursor", "Codex", "Gemini", "PocketBase", "ollama", "Excalidraw"], "alternates": {"html": "https://wpnews.pro/news/graft", "markdown": "https://wpnews.pro/news/graft.md", "text": "https://wpnews.pro/news/graft.txt", "jsonld": "https://wpnews.pro/news/graft.jsonld"}}