Releases: optave/ops-codegraph-tool
Release list
v3.16.0
Remote embedding providers, complexity metrics for six more languages, and a broad resolver/extractor accuracy sweep across both engines. codegraph embed and codegraph search can now call a self-hosted or third-party OpenAI-compatible /embeddings endpoint instead of only the bundled local model, configured via a new embeddings.provider setting. codegraph complexity gains cognitive/cyclomatic/Halstead metrics for C, C++, Kotlin, Swift, Scala, and Bash on the WASM engine, which previously returned nothing for these languages. The Leiden community-detection algorithm is now ported to native Rust, closing a long-standing engine-parity gap where native ran classic Louvain while WASM ran Leiden. codegraph watch's incremental rebuild path gains CHA/RTA virtual-dispatch, points-to fallback, and dynamic-sink edge emission that only full builds previously produced, and reverse-dependency edge reconnection is fixed for a compound sibling-count-and-line-shift edge case. Deleted-file export tracking is now durable across purge ordering, so codegraph check's signature-change gate can no longer be bypassed by rebuild timing. Beyond these headline items, over a hundred targeted fixes improve call-edge resolution, symbol extraction, and native/WASM parity across nearly every supported language.
Features
- search: configurable remote embedding provider — set
embeddings.provider: "openai"andllm.baseUrlin.codegraphrc.jsonto routecodegraph embedthrough any OpenAI-compatible/embeddingsendpoint (self-hosted text-embeddings-inference, Ollama, LM Studio, vLLM, or OpenAI itself);codegraph searchand thesemantic_searchMCP tool embed queries through the same remote provider; newCODEGRAPH_LLM_BASE_URLenv override; reuses the existingllm.apiKey/apiKeyCommandsecret resolution (#1716)
Bug Fixes
- check: persist a durable snapshot of each deleted file's exported symbols and external consumers (new
deleted_export_advisoriestable, mirrored in the native orchestrator) so the no-signature-change gate can't be bypassed depending on whethercheckruns before or after a rebuild has already purged the deleted file's rows (#1939, #2103) - incremental: port CHA/RTA virtual-dispatch, points-to/alias fallback, and dynamic-sink edge emission into
codegraph watch's single-file rebuild path — these three edge categories were silently absent whenever a file was rebuilt via watch mode instead of a full build (#1997) - native: port the Leiden algorithm to Rust so both engines run the identical community-detection implementation, closing a documented "intentional" engine mismatch where native ran classic Louvain and WASM ran Leiden (#1937)
- incremental: fix reverse-dependency edge reconnection when a same-named sibling group's size changes in the same edit as a line shift — replaces the ordinal/nearest-line heuristic with alignment by the dominant shift that best explains surviving siblings (#2014)
- complexity: add cognitive/cyclomatic/Halstead rule support for C, C++, Kotlin, Swift, Scala, and Bash to the WASM engine, which previously had no rules at all for these languages; also fixes three native-only rule bugs surfaced during the port (C/C++ else-clause detection, Bash trailing-
elsescoring, Swift logical-operator node types) and a WASM-only double-counting bug for node types shared between branch and case sets (#2059) - complexity: stop using dotted names as a proxy for signature-only stubs; scope summary stats to file/target/kind filters; recognize Lua function nodes in complexity computation (#2056, #1953, #1924)
- resolver: attribute
new ClassName()calls to the constructor method, kind-filter same-file bare-name lookup for receiver-bearing calls, recognize identifier arguments passed to user-defined higher-order functions via parameter type, require invocation evidence for object-literal value-ref liveness, pick the single best match inresolveByGlobalinstead of every candidate, and scopeextends/implementsedges to same-file/import/language candidates (#2028, #2026, #1989, #2034, #2000, #1957) - extractors: extract bare
super(...)constructor calls as call edges, extract rest/default bindings from dynamicimport()destructures, emit per-element definitions for array-pattern destructuring, resolve inline object-literal dispatch tables on WASM, attribute same-file ES6 getter/setter property reads as call edges, capture top-levelconstdefinitions regardless of initializer shape, align native/WASM definition order for object-literal methods, and labelproperty_signatureinterface/type members with kindproperty(#2102, #2052, #2038, #2035, #2031, #1964, #1963, #1955) - native: resolve monorepo workspace package imports, add an
Object.definePropertyaccessor dispatch post-pass to the native orchestrator, scope barrel-only import skipping to transient barrel re-parses, propagate edge/node write failures out ofrun_pipelineinstead of swallowing them, writedynamic_kindfor sink edges, recover renamed import names in the FFI hybrid import-edge path, and resolveself.field/unit-struct/constructor-typed locals (#2061, #2024, #1991, #1972, #1698) - dynamic-calls: port Lua
eval/computed-key dynamic-call detection to the WASM extractor; restore reflectiondynamicKindfor.call/.apply/.bind; gate identifier-argument dynamic call edges on callback-accepting callees; apply the plain-identifier guard to the query path for.call/.apply/.bind(#2043, #1917, #1846, #1697) - roles: exclude genuine class/struct properties from dead-role classification; restrict entry-role classification to function/method-kind symbols; attribute liveness to Lua functions assigned to global/builtin identifiers (#1956, #1919, #1912)
- exports: credit plain imports of TypeScript interfaces/type aliases, discriminate file-level from symbol-level consumer entries, credit
instanceof ClassNamechecks, and credit destructured dynamicimport()bindings as consumers (#1978, #1974, #1930, #1921) - cycles: classify cycles whose only closing edges are speculative (#1988)
- incremental: correct blast-radius/fn-impact computation for line-shifted declarations, add a same-class bare-call fallback, recompute directory structure metrics for affected directories, couple
file_hashesupdates with edge regeneration, scope neighbor expansion to the changed file, and eliminate non-deterministic ordering in community detection (#1916, #1886, #1840, #1828, #1950, #1835) - db: derive
loadConfig()rootDir from the--dbpath in read-only query functions, thread configuredbusyTimeoutMsintoNativeDatabaseopen factories and remaining read-only query call sites, resolve directory paths infindDbPathto.codegraph/graph.db, resolvecodegraph embed's default DB path from the positional dir instead ofcwd, and log (instead of silently swallowing)statSyncfailures infindDbPath(#2018, #2021, #1943, [#1706](https://github.com/optave/ops-codegrap...
v3.15.0
Dynamic call detection ships across all 34 languages, plus richer codegraph stats output and a new ignoreAdditionalDirs config option. Seven phases of dynamic dispatch detection land in a single release: Reflect/decorator patterns in JS/TS, JVM dynamic dispatch with Kotlin callable-reference resolution, Python getattr/eval/functools.partial, Ruby send/public_send and PHP call_user_func, Go MethodByName and C/C++ dlsym/function-pointer detection, and C#/Swift/Elixir/Lua long-tail patterns. Every pattern is classified with a DynamicKind taxonomy (computed-literal, computed-key, reflection, eval, unresolved-dynamic) and visible via a new codegraph roles --dynamic flag; resolved calls remain in the normal call graph while unresolvable patterns emit zero-confidence sink edges that never pollute regular queries. A closed dispatch-table resolver (RES-2) handles ({a:fnA,b:fnB})[key]() via the existing PTS wildcard solver. codegraph stats now breaks down dead symbols by actionable sub-role (dead-leaf, dead-unresolved, dead-ffi, dead-entry, dead-callable), fixing a pre-existing double-counting bug in the dead total. A new ignoreAdditionalDirs config field lets projects append additional directories to the built-in IGNORE_DIRS without patching codegraph source.
Features
- dynamic-calls: detect and flag dynamic call sites in JS/TS —
DynamicKindtaxonomy classifies every dynamic call at extraction time; sink edges flagged asdynamic_kindin a new DB column (migration v20); newcodegraph roles --dynamicflag surfaces them; Rust native extractor fully mirrored (#1629) - dynamic-calls: Reflect.apply/construct/get and TypeScript
@Foodecorator detection (Phase 1) — JS/TS-specific reflection idioms emitreflection-kind calls; both walk and query extractor paths updated; Rust mirrored (#1637) - dynamic-calls: JVM dynamic dispatch — Java
Method.invoke/Class.forName/getMethod, Kotlin callable references (::fn), Scala/Groovy invoke patterns; Kotlin::fnrefs resolve at 100% recall (Phase 2) (#1646) - dynamic-calls: Python
getattr/eval/exec/functools.partialdetection;getattr(obj, 'method')resolves at 100% recall for top-level functions (Phase 3) (#1653) - dynamic-calls: Ruby
send/public_sendand PHPcall_user_func/$fn()detection; literal symbol calls resolve at 100% recall (Phase 4) (#1654) - dynamic-calls: Go
reflect.MethodByNameand C/C++ function-pointer/dlsymdetection; both resolve at 100% recall for literal names (Phase 5) (#1655) - dynamic-calls: C#
GetMethod/Invoke, SwiftNSSelectorFromString/performSelector, Elixirapply, Luaload/loadstring/bracket-index calls, ObjCperformSelector, DartFunction.apply; Swift resolves at 100% recall; Rust extractor mirrored for all four languages (Phase 6) (#1657, #1670) - dynamic-calls: RES-2 — closed dispatch-table resolution —
({a:fnA,b:fnB})[key]()resolves to each table entry via the PTS wildcard solver; Rust native mirror included (#1677) - stats: separate dead-code categories in
codegraph stats— per-sub-role breakdown with actionability labels (dead-leaf,dead-unresolved,dead-ffi,dead-entry,dead-callable); fixes pre-existing double-counting bug where the syntheticdeadaggregate was summed alongside individual sub-role counts (#1648) - config: add
ignoreAdditionalDirsto.codegraphrc.json— array of directory names merged with the built-inIGNORE_DIRSat file-collection time; included inBUILD_HASH_KEYSto trigger a full rebuild when changed;cratesremoved from the globalIGNORE_DIRSdefault (add it toignoreAdditionalDirsin your own.codegraphrc.jsonif needed) (#1666)
Bug Fixes
- dataflow: P4 incremental re-stitch + P6 vertex extraction on native engine path — vertex rows extracted during bulk-insert pass; re-stitch fires on callee-only changes without a full rebuild; P6 parity fix for incremental vs full-build paths (#1635)
- dataflow: make dataflow vertex write and inter-procedural stitch atomic — closes half-written state gap when the process is killed between vertex insert and stitch (#1658)
- dataflow: purge dataflow rows keyed by
call_edge_idbefore edge deletion — prevents FK constraint failures during incremental file purge that left stale nodes after file deletion (#1662) - dynamic-calls: RES-3 type-aware method name lookup for JVM
getMethodpatterns — Rust resolver now uses receiver type to construct qualified lookup for Groovy, Java, and ScalagetMethodcalls - native: Kotlin callable-ref prefers class method over top-level function; suppress spurious
invokesink edge that diverged from WASM (#1686) - native: CJS require bindings now produce receiver-edge parity with WASM —
require()-destructured class types emit receiver call edges on the native path (#1671, #1678, #1679) - native: always run JS role re-classification on full builds to fix
hasActiveFileSiblingsparity — incremental-only re-classification left stale role assignments on fresh builds - wasm: preserve
dyn=1when deduplicating edges with same source/target/kind/confidence — bare@Logdecorator was silently dropped when@Log()call-expression had already been processed first (#1688) - native: dispatch-table PTS resolution in JS extractor —
({a:fnA,b:fnB})[key]()now resolves on the native path, matching WASM output (#1690) - stats: exclude sink edges (dynamic call placeholders, confidence=0.0) from the call-confidence denominator; lift minimum confidence for resolved
ts-nativeedges from 0.3 → 0.5 (#1641) - stats: suppress false-positive high-fan-in warnings for Rust
::new()constructors (#1643) - mcp: break 37-file circular dependency by extracting
McpToolContexttomcp/types.ts— two consecutive architectural audits had flagged this cycle (#1638) - config:
ignoreAdditionalDirsandignoreDirsnow respected in watch mode (#1666) - analysis: exclude gitignored NAPI-RS artifacts from native gap detection — prevents spurious WARN and unnecessary WASM backfill on every fresh
--no-incrementalbuild (#1647) - analysis: exclude NAPI-RS generated
index.jsfrom WASM engine analysis — eliminates false 359 cognitive-complexity reading forrequireNativeincodegraph triage(#1636) - types: use
@types/better-sqlite3types ingetDatabase()andMcpToolContext— removes lastanyusages in the DB layer (#1639)
Chores
v3.14.0
Interprocedural dataflow analysis ships in full, plus a sweep of role-classifier accuracy fixes. The headline feature is a complete variable-level dataflow model: dataflow_vertices tracks param, return, and local variable locations per function; def_use edges connect definitions to uses within a function; arg_in and return_out edges stitch caller and callee dataflow across call boundaries. All 34 supported languages have dataflow rules. Incremental re-stitch (P4) fires on both the WASM/JS and native engine paths so arg_in edges are rebuilt when only a callee file changes, without a full rebuild. codegraph fn-impact --json gains direct and transitive shorthand fields alongside the existing levels breakdown. The role classifier received five accuracy fixes that eliminate a family of false-positive dead-symbol reports on real TypeScript and Rust codebases: exported interfaces with no cross-file call edges, type-def kinds in files with active callables, Commander.js dispatch methods, methods with active file siblings, and self-sibling sole-callable false-negatives. Erlang WASM parity is restored after the malicious package removal in v3.13.0.
Features
- dataflow: interprocedural variable-level model across all 34 languages — new
def_use(intra-function define-use),arg_in(caller arg → callee param), andreturn_out(callee return → caller capture) edge kinds;dataflow_verticestable tracks param/return/local locations;dataflow_summarytable stores per-param transfer functions (flows_to_return,is_mutated); DB migrations v18 + v19; P4 incremental re-stitch runs on both JS and native engine paths soarg_inedges are rebuilt on callee-only changes without a full rebuild;parity-compare.mjs --dataflowflag for vertex multiset comparison (#1608, #1612, #1615)
Bug Fixes
- fn-impact: add
directandtransitiveshorthand counts to JSON output —directis the level-1 caller count,transitiveis all callers at depth 2+; computed from the existing BFS levels data with no extra DB queries; fully backward-compatible (#1603) - roles: honour
exported=1flag for interfaces and type aliases with no cross-file edges — exported symbols used only as same-file type annotations now classify asentryrather thandead-unresolved(#1599) - roles: classify type-def kinds as
leafwhen the file has active callables —type,interface,struct,enum, andtraitdefinitions in files with at least one callable (fan-in or fan-out > 0) no longer producedead-ffiordead-unresolvedfalse positives (#1600) - roles: classify Commander.js
execute/validatemethods in framework dispatch directories asentry— eliminates ~12,000 false positives incodegraph roles --role deadoutput on Commander.js-based CLIs (#1601) - roles: classify methods and functions with active file siblings as
leaf— interface-dispatch callbacks (visitor pattern), logical-or function defaults, and handler-table property callbacks no longer report asdead-unresolved;fanOut > 0guard prevents over-promotion of trivial helpers; mirrored in Rust native classifier (#1602) - roles: prevent sole-callable self-sibling false-negative — a function whose only active file sibling is itself no longer satisfies the
hasActiveFileSiblingsheuristic; applied symmetrically in TypeScript and Rust classifiers (#1603) - db: derive
rootDirfromcustomDbPathwhen a custom--dbpath is set —openRepo/openReadonlyWithNativeno longer always default toprocess.cwd()when a custom DB path is provided (#1606) - config: wire
config.build.enginefrom.codegraphrc.jsoninto pipeline and DB connection —openRepo/openReadonlyWithNativenow read the file-level engine config; CLI--engineflag still takes priority (#1604) - dataflow: guard C/C++ function name and parameter extraction against unnamed declarators (#1608)
- dataflow: guard
exitFunctionscope-stack pop against early-return fromenterFunctionScope(#1612) - parity: restore Erlang WASM grammar — the grammar was lost when the malicious
tree-sitter-erlangnpm package was removed in v3.13.0; the validated WASM is now committed directly via a.gitignorenegation rule so the WASM engine has Erlang support without reinstating the removed devDependency (#1598) - ci: fix Windows SSH git dependency resolution — add
--addflag to the secondgit config insteadOfcall to prevent silent overwrite on Windows runners (#1597)
Refactors
- native-orchestrator: decompose into focused helper functions; extract call-resolution strategy dispatch to
resolver/strategy.ts(#1591, #1592) - cfg: replace TS and Rust
processStatementswitch with handler-table dispatch — O(1) single-type handler lookup (#1590) - types: consolidate shared interfaces and extract DRY abstractions (#1588)
- config: split config command subcommands; move JS type-resolution confidence threshold and engine env vars to
DEFAULTS(#1589) - complexity: address warn-level complexity in ast-analysis visitors, features domain, and infrastructure (#1593)
- roles: extract
ANNOTATION_ONLY_KINDSconstant in Rust classifier for parity with TS (#1602)
Chores
v3.13.0
User-level global config, codegraph config scaffolding, and an explain alias land. The headline feature is a new user-level configuration layer (~/.config/codegraph/config.json via XDG, or ~/.codegraph/config.json fallback) with an interactive per-repo consent model — DEFAULTS → global (if consented) → project → env → secrets. codegraph config now shows a human-friendly key/value/source table by default (pass --json for machine output), and gains --init (scaffold a .codegraphrc.json with all sections pre-populated), --edit (open in $EDITOR), --enable-global, --disable-global, and --list-global flags. Global --user-config [path] and --no-user-config CLI flags are also new. The explain command lands as a discoverable alias for audit. TypeScript compiler-based type resolution now auto-enables for TS projects that have a tsconfig.json. A supply-chain incident is resolved — a malicious tree-sitter-erlang npm package is replaced with a clean source build. On engine accuracy, super-dispatch cross-file false edges are eliminated, CHA confidence is aligned between WASM and native, and a sweep of parity fixes improves call-graph correctness for Go, Python, C++, CUDA, Haskell, and Zig.
Features
- cli: add
explainas alias foraudit—codegraph explain <target>is equivalent tocodegraph audit <target>; makes the audit command easier to discover - config:
codegraph confignow shows a key/value/source table when--jsonis not passed — each key displays its current value and which layer it came from (default,user,project,env) - config: add
--initand--editscaffolding helpers —codegraph config --initscaffolds a.codegraphrc.jsonwith all sections pre-populated;codegraph config --editopens the project config file in$EDITOR - config: user-level (global) config with per-repo consent — new
~/.config/codegraph/config.json(XDG) or~/.codegraph/config.jsonfallback; interactive per-repo consent model;codegraph config --enable-global,--disable-global,--list-globalflags; global--user-config [path]and--no-user-configCLI flags; layered merge order: DEFAULTS → global (if consented) → project → env;config_hashinvalidation triggers a full rebuild when build-relevant config changes;loadConfigWithProvenancereturns per-key source map (#1559)
Bug Fixes
- config: auto-enable TypeScript compiler resolver for TS projects —
typescriptResolvernow defaults totrue; silently skips whentypescriptis unavailable or notsconfig.jsonis present, so JS-only projects and environments without TypeScript are unaffected (#1461) - config: clarify consent prompt wording to reflect per-key inheritance semantics and improve question clarity
- cha: eliminate super-dispatch cross-file false edges —
super.method()calls no longer resolve to methods outside the class hierarchy; the native engine expands super-dispatch into CHA sibling overrides (#1506, #1514, #1537, #1544) - native: resolve this-dispatch in func-prop methods —
fn.method = function(){ this.other() }now resolvesotherthrough the func-prop enclosing context (#1512) - native: seed typeMap entries for let/var object-literal methods — object literal methods defined with
let/varnow register their receiver types for downstream resolution - native: prefer bare name over qualified in span-tie caller attribution — when two candidates share the same span, the bare-name symbol wins to avoid false qualified-name attribution
- native: resolve Go factory and Python constructor receiver types —
NewFoo()in Go andFoo()constructors in Python now seed the typeMap for downstream method-call resolution (#1498) - native: align object-literal shorthand method node ordering with WASM — extraction order is now consistent between engines
- wasm: align TypeScript CHA dispatch confidence (0.6 → 0.8) — WASM now matches the native engine's confidence for CHA-resolved edges (#1505)
- wasm: port missing node extractions to JS extractor (jelly-micro #1471) — several edge-type gaps in the WASM engine aligned with the native engine (#1509)
- wasm: emit receiver edges for declaration-typed locals (C++, CUDA) — typed local declarations in C++ and CUDA now produce receiver call edges (#1497)
- parity: port the JS points-to solver to native — WASM and native now run identical resolution logic for JavaScript/TypeScript points-to bindings; the four JS pts post-passes on the hybrid path are removed, leaving a single source of truth (#1465)
- parity: align Java interface dispatch across WASM, native, and hybrid engines — all three engines now agree on interface method resolution confidence and edge set (#1503)
- parity: align enclosing-caller attribution for variable bindings (Haskell, Zig) — multi-binding
letpatterns now attribute calls to the correct enclosing caller (#1499) - extractor: strip brackets from computed string-key method names —
obj['method']()no longer emits['method']as the method name - receiver: local function constructors block cross-file class receiver edges — prevents false cross-file receiver matches when a same-file function constructor is in scope
- resolver: class-scope field annotation typeMap keys prevent cross-class collision —
private repo: Repositoryin two classes no longer shares a typeMap key (#1495) - triage: normalize JSON output to use
itemskey at all levels — all triage JSON responses now use a consistentitemsarray structure - cli: accept
--jsonflag in batch command as no-op — batch command no longer errors when--jsonis passed (#1563) - parser: downgrade WARN to debug for optional parsers with missing WASM grammar — language parse errors for optional grammars no longer pollute stderr with WARN messages
- native: don't warn when a natively-supported file produces 0 symbols via WASM — gitignored Rust addon artifacts no longer trigger false-positive extractor failure warnings
- deps: remove malicious
tree-sitter-erlang, fix 3 moderate vulnerabilities — replaces the compromised npm package with a clean source build; also fixes 3 moderate-severity vulns (#1478) - hooks: track Bash file modifications to prevent false-positive commit blocks (#1483)
- perf: scope
runPostNativeChato changed files on incremental builds — incremental rebuilds no longer run the full CHA post-pass on unchanged files (#1490) - perf: pass
symbolsOnlythroughparseFilesWasmInline— avoids unnecessary data extraction during symbol-only parse passes (#1489) - bench: update Elixir, Julia, and Objective-C expected-edges to module-qualified names (#1496)
- ci: accept v-prefixed versions in
publishworkflow_dispatchinput (#1443)
Performance
- native: replace O(n²) type-map dedup with O(n) write-then-dedup — large files with many type-map entries no longer degrade quadratically during the native post-pass
Refactors
- native: mirror Rust crate module layout to the TypeScript
src/tree —crates/codegraph-core/src/modules now follow the snake_case equivalent of their TypeScript counterparts (#1463) - extractors: deduplicate C-family primitive types into a shared constant
Chores
- ci: add per-PR perf canary for extractor/graph/native changes (#1488)
- ci: add dev-dependency audit step at critical severity (#1479)
- deps-dev: bump
@biomejs/biomefrom 2.4.16 to 2.5.0 (#1523) - deps-dev: bump
tree-sitter-gleam(#1522) - deps-dev: bump
@vitest/coverage-v8from 4.1.7 to 4.1.8 (#1521) - deps: bump
anthropics/claude-code-actionfrom 0.0.63 to 1.0.148 (#1520)
v3.12.0
Phase 8 Analysis Depth lands in full, plus a 30-technique JavaScript/TypeScript resolution sweep. Sub-phases 8.1 through 8.6 are now complete, with 8.3 substantially complete (one stretch-goal item — full allocation-site abstraction with fixed-point iteration — deferred to a future release): TypeScript compiler API type resolution (typescriptResolver opt-in in .codegraphrc.json) upgrades confidence-0.7 heuristic edges to compiler-verified 1.0; inter-procedural return-type propagation resolves method chains and factory patterns up to 3 hops; field-based points-to analysis (Phases 8.3 through 8.3f) covers callbacks, event handlers, parameter flows, object property writes, and object destructuring rest parameters in both WASM and native engines; barrel re-export chain resolution traces symbols through index.ts re-exports to their actual declaration files; CHA+RTA dynamic dispatch resolves interface method calls to all instantiated concrete implementations; and Phase 8.6 adds a byTechnique breakdown to codegraph stats --json showing edges attributed to each resolver technique. Beyond the Phase 8 work, a parallel accuracy sweep adds resolution for prototype-based method calls, Object.defineProperty accessor this-dispatch, super.method() dispatch via class expressions and static blocks, .call/.apply/.bind receiver rebinding, for-of/Set/Array.from iteration callbacks, inline-array spread call edges, and constructor-assigned property types. C# call graphs improve with same-class bare static call resolution and var-typed local type inference. Six native engine parity issues in the incremental rebuild path are fixed. Caller coverage for real-world TypeScript projects is substantially higher after this release. Note: most resolver improvements appear under Bug Fixes below — they used fix: commit prefixes because they corrected missing edges in existing resolution logic rather than introducing entirely new CLI capabilities.
Features
- stats: add
byTechniquebreakdown tocodegraph stats—codegraph stats --jsonnow includescaller_coverage.byTechniquewith edge counts per resolution technique (ts-native,points-to); displayed in human-readable stats output under the caller coverage line; DB migration v17 addstechniquecolumn toedgestable (#1303) - config: new
typescriptResolveroption in.codegraphrc.json— set"build": { "typescriptResolver": true }to enable the TypeScript compiler API enrichment pass; compiler-verified edges (confidence 1.0) replace heuristic typeMap values for factory calls, generic constructors, and other patterns tree-sitter can't resolve alone (#1278)
Bug Fixes
- resolver: TypeScript-native type resolution via
ts.createProgram+ type checker (Phase 8.1) — upgrades heuristic typeMap entries to compiler-verified confidence 1.0 for.ts/.tsxfiles; resolvescontainer.get<MyService>()→MyService.doThing()class of edges that tree-sitter cannot see (#1278) - resolver: inter-procedural return-type propagation (Phase 8.2) —
const x = createUser()propagates return type toxfor downstream method-call resolution; chain propagation up to 3 hops with confidence decay (1.0 → 0.9 → 0.8 → 0.7);analysis.typePropagationDepthconfig knob (#1279) - resolver: field-based points-to analysis for higher-order calls (Phase 8.3) — tracks callback assignments, event-handler registrations, and strategy-pattern wiring; resolves
app.use(handler)andevents.on('click', handler)call edges (#1289) - resolver: cross-module points-to propagation (Phase 8.3 + 8.3b) — WASM + native parity; inter-module flows through import edges now propagate type bindings across file boundaries (#1296)
- resolver: parameter-flow tracking in points-to analysis (Phase 8.3c) — function parameters tracked through the call graph; typed parameters seed the receiver typeMap for downstream method resolution (#1294, #1308)
- resolver: object property write tracking in points-to analysis (Phase 8.3d) —
obj.handler = fnassignments tracked soobj.handler()resolves to the assigned function (#1295) - resolver: constructor-assigned property types for receiver-typed resolution (JS/TS) —
this.svc = new Service()in constructors seeds the typeMap sothis.svc.call()resolves toService.call(#1314) - resolver: object destructuring rest parameter resolution (Phase 8.3f) —
const { a, ...rest } = obj; rest.method()now resolvesmethodvia the rest binding's source type; WASM + native parity (#1355) - resolver: barrel re-export chain resolution — imports via
components/index.tsbarrel files now trace to the actual declaration file rather than mapping to the barrel; both WASMbuildImportedNamesMapandbuildBarrelEdgesupdated (Phase 8.4) (#1298, #1302) - resolver: CHA + RTA enhanced dynamic dispatch (Phase 8.5) — interface method calls emit edges to all instantiated concrete implementations;
new X()calls tracked for RTA filtering;this.method()resolved through the class's own method table and parent hierarchy (#1302) - resolver: prototype-based method calls, func-prop this-dispatch, and spread/iteration callback resolution —
Dog.prototype.bark = function()definitions extracted;fn.method = function(){ this.other() }this-dispatch wired; object-rest param dispatch (#1331) - resolver:
Object.definePropertyaccessor this-dispatch —this.method()calls insidedefinePropertygetter/setter callbacks resolve through the enclosing class (#1346, #1351) - resolver: calls through
Object.defineProperty/defineProperties/Object.create— accessor definitions emit call edges to the object's own prototype chain (#1328) - resolver: generator functions extracted as definitions (JS/TS) —
function* gen()andasync function* gen()now emit definition nodes so callers that iterate them appear in the call graph (#1333) - resolver:
super.method()dispatch via class expression, static block, and field def —super.f()in class bodies,class Foo extends Bar { static { super.f() } }, and field-level assignments now resolve to the parent class method (#1399) - resolver:
.call()/.apply()this-rebinding —fn.call(obj, ...)andfn.apply(obj, [...])patterns now resolve the call tofnwithobj's type as receiver (#1405) - resolver:
Function.bind/call/applyreceiver-typed resolution —bound = fn.bind(obj)seeds the typeMap sobound()resolves as a method call onobj's type (#1330) - resolver:
for-of,Set, andArray.fromiteration-callback edges —for (const x of items) x.method()andnew Set([...]).forEach(item => item.method())patterns emit call edges (#1397) - resolver: inline-array spread call edges —
fn(...[a, b, c])unwraps the spread array and emits call edges to each element's method (#1394) - extractor: inline-new expression recognized as receiver type in
extractReceiverName—(new Dog()).bark()directly infersDogas the receiver type without a prior assignment (#1415) - resolver: this.prop typeMap key scoped to enclosing class — prevents false edges in multi-class files where two classes define a property of the same name (#1382)
- parity: C# same-class bare static calls resolved + confidence filter for static receiver fallback —
MyClass.StaticMethod()from within the same class now resolves; heuristic static-receiver fallback gated on confidence ≥ 0.75 to reduce false positives (#1417, #1427) - parity: C#
var-typed local types inferred fromnew-expression initializers —var svc = new MyService()now seeds the typeMap withMyServicefor downstream method-call resolution (#1424) - parity: C# static receiver calls in WASM engine — static method resolution aligned with the native engine for same-class and qualified receiver patterns (#1395)
- native: extract parameters for prototype method definitions —
Dog.prototype.bark = function(name) {}now emitsnameas a parameter node in the native engine (#1345) - native: complexity/CFG computed for prototype method definitions — Rust engine now calcul...
v3.11.2
Watch mode correctness sweep. Five independent bugs in the incremental rebuild path are fixed: the call resolver had drifted from the full-build authoritative version, causing inflated calls edges on any watch rebuild touching a widely-imported file; a missing dedup set let the same (caller, target) pair be inserted multiple times; receiver, extends, implements, and dynamic-import edges were silently absent from watch-mode rebuilds; top-level Ruby constants and program-level Python assignments were dropped by the native extractor while WASM captured them; and 10 native grammar crate versions had drifted from their WASM npm counterparts. A new shared call-resolver.ts module now backs both the full-build and incremental paths, closing the structural gap that let these bugs accumulate.
Bug Fixes
- watch: align incremental call resolver with full build — the watcher's
resolveCallTargets/buildCallEdgeshad drifted from the authoritative full-build resolver inbuild-edges.ts; on a comment-only rebuild of a widely-imported file,callsedges inflated by ~700 (#1261) - watcher: eliminate calls-edge inflation in incremental cascade — adds the missing
seenCallEdgesdedup set tobuildCallEdgesin the incremental path, and tightens the global name fallback inresolveCallTargetsto match the full-build resolver (#1264) - extract: eliminate WASM/native node divergence — native Ruby extractor now handles top-level
assignmentnodes (program-level constants); native Python extractor extracts program-level function and class definitions that were previously dropped; eliminates the persistent full-build node count gap between engines (#1266) - watcher: add missing receiver/extends/implements/dynamic-import edges —
receiveredges (method call receiver resolution),extends/implementsclass hierarchy edges, anddynamic-importedges were silently absent from watch-mode incremental rebuilds; now parity-aligned with the full build (#1267) - engine: align native grammar crate versions with WASM npm packages — upgrades 10 Rust tree-sitter grammar crates in
Cargo.tomlto match the npm devDependency versions, eliminating grammar-version drift identified as the structural source of native/WASM call-edge divergence (#1268)
Refactors
- engine: extract shared call-resolver, eliminate build/watch duplication —
findCaller,resolveByMethodOrGlobal,resolveCallTargets, andresolveReceiverEdgeextracted intosrc/domain/graph/builder/call-resolver.ts; both the full-build and incremental paths share a single implementation via aCallNodeLookupinterface (#1272)
Chores
- ci: add grammar version parity check between npm devDeps and Cargo.toml — new
scripts/check-grammar-versions.mjscompares grammar major versions across both package managers; wired as a lightweight CI job to catch future drift early (#1270) - deps: bump commander from 14.0.3 to 15.0.0 (#1251), tree-sitter-erlang to 0.18 (#1252), @biomejs/biome to 2.4.16 (#1250), commitlint to 21.0.2 (#1253, #1254)
v3.11.1
Four new embedding models, sticky model resolution, and a large internal refactor. codegraph embed adds mxbai-large, mxbai-xsmall, bge-m3, and modernbert to the model registry — all publicly accessible without an HF token, covering multilingual, high-quality large, tiny-with-long-context, and ModernBERT-architecture use cases. Sticky model resolution ensures that subsequent codegraph embed runs on an existing graph reuse the model it was originally built with rather than the global default; the default for fresh graphs shifts from nomic-v1.5 to nomic (same dimensions and context window, but the public Xenova mirror instead of the occasionally-gated nomic-ai org). Watch mode delta reporting is corrected — the rebuild log now shows the net edge change instead of an inflated gross re-insertion count. Under the hood, a 10-PR refactor (Titan Grind) decomposed the largest modules — ast-analysis, domain, graph, presentation, extractors, and core-rs — into focused, independently-testable units with no user-visible behavioral changes.
Features
- embed: add
mxbai-large,mxbai-xsmall,bge-m3, andmodernbertembedding models — all Apache-2.0/MIT licensed, noHF_TOKENrequired;bge-m3is multilingual (100+ languages, 8192 ctx),mxbai-largetops the MTEB BERT-large leaderboard,mxbai-xsmallis tiny with 4096-token context,modernbertuses the ModernBERT architecture (#1229) - embed: sticky model resolution —
codegraph embedon an existing graph now reuses the model stored inembedding_metarather than falling back to the global default; the default for fresh graphs changes fromnomic-v1.5tonomic(same dim/context, public Xenova mirror avoids occasional HF gating) (#1228)
Bug Fixes
- watch: report net edge delta in rebuild log — previously the count was inflated by re-inserted edges that cancel out; now shows only the true net change (#1245, #1220)
Refactors
- ast-analysis: decompose engine and visitors, break visitor-utils cycle (#1231)
- extractors: shared helpers across language extractors (TS+Rust); adopt shared child-iteration helpers (#1230, #1238)
- core-rs: decompose pipeline, read_queries, edge_builders; collapse walker recursion (#1232)
- graph: decompose Leiden optimiser and roles classifier (#1233)
- presentation: extract shared rendering helpers in cfg and flow (#1234)
- domain: decompose parser, analysis, and search modules (#1236)
- features: decompose complexity/structure/owners; reduce cfg/cochange/feature-warnings complexity (#1237)
- parity: render orchestrator-drop summary as a per-extension table (#1225, #1240)
v3.11.0
Native engine reaches feature parity with WASM, plus an engine-parity sweep across 14 languages. The final 11 extractors (Clojure, CUDA, Julia, Solidity, Erlang, R, Groovy, Gleam, Objective-C, F#, Verilog) are now ported to Rust, so every supported language extracts symbols natively when the prebuilt binary is available — no more silent fallback to WASM for these. In parallel, a multi-PR parity sweep aligned the contains/parameter/inheritance edges that the two engines disagreed on across Java/Kotlin/CUDA/Ruby/Objective-C/HCL/Dart/Scala/Elixir/Haskell/Python/C#/Groovy/C++, so the native engine no longer drops parameters, function-pointer fields, default-value arguments, or interface inheritance edges that WASM was already emitting. F# .fsi signature files now route through a dedicated grammar instead of being parsed as .fs source. On the CLI, -n is now the short form of --limit on every limit-accepting command (previously only on five), build accepts -d/--db, and MCP semantic_search accepts file_pattern to scope hybrid/semantic/keyword searches to a subtree. Watch mode no longer crashes on rebuild when embeddings exist for the file, and barrel-chain re-parse discovery iterates until stable so chained re-exports stop dropping edges.
Features
- cli: unify
-nshort flag across all--limit-accepting commands —roles,structure,communities,audit,check,children,diff-impact,ast,brief,cfg,context,dataflow,deps,exports,flow,fn-impact,impact,implementations,interfaces,query,sequence, andwherenow all accept-nin addition to--limit(#1184) - cli: accept
-d/--dbonbuildto match every other DB-scoped command — pre-built graphs can now be re-targeted at build time withoutcd-ing into the project root (#1183) - mcp: forward
file_pattern(string or string[]) insemantic_searchto scope hybrid/semantic/keyword results — closes a silent-drop where MCP callers passingfile_patterngot unscoped global hits back with no error (#1149) - fsharp: route
.fsifiles through a dedicated signature grammar — newfsharp-signaturelanguage id with aval foo : typehandler that distinguishes signature declarations fromlet foo = ...source bindings (#1162)
Performance
- native: port Clojure extractor to Rust (#1097)
- native: port CUDA extractor to Rust (#1099)
- native: port Julia extractor to Rust (#1098)
- native: port Solidity extractor to Rust (#1100)
- native: port Erlang extractor to Rust (#1103)
- native: port R extractor to Rust (#1102)
- native: port Groovy extractor to Rust (#1101)
- native: port Gleam extractor to Rust (#1105)
- native: port Objective-C extractor to Rust (#1106)
- native: port F# extractor to Rust (#1104)
- native: port Verilog extractor to Rust (#1107)
- native: skip backfill on incrementals when orchestrator preserved files — avoids redundant WASM-side backfill work on clean incremental rebuilds (#1082)
- native: skip backfill on clean incrementals + bench guard tuning — removes the residual cost when nothing actually changed (#1085)
- bench: exclude resolution-benchmark fixtures from dogfooding and incremental-benchmark sweeps so per-file timings reflect real source code, not pinned-precision fixture corpora (#1131, #1134)
Bug Fixes
- extractors: drill through
function_declaratorfor parameter names — restores parameter extraction for C-family pointer-to-function declarations across all engines (#1213) - extractors: recursively walk Haskell pattern parameters so destructured arguments emit edges (#1203)
- extractors/cuda: keep function-pointer class fields that were previously dropped at parity-align time (#1207)
- native/kotlin: strip
navigation_suffixwrapper from call name so qualified calls resolve to the correct callee instead of the suffix node (#1205) - extractors/elixir: extract default-value and pattern-match parameters that were silently dropped on multi-clause functions (#1202)
- extractors: align Ruby/Objective-C
containsparity across engines (#1201) - extractors: align Java/Kotlin/CUDA
containsparity across engines (#1199) - extractors: align HCL/Dart/Scala
containsparity across engines (#1196) - extractors: align Elixir/Haskell/Python
containsparity across engines (#1195) - native/csharp: align extractor with WASM on three engine-parity divergences (#1194)
- db: stop
findDbPathwalk at cwd when there is no git ceiling, socodegraphinvoked outside a repo no longer climbs to the filesystem root (#1193) - native/cpp: strip reference modifier from parameter names so
T& fooextractsfoo, not& foo(#1192) - native: apply WASM callback-callee gating in JS extractor so
member_expressioncallback args no longer create false-positive edges (#1191) - watch: purge embeddings before nodes to stop FK crash in
rebuildFile— incremental rebuilds on watched files with embeddings no longer crash with a foreign-key constraint violation (#1182) - builder: iterate barrel re-parse discovery to stop dropping chained-barrel edges — the WASM builder now loops until the dirty-barrel set is stable, so
a → b → c → dchained re-exports no longer leave edges on the floor (#1179) - embed: install
@huggingface/transformersinto codegraph's host node_modules —codegraph embedno longer fails when invoked from a project that hasn't installed transformers itself (#1178) - hooks: use POSIX
[[:space:]]inguard-git.shgrep patterns so the hook works under BSD grep on macOS (#1170) - hooks:
guard-git.shsed patterns work on macOS BSD sed — closes a silent no-op where the hook ran but matched nothing under BSD (#1146) - groovy: emit
ClassRelationfor interface inheritance in both engines soimplementsedges no longer go missing on Groovy classes (#1158) - builder: remove duplicate early-return in
backfillNativeDroppedFiles(#1148) - julia: port parameterized-type / qualified-def / qualified-import fixes to WASM so Julia parity matches between engines (#1128)
- gleam: extract parameters for external functions so cross-module Gleam calls resolve (#1127)
- native: purge stale rows when WASM-only files are deleted (#1122)
- native: backfill new dropped-language files on quiet incrementals so newly-added Solidity/Erlang/Verilog files appear on the next rebuild even when the file system signal looks quiet (#1123)
- r:
setMethodemits a call edge, not a duplicate definition (#1125) - groovy: dispatch
juxt_function_callin both engines so Groovy DSL-style calls (task { ... }) emit edges (#1124) - bench: warmup + median for
queryTimeMsto remove cold-start noise from the publish gate (#1133) - scripts: trend annotations fall back to nearest non-null prior release so a missing run no longer breaks the trend chart (#1120)
- scripts: preserve manual NOTES block in incremental report generator (#1119)
Refactors
- objc: use
if let Somein for-loop instead of?to fail-...
v3.10.0
Selective MCP tool filtering, WASM build-speed regression fix, and Haskell parity restoration. A new mcp.disabledTools config field lets you remove specific MCP tools from the schema entirely — useful for smaller-context models that don't need all 33 tools competing for the initial prompt budget. The 3.9.6 expansion of AST_TYPE_MAPS to 23 languages had a side effect of making WASM full builds re-parse every WASM-parseable file in the corpus; the per-file needsFn filter now scopes the re-parse correctly, dropping the 744-file dogfooding build from 14.0s back to 7.8s (matching the 3.9.5 baseline). A second parity fix gates astTypeMap lookups with Object.hasOwn so Haskell's constructor node type no longer walks the prototype chain to Object.prototype.constructor — restoring the Haskell resolver from 0%/0% precision/recall in 3.9.6 to 100%/33% (matching the 3.9.4 baseline). The release benchmark workflow has also been restructured: regression guards now run inside publish.yml before npm publishes, instead of after the docs PR lands, so a regression can no longer ship to npm and then fire on unrelated dev commits.
Features
- mcp: add
mcp.disabledToolsconfig to remove specific tools from the MCP schema — drops disabled tools entirely from the schema (not just rejected at runtime) so smaller-context models save initial-prompt tokens; tool names are normalized for matching (#1035)
Bug Fixes
- parity: gate
astTypeMaplookup withObject.hasOwn— Haskellconstructornodes (Left,Right,Just, …) no longer fall through toObject.prototype.constructor, which was dropping the non-cloneableObject()function intoastNodes.kindand crashing the worker boundary withfunction Object() { [native code] } could not be cloned; Haskell resolver returns to v3.9.4 baseline (precision=1.0, recall=0.333) (#1041)
Performance
- wasm: scope
ensureWasmTreesre-parse to files that actually need it —wasm-worker-entry.tsnow serializes emptyastNodesarrays (empty ≠ undefined) andensureWasmTreesaccepts an optionalneedsFnfilter so only files genuinely lacking data are re-parsed; WASM full build on the 744-file dogfooding corpus drops from 14.0s back to 7.8s, restoring the 3.9.5 baseline (#1038)
CI
- release: gate npm publish on benchmark regressions — moves the regression guard into a
pre-publish-benchmarkjob inpublish.ymlso a regression fails the publish workflow before npm sees the new version, instead of firing on unrelated dev commits after the post-publish benchmark PR lands (#1040) - bench: rename auto-generated benchmark branch prefix from
benchmark/tochore/— aligns with the localguard-git.shallow-list so post-publish benchmark PRs no longer require hook bypass when pushed from a Claude Code session (#1044)
v3.9.6
Native engine parity and incremental-build performance. Native single-file incremental rebuilds drop from 876ms to 43ms (95% faster, 0.78× WASM) by adopting the WASM save-and-reconnect strategy so reverse-dep files no longer get re-parsed when they didn't change. Native full-build edge construction now beats WASM (119ms vs 184ms) by replacing per-row query_row lookups with one-shot HashMap pre-loads and chunked multi-row inserts. AST-node extraction is now within 0.12% parity between engines after fixing three independent divergences (missing language coverage in WASM, await_expression recursion, UTF-8 byte-length gating). The release-triggered benchmark workflow that silently hung at 600s on v3.9.5 is fixed — workers now dispose the WASM parser pool and embedding progress writes to stderr instead of corrupting stdout JSON. A new CI parity gate runs after every release benchmark and fails loudly when any of five engine-parity thresholds regress, so silent drift can no longer ship.
Bug Fixes
- parity: align WASM and native
ast_nodesextraction — registered 19 missing languages in WASM'sAST_TYPE_MAPS, removedawait_expressionskipChildrenquirk, and fixed UTF-8 byte-length gating in native; total AST-node parity now within 0.12% across self-build (was ~7,200 row delta) (#1016) - parity: log per-file reasons for native orchestrator drops — bucket dropped files by
unsupported-by-native(info) vsnative-extractor-failure(warn) with sample paths so legitimate parser limits no longer mask real Rust extractor bugs (#1024) - bench: dispose WASM worker pool and keep progress off stdout — release-triggered benchmark workflow no longer hangs at 600s; embedding progress writes to stderr so JSON-consuming workers stop parsing
Unexpected token 'E'(#1009)
Performance
- native: scope incremental rebuild to truly-changed files — 1-file incremental drops from 876ms to 43ms (95% faster, 0.78× WASM) by saving reverse-dep edges before purge and reconnecting them post-rebuild instead of re-parsing the full reverse-dep cone (#1027)
- native: batch-load file/symbol IDs in edges phase — replaces per-import
query_rowlookups with one-shot HashMap pre-loads and chunks import-edge inserts into 199-rowVALUESbatches; full-buildedgesphase now 119ms vs WASM's 184ms (0.65×) (#1028)
CI
- bench: gate release benchmark on engine parity thresholds — five thresholds (file-set gap, DB size ratio, edges/roles ratios, 1-file incremental ratio) fail the release benchmark workflow when engine parity regresses, with a markdown summary linking each breach to its tracking issue (#1014)
Chores
- deps-dev: bump @vitest/coverage-v8 from 4.1.4 to 4.1.5 (#1021)
- deps-dev: bump @biomejs/biome from 2.4.11 to 2.4.13 (#1019)
- deps-dev: bump @commitlint/cli from 20.5.0 to 20.5.2 (#1018)
- deps-dev: bump tree-sitter-erlang from 0.0.0 to 0.16 (#1017)
- deps-dev: bump tree-sitter-gleam from
0153f8bto1627dc5(#1020)