Graft : cartographier un dépôt pour les agents de code
Découvrez comment Graft analyse votre dépôt avec tree-sitter et expose une cartographie persistante via MCP, pour éviter de reparcourir sa structure à chaque session.
Ce qu’est une cartographie du code pour les agents de programmation
Une cartographie du code pour les agents de programmation est un index persistant de votre dépôt. L’agent y effectue des recherches au lieu de parcourir le dépôt avec grep depuis le début à chaque nouvelle session. Graft est une implémentation de cette approche. Il analyse votre code avec tree-sitter, écrit un dossier de nœuds Markdown liés ainsi qu’un graphe de connexions par symbole, puis fournit des outils de récupération via MCP (model context protocol, l’interface standard utilisée par les agents de programmation pour appeler des outils externes).
Graft n’est ni un proxy ni une gateway. Rien ne s’interpose entre votre agent et l’API du modèle. La cartographie est un dossier sur le disque que l’agent lit. Cette distinction détermine le problème que vous cherchez à résoudre : une gateway de tokens auto-hébergée mesure et achemine les requêtes que vous envoyez déjà, tandis qu’une cartographie modifie le nombre total de requêtes nécessaires.
Cette technique est antérieure à cet outil et lui survivra. Commencez par apprendre la technique, puis passez aux aspects pratiques.
Pourquoi les agents de programmation consomment du contexte pour redécouvrir la structure
Observez un agent commencer à travailler sur un dépôt qu’il a déjà vu cinquante fois. Il liste les répertoires. Il recherche un symbole avec grep. Il ouvre trois fichiers pour trouver lequel définit la fonction, puis un quatrième pour déterminer qui l’appelle. Rien de tout cela ne fait partie de la tâche. C’est une phase de repérage, facturée en tokens d’entrée à chaque session.
La cause est simple. Un modèle n’a aucune mémoire d’une session à l’autre. Tout ce que l’agent a appris sur votre organisation se trouvait dans une fenêtre de contexte supprimée à la fin de la session. La même découverte recommence donc depuis zéro, au coût total. Dans un dépôt volumineux, la phase de repérage coûte plus cher que la modification : dix appels d’outils pour localiser le code, un seul pour le modifier. Le repérage ne représente que la moitié de cette facture et la modification l’autre moitié. C’est pourquoi une compétence qui impose à l’agent de limiter la modification au strict nécessaire mérite d’être associée à une carte plutôt que choisie à sa place.
Une carte élimine cette boucle en déplaçant la découverte du modèle vers le disque. Un parser parcourt le dépôt une fois, enregistre l’emplacement de chaque définition de symbole et les appels entre symboles, puis maintient ces informations à jour lorsque le code change. L’agent pose une question et obtient une réponse indiquant le fichier et la ligne concernés. L’exploration répétée devient une simple recherche peu coûteuse.
Vous utilisez déjà une version moins complète de ce mécanisme. Un fichier AGENTS.md qui définit vos conventions évite à l’agent de redéduire vos conventions à chaque fois. Une carte générée lui évite de redéduire votre structure. La différence tient à celui qui l’écrit. Vous rédigez le fichier d’instructions à la main, il reste donc compact. Un parser génère la carte, qui peut ainsi couvrir dix mille fichiers. Pour comprendre comment le budget est réellement consommé dans une session, la façon dont Claude Code utilise sa fenêtre de contexte présente le détail des coûts.
Ce que Graft construit
Deux artefacts, regroupés dans un seul dossier graft/ à la racine du dépôt.
Le premier est un graphe de nœuds au format Markdown lié, avec un fichier par nœud. Chaque nœud contient un résumé en anglais simple, un « crux » regroupant les lignes importantes de la logique extraites du code source, les fichiers source exacts avec un hash de contenu, des wikilinks typés vers d’autres nœuds (depends_on, part_of, uses, implements), ainsi qu’une section de notes conservée lors des régénérations. Vous pouvez y consigner le contexte qu’un parser ne peut pas déduire.
Le second est graft/.graph/wiring.json, le graphe structurel par symbole extrait par tree-sitter : définitions, références et relations d’appel entre ces éléments.
Cette séparation est importante, car une seule moitié nécessite un modèle. graft build utilise uniquement tree-sitter et n’appelle jamais de LLM (large language model). Son résultat est donc déterministe et ne coûte rien. graft build --deep ajoute les résumés rédigés et les crux par symbole. Ces éléments nécessitent des appels au modèle, qui sont facturés.
La prise en charge des langages est organisée par niveaux. Le niveau indique dans quelle mesure vous pouvez faire confiance à un graphe d’appels. TypeScript, JavaScript, Python, Go et Java bénéficient d’une résolution inter-fichiers tenant compte de la portée. Rust, C, C++, C#, Ruby, PHP, Kotlin, Scala, Swift, Elixir, Solidity, OCaml, Zig et Dart fournissent les symboles et des relations d’appel génériques. Une relation peut donc reposer sur une correspondance de nom plutôt que sur une référence résolue. Les relations de niveau compilateur sont activées explicitement avec --lsp et un language server tel que rust-analyzer ou gopls.
Installer Graft et figer la version
Graft nécessite Node.js 20 ou une version ultérieure et est distribué sous licence MIT. En août 2026, la version actuelle est 0.10.1. La première version publiée, 0.1.0, date de juillet 2026. Considérez donc ce logiciel comme récent.
npm install -g @nanonets/graft@0.10.1
npm ls -g @nanonets/graftnpm ls -g doit afficher @nanonets/graft@0.10.1. Figez volontairement cette version. Une commande npm install -g @nanonets/graft seule résout le tag latest au moment de son exécution. Pour un projet qui publie plusieurs versions mineures par mois, vous pouvez ainsi utiliser un outil différent le mardi de celui que votre collègue a installé le lundi. Une version figée conserve les mêmes options CLI et le même format de graphe pour tout le monde. Vous effectuez donc la mise à niveau quand vous le décidez.
Intégrez-le ensuite dans un dépôt dont vous avez la responsabilité :
cd /path/to/your/repo
graft init --dry-run
graft initgraft init demande quels coding agents vous souhaitez intégrer, puis construit le graphe. Exécutez d’abord --dry-run et lisez la liste des fichiers qu’il prévoit de modifier, car certains se trouvent en dehors du dépôt. graft init est idempotente et n’écrase pas les configurations existantes. Vous pouvez donc l’exécuter une deuxième fois sans risque.
En août 2026, l’intégration couvre Claude Code, Cursor, Codex, GitHub Copilot, Google Gemini, Kiro, Windsurf et AdaL. Claude Code bénéficie de l’intégration la plus complète : une entrée de serveur MCP, une statusline indiquant la taille et l’ancienneté du graphe, des hooks post-edit qui reconstruisent le graphe et un fichier de skill dans .claude/. Les autres outils reçoivent un fichier d’instructions ou de règles qui indique à l’agent que ces outils existent. « Supported » signifie donc que Graft écrit cette intégration. Un agent qui ignore son propre fichier de règles ignorera aussi la cartographie. C’est la raison habituelle pour laquelle les agents ignorent les instructions que vous leur donnez, et ce cas ne fait pas exception.
Ce qui est ajouté au dépôt et ce qui reste hors de git
Après graft init, vous devez trouver les éléments suivants :
graft/: le graphe de nœuds markdown etgraft/.graph/wiring.json. Ajouté automatiquement à.gitignore..mcp.json: enregistre le serveur MCP graft pour que Claude Code le démarre..claude/settings.json: fusionné sur place, avec la statusline et les hooks post-edit.AGENTS.md,GEMINI.md,.github/copilot-instructions.md,.cursor/rules/graft.mdc,.kiro/steering/graft.md,.windsurf/rules/graft.mdet.adal/skills/graft/SKILL.md: sections délimitées par des marqueurs, ajoutées aux fichiers correspondant aux agents sélectionnés.~/.codex/config.toml,~/.codex/hooks.jsonet~/.codex/hooks/graft/graft-hooks.cjs: éléments propres à la machine, écrits uniquement si vous sélectionnez Codex.graft init --no-globalles ignore, etgraft init --no-hooksignore à lui seul le shim du hook.
Le graphe est un cache, comme node_modules. Ne le commitez pas. Il est régénéré en quelques secondes à partir du code, il change presque à chaque modification et son commit transforme une correction d’une ligne en un diff de plusieurs centaines de fichiers qu’aucun reviewer ne lira. Commitez plutôt le câblage, notamment AGENTS.md et .mcp.json. Un collègue clone le dépôt, exécute graft build et obtient son propre graphe local.
Vérifiez que la règle d’exclusion a bien été ajoutée avant votre premier commit :
grep -n graft .gitignore
git status --shortgrep doit afficher une ligne contenant graft/, et git status --short ne doit rien lister sous graft/. Si des fichiers sous graft/ apparaissent dans cette sortie, l’entrée d’exclusion est absente ou remplacée ailleurs. Corrigez le problème avant de commiter, car git continue de suivre un fichier dès qu’il a été ajouté, et une modification ultérieure de .gitignore ne le désindexera pas.
Si vous préférez enregistrer manuellement le serveur MCP ou le figer sur la même version que celle que vous avez installée, l’entrée est courte :
{
"mcpServers": {
"graft": {
"command": "npx",
"args": ["-y", "@nanonets/graft@0.10.1", "mcp"]
}
}
}Les outils de récupération que votre agent utilise à la place de grep
Graft expose six outils via MCP. graft_find_code renvoie les nœuds classés par pertinence pour une description de tâche, avec le fichier et la ligne concernés. graft_file_api renvoie toutes les signatures d’un fichier sans leur corps. graft_trace_calls parcourt les appelants ou les fonctions appelées sur plusieurs niveaux. graft_find_all renvoie les correspondances d’une expression régulière regroupées par symbole. graft_repo_map fournit une première vue d’un dépôt que vous ne connaissez pas. graft_check_freshness indique si le graphe correspond toujours au code.
Chacun dispose d’un équivalent CLI. C’est ainsi que vous vérifiez ce que votre agent reçoit réellement :
graft map .
graft ask "where do we validate the refresh token"
graft skeleton src/auth/session.ts
graft callers validateRefreshToken
graft callers validateRefreshToken --direction out
graft grep "refresh_token" --jsongraft ask doit afficher les nœuds classés par pertinence avec des références file:line, et non le contenu des fichiers. C’est tout le mécanisme : l’agent reçoit un pointeur et ouvre un seul fichier, au lieu d’en lire dix pour trouver le bon. graft viz ouvre une visionneuse interactive sur localhost si vous voulez examiner vous-même le graphe. Si graft ask ne renvoie rien d’utile pour une question à laquelle vous pourriez répondre en trente secondes, le graphe est obsolète ou votre langage appartient au niveau large, et la carte n’aidera pas davantage votre agent.
Un coût est facile à oublier. Les six définitions d’outils sont injectées dans le system prompt de chaque requête pendant toute la session. Vous les payez, que l’agent utilise la carte ou non. Dans un dépôt suffisamment petit pour tenir dans le contexte, ce coût fixe peut être supérieur au temps d’exploration économisé.
Ce qui arrive au graphe lorsque le code change
La mise à jour structurelle est rapide et automatique. Graft lit votre working tree, et non git. Une modification non commitée et une modification staged sont donc visibles de la même manière. Une requête n’analyse à nouveau que les fichiers dont le statut a changé. La documentation du projet estime cette surcharge à environ 3 ms. Une reconstruction en fin de tour ne traite que les fichiers dans lesquels du code a été déplacé. Définissez GRAFT_NO_REFRESH=1 ou transmettez --no-refresh pour répondre à partir du graphe sur disque sans effectuer de nouvelle analyse. Transmettez --no-reuse pour forcer une nouvelle analyse complète à froid. C’est ce qu’il faut faire après la mise à niveau de Graft lui-même.
La partie rédigée par le modèle fonctionne autrement. C’est là que les problèmes peuvent apparaître sans bruit. Les résumés et les crux sont mis en cache. Chaque nœud enregistre le hash de contenu de ses sources. Lorsqu’un fichier source change, le nœud est donc marqué comme obsolète au lieu d’être considéré comme à jour. Ce marqueur n’est utile que si un traitement en tient compte. Actualisez avec graft build --deep. Cette commande consomme à nouveau des tokens du modèle.
Rendez l’obsolescence visible :
graft check .
echo $?Un code de sortie 0 signifie que le graphe correspond au code. Un code de sortie 1 signifie qu’il y a une dérive. Exécutez cette commande depuis un hook pre-push ou sur la branche dans la CI. Vous éviterez ainsi qu’une carte vieille de six mois réponde avec assurance à propos d’un code réécrit en mars.
Lisez attentivement les chiffres des benchmarks publiés
L’affirmation principale de Graft est « jusqu’à 4 fois moins cher et 3 fois plus rapide, sans perte de correction ou avec une meilleure correction ». Ces chiffres proviennent des benchmarks du projet, publiés dans son README. Voici les deux exécutions qu’il présente dans leur intégralité.
The data behind this chart
[
{
"label": "Controlled sweep",
"run_count": 162,
"token_saving_pct": 42,
"tool_call_saving_pct": 46,
"correctness_pct": 93,
"baseline_correctness_pct": 93
},
{
"label": "SWE-bench Verified",
"run_count": 50,
"token_saving_pct": 23,
"tool_call_saving_pct": 25,
"correctness_pct": 66,
"baseline_correctness_pct": 54
}
]Le balayage contrôlé comprend 162 exécutions sur deux dépôts, dont Graft lui-même, avec trois essais par tâche. Il indique 42 % de tokens en moins et 46 % d’appels d’outils en moins. L’exécution SWE-bench Verified porte sur 50 instances, avec le même modèle dans les deux configurations, et indique une économie plus faible : 23 % de tokens et 25 % d’appels d’outils en moins. Une troisième exécution a reproduit cinq pull requests de PocketBase fusionnées, pour un coût de 11.02 dollars US contre 13.91 pour la configuration de référence.
Considérez l’ensemble comme un benchmark fourni par l’éditeur. Deux éléments limitent ce qu’il permet de conclure. Le balayage contrôlé inclut le dépôt de Graft, c’est-à-dire le code source sur lequel ses auteurs ont effectué leurs optimisations. SWE-bench Verified est un jeu de données public contenant des problèmes issus de projets Python open source connus. Les jeux de données publics sont précisément ceux pour lesquels les outils sont optimisés, même si ce n’est pas intentionnel. Aucun de ces résultats ne décrit votre monorepo privé, qui possède ses propres conventions de nommage et son propre code mort.
La correction mérite une deuxième lecture. Elle n’a pas changé lors du balayage contrôlé : 93 % avec la map, contre 93 % sans la map. Le passage à 66 %, contre 54 %, apparaît uniquement dans SWE-bench Verified. Un outil qui réduit votre facture de tokens sans dégrader la qualité constitue déjà un bon compromis. En revanche, ne transposez pas le résultat de correction de SWE-bench au résultat sur les tokens du balayage, et ne présentez pas les deux comme une seule affirmation.
Mesurez votre propre écart de tokens avant d’en tirer des conclusions
Le seul chiffre qui compte est celui de votre dépôt. Cette méthode prend un après-midi.
Choisissez une tâche que vous pouvez répéter à l’identique. Une question est préférable à une modification, car une modification change le dépôt et la deuxième exécution ne constitue plus la même expérience. « Quel module applique la limite de débit sur la route de connexion ? » est une question adaptée.
Activez la télémétrie et envoyez-la vers votre propre terminal :
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=console
claudeL’exporter de console affiche les enregistrements de métriques au fur et à mesure de leur collecte. Celui qui vous intéresse est claude_code.token.usage, qui contient un attribut type égal à input, output, cacheRead ou cacheCreation. L’orientation apparaît dans input et cacheRead, car c’est là que le contenu des fichiers est transmis. Additionnez ces deux valeurs.
Exécutez la tâche trois fois, chaque fois dans une nouvelle session, avec la map configurée. Supprimez ensuite l’entrée graft de .mcp.json et exécutez-la encore trois fois. Comparez les médianes plutôt que des exécutions isolées, car les exécutions par l’agent varient fortement et une exécution défavorable peut vous faire conclure exactement l’inverse de la réalité. Relevez également le nombre d’appels d’outils : les appels d’outils sont le mécanisme et les tokens l’effet. Une économie de tokens sans baisse du nombre d’appels d’outils signifie qu’un autre facteur a changé.
Soustrayez ensuite les coûts que le benchmark n’affiche pas. graft build --deep consomme des tokens du modèle à chaque actualisation complète. Les six schémas d’outils sont transmis dans chaque requête. Si vos agents s’exécutent sur un serveur que vous louez, définir un plafond strict pour les dépenses des agents transforme cette surprise en budget, et ce que la télémétrie d’un agent de codage signale réellement explique quelles données quittent la machine une fois l’exporter activé.
Quand une cartographie du code cesse-t-elle d’être utile ?
- Le dépôt tient déjà dans le contexte. Un petit service unique n’a pas besoin de cartographie, et vous payez quand même six schémas d’outils à chaque requête. Si votre agent trouve aujourd’hui n’importe quel fichier en un ou deux appels d’outils, ne l’utilisez pas.
- Votre langage appartient au niveau généraliste. Les liens d’appels génériques peuvent faire en sorte que
graft callersne trouve pas un appelant ou en crée un à cause d’une collision de noms. Vérifiez avecgraft grepavant de faire confiance au périmètre d’impact. - Le graphe est obsolète sans que personne ne s’en aperçoive.
graft checkse termine avec le code 1 en cas de divergence, ce qui n’est utile que si quelque chose l’exécute. Utilisez un hook ou une étape de CI, pas une vérification manuelle. - Le monorepo nécessite un périmètre. Un monorepo Git unique est automatiquement divisé selon le fichier workspace,
go.mod,pyproject.tomlouCargo.toml, etgraft ask "..." --in services/billing/limite une requête à un sous-projet. Le même réflexe qui conduit à utiliser des fichiers AGENTS.md imbriqués pour chaque package s’applique à la cartographie. - L’agent ignore le câblage. Observez les appels d’outils au cours d’une session réelle avant de conclure que la cartographie est utilisée. Un agent qui exécute encore
grepvous indique qu’il n’a jamais lu le fichier de règles.
FAQ
Dois-je ajouter le dossier graft/ à git ?
Non. graft build ajoute automatiquement graft/ à votre .gitignore, car le graphe est un cache régénérable, comme node_modules. Il change à presque chaque modification. Le versionner noie les vrais diffs sous des centaines de fichiers générés. Versionnez les éléments qui indiquent aux agents que la carte existe, notamment AGENTS.md et .mcp.json, puis laissez chaque membre de l’équipe exécuter graft build localement. Vérifiez avec grep -n graft .gitignore et git status --short avant votre premier commit, car git continue de suivre un fichier dès qu’il a été ajouté. Modifier .gitignore ensuite ne le retire pas du suivi.
Graft coûte-t-il de l’argent à l’exécution ?
La partie structurelle est gratuite. graft build, graft ask, graft check et les six outils de retrieval MCP utilisent tree-sitter et n’appellent jamais de modèle. graft build --deep est la partie payante : elle génère les résumés en anglais clair et les points essentiels de chaque symbole via un LLM. Elle se configure avec GRAFT_PROVIDER, GRAFT_API_KEY et GRAFT_MODEL, ainsi qu’avec GRAFT_BASE_URL pour tout endpoint compatible avec OpenAI. Vous pouvez exécuter Graft uniquement avec la structure et ne dépenser aucun token pour le graphe lui-même.
Quelle économie réelle une carte de codebase apportera-t-elle à mon dépôt ?
Personne ne peut le dire sans mesure. Le projet indique q:graft_published:token_saving_pct % de tokens en moins sur sa propre campagne de q:graft_published:run_count exécutions et q:graft_published:token_saving_pct@-1% % sur SWE-bench Verified, dans les deux cas par rapport à une référence sans carte. Il s’agit de benchmarks fournis par l’éditeur. L’un d’eux a été exécuté en partie sur le propre dépôt de Graft, et aucun ne décrit votre code privé. Exécutez trois fois une même question reproductible avec la carte et trois fois sans, en définissant CLAUDE_CODE_ENABLE_TELEMETRY=1 et OTEL_METRICS_EXPORTER=console, puis comparez la médiane de claude_code.token.usage pour les types input et cacheRead.
Que devient le graphe lorsque je refactorise le code ?
La structure est réanalysée automatiquement. Graft analyse l’arbre de travail et ne réanalyse que les fichiers modifiés. Un renommage est donc pris en compte à la requête suivante, avec environ 3 ms de surcharge. Les modifications non commitées sont également prises en compte, car Graft lit les fichiers et non l’historique git. Les résumés générés par le modèle sont les éléments qui deviennent obsolètes. Chaque nœud stocke un hash du contenu de ses sources. Une source modifiée marque le nœud comme obsolète au lieu de le réécrire. Exécutez graft check . pour voir l’écart, puis graft build --deep pour actualiser la partie rédigée.
Quels agents de programmation peuvent utiliser Graft aujourd’hui ?
En août 2026, graft init configure Claude Code, Cursor, Codex, GitHub Copilot, Google Gemini, Kiro, Windsurf et AdaL. Claude Code bénéficie de l’intégration la plus complète : une entrée de serveur MCP dans .mcp.json, une statusline, des hooks post-édition et un fichier de skill sous .claude/. Codex bénéficie d’une section AGENTS.md ainsi que d’entrées à l’échelle de la machine sous ~/.codex/, que graft init --no-global ignore. Les autres reçoivent un fichier de règles ou de steering. Tout autre client MCP peut utiliser directement le serveur en enregistrant la commande npx -y @nanonets/graft@0.10.1 mcp.