プログラミング
Show HN: Graft – Claude Code の grep トークンを 42% 削減するフック
Show HN: Graft – Claude Code hooks that cut grep tokens by 42% (github.com)
要約
Graftは、Claude Codeなどのコーディングエージェントの効率を向上させるツールです。コードベースのコンテキストを理解するグラフを構築し、関連情報をより速く安価に提供することで、トークン使用量とレイテンシを削減します。ベンチマークによると、Graftは効率と正確性の両方で大幅な改善をもたらします。
全文翻訳
Turbocharge Claude Code, Cursor, Codex, Gemini & every coding agent: faster, cheaper, with contextual understanding specific to your codebase. Up to 4× cheaper and 3× faster, with better or no loss of correctness. Metric Cold Claude Code Claude Code with graft Tool-call reduction Baseline +46% Token savings Baseline +42% Time savings Baseline +60% Correctness 54% 66% (+12 pts) Efficiency is a 162-run controlled benchmark (same agent, same file tools, only the context differs). Correctness is SWE-bench Verified, graded by the official harness — graft resolved 66% of instances tested against Cold Claude Code's 54%. Efficiency method ↓ · SWE-bench ↓ · Per-repo numbers ↓ Contents Quick start The problem What Graft does Benchmark SWE-bench Verified How the graph gets built Supported languages What's in a node What runs where Agent integration — MCP server · Claude Code (deep integration) CLI Search & orient (graft grep / graft map) Monorepos & multi-repo folders Visualize it (graft viz) Tested on your popular repos Development License Quick start npm install -g @nanonets/graft # install the CLI, once graft init # build the graph + wire it into Claude Code That is the whole setup. graft init asks which of your coding agents to wire up, builds graft/ from your code, and drops a statusline and hooks into .claude/, 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. Nothing is written until you pick. Run graft init --dry-run to see every file it would touch first, or graft init --agents claude to skip the prompt and wire Claude Code alone. graft build adds graft/ to your .gitignore automatically — the graph is a local, regenerable cache (like node_modules), not something you commit. What you share is the wiring init dropped into .claude/; each teammate runs graft build to generate their own graph: git add .claude && git commit -m "wire in graft" Prefer not to install globally? npx @nanonets/graft init works the same way. The problem Every 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: 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. Humans onboard to a codebase once. Agents onboard every single time. What Graft does Graft builds that understanding once and writes it into your repo as a folder of linked markdown files, one node per system, API, or concept. 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/. 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, graft check) is deterministic tree-sitter and never calls a model at all. Benchmark An 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. The 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 bundle pushed up front), and pull (graft_find_code/graft_file_api 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. 162 runs, two repos (graft itself and a real Node/Express auth service), 3 trials each, tasks split between single-file and multi-file questions. Metric (mean/task) Cold Claude Code Claude Code with graft Cost savings ($) 0.0429 0.0292 (+32%) Token savings 8,070 4,650 (+42%) Tool-call savings 4.2 2.3 (+46%) Latency savings (s) 39.8 15.8 (+60%) Correctness 93% 93% (equal) Graft 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. SWE-bench Verified The sweep above is our harness measuring our mechanism. So we ran the industry-standard one too — SWE-bench Verified, real GitHub issues from real repos, graded by the official swebench harness. No judge model, no similarity score: your patch is applied, the maintainers' own tests are run, and you either flip the failing test without breaking the passing ones or you don't. 50 instances, same model on both arms — Claude Sonnet 5 — same Docker images, same turn limits. The only difference is whether graft is wired in. Correctness & efficiency Cold Claude Code Claude Code with graft Improvement Correctness 27 / 50 (54%) 33 / 50 (66%) +12 pts Token savings 142.0M 109.4M +23% Cost savings $52.34 $42.43 +19% Tool-call savings 1,370 1,031 +25% API-request savings 2,455 1,875 +24% Wall-clock savings 13,094s 8,922s +32% graft resolved 33 of 50 instances against Cold Claude Code's 27 — and got there with 25% fewer tool calls, 23% fewer tokens, and 32% less wall-clock time. Every correctness win has the same shape: the baseline patches one file and misses its siblings. On django-11532 it patched 1 of the 5 files the fix requires and broke 18 previously-passing tests, twice over. On django-16263 it patched 1 of 4 and scored 102 / 103. graft found the rest — and on django-16263 did it in half the tokens and half the time. Two harnesses, two claims: the controlled sweep says graft is cheaper and faster, SWE-bench says it's also more correct. Correctness over all instances; tokens, cost and calls over the instances both arms resolved, for a like-for-like comparison. Official SWE-bench Verified images and official swebench 4.1.0 grader, native x86_64. How the graph gets built Graft builds the graph in two passes, both powered by a language model: 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. flowchart LR S[Source files] --> T["Tier 1 — tree-sitter<br/>no model, no key"] S --> P1["Pass 1 — LLM summarizes<br/>each file (--deep)"] T --> W["graft/.graph/wiring.json<br/>per-symbol code graph"] P1 --> P2["Pass 2 — group into nodes<br/>+ typed links"] P2 --> N["graft/*.md<br/>markdown node graph"] Loading Every pass is cached by content hash — the LLM one