What's new
Every release, straight from GitHub — newest first.
- v0.9.15Jul 13, 2026
v0.9.15
Full notes ↗A small, security-focused patch on top of 0.9.14.
Highlights
- Security (stored XSS): the exported
graph.htmlis fixed (#1838). The report's neighbor links dropped an unescaped stringified node id into an inlineonclick— which broke every neighbor link and, when a node id/label contained a double-quote (e.g. from a document or a title scraped viagraphify add <url>), let a hostile source inject a live event handler into a report opened locally. The id is now carried in an HTML-escapeddata-nidattribute dispatched through a single delegated listener. If you generate reports from untrusted corpora, upgrade.
All fixes
- Security: close a stored XSS and repair the (previously always-broken) neighbor "focus" links in the exported
graph.html(#1838, thanks @edgestack-ai). - Fix: detection honors nested
.gitignore/.graphifyignorefiles below the scan root, matching git — avendor/sub/.gitignoredeeper in the tree is now applied to its own subtree instead of being ignored (#1847, thanks @Mohak-Agrawal). Composes with.git/info/exclude(0.9.14): a nearer!re-include still wins. - Fix:
graphify updatenow keeps human-readablecommunity_namelabels instead of stripping them back to numeric ids on every incremental rebuild (#1808 / #1855, thanks @latreon).
Install
- Security (stored XSS): the exported
- v0.9.14Jul 13, 2026
v0.9.14
Full notes ↗graphify turns any folder of code, docs, papers, images, or videos into a queryable knowledge graph. 0.9.14 is a correctness-focused release: eleven fixes across detection, the git hooks, extraction caching, query seeding, and export — most from community reports, several with their own PRs.
Highlights
- Massive graph bloat from git worktrees is gone (#1810). Detection now honors
.git/info/exclude— wheregit worktree addrecords nested worktree paths — so graphify no longer walks into worktree copies of the repo. One reporter's 5-worktree repo had ballooned from ~9,400 nodes / 10 MB to ~210,000 nodes / 311 MB, regenerated on every commit. - No more username leaks in committed graphs (#1789). Visual Studio solution-folder nodes were keyed off the absolute scan path, embedding the local username into
graph.json. They're now relative. graphify export graphmlstopped crashing (#1831). Any dict/list-valued attribute (a nodemetadatadict, the graph-levelhyperedgeslist) failed the entire export and left a 0-byte file; non-scalars are now JSON-serialized and the write is atomic.- The CLI no longer reports success as failure through a pipe (#1807).
graphify query … | headused to exit 255 when the reader closed early, breaking CI wrappers and agent harnesses; an early-closing reader is now treated as success.
All fixes
- Fix: Visual Studio solution-folder node ids no longer embed the absolute scan path / username (#1789, thanks @fremat79).
- Fix: the CLI exits 0 (not 255) when a downstream reader closes the pipe early —
head,Select-Object -First N,sed q(#1807 / #1811, thanks @varuntej07). - Fix:
extract()writes its AST cache (and the stat-index) to CWD, never the analyzed source tree, with the cache location decoupled from the portable key/id anchor (#1774 / #1802, thanks @SimiSips).
- Massive graph bloat from git worktrees is gone (#1810). Detection now honors
- v0.9.13Jul 12, 2026
v0.9.13
Full notes ↗Maintenance release: a batch of correctness and privacy fixes across extraction, incremental update, and query. No breaking changes.
Highlights:
- Query log is now opt-in (off by default) — no more undocumented plaintext record of your queries in
~/.cache(#1797). - Incremental
graphify updateno longer silently evicts nodes for files that are merely newly-ignored but still on disk (#1795), andbuild_mergeno longer drops a re-extracted file passed inprune_sources(#1796). - Markdown files no longer split into duplicate document nodes across the quick-scan and semantic passes (#1799).
- New language coverage: Ruby
.rakefiles (#1784) and cross-file Bash script execution edges (#1756).
Fixes
-
Fix: the query log is now opt-in (off by default) (#1797, thanks @adam-pond-agent).
querylogwrote everyquery/path/explainquestion and corpus path (and full responses ifGRAPHIFY_QUERY_LOG_RESPONSES) to a default-on, unbounded, fail-silent plaintext file at~/.cache/graphify-queries.log— outside any repo's .gitignore/retention, and undocumented, which contradicts graphify's on-device / no-telemetry posture. Logging is now OFF unless you opt in withGRAPHIFY_QUERY_LOG_ENABLE=1(default path) orGRAPHIFY_QUERY_LOG=<path>;GRAPHIFY_QUERY_LOG_DISABLE=1still forces it off. All the query-log env vars are now documented in the README. -
Fix: a markdown file that went through semantic extraction is no longer duplicated into two disconnected nodes on later
graphify update(#1799, thanks @jerp86). The semantic pass mints<slug>_docwhile the markdown quick-scan mints the bare<slug>, so the file's edges split across two twins (a docs->code path query would dead-end on the bare half; centrality and communities split too).build_from_jsonnow merges the bare quick-scan node into the semantic_docnode when both share the samesource_fileand arefile_type: document, consolidating their edges/hyperedges onto one node. Gated so an unrelated code symbolfooandfoo_docnever merge.
- Query log is now opt-in (off by default) — no more undocumented plaintext record of your queries in
- v0.9.12Jul 10, 2026
v0.9.12
Full notes ↗-
Fix: live PostgreSQL introspection (
--postgres) now emits foreign-keyreferencesedges under a read-only role (#1746, thanks @rithyKabir). The FK query readinformation_schema.referential_constraints, which is privilege-filtered — a role with only SELECT sees zero FK rows while tables/views/routines still appear, so everyreferencesedge silently vanished. It now reads the world-readablepg_catalog.pg_constraint(keyed by oid, which also fixes same-named constraints on sibling tables cross-matching in the old name-based joins), preserving composite-FK column order viaUNNEST ... WITH ORDINALITY. -
Fix:
json_configno longer emitsimports/extendsedges to node IDs it never creates (#1764, thanks @oleksii-tumanov).package.jsondependencies andtsconfig.jsonextends/$reftargets produced edges whose endpoint node was absent, sobuild_from_jsonsilently dropped them (the "no matching node id" case is filtered out of real errors) — losing dependency/extends structure on two of the most common files in any JS/TS repo. The extractor now creates the referenced target as aconceptnode before adding the edge. -
Fix:
graphify updateno longer deletes semantic hyperedges on every run (#1755, thanks @oleksii-tumanov). The AST-only rebuild treated every rebuilt corpus file as grounds to evict hyperedges anchored to it, but the AST pass never re-emits hyperedges, so doc-sourced hyperedges (exactly what semantic extraction produces) were permanently lost on the firstupdateafter a full build — even a no-op run. Hyperedge eviction is now scoped to genuinely deleted (or symlink-outside) sources, mirroring node/edge handling; replacement-by-id and dangling-member cleanup are unchanged. -
Fix: Java member calls resolve against the receiver's declared type instead of a bare method-name match (#1696/#1697, thanks @oleksii-tumanov).
gw.charge()wheregw: PaymentGatewaynow binds toPaymentGateway.charge, not a same-namedAuditLog.chargein another file. Explicit-type receivers andthisare exact; current-class fields, method parameters, and explicitly-typed locals resolve via a method-scoped type table; a missing, ambiguous, inherited, or chained receiver is skipped rather than guessed (same god-node guard as the C#/Swift/Ruby resolvers). Fully-qualified and nested-type receivers are deferred (they need package/nesting-aware type identity). -
Fix: output/cache artifacts no longer land in the scanned corpus or CWD when
--out/--graphpoint elsewhere (#1747, thanks @bbqboogiedwonsen).extract <corpus> --out <dir>correctly wrote the graph to<dir>butdetect()'s word-count/stat-index cache still created a straygraphify-out/cache/inside the corpus (it uses the scan root); it now honors the--outdir via a threadedcache_root. Andcluster-only --graph <elsewhere>/graphify-out/graph.jsonwroteGRAPH_REPORT.md/labels/analysis/re-clustered graph to the CWD instead of beside the input; it now writes beside--graphwhen that graph lives in agraphify-out/dir, while still restoring into the CWD for an archivedbackup/graph.json(#934). -
Fix:
imports/referencesedges no longer bind across a language boundary (#1749, thanks @philberndt). The spec already forbids cross-languagecalls, but an unresolved Pythonimport timecould still resolve by bare stem onto asrc/time.tsfile node — welding a polyglot repo's halves together at a phantom edge (in the reporter's repo, 3 such edges were the only thing bridging 2409 Python nodes to 1403 TS nodes, inflatingtime.tsbetweenness ~90x and making it the #1 "god node"). The build-time cross-language guard now coversimports/imports_from/referencesin addition tocalls, dropping an edge only when both endpoints are known code languages of different interop families (so a config/manifest → code reference is untouched). -
Fix: files whose extractor bailed out for a missing optional dependency no longer vanish without a trace (#1745, thanks @rithyKabir).
.sqlfiles (and other extra-gated languages) have a dispatch entry, so the #1689 no-extractor warning can't fire, andextract_sqlreturns an error result whentree-sitter-sqlis absent, so the #1666 zero-node warning skips it too — the graph built "successfully" while an entire SQL corpus contributed nothing.extract()now surfaces these grouped by extension, naming the extra that restores the language (e.g.pip install "graphifyy[sql]").
-
- v0.9.11Jul 9, 2026
v0.9.11
-
Fix: file enumeration no longer silently drops a directory subtree.
detect()'sos.walkhad noonerrorhandler, so anos.scandirfailure (a permission error, or a directory created/deleted mid-walk by concurrent writes) was swallowed and that whole subtree vanished from the scan with no log, yielding a silently partialgraph.json. The walk now records every skipped directory (surfaced in the result'swalk_errors) and warns to stderr, while still enumerating the rest. Relatedly,to_json's anti-shrink guard (#479) now fails safe: a non-empty but unreadable existinggraph.jsonrefuses the overwrite (passforce=Trueto override) instead of silently clobbering a good graph; an empty file still proceeds. -
Fix: Pascal/Delphi extractors no longer emit duplicate
method/contains/inheritsedges. A class method declared in the interface section and defined in the implementation section each emitted an edge to the same node, so ~half of a Pascal graph's method edges were doubled (skewing degree/centrality and tripping the new cross-file resolver's god-node guard). Both extractors now dedup edges on (source, target, relation), mirroring the existing node dedup. -
Fix: Pascal/Delphi call resolution is scoped to the caller's class + inherits chain, and calls to methods inherited across file boundaries now resolve (#1739, thanks @richtext). Both extractors previously resolved every call via a single file-wide
{name: node_id}dict, so two unrelated classes with a same-named method (property accessors, generated COM/TLB wrappers) collapsed onto whichever was inserted last, producing wrong cross-classcallsedges. Resolution now walks own-class then ancestor chain then file-level free functions, emitting no edge when ambiguous (same god-node guard as the Ruby resolver). A new corpus-wide resolver (graphify/pascal_resolution.py) resolves calls from a descendant to a base-class method declared in a different file (the common generated-base/manual-descendant split). Also stops emitting a duplicate cross-file base-class stub carrying the wrongsource_file. -
Fix: query ranking no longer lets a lone generic term that exact-matches a short leaf label hijack seed selection in multi-term queries (#1602/#1724, thanks @fkhawajagh).
_score_nodesscales the per-term exact/prefix tiers by squared term coverage; single-term and full-coverage queries are unchanged. -
Fix: Kotlin enum entries are extracted as nodes with
case_ofedges to their enum (#1700, thanks @ivanzhilovich). Closes the Kotlin half of #1700 (the Java half shipped in 0.9.10 via #1719);enum class ChatType { NORMAL, GROUP, SYSTEM }now yields NORMAL/GROUP/SYSTEM nodes and "where is ChatType.X used" works for Kotlin. -
Fix: SKILL.md's POSIX interpreter probe no longer silently falls back to a graphify-less system python (#1735, thanks @mohammedMsgm). Step 1 ran
uv tool run graphifyy python -c ..., but thegraphifyypackage's executable isgraphify, so uv treatedpythonas a missinggraphifyycommand;2>/dev/nullhid uv's own--fromhint, leavingPYTHONon an interpreter without graphify. The probe now runsuv tool run --from graphifyy python -c .... The PowerShell path was already correct. -
Refactor: decomposed the two largest modules into focused, single-responsibility modules — verbatim moves only, every original import path preserved via re-exports, no behavior change (#1737, thanks @TPAteeq).
extract.py17,054 → 4,740 LOC (the tree-sitter engine, cross-file resolution, shared models, and 23 language extractors moved undergraphify/extractors/),__main__.py5,368 → 673 (install/uninstall + CLI dispatch split intographify/install.pyandgraphify/cli.py),export.py1,671 → 962 (HTML + graph-DB exporters undergraphify/exporters/). Full suite unchanged. -
Fix:
merge-graphsgives each input a distinct repo tag so same-stem nodes from different source graphs don't collapse (#1729). Two graphs under a same-named repo dir (src/graphify-outandfrontend/src/graphify-out, both →src) shared thesrc::prefix, so a backendsrc/app.jsand a frontendApp.jsx(both bareapp) merged into one node with edges from both — false cross-runtimepathresults. Colliding tags are now widened (frontend_src) with an index-suffix backstop, and the command prints a note when it disambiguates. -
Fix:
uninstallremoves the graphify hook/section from Claude's local-only files too (#1731, thanks @TPAteeq). It now cleans.claude/settings.local.jsonand bothCLAUDE.local.mdlocations in addition to the standard files, via bothgraphify uninstallandgraphify claude uninstall. -
Feat:
graphify extract --code-onlyindexes code (local AST, no API key) and skips the doc/paper/image semantic pass, so a mixed repo no longer hard-fails when no LLM backend is configured (#1734). Reports what it skipped; the no-key error now points users at the flag.
-
- v0.9.10Jul 8, 2026
v0.9.10
Full notes ↗graphify 0.9.10 — a correctness batch focused on phantom cross-file/cross-language edges.
Highlights: TS/JS builtin-typed receivers (
x: Date) no longer collapse onto same-named user symbols; no cross-languagecallsedges;build_mergeambiguous aliases no longer merge unrelated files; base-class stubs disambiguated per file; Java enum constants extracted; resumable per-chunk semantic cache.Install:
uv tool install "graphifyy==0.9.10"orpip install graphifyy==0.9.10Changelog
- Fix: TS/JS member calls on a builtin-typed receiver no longer collapse onto a same-named user symbol (#1726).
_resolve_typescript_member_callsmatched a receiver's type to a definition by casefolded label, sox: Date; x.getTime()bound the caller to a userclass DATE/const DATEin another file — inventing hundreds of phantomreferencesedges and a false god node. Builtin-global receiver types (Date,Promise,Map, ...) are now skipped, mirroring the cross-file call guard; genuine user types are unaffected. - Fix: never bind a cross-file
callsedge to a definition in a different language family (#1718, thanks @edinaldoof). Name-only matching resolved a TSX callback passed by name to a same-named Kotlin method (and a Python call to a Kotlin fun) — phantom edges the spec forbids. Candidates are now filtered by interop family (JVM, native C-family, JS/TS module graph, ...); unknown families stay permissive. - Fix: an ambiguous legacy-stem alias in
build_mergeno longer silently merges two unrelated files (#1713, thanks @mallyskies). The#1504old-stem alias (ping.h/ping.php→ bareping) resolved by hash-order, riding a dangling edge onto an arbitrary same-named file. Aliases are now committed only when exactly one file claims them; a salted.h/.cppfile node is recognized as its own claimant so a genuine collision stays ambiguous (and dropped) instead of picking a wrong winner. - Fix: inline base-class stubs are tagged with
origin_file(#1707, thanks @mallyskies). Five inheritance handlers built cross-file base-class stubs withoutorigin_file, so same-named bases across files collapsed onto one shared stub that could then merge with an unrelated real class (218 wronginheritsedges observed). They now route throughensure_named_node, which sets the tag. - Fix: Java enum constants are extracted as nodes with
case_ofedges to their enum (#1719, thanks @ivanzhl). Closes the Java half of #1700;affected ErrorCode/ "where is ErrorCode.X used" now works for Java.
- Fix: TS/JS member calls on a builtin-typed receiver no longer collapse onto a same-named user symbol (#1726).
- v0.9.9Jul 7, 2026
v0.9.9
Full notes ↗graphify 0.9.9 — reliability + honesty round on top of the 0.9.8 Windows-hooks release.
Highlights: explain resolves punctuated labels; code files with no AST extractor (R, .ejs, .ets) and unclassifiable files (Dockerfile/Makefile) are now surfaced instead of silently dropped; MATLAB .m no longer garbage-parses through the Objective-C grammar; GRAPH_REPORT.md no longer emits dangling Obsidian wikilinks by default.
Install:
uv tool install "graphifyy==0.9.9"orpip install graphifyy==0.9.9Changelog
- Fix:
graphify explainresolves an exactly-typed punctuated label symmetrically againstnorm_label(#1704). The search term tokenized on\w+("blockStream.ts" -> "blockstream ts", space where the '.' was) while a node's storednorm_labelkeeps punctuation ("blockstream.ts"). The verbatim case was already rescued by the tokenized-label tier, but that broke if a node'slabelandnorm_labeldiverged; a punctuation-preservingnorm_queryis now matched againstnorm_labelacross the exact/prefix/substring tiers (and fed to the trigram prefilter), so it is robust by construction. - Fix: code files with no AST extractor are surfaced instead of silently dropped (#1689, thanks for the precise root-cause).
.r/.R(also.ejs,.ets) are inCODE_EXTENSIONSso they are counted as code, but there is no extractor for them, so they produced zero nodes with no warning.extractnow prints a grouped warning ("N file(s) are classified as code but graphify has no AST extractor ...: .r (17)"). Adding a realtree-sitter-rextractor remains a follow-up. - Fix: the AST-extraction progress line keeps a consistent denominator to the end (#1693). Intermediate lines counted against
len(uncached_work)but the final line switched tototal_files(which includes cached hits and no-extractor files), so on a large corpus the count appeared to jump upward right after 99%. Both the parallel and sequential final lines now use theuncached_workdenominator. - Fix:
GRAPH_REPORT.mdno longer emits dangling[[_COMMUNITY_*]]Obsidian wikilinks by default (#1712). The_COMMUNITY_*.mdnotes those links target are only created by the opt-in--obsidianexport, and the report is written at build time before any export, so on a default run every link dangled (spawning phantom nodes in a vault's graph view, literal brackets elsewhere). The Community Hubs section now renders as plain text by default; the wikilink form is behind anobsidian=Trueopt-in. - Fix:
.mfiles are no longer force-parsed by the Objective-C grammar when they are MATLAB (#1702, thanks @catalystdream for the diagnosis)..mis shared by Objective-C and MATLAB, but the dispatch routed every.mtoextract_objc, which turned real MATLAB into garbage nodes/edges..mis now content-sniffed like.h: a genuine Objective-C.m(with@implementation/@interface/@import/#import) still routes toextract_objc; a MATLAB.mgets no extractor and is surfaced by the #1689 warning rather than mis-parsed..mmis unchanged (unambiguously Objective-C++). A realtree-sitter-matlabextractor remains a follow-up.
- Fix:
- v0.9.8Jul 6, 2026
v0.9.8
Full notes ↗graphify 0.9.8 — Windows hook fix + reliability round.
Highlights: the graph-nudge hooks now work on Windows (Claude Code, Codebuddy, Gemini CLI); plus fixes for CLAUDE.md section-write data loss, a tiktoken special-token crash, an Ollama hang, community-labeling robustness + token accounting, discovery-layer file drops, and the deepseek thinking default.
Install:
uv tool install "graphifyy==0.9.8"orpip install graphifyy==0.9.8Changelog
- Fix: the Claude Code / Codebuddy
PreToolUseand Gemini CLIBeforeToolgraph-nudge hooks now work on Windows (#522). The hooks were inline POSIX bash (case/esac,[ -f ], single-quotedecho), which Windows cmd.exe/PowerShell cannot parse — so on Windows the hook failed silently, no "rungraphify querybefore grepping/reading raw files" context was injected, and users had to invoke/graphifyby hand. The detection logic (grep-command match, source-file extension match, skip-if-under-output-dir, graph-exists check) moved into a shell-agnosticgraphify hook-guard <search|read>subcommand invoked via the absolute exe path (the same pattern the codex hook already uses), so the hook parses and runs identically on Windows, macOS, and Linux. Behavior on macOS/Linux is unchanged (byte-identical nudge payload); the graph path now also honorsGRAPHIFY_OUT. The GeminiBeforeToolhook got the same treatment (graphify hook-guard gemini), which also removes its dependency on a barepythonbeing on PATH. Codex stays a no-op there because Codex Desktop rejectsadditionalContext. - Fix:
--update-style section writes toCLAUDE.md/AGENTS.mdno longer corrupt or drop content (#1688, thanks @bdfinst)._replace_or_append_sectionlocated its managed block by substring (marker in content) andnext(... if marker in line), so a heading that appeared as a substring of another line (or duplicate headings) matched the wrong offset and the rewrite could truncate the file. It now matches the section heading exactly (line.strip() == marker), appends when absent, and prefers the last exact match when several exist, so unrelated content is preserved. - Fix: token estimation no longer crashes on files containing tiktoken special-token text like
<|endoftext|>(#1685, thanks @Kyzcreig)._TOKENIZER.encode(content)raisesValueErrorby default when the text contains a special token, which aborted packing on docs/corpora that merely mention these strings. Bothencodesites now passdisallowed_special=()so such text is tokenized as ordinary bytes. - Fix: the Ollama backend no longer multiplies a hang by the retry count (#1686, thanks @Kyzcreig). A stalled local model would wedge for
timeout * (max_retries + 1), which with the default 6 retries turned one long stall into a very long one. Ollama now defaults to zero client-side retries (a local model that stalls will not un-stall on retry); setGRAPHIFY_MAX_RETRIESto opt back in. Other backends are unchanged. Note: the underlying stall is non-deterministic and driven by the model server, so this bounds the wait rather than eliminating the hang. - Fix: a truncated or slightly malformed community-labeling reply no longer discards the whole batch (#1690, thanks @vdgbcrypto).
_parse_label_responsenow salvages the complete"id": "name"pairs from a reply that failed a strictjson.loads(e.g. a reply truncated mid-object), raising only when no pairs can be recovered. The per-batch token budget was also raised (256 + 48*n, was64 + 24*n) to give models that prepend a short preamble enough headroom to finish the JSON. The exact provider truncation in the report could not be reproduced without a live key; the parser and budget fixes address the mechanism.
- Fix: the Claude Code / Codebuddy
- v0.9.7Jul 6, 2026
graphify 0.9.7
Full notes ↗graphify 0.9.7 — 17 fixes and features since 0.9.6.
Ruby
include/extend/prepend <Module>now emitsmixes_inedges (#1668), so Rails concern composition is visible toaffected.affected <Class>reaches callers that bind to a class's method nodes (#1669), seeding the reverse walk from the root's members (one method/contains hop).
Extraction
- Extensionless shebang CLIs (
devctl,manage) are now extracted instead of silently dropped (#1683, @Stashub). - JS/TS rationale comments (
// NOTE:) and ADR/RFC citations becomerationale/doc_refnodes, matching Python (#1599, @niltonmourafilho-arch). - Java standard-library types (
String,List,Optional, ...) no longer emitted asreferencesnoise (#1603, @NydiaChung). - New
pascaloptional extra for AST-quality Delphi extraction (#1616, @vinicius-l-machado). - JS/TS calls with no local definition and no import no longer bind to a same-named export in an unrelated package (#1659, @leonaburime-ucla).
- Case-insensitive file-extension dispatch, so
App.PY/script.JSare no longer skipped (#1671, @raman118).
- v0.9.6Jul 4, 2026
graphify 0.9.6
Full notes ↗graphify 0.9.6 — 19 fixes and features since 0.9.5.
Ruby (major)
- Module / Struct.new / Class.new / Data.define container nodes (#1640). Plain
module Foo,Foo = Struct.new(...) do ... end,Foo = Class.new(StandardError), andResult = Data.define(...)now get real container nodes with their methods attached;Class.new(Super)emits an inherits edge. - Constant-receiver singleton-call resolution (#1634).
Service.call,Model.where,SomeJob.perform_asyncnow resolve cross-file, so Rails/Zeitwerk apps (no requires) get real cross-file edges instead of near-zero. Binds to the class's owned singleton method when present, else the class node for blast-radius; single-owning-class guard kept.
Correctness and stability
- No more cross-language phantom import edges (#1638). An unresolved bare npm import (
import x from "pkg/colors") no longer aliases onto an unrelated localcolors.pyvia the build alias index. - Semantic extraction hardened (#1631). A malformed LLM chunk (a stray non-dict entry) no longer crashes the merge and discards every successful chunk.
- Deterministic graph.json ordering (#1632). Parallel semantic backends now merge chunks in submission order, so node/edge ordering is stable run-to-run (the model's content variance is separate).
Contributor extractor fixes
- Apex interface multiple inheritance (
interface X extends A, B) (#1645, @Synvoya). - Kotlin interface delegation (
class Foo : Bar by baz) (#1644, @Synvoya).
- Module / Struct.new / Class.new / Data.define container nodes (#1640). Plain
- v0.9.5Jul 2, 2026
v0.9.5
Full notes ↗graphify 0.9.5
pip install -U graphifyy==0.9.5Correctness (two 0.9.4 regressions + a false-hub fix)
- Cross-file
indirect_callnow resolves via thegraphify extractCLI, not just theextract()API — a 0.9.4 regression where the callable-target guard went stale after id relativization dropped every cross-file indirect edge on the CLI path. graphify cluster-onlyno longer reuses stale community labels after the graph changed — a re-scoped/re-clustered graph kept old labels on a different community set. It now writes per-community membership signatures and hub-relabels changed communities with a warning.- Cross-file name resolution respects case in case-sensitive languages (#1581) —
from pathlib import Pathno longer resolves to a shellexport PATH=...node (one variable had become the #1 god-node with 266 false edges). Only PHP/SQL/Nim still fold.
Language extractor fixes (thanks @Synvoya, @jerryliurui)
Ruby & Groovy inheritance edges; Elixir multi-alias imports; Fortran function calls; Rust enum-variant + tuple-struct references; Julia qualified/relative/scoped imports; SystemVerilog qualified fields; Scala
varfields; PowerShell class base types; ObjC protocol-to-protocol adoption; PHP promoted constructor properties; C# auto-properties; C++ base-class template args; Swift enum associated values; and Swift singleton-into-local resolution (let x = Type.shared; x.method(), #1604).Incremental
--updatedata fixesHyperedges from unchanged files no longer dropped (#1574); deleted files no longer leave ghost nodes, with symlinked-root hardening (#1571).
- Cross-file
- v0.9.4Jul 1, 2026
v0.9.4
Full notes ↗graphify 0.9.4
Published to PyPI:
pip install -U graphifyy==0.9.4Blast-radius / call-graph
- indirect_call dispatch — a function referenced by name is now a first-class dependency
graphify affectedtraverses: call arguments (submit(fn),Thread(target=fn),map(fn, xs)), cross-file/imported callbacks, dispatch tables (ROUTES = {"x": fn},[a, b]), assignment/return aliases (cb = fn,return fn),getattr(obj, "name")reflective dispatch, and JS/TS (call args, object/array tables, Express-styleapp.get("/", h)). Kept as a distinct INFERRED relation so strictcallsqueries stay precise. (#1565, #1566, #1569, #1575)
Incremental
--updatedata fixes- Hyperedges from unchanged files are no longer dropped on every incremental update. (#1574)
- Deleted files no longer leave ghost nodes — prune matching relativizes absolute paths against the scan root (with symlinked-root hardening) even when a caller omits
root. (#1571)
Language coverage
- Ruby class inheritance emits
inheritsedges. (#1535) - Groovy
extends/implementsemitinherits/implementsedges. (#1534)
- indirect_call dispatch — a function referenced by name is now a first-class dependency
Full release history: github.com/safishamsi/graphify/releases ↗