Repository navigation
Conversation
colbymchenry
added a commit
that referenced
this pull request
Oct 27, 2026
… runs The MCP server's watcher holds one CodeGraph for the whole session, and its framework detection was cached from the first time it ran: the orchestrator reset it only in indexAll, and CodeGraph.sync never re-initialized the resolver. A framework that arrived mid-session, like Express in a new package.json, was missing from every file synced after it, so a new route file got no route nodes until `codegraph index`. The resolver's declared-dependencies cache was also keyed by the indexed file COUNT (PR #2519): swapping one app for another of the same size, or editing a manifest, kept the old dependency set, even through reinitializeResolver(). Detection now records what it read: indexed source files, which a sync's change lists cover, and every other path it read or probed, with a size and mtime signature, including the per-app manifests the extractor gating reads. Before a sync extracts files, it detects again only when files were added or removed since, a source file detection read changed, or one of those paths changed on disk. A scoped sync takes the file list from the index plus the files it adds, so it never walks the project (the first sync in a process used to run a full scan for its first detection). What detection read is kept between detections and dropped by the same change lists and signatures, so detecting again reads only what changed. When the detected set differs from the resolver's, the sync re-initializes the resolver. Its initialize() now clears its caches before detecting, and clearCaches() forgets the declared-dependencies cache with the other per-context memos. Files indexed before a framework change are not re-extracted. A framework like Express applies to every file of its language, and re-reading them all inside a watcher's sync would cost what a full index does. Instead a full index records the frameworks it used, a sync records how detection moved since, and `codegraph status` (text, and --json with reindexRecommended and frameworksChangedSinceIndex) recommends `codegraph index` while they differ. Status only reads that record. Validation: - sync-framework-detection.test.ts (new, 8 cases): Express arriving with a new route file, by full and by scoped sync; a .svelte file added with no manifest change; an edited source file that detection read; a manifest-only edit picked up by the next sync, with the status record, cleared on revert and by a full re-index; a fresh process's scoped syncs detecting once, from the index, with no scan; the equal-size app swap; and the dependency cache forgetting on request. 7 of 8 fail on main's source (the 8th needs the new export). Ten mutations, one per mechanism, each fail a test. - The triage probes: main detects [] after Express arrives and routes.js gets no route; the fix detects express and extracts GET /hello, by full and by scoped sync. For the swap, main's reinitializeResolver() gives [react] and the fix [express]. - Cost on excalidraw (737 files, scoped syncs in one long-lived instance): the detection step takes 8 ms on a sync where nothing it read changed and 8-13 ms when it detects again, against syncs of about 260 ms (add) to 2 s (modify). The first sync in a process detects once (about 300 ms) and no longer scans the project. Reported in PRs #2516 and #2519 by @nk33-dev. This reimplements both fixes independently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry
added a commit
that referenced
this pull request
Oct 27, 2026
… runs The MCP server's watcher holds one CodeGraph for the whole session, and its framework detection was cached from the first time it ran: the orchestrator reset it only in indexAll, and CodeGraph.sync never re-initialized the resolver. A framework that arrived mid-session, like Express in a new package.json, was missing from every file synced after it, so a new route file got no route nodes until `codegraph index`. The resolver's declared-dependencies cache was also keyed by the indexed file COUNT (PR #2519): swapping one app for another of the same size, or editing a manifest, kept the old dependency set, even through reinitializeResolver(). Detection now records what it read: indexed source files, which a sync's change lists cover, and every other path it read or probed, with a size and mtime signature, including the per-app manifests the extractor gating reads. Before a sync extracts files, it detects again only when files were added or removed since, a source file detection read changed, or one of those paths changed on disk. A scoped sync takes the file list from the index plus the files it adds, so it never walks the project (the first sync in a process used to run a full scan for its first detection). What detection read is kept between detections and dropped by the same change lists and signatures, so detecting again reads only what changed. When the detected set differs from the resolver's, the sync re-initializes the resolver. Its initialize() now clears its caches before detecting, and clearCaches() forgets the declared-dependencies cache with the other per-context memos. Files indexed before a framework change are not re-extracted. A framework like Express applies to every file of its language, and re-reading them all inside a watcher's sync would cost what a full index does. Instead a full index records the frameworks it used, a sync records how detection moved since, and `codegraph status` (text, and --json with reindexRecommended and frameworksChangedSinceIndex) recommends `codegraph index` while they differ. Status only reads that record. Validation: - sync-framework-detection.test.ts (new, 8 cases): Express arriving with a new route file, by full and by scoped sync; a .svelte file added with no manifest change; an edited source file that detection read; a manifest-only edit picked up by the next sync, with the status record, cleared on revert and by a full re-index; a fresh process's scoped syncs detecting once, from the index, with no scan; the equal-size app swap; and the dependency cache forgetting on request. 7 of 8 fail on main's source (the 8th needs the new export). Ten mutations, one per mechanism, each fail a test. - The triage probes: main detects [] after Express arrives and routes.js gets no route; the fix detects express and extracts GET /hello, by full and by scoped sync. For the swap, main's reinitializeResolver() gives [react] and the fix [express]. - Cost on excalidraw (737 files, scoped syncs in one long-lived instance): the detection step takes 8 ms on a sync where nothing it read changed and 8-13 ms when it detects again, against syncs of about 260 ms (add) to 2 s (modify). The first sync in a process detects once (about 300 ms) and no longer scans the project. Reported in PRs #2516 and #2519 by @nk33-dev. This reimplements both fixes independently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry
added a commit
that referenced
this pull request
Oct 27, 2026
… runs (#2941) The MCP server's watcher holds one CodeGraph for the whole session, and its framework detection was cached from the first time it ran: the orchestrator reset it only in indexAll, and CodeGraph.sync never re-initialized the resolver. A framework that arrived mid-session, like Express in a new package.json, was missing from every file synced after it, so a new route file got no route nodes until `codegraph index`. The resolver's declared-dependencies cache was also keyed by the indexed file COUNT (PR #2519): swapping one app for another of the same size, or editing a manifest, kept the old dependency set, even through reinitializeResolver(). Detection now records what it read: indexed source files, which a sync's change lists cover, and every other path it read or probed, with a size and mtime signature, including the per-app manifests the extractor gating reads. Before a sync extracts files, it detects again only when files were added or removed since, a source file detection read changed, or one of those paths changed on disk. A scoped sync takes the file list from the index plus the files it adds, so it never walks the project (the first sync in a process used to run a full scan for its first detection). What detection read is kept between detections and dropped by the same change lists and signatures, so detecting again reads only what changed. When the detected set differs from the resolver's, the sync re-initializes the resolver. Its initialize() now clears its caches before detecting, and clearCaches() forgets the declared-dependencies cache with the other per-context memos. Files indexed before a framework change are not re-extracted. A framework like Express applies to every file of its language, and re-reading them all inside a watcher's sync would cost what a full index does. Instead a full index records the frameworks it used, a sync records how detection moved since, and `codegraph status` (text, and --json with reindexRecommended and frameworksChangedSinceIndex) recommends `codegraph index` while they differ. Status only reads that record. Validation: - sync-framework-detection.test.ts (new, 8 cases): Express arriving with a new route file, by full and by scoped sync; a .svelte file added with no manifest change; an edited source file that detection read; a manifest-only edit picked up by the next sync, with the status record, cleared on revert and by a full re-index; a fresh process's scoped syncs detecting once, from the index, with no scan; the equal-size app swap; and the dependency cache forgetting on request. 7 of 8 fail on main's source (the 8th needs the new export). Ten mutations, one per mechanism, each fail a test. - The triage probes: main detects [] after Express arrives and routes.js gets no route; the fix detects express and extracts GET /hello, by full and by scoped sync. For the swap, main's reinitializeResolver() gives [react] and the fix [express]. - Cost on excalidraw (737 files, scoped syncs in one long-lived instance): the detection step takes 8 ms on a sync where nothing it read changed and 8-13 ms when it detects again, against syncs of about 260 ms (add) to 2 s (modify). The first sync in a process detects once (about 300 ms) and no longer scans the project. Reported in PRs #2516 and #2519 by @nk33-dev. This reimplements both fixes independently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
|
Thank you, @nk33-dev: both PRs found real problems, the framework set frozen for a long-running session and the dependency cache keyed by file count. We've landed the fix in #2941, reimplemented with credit to you in the changelog. Detection now re-runs only when what it read changes, without a full scan on the watcher's path, and files indexed before a framework change are flagged by |
bompus
added a commit
to bompus/codegraph
that referenced
this pull request
Oct 30, 2026
…598) * fix(explore): Chinese, Japanese and Korean names reach explore, its Flow and context (colbymchenry#3031) The index stores a name in any script as one full-text token, and `codegraph query 로그인` found it, but every tokenizer between a question and the index knew only ASCII: extractSearchTerms split on [^a-zA-Z0-9] and kept 3+ characters, context's extractSymbolsFromQuery matched ASCII patterns only, and explore's exploreQueryTokens and the Flow's flowTokens required a leading [A-Za-z_$] and 3+ characters. So `explore 寻路服务` answered "No relevant code found", `context 로그인` printed only its header and `explore 入口 末端` had no Flow, while their ASCII twins worked. Shared helpers in search/query-utils now read words in any script whole: letters, digits and combining marks, cut where Han, kana or Hangul meets another script (NavMeshAgent类型 is NavMeshAgent and 类型, never agent类型), at least 2 characters in Han, kana or Hangul and 3 elsewhere, so Russian на and из stay out. extractSearchTerms and the explore and Flow tokens use them. Context adds names with a non-ASCII letter as whole runs, and a mixed name like get用户信息 as written, with no n-grams, so a partial Chinese word still never becomes a search of its own (PR colbymchenry#2005). The ASCII-only patterns see a word with accented or other non-ASCII letters blanked out, so état is no longer read as tat. A Han, kana or Hangul token counts as a deliberately named identifier, as such a name has no case or underscore to show it, so a query of such names is neither damped as prose nor flagged low-confidence. Validation: new cjk-query-tokens tests (12) cover CJK, mixed, Cyrillic and accented words, reference copies of the old tokenizers over an ASCII corpus, and explore and context end to end on Korean, Chinese and Japanese fixtures; 9 fail on main's source. Explore and context A/B with 214 ASCII queries (gin 76, Alamofire 79, express 59; 49 lead with a Flow): all byte-identical. Of 12 non-ASCII queries, 6 changed on gin and 4 on express, as intended: état, Señal and naïve are read whole, and handleHTTPRequest处理 now names handleHTTPRequest. Tokenizing a 10,000-character query takes at most 1.8 ms, against 0.1-0.3 ms before. Reimplements PR #516 by @andy-sg and PR colbymchenry#1262 by @yimingll. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit c12a7b6) * fix(sync): a long-running session picks up a framework added while it runs (colbymchenry#2941) The MCP server's watcher holds one CodeGraph for the whole session, and its framework detection was cached from the first time it ran: the orchestrator reset it only in indexAll, and CodeGraph.sync never re-initialized the resolver. A framework that arrived mid-session, like Express in a new package.json, was missing from every file synced after it, so a new route file got no route nodes until `codegraph index`. The resolver's declared-dependencies cache was also keyed by the indexed file COUNT (PR colbymchenry#2519): swapping one app for another of the same size, or editing a manifest, kept the old dependency set, even through reinitializeResolver(). Detection now records what it read: indexed source files, which a sync's change lists cover, and every other path it read or probed, with a size and mtime signature, including the per-app manifests the extractor gating reads. Before a sync extracts files, it detects again only when files were added or removed since, a source file detection read changed, or one of those paths changed on disk. A scoped sync takes the file list from the index plus the files it adds, so it never walks the project (the first sync in a process used to run a full scan for its first detection). What detection read is kept between detections and dropped by the same change lists and signatures, so detecting again reads only what changed. When the detected set differs from the resolver's, the sync re-initializes the resolver. Its initialize() now clears its caches before detecting, and clearCaches() forgets the declared-dependencies cache with the other per-context memos. Files indexed before a framework change are not re-extracted. A framework like Express applies to every file of its language, and re-reading them all inside a watcher's sync would cost what a full index does. Instead a full index records the frameworks it used, a sync records how detection moved since, and `codegraph status` (text, and --json with reindexRecommended and frameworksChangedSinceIndex) recommends `codegraph index` while they differ. Status only reads that record. Validation: - sync-framework-detection.test.ts (new, 8 cases): Express arriving with a new route file, by full and by scoped sync; a .svelte file added with no manifest change; an edited source file that detection read; a manifest-only edit picked up by the next sync, with the status record, cleared on revert and by a full re-index; a fresh process's scoped syncs detecting once, from the index, with no scan; the equal-size app swap; and the dependency cache forgetting on request. 7 of 8 fail on main's source (the 8th needs the new export). Ten mutations, one per mechanism, each fail a test. - The triage probes: main detects [] after Express arrives and routes.js gets no route; the fix detects express and extracts GET /hello, by full and by scoped sync. For the swap, main's reinitializeResolver() gives [react] and the fix [express]. - Cost on excalidraw (737 files, scoped syncs in one long-lived instance): the detection step takes 8 ms on a sync where nothing it read changed and 8-13 ms when it detects again, against syncs of about 260 ms (add) to 2 s (modify). The first sync in a process detects once (about 300 ms) and no longer scans the project. Reported in PRs colbymchenry#2516 and colbymchenry#2519 by @nk33-dev. This reimplements both fixes independently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 31ec908) * test(sync): adapt the framework-detection tests to the fork's skip-table detection The fork detects with the skip table (detectFrameworksWithSkips) and has an http-routing detector that reads every JS/TS source file, so the once-only detection test counts the sync's skip-table detections and edits a Python file after the first sync. A new test covers a file the skip table let detection pass over: editing it must detect again (the SynthSkips onSkipped hook records it as a detection input). * fix(status): recommend a re-index when package.json alone adds or drops a framework Since PR colbymchenry#2941, a sync records the frameworks that came or went since the last full index, and `codegraph status` recommends a re-index. But a sync detects frameworks again only when it extracts a file, so adding `express` or `@sveltejs/kit` to package.json with no source file edited recorded nothing: status said the index was up to date while an unchanged Express app's routes, or the `goto('/login')` in an unchanged +layout.svelte, stayed unlinked until a full index. A full index now also stamps the resolver's framework set, and getFrameworkChangesSinceIndex() adds the difference between that stamp and the set the resolver detects now (it detects on every open) to what syncs recorded. So a fresh `codegraph status` sees a manifest-only change whether or not a sync ran. An index built before this has no stamp, so that half starts at its next full index. A long-running server's resolver keeps its old set until it extracts a file, as before. Validation on main 6177692 (Windows 11): - Red/green: 3 of the 4 new tests fail with main's src/index.ts; the 4th, an unchanged project staying silent, passes on both. All 4 pass with the fix, as do the 8 sync-framework-detection tests. - The CLI before and after, on a SvelteKit fixture indexed without @sveltejs/kit, then given it in package.json, then synced: main's status says "Index is up to date", and --json has reindexRecommended false and frameworksChangedSinceIndex null. With the fix, status adds "Frameworks changed since the last full index (added sveltekit-router)", and --json has true and {added: [sveltekit-router]}. Dropping the dependency gives {removed: [sveltekit-router]}, adding express to an Express app gives {added: [express]}, an unchanged project gives null on both arms, and a full `codegraph index` clears the advice. - No false advice: a fresh index and then `status --json` on 18 real repos reports no change on any. They include express, flask, gin, vuejs/core, excalidraw, vuejs/docs, starlight, codegraph's own source and vscode's base and editor; Express, Flask, Next.js, React, Vue, Vue Router, Astro, Svelte, Spring, Go and UIKit are detected among them. Original PR colbymchenry#3005 by @inth3shadows. Fixes colbymchenry#3007 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 5526e33) * fix(mcp): a projectPath naming a file resolves through its directory An agent that passed the file it was looking at as `projectPath` got `isError: true` ("Path is not a directory"), while a path that doesn't exist at all walked up to the project's `.codegraph/` fine. An error teaches an agent to stop calling codegraph, and this mistake is recoverable. `execute()` now takes an existing file to stand for its directory, right after the length check, and rewrites `args.projectPath` there, so the project gate, the worker dispatch, the worktree notice and getCodeGraph all see that directory. A path that can't be stat'd walks up as before. Path safety: the directory goes through the same checks as any projectPath, the sensitive-folder refusal on the path itself and on the root its up-walk lands on, so a file reaches exactly what its directory reaches and never more. `/etc/hosts` is refused as `/etc` is, a file in `~/.ssh` as `~/.ssh` is, and `win.ini` in the Windows folder as that folder is. A symlink inside a project that points at a file in a sensitive folder is answered from the link's own folder, as a symlinked folder already is; nothing in the sensitive folder is opened. Validation on main de0a4eb (Windows 11): - Red/green: the PR's 2 tests (a file in an indexed project, and a file in a linked worktree, which keeps its worktree notice) and 1 of ours fail with main's tools.ts and pass with the fix. Ours asks for a file and for its directory in 4 cases, an indexed project, `~/.ssh`, a folder in no project, and the Windows folder's `win.ini`, and requires the same answer for both, refusals included. - A real `serve --mcp` session over stdio with a private HOME: on main, codegraph_search and codegraph_status for `<project>/src/a.ts` both returned isError "Path is not a directory"; with the fix both answer from the project, and `win.ini` is refused as a sensitive system directory. Original PR colbymchenry#2996 by @maxmilian. Fixes colbymchenry#2982 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 1c40e34) * fix(callers): list every call site of a caller, and a function that calls itself (colbymchenry#3077) getCallers keeps one row per caller (issue colbymchenry#1086) with the caller's first edge only, and seeds its reported set with the symbol itself, so codegraph callers showed a caller that calls the symbol on two lines once, at the line its own declaration starts on, and left a recursive call out, though the index stores every one of those edges and each is a line to edit when the symbol changes. The CLI also merged edges on source, target and kind, which dropped a second call site from --json. A new GraphTraverser.getCallSites (CodeGraph.getCallSites) reads the direct callers or callees with every edge: one row per neighbour in the order getCallers / getCallees list them, every site kept, and the symbol's own row, flagged recursive, when it calls itself. getCallers and getCallees are unchanged, so ranking, explore's Flow and blast radius, impact and the viewer read exactly as before; a test pins that every non-recursive row of getCallSites equals getCallers' row and first edge. src/graph/call-sites.ts holds the one wording of a site list (9, 10, +N more) the surfaces share. - codegraph callers prints a caller at its call-site lines (up to 10, then +N more), a recursive row as "(calls itself)", and --json gives each row lines and recursive, with every site in edges. Callees keep the callee's declaration line, where a call leads. - codegraph_callers / codegraph_callees read the same rows (10 sites per caller), and codegraph_node's trail shows callers at their sites, 3 at most, and the symbol once among its callers when it calls itself. Validation: new callers-call-sites.test.ts (7 tests) fails 7 of 7 with main's sources and passes with the fix; 11 neighbouring callers, trail and lookup test files (217 tests) pass. Before/after on one index per repo (expressjs/express 0dee2c52, excalidraw d3a9c2a1, gin-gonic/gin 0f09c3a9): trails changed for 108/253, 3630/4180 and 557/1395 symbols, nearly all callers moving from the declaration line to the call sites; recursive rows newly shown 2/20/7. Budget: trail median 115->117, 354->360, 185->187 chars, largest growth +131/+212/+91 chars, and the busiest caller rows (67, 300 and 100 sites) stay one capped entry. codegraph_callers on each repo's 15 busiest callees: largest answer 1025->1358, 1882->2127, 1790->1805 chars. Not in this change: the issue's own recursion, child.walk() on a for...of element over children: TreeNode[], is not linked at all on main (the receiver's element type isn't inferred), so it needs a resolution fix of its own; this.walk() and a plain recursive call are. Reimplements PR colbymchenry#2994 by @johnhldiver-beep. Fixes colbymchenry#2909 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit fa03fa6) * fix(explore): Flow reaches a named symbol a shorter route gets to first (colbymchenry#3079) The named walk behind codegraph_explore's Flow (and the viewer's /api/flow) kept one route per node and replaced it only with a better route of the same length. In entry -> mid1 -> deep -> leaf beside entry -> mid2 -> leaf, leaf was reached first through the unnamed mid2, so the route through the named deep was turned away for being longer, and the query "entry deep leaf" got a Flow that dropped deep or leaf. On django, SQLCompiler.as_sql pre_sql_setup get_select ran as_sql -> get_qualify_sql -> get_select and left out pre_sql_setup (issue colbymchenry#2970). A node now keeps every route to it that no other route to it beats: one that got there no later, through as many named symbols, ending on no more unnamed hops. That is the same-depth rule of PR colbymchenry#2453 widened, so a longer route through more named symbols survives, while a longer route through fewer is still turned away and can't win the deepest-sink choice; that choice and the longest-chain ranking across seeds are unchanged. A route that beats one waiting at its own depth takes its place in the queue, so nodes are visited in the order they were reached, as before. A route never passes a node twice: with several routes per node, a cycle among named symbols (ping -> pong -> ping) would count them again and out-rank the flow it repeats. Each node's callees are read once per search, across seeds. The explore-exact-target fixture's Flow now runs through every symbol its query names, as django's does. Its compiler.py stays a per-symbol view only while the named and spine bodies outgrow the file's share, which django's do (18,390 chars against 8,895, measured on django/db at 11af25c0) and the miniature's no longer did once the oversized unnamed bridge left the spine, so its get_select grows to django's proportions; no explore render rule changes. Validation: new flow-named-longer-route.test.ts (issue repro through resolveNamedSymbolFlow and codegraph_explore, the bridge budget, no cycles) and the updated explore-exact-target fixture: 3 assertions fail with main's walk, all 11 pass with the fix; 64 other Flow tests pass. Explore A/B, both arms on one index per repo (expressjs/express 0dee2c52, gin-gonic/gin 0f09c3a9, django/db of django 11af25c0, excalidraw d3a9c2a1), 290 queries: 152 graph-derived colbymchenry#2970-shape queries all changed their lead chain, each to one more named symbol (37/37, 55/55, 60/60); of 125 random named-chain controls 11 changed and of 32 hand-written flow queries 1 (django's), each gaining a named symbol; no query lost one, no chain repeats a node, and every query whose Flow is unchanged returns a byte-identical answer. Largest answer 24,997 -> 24,998 chars (cap 25,000). getCallees reads per query set drop 35-50% (gin 657 -> 410, django 1207 -> 752, excalidraw 1407 -> 951); explore time unchanged within noise. Reimplements PR colbymchenry#3003 by @johnhldiver-beep. Fixes colbymchenry#2970 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit a3e9e76) * fix(docs): agent and command lists match the code; worktree warning drops init -i (colbymchenry#3067) The warning for a git worktree answered from another checkout's index still said to run "codegraph init -i", a flag that has done nothing since init started indexing by default. Its one-line form is prepended to every read-tool result and codegraph_status embeds the long form, so both told the agent to build the index, which the server instructions rule out ("Indexing is the user's decision ... don't run it yourself"). The CLI's codegraph status now says: Run "codegraph init" in this worktree. What an agent reads (the read-tool notice, which also reaches it through the prompt hook and the explore/node CLI, and codegraph_status) says the user can run "codegraph init" there. Docs that no longer matched the code: - AGENTS.md listed 4 of the 11 installer targets, called a new one "a 5th agent", said the contract suite covers "all 4" targets, and listed 11 of the 24 subcommands. It now lists every target and subcommand, and names the hidden ones (serve --mcp, prompt-hook, and ui/web unless CODEGRAPH_UI=1). - README's Supported Agents section said no instructions file is written, but Claude Code, Codex CLI, Gemini CLI and opencode get the short CodeGraph section. Its Codex entry now says the Codex IDE extension reads the same config: $CODEX_HOME/config.toml (default ~/.codex/config.toml) for a global install, and .codex/config.toml, used only in a trusted project, for a local one. - README's WSL2 "Transport closed" entry named only the in-process fallback. The daemon also moves its socket to the temp directory when the project's filesystem can't host one. - init -i and -iv leftovers in internal docs, scripts, the add-lang skill and the repo's own Cursor rule, which now also leaves indexing to the user. Two stay on purpose: scripts/agent-eval/audit.sh can audit a published version older than 0.9.8, where init built the index only with -i (now commented there), and docs/benchmarks/codegraph-ab-matrix.md records a 0.9.4 run. Validation: - worktree-detection.test.ts: on main 6177692's worktree.ts and tools.ts, 4 of its 20 tests fail (the CLI wording, the agent wording, the read-tool notice and codegraph_status). With only the worktree.ts fix, the codegraph_status test still fails. All 20 pass here, and so do mcp-nested-repo-projectpath.test.ts's 13. - Full suite on this patch over 6177692 (CODEGRAPH_KERNEL_EXPECT=1, 4 workers): 815 files (809 passed, 6 skipped), 9608 tests passed, 0 failed, 86 skipped. - Rebased unchanged onto f67eafc. Its three new commits share only CHANGELOG.md with this PR, in other bullets. On the rebased tree these pass (81 tests): worktree-detection, mcp-nested-repo-projectpath, and the new commits' default-export-declaration, rust-path-attribute-modules, rust-path-super-glob, search-special-characters and strip-comments. Reimplements #549 by @ashishpatel26, colbymchenry#651 by @GaoSSR, colbymchenry#885 and colbymchenry#886 by @Dashsoap, and colbymchenry#1083 by @zengwenliang416. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit cb7fb61) * fix(site): agent lists include GitHub Copilot, and Codex notes its IDE extension (colbymchenry#3066) The site's agent lists stopped at Kiro, though the installer configures GitHub Copilot in VS Code, the Copilot CLI and JetBrains IDEs, and the README lists it everywhere. Copilot is now in every list: Integrations, Installation (the detected agents and the restart line), Introduction, Quickstart, Indexing and the landing page. The Integrations page's Codex CLI entry now says the Codex IDE extension reads the same config: $CODEX_HOME/config.toml (default ~/.codex/config.toml) for a global install, and .codex/config.toml, which Codex uses only in a trusted project, for a local one, as src/installer/targets/codex.ts writes them. Installation said "Cursor and Kiro get the MCP config only", which, with more agents listed, read as if every other agent gets an instructions-file section. It now names the four that do (Claude Code, Codex CLI, Gemini CLI and opencode); the others get the MCP config only. Validation: site npm ci and astro build pass (21 pages); the built pages carry the new entries. Reimplements the site half of #549 by @ashishpatel26. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 910e9bd) * fix(add-lang): the extraction check finds codegraph's npm shim on Windows, and takes CODEGRAPH_BIN (colbymchenry#3047) scripts/add-lang/verify-extraction.mjs ran execFileSync('codegraph', ...). On Windows, npm installs codegraph as a .cmd shim (so do npm link and local-install.sh), and Node neither finds a .cmd by bare name nor runs one without a shell. The check always died with "spawnSync codegraph ENOENT" (exit 2), and /add-lang's bench.sh read that as broken extraction and skipped its A/B. The script now asks, in order: - CODEGRAPH_BIN, when set. A .js, .mjs or .cjs path runs with the current node; anything else runs directly (a .cmd or .bat through cmd.exe). - On Windows, the codegraph Windows itself would pick: the first PATH folder holding codegraph under one of PATHEXT's runnable extensions. A .cmd or .bat runs through cmd.exe (shell: true) as one command string. Each token is quoted for the C runtime and caret-escaped for cmd, so spaces, &, parentheses, a %NAME% and a trailing backslash in the repo path arrive intact. An .exe or .com needs no shell. - Otherwise codegraph by bare name, exactly as before. On macOS and Linux without CODEGRAPH_BIN, the call and the output are unchanged. bench.sh takes CODEGRAPH_BIN too. It makes a path absolute (indexing runs from inside the repo), exports it so verify-extraction.mjs reads the index with the same build, and runs its own --version and init calls with it. Without it, bench.sh behaves exactly as before. The A/B's MCP server stays run-all.sh's own CG_BIN. Validation (Windows 11, Node 22.22.0): - New __tests__/add-lang-verify-extraction.test.ts covers CODEGRAPH_BIN with a .js stand-in (all platforms), a .cmd shim on PATH with an awkward repo path (Windows only), and the bare-name call (macOS and Linux only). With main's script both tests that run here fail ("could not read codegraph status"); with the fix, 2 pass and 1 is skipped. - The fixture from PR 1246's review (a codegraph.cmd on PATH): main exits 2 with ENOENT; the fix reads the status. - 13 scenarios with npm's own generated shims, and a real index in a folder named "lua repo & co (x86) 100%": main fails every one that needs the shim or CODEGRAPH_BIN, and the fix passes them with the exact argv reaching codegraph. With no codegraph anywhere, both exit 2 the same way. - bench.sh with the A/B stubbed: with npm link's shims on PATH, main's copy skips the A/B (verify exit 2) and the fix reaches it (PASS). CODEGRAPH_BIN as an absolute or a relative path reaches it too, and a missing one stops with a clear message. - With process.platform reported as linux or darwin, main's script and the new one make the same execFileSync call and print the same output. Raised by PR 1246 (@bslatner). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 1941caf) * chore: keep the kernel's vendored grammars out of GitHub's language stats (colbymchenry#3045) GitHub reports the repository as 79% C and 18% TypeScript. Nearly all of that C is the third-party tree-sitter grammars the native kernel compiles from source (codegraph-kernel/grammars/, 29 files, about 78 MB). A new .gitattributes marks every file under codegraph-kernel/grammars/ linguist-vendored, so Linguist leaves them out of the language statistics, and marks the five generated parser.c files linguist-generated, so pull requests show those collapsed. The hand-written scanner.c files stay expanded in diffs on purpose: that is where C gets patched or added (PR 2173, and the open PR 2866), and every contributor PR is security-reviewed, so such a change must stay visible. The patterns use /** because an attributes pattern that names a directory does not reach the files inside it. Validation: git check-attr over all 1,722 tracked files reports linguist-vendored set on exactly the 29 files under codegraph-kernel/grammars/ and linguist-generated set on exactly the five parser.c files; scanner.c, the headers and the LICENSE files are vendored only, and everything else, src/index.ts included, has neither. In a scratch repository the bare directory pattern leaves parser.c unspecified. Git does not act on linguist attributes and codegraph never reads .gitattributes, so builds and tests are unaffected. Raised by PR 2513 (@build8421) and PR 1668 (@kidonng). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 407ec4f) * docs(changelog): drop duplicate and unported-fix entries the union merge added --------- Co-authored-by: Colby Mchenry <me@colbymchenry.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Eric Minish <eric.minish@gmail.com> Co-authored-by: Max Hsu <maxmilian@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Framework dependency detection caches its answer by the number of indexed files. Replacing one source tree with another of the same size can retain the old dependencies. The resolver also repeatedly materializes the same tracked-file list while the database is unchanged.
Changes
This is limited to file lists and framework dependencies. It does not modify the node-name or stats caches proposed in #2115.
Validation
Windows, upstream baseline 7ba8eee.
npx tsc --noEmitpassed.git diff --checkpassed.No indexing-time or memory benchmark has been run, so this PR does not claim a measured speedup. Full-suite and cross-platform validation are left to CI.