[1.6.1] - 2026-09-29
Highlights
- Sharper answers from
codegraph_explore. It returns the functions and methods you name — even from a very large file, or one full of same-named overrides — honours line numbers in a query, and says when it shortened an answer instead of calling it complete. - Your app's navigation and requests are in the graph. Expo Router, Next.js, React Router, TanStack Router, Vue Router / Nuxt and SvelteKit pages and the links between them are routes and
navigatesedges, and a page'sfetchreaches the route that serves it, so "where does this go" and "what calls this endpoint" are one question tocodegraph_explore. - Explore says when a call happens. Each hop in
codegraph_explore's Flow section carries the condition it runs under, for JavaScript, TypeScript, Swift, Python, Java, Kotlin, C#, Go and C. - Fewer wrong links. Many fixes to how calls, imports and inheritance resolve in TypeScript, JavaScript, Python, Java, Kotlin, Go, Rust, Scala, PHP and C/C++, so a call stops landing on an unrelated symbol that happens to share its name.
- Live sync you can rely on. The MCP server follows a rebuilt index after
codegraph index, a sync interrupted halfway recovers what a clean index would have, symlinked folders stay in sync, and a WSL project on a Windows drive gets an index of its own. - Lighter on big repositories. Repeated explores from the MCP server no longer rescan the whole graph, and oversize files and
.tsvideo fixtures are no longer read or parsed while indexing. - Upgrading: re-index your projects after this release — several fixes and the new navigation links are written while indexing.
New Features
-
Codex and Astra read project guidance from
AGENTS.md. The canonical agent guide now lives inAGENTS.md(with a nesteddocs/AGENTS.mdfor long validation notes);CLAUDE.mdis a thin@AGENTS.mdwrapper for Claude Code. Codex/Astra no longer miss the old CLAUDE-only instructions. -
Next.js pages and their navigation are in the graph. App Router pages (
app/(group)/blog/[slug]/page.tsx→/blog/:slug) and Pages Router pages are routes bound to the component they export, and<Link href>, an internal<a href>,router.push/router.replace(next/navigationandnext/router),redirect()/permanentRedirect()and the middleware'sNextResponse.redirect(…)arenavigatesedges between them.app/api/**/route.tsexports (GET,POST, …) are endpoints bound to their functions, andpages/api/*handlers areANY /api/…. Re-index after upgrading. -
TanStack Router routes and navigation are in the graph. Routes are read both ways a TanStack app declares them — file-based, where
createFileRoute('/posts/$postId')carries the whole path, and code-based, where eachcreateRoute({ path, getParentRoute })is composed through its parent — andnavigate({ to }), a thrownredirect({ to })and<Link to>/<Navigate to>arenavigatesedges to the route they name. A pathless_authlayout, a(group)folder and__rootare not pages, and a computed destination is left out rather than guessed. Re-index after upgrading. -
Vue Router, Nuxt and SvelteKit navigation is in the graph. Vue: routes are read from
createRouter({ routes: [...] }), including a lazycomponent: () => import('@/views/Login'), androuter.push/router.replace/$router.push, Nuxt'snavigateToand<router-link>/<RouterLink>/<NuxtLink>arenavigatesedges, resolved by route name as well as by path. SvelteKit:goto('/login'),redirect(303, …)from a load or a form action, and a plain<a href>arenavigatesedges, and a page is joined to the page file that serves it and the+page.server.jsbeside it. A computed destination or a path nothing declares is left out rather than guessed. Re-index after upgrading. -
React Router navigation is in the graph.
history.push/history.replace,navigate(…)fromuseNavigate,redirect()in a loader or an action, and<Link to>/<NavLink to>/<Navigate to>/ react-router-bootstrap's<LinkContainer to>are nownavigatesedges to the route they open, and a route with an optional parameter (/cart/:id?) is reached by both/cartand/cart/5. A computed destination or a path no route serves is left out rather than guessed, and an ordinarypaths.push('/x')on an array is never mistaken for navigation. Re-index after upgrading. -
codegraph_explorefollows a web app across its tiers. A page'sfetch('/api/users', { method: 'POST' })— or anaxios/ky/got/$fetchcall, including one through anaxios.create({ baseURL })instance — now reaches the route that serves it in the same index. A job put on a BullMQ / Bull queue reaches the@Processmethod,Workerorqueue.processhandler that consumes it, a NestJSEventEmitter2event its@OnEventlisteners, a socket message the gateway's@SubscribeMessagehandler and back, and a Next.js server action called from a client file is a crossing to the server. Each is a synthesized hop that the Flow section names, with where it was wired up. Only a literal path or event name pairs. Re-index to pick the new edges up. -
codegraph_exploresays when each call happens. Every hop in the Flow section carries the condition its call site sits under —↓ calls (when isCollected)— read from theif/else,switch/when/match, ternary,tryand&&/andbranches around the call and the early returns before it (if (busy) returnreads as!busy, Go'sif err != nil { return }aserr == nil). It works for JavaScript, TypeScript, Swift, Python, Java, Kotlin, C#, Go and C, is read from the source as it stands, and a language without rules says nothing rather than guess. -
React Native apps: Swift native modules and their events connect end to end. A JS call like
captureView.finalizeCaptureSession()— wherecaptureViewis bound toNativeModules.CaptureViewand the module is a Swift class exposed through anRCT_EXTERN_MODULEshim — now resolves to the Swift method itself instead of stopping at the constant, socodegraph_explorefollows the code into native. Native → JS events now also land on listeners written inline (addListener('onZipComplete', (data) => { … })), attributed to the component that registers them. Re-index after upgrading to pick the new edges up. -
Expo Router apps: screens and navigation are in the graph. Every screen file under
app/(orsrc/app/) is now a route node named by its path —/object-detail,/item/[id], with(group)folders stripped — linked to the component it renders. Calls likerouter.push('/object-detail?…'),router.navigate({ pathname: '/item/[id]', params }), template-literal hrefs, an href held in a localconst, androuter.push(await pickRoute())where the helper returns screen paths (one edge per screen it can return) resolve to the screen they open as a newnavigatesedge that remembers the href, so "where does tapping this go" and "who opens this screen" are one hop incodegraph_exploreandcallersinstead of a dead end at a string. Re-index after upgrading to pick the new edges up.
Fixes
-
The Claude Code prompt hook no longer runs on the task-notification messages Claude Code injects when a background agent finishes, removing a multi-second stall on every such turn. (#1832)
-
Rust calls on
selfnow stay with the enclosing type instead of linking to an unrelated type’s same-named method. Thanks @L4XB. (#1861) -
Turning telemetry off now resets its identity and stops running processes from recording, sending, or restoring unsent data. (#1869)
-
Calls between JavaScript, JSX and TypeScript files keep their callers and callback flows.
-
Zustand actions keep their callers when read through typed stores, destructured from store state, or selected by a hook.
-
Direct React Native bridge calls retain their native implementations and cross-platform relationships.
-
Dart extension-type getters remain searchable when using the WebAssembly parser.
-
Calling a built-in method on an awaited value no longer records a call into an unrelated class that happens to declare a method of the same name, and a variable bound to an awaited call now resolves methods on the type that call returns. Thanks @maxmilian. (#1840)
-
Spring mappings now include every declared path combination and resolve constants declared in the same file, while unresolved paths no longer appear as false root routes. (#1461)
-
codegraph nodenow accepts a file reference that carries a line number —src/app.ts:42,src/app.ts:42-80,src/app.ts#L42,src/app.ts#L42-L80— instead of reporting the file as not indexed; the line range becomes the window that is read, and an--offset/--limityou pass yourself still wins. A path that really is named that way is still looked up as written. (#1831) -
codegraph callers,codegraph calleesandcodegraph impactnow resolve qualified names, group results and JSON edges by definition, and accept--fileto narrow ambiguous names; thanks @ferrine. (#1512, #1656) -
codegraph callers,codegraph calleesandcodegraph impact(CLI and MCP) now report missing names with did-you-mean suggestions instead of another symbol's results, and exact matches with no callers stay empty; thanks @uvmplus. (#1473, #1481) -
When
codegraph_exploretrims a large file, the gaps in its source now name the symbols that were left out, using only room the file's budget has left, so the names never push out the code itself. (#1711) -
codegraph_exploreno longer reports "runtime dispatch" through a base class that doesn't declare the method, so a common name shared by many unrelated subclasses no longer opens an answer with a list of thousands of implementations. -
When you name several functions in one file and unrelated code sits between them,
codegraph_explorenow returns the functions you named before the code around them, instead of spending the file's room on that code and leaving one of the functions you asked for with no source at all. -
codegraph_explorenow returns the body of a method you name together with its class, such asSQLCompiler.as_sql, even in a file full of same-named overrides. It used to shrink the method you asked for to its signature line while smaller methods around it came back in full. A method too long for the answer now comes back as its opening lines plus the places it calls the other symbols you named, with the exact follow-up query that returns the rest. -
codegraph_explorenow honours line numbers in a query:compiler.py:776returns the method containing that line, andcompiler.py lines 900-1003(or:900-1003,#L900-L1003,L900-L1003) returns exactly those lines. It used to pin the file and ignore the numbers. -
When a file name in a
codegraph_explorequery matches files in more than one folder, only the ones defining a function or class the query also names are pinned, and the summary line lists the files left out. Every match used to be pinned, so the file you meant got half the room it gets when you leave its name out; a name shared by many files, likemodels.py, now resolves the same way instead of being reported as ambiguous.
MCP / indexing
-
On Windows, the shared MCP daemon now waits longer for another program — an antivirus scan, an indexer, or another session reading its lock file — to let go of that file, so it starts instead of leaving the session to fall back to a slower in-process server. (#1773)
-
A file over the size limit is no longer read before it is skipped. Large committed video and blob fixtures used to be loaded into memory in full, once per indexing pass, only to be discarded; indexing and change detection now go by the file's size instead. Thanks @danusha2345. (#1910)
-
An MPEG transport stream video that happens to be named
.ts(golden fixtures undertestdata/, e2e clips) is now recognised from its first bytes and skipped instead of being fed to the TypeScript parser, which spent a long time on each clip for no symbols. Real TypeScript files are still indexed. Thanks @danusha2345. (#1910) -
Repeated
codegraph_explorecalls from a running MCP server no longer re-rank the whole graph each time to find the project's core file; the answer is reused until the index changes. Thanks @danusha2345. (#1864) -
File watching no longer drops the full re-scan a removed directory asks for when that sync fails, so the deleted files leave the index instead of lingering. (#1964)
-
Running
codegraph indexwhile the MCP server is running no longer leaves live auto-sync writing into the old, replaced index: the server now switches to the rebuilt index before its next update and catches up on everything it would otherwise have missed. Thanks @danusha2345. (#1902) -
Daemon startup and cleanup now preserve live legacy PID-only locks while still reclaiming dead or identity-disproved records, preventing two writers from serving the same project.
-
An empty or table-less
codegraph.dbleft behind in a parent directory (by an interruptedcodegraph init, or a never-populated~/.codegraph/) no longer counts as an initialized project, so it can no longer hide the real index of every project beneath it. Only a database that actually carries the codegraph schema is treated as initialized, andcodegraph initin the directory with the broken file now repairs it instead of refusing with "Already initialized". Thanks @danusha2345. (#1895) -
Incremental sync now keeps edge rebinding crash-safe: replacing a resolved edge with its recovery reference commits atomically, so an interruption cannot permanently remove the relationship.
-
Status now detects committed but unindexed changes and restored edits without scanning every source file; thanks @inth3shadows. (#1829)
-
The prompt hook no longer injects unrelated projects when run from your home directory or a broader directory containing a stray workspace manifest. (#1454)
-
codegraph_explorenow says so when a query names an extension-less file the index doesn't hold. A path likescripts/deployhas no extension on its last segment, so it failed the shape test that decides a span is a path beyond doubt — the name was left in the query, shredded intoscriptsanddeploy, and the answer came back as a pile of unrelated source with no hint that the file you named was never consulted. Such a span is now checked against the project directory: if it is a real file, the answer carries the sameNo indexed file uniquely matches ...note a misspelledsrc/foo.tsalready got. Slashed prose —and/or,input/output,gen_server:call/2— has no file behind it and is still left in the query untouched. (#1830) -
Indexing now succeeds when Node.js's SQLite lacks FTS5, with search falling back to name and fuzzy matching; thanks @aniruddhaadak80. (#1532)
-
codegraph_explorenow makes clear that suggested call counts are advisory, so agents keep exploring when an answer is incomplete; thanks @rongbc. (#1504, #1570) -
C++ functions following anonymous namespaces containing raw-string templates are now indexed correctly, even when template text resembles an unfinished macro call. (#1505)
-
Indexing now warns when parser errors leave a file with no symbols, including C++ raw strings with 16-character delimiters, so missing code is no longer silent. (#1522)
-
codegraph index <path>now refuses uninitialized paths and names the nearest initialized parent instead of silently rebuilding it; thanks @danusha2345. (#1524, #1689) -
Sync now recovers the same connections as a clean index after interrupted reference resolution, including inherited calls and callbacks that previously stayed missing. (#1577)
-
codegraph_explorenow re-serves source to fresh subagents and after context compaction, with cross-call dedup available only through an explicitCODEGRAPH_EXPLORE_DEDUP=1opt-in; thanks @danusha2345. (#1620, #1624) -
Watcher scope now matches
git ls-files --exclude-standard(#1728).buildDefaultIgnore/buildScopeIgnoreread.git/info/excludeandcore.excludesFile(not only the root.gitignore), and seed directories git reports as ignored-untracked so nested.gitignoreeffects prune the live watcher the same way the indexer skips them. Single-file auto-sync was already incremental (pendingFiles→ scopedsync({ paths })); the remaining gap was watching trees git had excluded. -
Live sync no longer lets the write-ahead log grow without a bound when a reader is holding it open (#1539). Incremental sync now uses the same writer pause that full indexing already used, and if checkpointing still cannot finish once the log is past its documented size limit — typically because the query pool is reading at the same time — sync stops with a clear error instead of keeping writing until the disk fills. The previous behaviour could leave a multi-tens-of-gigabyte log beside a few-gigabyte index on a large project. Close concurrent readers and retry, or raise
CODEGRAPH_WAL_VALVE_MBif the limit is too tight for the project. -
A second
codegraph serve --mcpon the same project no longer silently kills auto-sync (#1740). Direct mode (CODEGRAPH_NO_DAEMON=1or proxy→in-process fallback) now takes an exclusive.codegraph/writer.pidlock; a second writer exits immediately with guidance to stop the other server or unsetCODEGRAPH_NO_DAEMONso clients share the daemon. The shared daemon already multiplexes N clients onto one watcher — this closes the same-OS dual-direct gap the docs warned about for Windows/WSL but did not guard. -
Indexing no longer checks whether files outside your project exist. A relative import that points above the project directory (
../../something) made CodeGraph probe that location on disk while resolving it. Nothing outside the project was ever read, and no such file was ever added to the index or linked to, but the check itself should not have happened — such an import now simply resolves to nothing. Symlinks inside your project that point at code kept elsewhere are unaffected and still index as before. Thanks @ErQrYfkrju. (#1631) -
codegraph installnow honorsCLAUDE_CONFIG_DIRandCODEX_HOMEfor global Claude Code and Codex setup so CodeGraph loads in your chosen profile (thanks @seanchann; #1627). -
codegraph install --refreshno longer leaves a.backupfile beside the config of an agent it isn't set up for. Checking whether an agent already has CodeGraph meant reading its config, and a config that wasn't valid JSON — an emptymcp_config.jsonnext to an Antigravity install, say — got copied aside with a warning that CodeGraph was about to overwrite it, even though the agent was then skipped untouched. Checking is silent now; a config that can't be read is still backed up, but only when CodeGraph really is replacing it. Thanks @Gotman08. (#1870) -
Files opted in with
includeIgnorednow stay indexed on Git older than 2.36, and embedded repositories remain visible to the watcher (thanks @maxmilian and @newshowardz777; #1549). -
codegraph initandcodegraph indexnow list unsupported file extensions and explain that CodeGraph is inactive when no supported source files are found (#1502). -
codegraph_explorenow flags a dynamicimport()/require()built from a template literal with a substitution or from string concatenation, while an escaped\${…}stays a plain string. Thanks @inth3shadows. (#1967) -
The stale-file warning no longer names a pending file just because its path is a prefix or suffix of a path the answer shows (
src/app.tsvssrc/app.tsx). Thanks @inth3shadows. (#1968) -
When
codegraph_explorefinds nothing, it now says that matching is lexical, lists the query words the index doesn't contain, and suggests real symbol names to retry with. Thanks @iacore. (#1904) -
codegraph_explorenow finds a file when the query is its exact Chinese filename, with or without the extension. Thanks @Syh1906. (#1372) -
codegraph_explorenow returns the methods and types you name from a large file instead of a few lines around one call. When a file's symbols sat close together, and whenever you named the file by path, the answer could show only its first lines and a window on one call site, leave out the bodies you asked for, and still call the file complete, so your agent read it back. A genuinely long function on the call path is still shortened to its signature and the call that matters, and the answer now says when it did. -
When your question names symbols concentrated in one file and the answer has room to spare,
codegraph_explorenow uses that room for the file you named instead of stopping at a fixed share of the response, so every body you asked for fits. Other files in the answer keep the space they were given. -
codegraph_exploreno longer tells your agent an answer's source is complete when part of it was shortened to fit. On larger projects it now names the files it trimmed and the functions it left out, so your agent asks for those by name instead of reading the file, and it no longer suggests reading a file as a fallback. -
Java, Kotlin and Scala packages named
buildunder a source root are indexed again, while real build-output directories stay excluded. Thanks @CmmVoid for the report and @danusha2345. (#1642) -
Files over the 1 MB source limit, such as a package archive an import points at, are now rejected before they are read during resolution, removing a multi-gigabyte memory spike at startup. Thanks @hcg1023 for the report and @danusha2345. (#1553)
-
codegraph syncnow reports that the index is busy and exits non-zero when another process holds the write lock, instead of printing "Already up to date". Thanks @inth3shadows. (#1361) -
codegraph syncnow reports the pending references it resolved instead of printing "Already up to date" while it did that work. Thanks @inth3shadows. (#1360) -
An incremental sync now refreshes event, callback and cross-tier links when a change touches a registration, a handler or a file that feeds them, so the graph matches a full re-index; ordinary edits stay as fast as before. Thanks @bompus. (#1988)
-
Edits inside a symlinked directory now auto-sync on macOS, Windows and Linux, including links added, removed or retargeted while watching. Thanks @ztibeike for the report and @dengzhongyuan365-dev. (#770)
-
A project queried through
projectPathnow catches up on first access and stays in sync like the default project, coordinating with any daemon that already owns it. Thanks @nakisen for the report and @danusha2345. (#1835) -
Two spellings of one repository (a symlink, or different casing on a case-insensitive drive such as WSL
/mnt/c) now share one index connection instead of opening it twice. Thanks @yjtdkj for the report and @inth3shadows. (#1057) -
In WSL, a project on a Windows drive (
/mnt/c/…) indexed for the first time now gets its own.codegraph-wsl/index, so it never shares one with CodeGraph on Windows; an index you already have in.codegraph/stays where it is, andCODEGRAPH_DIRstill picks the name when set. Thanks @Jek-Mena for the report and @inth3shadows for narrowing it down. (#995) -
In WSL, a project on a Windows drive whose
.codegraph/index CodeGraph on Windows also uses no longer fails with a bare "disk I/O error": the CLI, and your agent through MCP, now explain that Windows and WSL can't share one index there and that settingCODEGRAPH_DIR=.codegraph-wslin WSL gives it its own. (#995) -
Direct (non-daemon) MCP sessions now serve concurrent calls from a small off-thread query pool (2 workers by default;
CODEGRAPH_QUERY_POOL_SIZEchanges it). Thanks @blabla-yy. (#1465) -
impact --depth N, and callers/callees at a depth, now find every dependent within N hops even when a longer path reaches one of them first. Thanks @inth3shadows for the report and @danusha2345. (#1974) -
Path finding no longer re-queues the same node many times through a dense fan-in hub. Thanks @inth3shadows. (#1359)
-
Everyday Portuguese/Spanish "como" and German "wie" no longer escalate a prompt to a full code search on their own. Thanks @AndreLFSMartins. (#1654)
-
The Antigravity installer now saves the permanent path of
codegraphrather than fnm's per-shell directory, so the command keeps working after the installing shell exits. Thanks @kevocodes. (#1443) -
A live writer's index lock is no longer treated as stale after two minutes, and the file watcher re-arms and reconciles after lock contention instead of leaving
serve --mcpanswering from a frozen index; the non-git scan fallback keeps honoring.git/info/exclude. Thanks @LunarWerxs for the report and @danusha2345. (#1959) -
A shared daemon now closes connections that are still mid-handshake when it stops, a peer that disconnects mid-handshake no longer leaves a phantom client that blocks idle exit, and a fallback that finds another process holding the writer lock serves reads instead of refusing. Thanks @bompus and @inth3shadows for the reports and @danusha2345. (#1963, #1356)
-
A query worker whose database open fails is now retired and replaced instead of answering every routed call with an error. Thanks @inth3shadows. (#1357)
-
Very large watchdog or startup-handshake timeouts are now capped at the largest delay a timer can hold, so they no longer fire after about 1 ms. Thanks @inth3shadows. (#1966)
-
codegraph indexnow coordinates with a running MCP daemon — it stops the daemon cleanly (refusing rather than guessing if it cannot verify it stopped) and then rebuilds — so it no longer fails with "database file is in use" on Windows, and MCP calls made during the rebuild are told to retry. Thanks @bithy0825 for the report and @hbui290. (#1325) -
The local MCP proxy now arms the same liveness watchdog as the other launch modes, so a wedged proxy exits instead of lingering and piling up across client restarts on Windows. Thanks @Neurotoxin0 for the report and @snvtac. (#943)
-
On Windows, simply reading a file no longer shows up as a pending edit: watcher events whose file still has the indexed size and modification time (NTFS access-time notifications) are ignored. Thanks @radeilic for the report and @JJordan0C. (#1451)
-
The Windows bundle now includes an extensionless
codegraphlauncher, so Git Bash — and the Claude Code prompt hook that runs through it — can findcodegraphinstead of failing with exit code 127. Thanks @Dj-Khalle. (#1278) -
Running
install.shfrom Git Bash, MSYS or Cygwin on Windows now prints the PowerShell install command instead of failing with "unsupported OS". Thanks @Dnllns. (#1294) -
Indexing no longer hangs at full CPU when a database write fails partway through; it now stops and reports the error. (#1773)
Links and navigation
-
A
<Card/>is the Card that file imports, not the first one in the repo. When two components share a name —Section,Picker,FrameCard, one per feature folder — the render link out of a component picked whichever happened to be indexed first, ignoring what the file actually declares or imports, so a component gained a link to something it never renders and callers and impact dead-ended at the one it does. It now follows the file's own declaration or import, and a JavaScript tag prefers a JavaScript component over a same-named class in a mobile app's native half. Re-index after upgrading. -
A navigation that goes to one of several places now links to all of them. A destination written as a choice — a paginator's
!isAdmin ? keyword ? \/search/${keyword}/page/${x}` : `/page/${x}` : `/admin/productlist/${x}`,redirect(307, user ? `/profile/@${user.username}` : '/login')in a loader,router.push(cond ? '/a' : '/b')— used to produce no edge at all, because one edge carried one destination and picking an arm would have been a guess. Every arm is now its ownnavigates` edge to the route it names; arms that name the same route still make one. -
A conditional inside a conditional no longer reads the wrong arm. A paginator written
!isAdmin ? keyword ? '/search/…' : '/page/…' : '/admin/…'was split at the first:rather than the matching one, so an admin's page links pointed at the storefront's pagination. The arms are paired properly now, and a three-way choice — which is more destinations than one link can name — is left alone. -
In a repository with several apps, a link no longer points into a different one. Every app has a
/and most have a/login, and the route table was built for the whole repository at once, so whichever app was indexed first claimed each address — a<Link to="/posts">in one app resolved to another app's/posts. Measured on a monorepo of 477 apps: 82% of navigations pointed at a route belonging to a different app, and all of them now point within their own. -
A client call with a type argument —
useSWR<Team>('/api/team', fetcher),ky.get<User>('/api/users/1')— now reaches its route like the untyped form.
Routes and framework detection
-
A SvelteKit layout is no longer a second route at a page's address.
+layout.svelteand+error.sveltesit at the same path as the+page.sveltebeside them and were each indexed as a route, so one address appeared in the index two and three times over. Only a page is a route now. -
A framework whose package lives in a subfolder is detected again. In a project that keeps its dependencies one level down — a
frontend/and abackend/, or anapps/web/— the framework check ran once before any file had been indexed, found no folders to look in, and remembered that empty answer for the rest of the run. Every React, React Router and Next.js behaviour that depends on knowing the framework is there silently did nothing for those projects. -
Express routes behind
app.use('/api', router)are named by the path a request takes. A router mounted at a prefix — through as manyrouter.use('/users', usersRouter)levels as the app nests, by import orrequire— now names its routesPOST /api/usersinstead ofPOST /, and the chained formrouter.route('/:id').get(getProduct).put(protect, updateProduct)(split across lines or not) registers one route per method. A client's request is paired with its route by that real path too. -
An Express handler written through a wrapper is the endpoint's handler.
const authUser = asyncHandler(async (req, res) => { … })— theexpress-async-handleridiom — is now the handler its route is bound to. -
A framework declared in a workspace's
package.jsonis detected. React, Next.js, Expo Router, Express and NestJS are found when their dependency lives infrontend/,backend/,apps/web/orpackages/api/rather than at the repository root, so a monorepo's pages and endpoints exist in the index. -
FastAPI routes are named by the path a request takes.
APIRouter(prefix="/items")and a literalapp.include_router(router, prefix="/api/v1")— nested through an aggregate router, through a module import or an alias — now compose onto the route names (GET /api/v1/items/{id}instead ofGET /{id}); a prefix that is a setting rather than a string leaves that mount alone rather than guessing. -
ASP.NET Minimal API endpoint groups are routes. The handler-first form —
groupBuilder.MapPost(CreateTodoItem),MapPut(UpdateTodoItem, "{id}")inside anIEndpointGroup/EndpointGroupBaseclass (the Clean Architecture template and its descendants) — now registersPOST /api/TodoItemsandPUT /api/TodoItems/{id}, with the/api/head read from the app's ownMapGroup($"/api/{groupName}")and a class'sRoutePrefixhonoured, each bound to its handler. -
A FastAPI service that lives in one directory of a monorepo is detected.
backend/pyproject.tomlandbackend/app/main.pycount, not only files at the repository root, so the official full-stack template's routes are indexed. -
React Router routes are now read from each
<Route>tag's own attributes and each route object's own fields, so anelementno longer bleeds into a sibling or parent route. Thanks @inth3shadows. (#1348) -
MyBatis mappers with whitespace inside a closing tag, and
<include refid>references to another namespace, are now extracted correctly. Thanks @ESPINS for the report and @fengshao1227. (#1209) -
render,include,sectionandassignwritten inside a{% liquid %}block are now extracted with their real line numbers, and commented or raw content is ignored. Thanks @Puma7. (#1906) -
Rust functions annotated
#[tauri::command]are no longer reported as dead code. Thanks @vdavid. (#1543)
Symbols and tests
-
Kotlin functions and methods now carry their signature —
(params): ReturnType— incodegraph_exploreandnode, instead of no signature at all. Re-index Kotlin projects after upgrading. (#1495) -
TypeScript/JavaScript value aliases —
export const alias = fn,export { fn as alias }, object-literalapi = { run: fn }, and same-fileconst local = fn— now forward calls edges to the aliased function, so callers and impact on the implementation include consumers that call through the alias instead of stopping at the binding. Genuine wrappers (() => fn()) are unchanged. Re-index after upgrading. Thanks @valkyriweb. (#1482, #1485) -
codegraph affectednow finds Go, Python and JVM test files that previously went unreported, while preserving custom--filterbehavior (thanks @danusha2345; #1507, #1688). -
Calls inside declaration initializers in Kotlin, Java, TypeScript, JavaScript, Scala, Rust and Python now appear under the declaration that owns them, making callers and impact results more accurate after re-indexing with
codegraph index -f(thanks @danusha2345; #1510, #1511). -
Java fields initialized with anonymous classes now expose their methods and calls in the graph.
-
Kotlin property accessors, initialization blocks and destructuring declarations now retain their calls with the correct owner.
-
codegraph_exploreagain lists a dynamic-dispatch link when the same two symbols are also joined by an ordinary call. -
Rust unit structs (
struct Unit;) and their trait implementation relationships now appear in the graph after re-indexing. (#1513, #1514) -
Imports from Node built-ins or npm packages no longer connect to unrelated type members with matching names; re-index after upgrading to clear existing false dependencies. Thanks @ctype-lab. (#1537)
-
Inheritance relationships no longer attach external Rust or npm supertypes to unrelated local symbols with the same name, including in Svelte, Vue and Astro components; re-index after upgrading to clear existing false relationships. A TypeScript class implementing a service that is declared as both a value and an interface under one name, the pattern VS Code uses for every service, now links to the interface instead of the value. Thanks @ctype-lab. (#1536)
-
Spring's dependency-injection resolution patterns no longer capture inheritance references or references from other languages. In a polyglot repository where Spring is detected (a sibling Java module is enough), a Scala or Kotlin
extends/implementsreference could be resolved by Spring's directory-convention heuristics to an unrelated same-named class — for example a test fixture — corrupting the inheritance graph thatcodegraph_impactandcodegraph_explorewalk. Inheritance now always resolves through imports and name matching, and the Spring DI patterns only apply to Java/Kotlin references. Re-index after upgrading. (#1825) -
PHP static calls through imported class aliases now reach the correct class when services and repositories share method names, so callers and impact analysis show the right dependencies after re-indexing. (#1545)
-
TypeScript/JavaScript: a call through a field of the enclosing class —
this.mailer.send()— now resolves on the field's declared type, so a delegating wrapper that shares the method's name no longer records itself as its own callee andcallers,impactand trace stop lying on that shape. A field whose type is external or a builtin stays unresolved rather than guessed. Re-index after upgrading. (#1496) -
TypeScript and JavaScript collection calls through local variables and their nested properties no longer link to unrelated project methods; re-index after upgrading. (#1566)
-
Objective-C headers now index in a project that has no
.mfile. A.hfile is read as C from its name alone, and only later — once its contents are read — recognized as Objective-C; the grammar for that was never loaded up front, so the file failed with a parser error and nothing in it reached the index. Adding any.mfile used to make the same header work, which is what made this look arbitrary. Thanks @Juddd. (#1628) -
TypeScript interface methods and properties are now indexed, so
node,callersand impact can find platform.d.tsAPIs while declaration-only files keep their lower ranking on flow queries, and an interface's members don't pull unrelated files into answers to plain-English questions; re-index TypeScript projects after upgrading. (#1638) -
Lua and Luau function expressions assigned to locals, table members, or keyed table fields are now indexed as callable nodes. Calls from
local f = function() ... end,M.f = function() ... end, and callback tables such asM.handlers = { onClick = function() ... end }are attributed to the named function or method instead of collapsing onto the file node, so callers and impact no longer omit these handlers. Re-index after upgrading. (#1616, #1650) -
Functions bound with
constinside another function are symbols now.const handleClear = () => {…}inside a React component — every handler that skipsuseCallback— was invisible tocallers,calleesand impact, answering "Symbol not found" exactly the way a function with no callers would. It is indexed like its module-level twin, contained by the enclosing function, with its own calls. Re-index after upgrading. (#1669) -
codegraph callers,callees, andquerynow clearly report when their result limit hides additional matches, including exact totals in callers/callees JSON output; thecodegraph_callersandcodegraph_calleesMCP answers carry the same "showing N of M" note. (#1639, #1674) -
CommonJS controllers written as
exports.getItems = async (req, res) => {…}ormodule.exports.x = function () {…}are now indexed as exported functions, sonode,callersand impact find every Express handler in that style and the calls inside them belong to the handler instead of the file. Re-index JavaScript projects after upgrading. (#1675) -
Python parameters annotated with a quoted forward reference —
def f(o: "Alpha"), or anything underfrom __future__ import annotations— now resolve the methods called on them, the same as the unquoted annotation. Re-index Python projects after upgrading. (#1684) -
A C macro call written with designated initializers no longer swallows every function after it. Betaflight resets each config struct with
RESET_CONFIG(type, dst, .field = value, …), a shape the C grammar cannot parse; past a hundred or so fields its error recovery ran the enclosing function to the end of the file, the next function vanished from the index and every later one was filed under the first, where name matching then treated it as an unreachable closure. The argument list of such a call is now blanked before parsing, offsets kept, so the file's functions come out with their real extents. On that tree 45 functions inpid.calone moved back to top level and their 117 callers resolve at exact-match confidence. Re-index after upgrading. (#1729) -
A method called on the result of another call —
d.setdefault(k, []).append(v),make().run()— no longer produces a call edge to an unrelated top-level function that merely shares the name, in Python and JavaScript/TypeScript. The receiver is kept so the inner call still resolves; the outer method stays unresolved rather than guessed. Re-index after upgrading. (#1683, #1681) -
A Python call through an imported project module whose name collides with a builtin collection method —
ledger.append(row)afterfrom . import ledger— is no longer dropped aslist.append. The builtin-method filter now lets the receiver through when it is an imported module that resolves to a file in the project, soresolveViaImportcan attach the real edge; a stdlib/PyPI receiver (os.remove) still produces none. Re-index after upgrading. (#1681, via #1704) -
Python method calls on module-scope builtin collections no longer create false calls or file dependencies to unrelated project methods with the same name; re-index after upgrading. (#1652)
-
A definition its language makes file-local no longer captures calls from other files. A C
staticin another source file (.c/.cc/… — not a header'sstatic inline, which is textually included), a Kotlin/Java/C#/Swift/Scala/Dart/PHPprivatemember, a Go unexported name in another package, and a Rust non-pubitem outside its module subtree cannot be what a name in another file means, but name matching accepted them whenever the names agreed: an Androideditor.apply()onto an unrelated class'sprivate fun apply, a JavaScriptfail(...)onto a Gofunc fail, a Rust.count()onto a privatefn countin another crate, and C USB helpers onto astaticin a.cthey never link. Such a target is now declined after the whole name-matching pipeline settles — the reference stays unresolved rather than falling through to a fuzzy namesake. Same-file definitions, a child Rust module reaching its ancestors' private items, and Rustimpl Trait for Typemethods stay resolvable. Re-index after upgrading. (#1730, #1731) -
A binding in a module that exports nothing is no longer a cross-file target. On vite, every
import { defineConfig } from 'vite'across the playground resolved onto aconst vite = await createServer(…)sitting at module scope inplayground/ssr-html/test-stacktrace.js— a file with an import and no export, so that binding is reachable from nowhere but itself. Name matching commits as soon as one candidate survives, and nothing asked whether an import could reach the survivor; that one binding took 157 edges. A JS/TS file holding animportand no export of any kind now offers its locals to no other file. Classic scripts, CommonJS (includingexports["x"] = …), a laterexport { … }, and names contributed throughdeclare globalare all unaffected. Across vite this removed 320 wrong edges and added 18, each addition a reference that was previously ambiguous rather than newly invented. Re-index after upgrading. (#1719) -
A bare call inside a JavaScript or TypeScript method no longer resolves to the method itself. When a method and a module-scope function share a name,
serialize(this.raw)written insideRecord.serializemeans the function, but the nearest same-named definition won the tie and the graph recorded the method calling itself. A call written without a receiver can never reach a method in JS/TS, so methods are no longer candidates for it;this.serialize()andother.serialize()resolve as before. (#1714) -
Fuzzy matching no longer lands on a closure it cannot reach. A function nested inside another function is only callable from inside its container, and exact-name matching already declined such candidates; the fuzzy fallback did not, so a builtin method call (
res.text(),items.push()) whose only same-named project symbol was some file's closure resolved onto that closure. The fallback now checks that the one candidate it would commit to is reachable, and declines otherwise — it does not filter the candidate list first, which would turn a crowd of same-named definitions into a single "unique" survivor and hand it every call of that name. On vite that removes the 12 edges onto nested functions and adds none. Re-index after upgrading. Thanks @bompus. (#1708, #1709) -
An import that names the emitted extension resolves to its source. Under
moduleResolution: node16 | nodenext | bundlerTypeScript requiresimport { x } from './util.js'forutil.ts, and no file of that name exists, so the import resolver returned nothing and every name imported that way fell through to bare-name matching: a method wrapping the same-named helper it imports (renderDockStyles() { return renderDockStyles(); }) resolved to itself, and cross-module edges in such projects were name guesses..js/.jsx/.mjs/.cjsspecifiers now retry with the source extensions TypeScript compiles from when the emitted file is absent; a real.jsbeside the.tsstill wins. On a 582-file repo whose.tsfiles import this way, import-backedcalls/importsedges went from 4,002 to 7,312 and the eight wrapper-method self-edges disappeared. Re-index after upgrading. Thanks @bompus. (#1705, #1706) -
Files under an
e2e/directory now count as tests. -
Production code under a
samplesorexamplespackage path is no longer treated as test code. A Kotlin or Java project whose package path runs throughcom/google/samples/…(Now in Android, for one) had nearly every file counted as a test fixture. Only the project layout above asrc/folder decides now; the package path below it never does. -
React handlers written with
useCallbackare now symbols.const handleSubmit = useCallback(() => {…}, [])— the way nearly every handler in a React or React Native component is written — is extracted as a function named by its binding (alsoReact.useCallback,useEffectEvent), soonPress={handleSubmit}andaddListener('x', handleSubmit)resolve to it, its calls are its own rather than the component's, and a tap's handler shows up incodegraph_explore. A JSX attribute value (onPress={handleSubmit},renderItem={renderRow}) and a handler a hook hands back in an object (return { handleSubmit, handleRetake }) are now function-as-value references from the component or hook, so the graph knows which functions are wired as handlers. -
Stores exported on a later line are read like any other.
const useStore = create((set, get) => ({ … }))followed byexport default useStore(orexport { useStore }) now has its actions extracted as functions, the same as anexport conststore — previously the two-statement form, common in React Native apps, left every action invisible. -
API objects exported as a default namespace resolve through to their functions.
import Api from './api'+Api.upload()where the module ends inconst Api = { upload, createFolder }; export default Apinow links the call touploaditself (through the object's own imports), and a default import of any const named by anexport default NAMEstatement finds that const rather than guessing the file's first exported function. -
The Claude Code prompt hook's context now arrives inline. The hook capped its injection at 16,000 characters, but Claude Code shows hook output inline only up to 10,000 and otherwise persists it to a file with a 2 KB preview, so on any repo where explore filled the cap the model saw a file path and the first 2 KB. The cap is now 9,000 characters, under the limit with room for the wrapper. (#1694)
-
codegraph_exploreis loaded from the first prompt in Claude Code. Claude Code defers every MCP tool behind a tool-search step, so a fresh session saw only the tool's name until the model searched for it, and the server's "callcodegraph_exploreinstead of Read" had nothing loaded to act on. The tool now carriesanthropic/alwaysLoadin its_meta, which exempts it on existing installs, andcodegraph installwritesalwaysLoad: trueon the Claude Code server entry (re-run it to add the key). Copilot CLI's tool search holds MCP tools back the same way once ~30 tools are connected, so its entry now carriesdeferTools: "never". (#1696) -
Python calls and file dependencies through
from package import module as aliasnow appear in the graph, so renamed imports no longer hide live callers or imported modules. Thanks @JoeyNPP. (#1626) -
Python docstrings are now indexed alongside preceding comments, so their prose is searchable and shown. Thanks @iacore for the report and @maxmilian. (#1905)
-
A Dart 3
extension typeis now indexed as a type with its members attached. Thanks @bompus for the report and @Dshuishui. (#1784) -
A receiver-less Go call (a function parameter or local func value) no longer links to a same-named method. Thanks @PCeltide for the report and @maxmilian. (#1857)
-
A function passed to a curried wrapper (
Effect.fn("x")(function* () {…}),connect(m)(fn)) is now indexed under its declarator or property name. Thanks @Dshuishui for the report and @maxmilian. (#1747) -
The bundled Scala grammar is now the official tree-sitter-scala v0.26.2, so classes with several parameter lists keep all their inheritance edges. Thanks @htarnacki for the report and @danusha2345. (#1823)
-
A Scala
objectis now indexed as a module, soextends/withresolves to the trait or class rather than its companion object. Thanks @htarnacki. (#1824) -
C/C++ functions defined through a single-argument macro are now indexed under their real name when the macro's own
#defineproves it. Thanks @Dshuishui. (#1373) -
MSVC COM
interfacedeclarations in C++ headers are now indexed as types with their methods. Thanks @timxx. (#1519) -
A function-like C/C++ macro invocation no longer binds to a same-named function or type in another file, a flag the build can still set on the command line keeps both sides of its
#ifdefreachable, and a local C++ object initialization now records a call to its constructor. Thanks @netbrah and @bompus for the reports and @danusha2345. (#1838, #1839, #2069, #2070) -
A Delphi DFM component's range now runs to its closing
end, so its properties and event bindings belong to it. Thanks @inth3shadows. (#1350) -
Two same-named symbols on one line (a one-line getter/setter pair) no longer collide and drop one; existing symbol IDs are unchanged. Thanks @inth3shadows. (#1349)
-
A callback registered with
subscribe(this.handler)now links to that exact handler, not the first same-named method in the project. Thanks @inth3shadows. (#1355) -
Calls through an object-literal member that references an outer function (
{ fn },{ fn: fn }) now resolve to that function. Thanks @nikamodis for the report and @danusha2345. (#1932) -
Calls, constructors and inheritance no longer link to a same-named symbol in an unrelated language, while real interop (web, JVM, native, .NET, cgo) keeps its edges. Thanks @bompus for the report and @danusha2345. (#1986)
-
A built-in method call such as
list.map()orcache.get()on an untyped value no longer links to a same-named project method, andthis.#field.method()resolves on the field's type. Thanks @bompus for the report and @danusha2345. (#1987) -
Python and Go methods passed as values (
executor.submit(self.store.fetch)) now appear in callers and impact results. Thanks @JosefAschauer. (#1820) -
Methods called by a base class outside the index (React lifecycle methods, a stream's
_transform, NestJS hooks) are no longer reported as dead code. Thanks @inth3shadows for the report and @danusha2345. (#1973)