Skip to content

Releases: Graphify-Labs/graphify

v0.9.48

Choose a tag to compare

@safishamsi safishamsi released this 20 Aug 16:39
  • Fix: a control character in a node label or id no longer aborts the whole export; the GraphML and Obsidian exporters scrub only the characters those formats forbid (tab, newline, and non-ASCII letters are preserved), and graph.json and its byte-identity round-trip are untouched (#2897, thanks @abhay-codes07).
  • Fix: graphify update / label / cluster-only no longer leave a large graph without a graph.html; the aggregated community view renders instead of raising, a failed render preserves the previous file, and a missing graph.html is regenerated on the no-change fast path without reclustering (#2853, thanks @oleksii-tumanov).
  • Feature: graphify extract --no-dedup skips the fuzzy near-duplicate merge on build and incremental merge, for operators who would rather keep distinct symbols that fuzzy-matched; exact-id uniqueness is unaffected and the flag arms the shrink guard so a surprising node drop is refused loudly (#2881, thanks @rajarshidattapy).
  • Fix: a response that parses but carries no symbols (a "hollow" reply) is now retried on the same chunk with backoff instead of being bisected like a truncated one, so an overwhelmed backend no longer wastes calls re-splitting a chunk it already answered emptily; on persistent hollow the files are marked partial and retried next run, and GRAPHIFY_MAX_RETRY_DEPTH=0 disables the hollow retry too so a chunk costs exactly one call (#2880, thanks @rajarshidattapy).
  • Fix: reasoning-first models that narrate before emitting JSON (or fence the answer) no longer have their chunk dropped; the parser recovers the JSON fragment from mixed text, preferring an object that carries the extraction keys so narration braces or a schema restatement cannot shadow the real answer, and gating on the sanitized content so a bare-string id sketch cannot either (#2882, thanks @rajarshidattapy).
  • Fix: a data-shaped JSON that the extractor deliberately declines is no longer counted as a failed extraction, so it is recorded in the incremental manifest and stops being re-processed on every run; genuine failures are still reported (#2879, thanks @rajarshidattapy).
  • Fix: C++ nested class/struct types and their members are now kept in the graph instead of being silently dropped, and C++/CLI sources (ref class, gcnew, ^/% handles) are normalized to plain C++ before parsing so tree-sitter no longer fabricates phantom symbols from the ERROR nodes it would otherwise produce (#2876, thanks @rajarshidattapy).
  • Fix: Obsidian-style [[wikilinks]] now resolve vault-wide by basename when sibling resolution misses, so cross-folder links are no longer silently lost; resolution stays deterministic on ambiguous basenames (shallowest then lexicographic path) and sibling matches keep priority (#2875, thanks @BaeHyunJae).

v0.9.47

Choose a tag to compare

@safishamsi safishamsi released this 19 Aug 13:37
  • Fix: extraction now bisects a file chunk on timeout instead of failing the whole chunk, so one slow file no longer drops its chunk-mates from the graph; recognized timeouts (subprocess, SDK, and botocore read/connect) route through the same bounded split-and-merge path as context-window-exceeded errors, and a single unsplittable file that times out is left unstamped and retried next run (#2866, thanks @hopstreax).
  • Fix: the cache no longer collapses symlinks when keying files, so a symlink alias and its target get distinct identities and one no longer displaces the other from the graph on a warm cache; the detect-layer containment guard that resolves paths to block symlink escape is unchanged (#2832, thanks @oleksii-tumanov).
  • Fix: graphify extract --out <dir> no longer writes converted Office/Google-Workspace sidecars back into the scanned source tree; sidecar writes go through the cache root while the content hash stays anchored to the scan root, so a read-only or pinned checkout stays clean (#2787, thanks @hopstreax).
  • Fix: the git-hook out-of-project read guard no longer misclassifies a rooted-but-driveless path (\foo\bar) as cwd-relative on Windows, where such a path resolves against the current drive root and is actually outside the project; POSIX behaviour is unchanged (#2795, thanks @abhay-codes07).
  • Fix: the partial-parse warning no longer ends with a hardcoded citation to the closed Kotlin-specific issue #2551 (which misdirected reporters of unrelated failures); it now reports the surviving symbol count so a total-loss file reads differently from a partially-recovered one (#2788, thanks @abhay-codes07).
  • Fix: Obsidian export now keeps non-ASCII letters (Hangul, CJK, Cyrillic, accented Latin) in community tags instead of collapsing every non-Latin community to the same underscore tag, and the graph-view colour groups query the identical tag so colours match again (#2862, thanks @josh-leung-KR).
  • Feature: JavaScript/TypeScript factory functions that assign callable members to a local object literal (const api = {}; api.foo = fn) now keep those members in the graph — the API object is modeled beneath its factory and the assigned functions attach as methods, including arrow-function assignments and their intra-factory call edges. The owner is minted only for identifiers proven to be object-literal bindings in the enclosing function, so arbitrary receivers do not reintroduce the phantom-owner flood, and the factory's contains edge is emitted once no matter how many methods hang off the object (#2745, thanks @rajanpanth).
  • Fix: graph.json field order is now stable across a read-rebuild round-trip, so re-running graphify update on an unchanged graph produces a byte-identical file instead of reshuffling node and link keys (#2582, thanks @C0KERNEL).
  • Fix: graphify query now names the graph it opened and its node count at the head of the answer (relative to the CWD when the graph is underneath it, absolute otherwise), so a query run from a parent project no longer silently answers from the wrong corpus with no indication which graph was used (#2789, thanks @abhay-codes07).
  • Fix: the claude backend no longer crashes with AttributeError: 'ThinkingBlock' when an extended-thinking response leads with a thinking block; the first text block is read instead (#2697, thanks @mdshzb04).
  • Fix: graphify update / save_manifest no longer rewrites manifest.json timestamps on a no-op run, so graphify-out/ stops showing as dirty (and stops producing a trailing graph commit) when nothing changed; a genuine change still updates and persists (#2838, thanks @hopstreax).
  • Fix: a C# 12 primary constructor's parameters are now walked, so class Svc(IRepo repo) emits the references edge to IRepo and calls through repo resolve — previously the class silently dropped its dependency; built-in and type-parameter types are not fabricated (#2829, thanks @brobl2008).
  • Fix: AST-derived INFERRED edges now carry a rubric confidence_score keyed to the relation (uses 0.95, indirect_call/unresolved cross-file calls 0.85) instead of landing at the rubric-forbidden 0.5 or a flat 0.8; the INFERRED default moves 0.5→0.55 so every score-less INFERRED edge is on the discrete rubric set (#2813, thanks @abhay-codes07).
  • Fix: a legacy graph.json that stored a numeric edge confidence (from a pre-enum version) no longer warns once per edge on every incremental reload; the numeric value is normalized to the INFERRED tag with the original float preserved in confidence_score (thanks @Trantor-develops).

v0.9.46

Choose a tag to compare

@safishamsi safishamsi released this 17 Aug 18:42

0.9.46

  • Fix: node-id normalization is now caseless-stable for combining-mark sequences — casefold and NFKC don't commute, so a single pass left normalize_id(s) != normalize_id(s.casefold()) for inputs like Greek ypogegrammeni followed by a combining accent; normalization now iterates casefold+NFKC to a fixpoint. Letter/digit-bearing ids are unchanged, so existing graphs are not re-keyed.
  • Fix: Java annotations now emit references edges to their type — including class-literal arguments (@Repeatable(Foo.class), @Uses({A.class, B.class})) and annotation-member return types — so a container annotation is no longer a disconnected island; string/enum arguments are not mistaken for type references (#2426, thanks @oleksii-tumanov).
  • Fix: graphify query treats _ as a token separator (like -), so an underscore-spelled query (user_service) matches a hyphenated label (user-service); coverage-scaling keeps the broader tokenization from surfacing unrelated single-token noise (#2473, thanks @nadiadatepe-eng).
  • Fix: the post-checkout hook skips its rebuild when HEAD is unchanged (e.g. git checkout -b with no start point), so creating a branch no longer triggers a full graph rebuild (#2421, thanks @nothariharan).
  • Feature: Markdown nodes now carry a node_kind (page vs heading) attribute so a docs corpus can be filtered by kind, and leading YAML frontmatter is parsed onto the page node as bounded, sanitized attributes; a # comment inside frontmatter is no longer mis-extracted as a heading (thanks @evanthomasgelders). Node ids are unchanged, so existing markdown graphs are not re-keyed.
  • Feature: Common Lisp .lisp/.cl/.lsp/.asd extraction via tree-sitter-commonlisp (optional [commonlisp] extra) — packages, classes, functions, methods, generics, macros, variable definers, and same-file calls; opened/:used packages resolve cross-file (thanks @fade).
  • Fix: a graphify query whose node set fits the budget but whose edges push the total over now prints an honest "complete answer over budget" notice with the real size, instead of silently returning a payload several times the requested budget (and no longer advises raising the budget, which was the exact trigger); edges are still never dropped from a complete answer (#2784, thanks @AromalBiju1).
  • Fix: a .gitignore/.graphifyignore saved in a non-UTF-8 encoding no longer silently drops its rules (which let an explicitly-excluded directory get scanned anyway); the file is decoded UTF-8-first, then by a UTF-16 BOM, then the host codepage/latin-1, so the rule survives intact with a warning instead of being truncated (#2798, thanks @abhay-codes07).
  • Fix: when node dedup merges two nodes, any hyperedge that listed the merged-away node as a member now rewires that member to the survivor instead of silently dropping it, so a grouping no longer loses participants on dedup (#2805, thanks @abhay-codes07).
  • Fix: pruning a source file now also sweeps the external-import placeholder nodes it strands at degree 0, instead of leaving them to accumulate in the node count, GRAPH_REPORT.md, and exports; genuinely-isolated real nodes (which carry a source_file) are never touched (#2807, thanks @abhay-codes07).
  • Fix: when two edges connect the same node pair with different relations, the graph builder now keeps the more specific relation (calls, imports, inherits, ...) instead of letting a generic references/uses/mentions overwrite it; previously a real calls could be downgraded to references and then dropped from the call graph (#2803, thanks @abhay-codes07).

v0.9.45

Choose a tag to compare

@safishamsi safishamsi released this 16 Aug 20:13

0.9.45

  • Fix: graphify install <platform> now advances the .graphify_version stamp only for the platform it actually (re)writes, instead of stamping every installed platform as current; a platform whose skill content was left untouched keeps its old stamp so its staleness warning stays truthful (#2694, thanks @ousamabenyounes). This completes #2694 (the CLAUDE_CONFIG_DIR half shipped in 0.9.44).
  • Fix: an incremental rebuild no longer collapses the whole graph when the .graphify_root marker records a subfolder while stored source_file paths are relative to the repo root; the marker is validated against the stored paths before it is trusted as their anchor, so a mismatched marker can't make every unchanged source look deleted (#2603, thanks @catpotd). A genuinely deleted source is still evicted, and incremental ids stay identical to a cold build.
  • Fix: a Go file that declares both an exported and an unexported symbol differing only by case (e.g. Run and run, which are distinct in Go's case-sensitive visibility rules) no longer collapses them onto one node id and drops one; the exported symbol keeps its stable id and the unexported one is disambiguated, so an intra-file call to the unexported symbol resolves locally instead of phantoming to another package (#2779, thanks @catpotd). Only the Go extractor's id assignment is affected; the shared id normalization is unchanged, so no other language's ids move.
  • Fix: loading a graph.json that contains a hyperedge with no id field (the semantic extractor emits them and they persist verbatim) no longer crashes the incremental re-extract with KeyError: 'id'; id-less hyperedges are tolerated and retained (#2775, thanks @ousamabenyounes).

v0.9.44

Choose a tag to compare

@safishamsi safishamsi released this 15 Aug 21:16

0.9.44

  • Feature: graphify hook install reads a committed .graphifyrc (viz_node_limit=<int>) and bakes the visualization node limit into the generated git hooks, so a project-wide limit is shared via version control and survives hook regeneration; hook status reports it (#2760, thanks @hopstreax). The baked value uses a ${GRAPHIFY_VIZ_NODE_LIMIT:-<n>} default so an explicit per-run env var still wins, and hook status degrades gracefully on a malformed .graphifyrc.
  • Fix: graphify install (Claude always-on) now writes the CLAUDE.md registration into $CLAUDE_CONFIG_DIR when that env var relocates the Claude profile, instead of always mutating the default ~/.claude/CLAUDE.md (part of #2694, thanks @AromalBiju1).
  • Fix: a JS/TS inline or nested function expression — including a generator function expression (function*(k){…}) — no longer fabricates an INFERRED indirect_call when one of its parameters/locals shares a name with an unrelated callable; the expression's own bindings now shadow the name (#2752, thanks @imagineers-tyler), completing the shadow family alongside catch/arrow/loop/external-import (#2757).
  • Fix: a git-tracked file that also matches a .gitignore pattern (a committed file later added to .gitignore, or a force-added one) is no longer dropped from the corpus, matching git's own behavior of never un-tracking such a file; .graphifyignore/--exclude stay authoritative and a non-git corpus is unaffected (#2759, thanks @NithishKumar04). The git ls-files probe is skipped entirely when no .gitignore is in play, so ordinary corpora pay nothing for it.
  • Fix: doctest/Catch2 string-named test cases (TEST_CASE("..."), SCENARIO, TEST_CASE_TEMPLATE), which tree-sitter-cpp drops as ERROR nodes, are recovered as callable nodes contained by the file (#2594, thanks @ousamabenyounes); a punctuation-only test name gets a distinct line-positional id instead of collapsing onto the file-stem id.
  • Fix: graphify affected resolves an absolute-path seed against the repo root derived from the graph's own location instead of the current working directory, so a blast-radius query with an absolute seed run from anywhere (an editor, a script) no longer silently returns nothing; a seed outside the root still misses cleanly (#2706, thanks @ousamabenyounes).
  • Fix: a lazy CommonJS require(...) inside a function body (the idiom for breaking circular dependencies) now emits the same imports_from/imports dependency edges as a top-level require, attributed to the enclosing function, instead of being silently dropped; a dynamic require(variable) is still skipped (#2700, thanks @rajanpanth).
  • Fix: a JS/TS identifier bound by an import whose target resolves outside the scanned corpus (e.g. a lucide-react icon) is now shadowed, so using it as a value no longer fabricates an INFERRED indirect_call onto an unrelated same-named callable elsewhere in the corpus; a relative/in-corpus import still resolves to its real target (#2757, thanks @phudayyy).
  • Fix: an OCaml qualified call M.f to an external module (one not defined in the same file, e.g. Hardcaml's Reg_spec.create) no longer binds to a same-named local let f — which produced a false calls edge and, when the caller was that local f, a f -> f self-loop. External qualified calls are kept as a distinct target labelled by the full path; unqualified calls and calls into a locally-defined module still resolve locally, and cross-file Geo.area still collapses onto another file's area.

v0.9.43

Choose a tag to compare

@safishamsi safishamsi released this 14 Aug 19:17

0.9.43

  • OCaml support.ml/.mli extraction via tree-sitter-ocaml (optional [ocaml] extra). Extracts modules, top-level and module-level values/functions, types and their variant constructors, open imports, and function calls; qualified calls (Geo.area) resolve to the value, and cross-file open/call targets collapse onto the unique real definition via the corpus stub rewire.
  • Fix: a cross-file INFERRED uses edge now binds to the symbol whose body actually references the imported name (a module-level function is a valid source; a co-located class that never touches the import gets no edge), instead of fanning out from the import line to every class in the file (#2652, thanks @ousamabenyounes).
  • Fix: a named function declaration nested inside another function — including inside an arrow-defined component (const Panel = () => { function handleClick(){} }) or an arrow callback (useEffect(() => { function h(){} })) — is now noded, contained by its enclosing scope, and its calls resolve instead of dangling (#2653, thanks @himanshupatro-334).
  • Fix: the Bash extractor resolves two more source path forms — source "$(dirname "$VAR")/lib/x.sh" and a .. suffix on a tracked-variable base — while a .. cannot walk past the base's parent to an arbitrary host path, so a hostile corpus can't make the extractor stat or record an out-of-tree file (#2596, thanks @hudsonwa).
  • Fix: a wiki article link now targets the article's filename verbatim instead of a percent-encoded twin, so a label with ( ) & # or non-ASCII no longer produces a link that names no file on disk (#2597, thanks @abhay-codes07).
  • Fix: export filenames are budgeted against the full destination path rather than only NAME_MAX, so a long output directory on Windows no longer pushes an Obsidian/wiki note path past MAX_PATH and aborts the export mid-write (#2655, thanks @abhay-codes07).

v0.9.42

Choose a tag to compare

@safishamsi safishamsi released this 13 Aug 14:15

A large correctness, determinism, and portability release with fixes from many community contributors.

Extraction / resolution

  • JS/TS for...of / for...in loop bindings are shadowed, so they no longer fabricate indirect_call edges (#2685, @ousamabenyounes) — completes the loop/closure/catch shadow family (#2568/#2569/#2517).
  • Python relative subpackage imports (from ..pkg.sub import x) resolve to the package __init__ (#2688, @ousamabenyounes).
  • A non-regular file (FIFO/device) in the tree no longer hangs extraction; non-regular files are skipped (#2463, @itskaism).
  • A .sql file that fails to parse with tree-sitter-sql installed-but-broken now reports the real error instead of "not installed" (#2602, @ousamabenyounes).

Determinism / data integrity

  • graphify update incremental runs re-queue a file rewritten to the same length within one mtime tick (#2466, @itskaism), complementing the 0.9.40 file-hash guard (#2612).
  • Graph provenance (built_at_commit) is stamped from the analysed repo, not the shell cwd (#2699, @C0KERNEL).
  • Corrupt semantic-cache entries are surfaced and re-extracted instead of silently missed (#2683, @ousamabenyounes).
  • graph_has_legacy_ids no longer false-positives on a global MCP node id (#2408, @aryanbonigala).

Paths / Windows portability

CLI / export

  • affected resolves a ./-relative seed instead of silently returning nothing (#2707, @phudayyy).
  • Hyperedge regions in graph.html are traced in convex-hull order, so the shaded polygon no longer self-intersects (#2449, @ysys143).

Test / docs

v0.9.41

Choose a tag to compare

@safishamsi safishamsi released this 12 Aug 13:28

Correctness, determinism, and data-integrity release — a large batch of community-contributed fixes.

  • Fix: graphify update refuses to overwrite the graph with a shrunken one when the shrink was caused by an extractor failure this run, instead of silently replacing good data (#2663, thanks @ousamabenyounes); a genuine deletion still shrinks the graph.
  • Fix: a JS/TS catch binding passed as a call argument no longer fabricates an indirect_call edge to an unrelated same-named callable (thanks @imagineers-tyler); completes the 0.9.38/0.9.40 arrow-parameter fixes (#2568).
  • Fix: Cargo.toml is recognized as a package manifest (#2434, thanks @ousamabenyounes), minting a canonical package node plus depends_on edges.
  • Fix: an explicitly-passed scan root is no longer excluded by an unanchored pattern in a parent-directory .gitignore (#2468, thanks @hopstreax).
  • Fix: the API extraction prompt now instructs backends to capture the per-node rationale attribute, matching the skill path (#2482, thanks @hopstreax). Invalidates cached semantic chunks, which re-extract on the next run.
  • Fix: source_file is canonicalized to POSIX separators, so relative inputs on Windows no longer produce non-portable node ids (#2627, thanks @rajarshidattapy).
  • Fix: a warm cache hit no longer re-anchors a CWD-relative source_file to a ghost path when the working directory differs from the graph root (#2632, thanks @rajarshidattapy).
  • Fix: the wiki/obsidian audit trail counts each incident edge once instead of double-counting intra-community edges (#2635, thanks @rajarshidattapy).
  • Fix: C# members inside a #if ... #endif preprocessor block are extracted and attached to their class (#2634, thanks @rohit-jsfreaky).
  • Fix: query no longer prints the truncation banner when no nodes were actually cut (#2601, thanks @ousamabenyounes); a genuine node truncation still warns.
  • Fix: a PHP use import written with a leading-backslash / fully-qualified prefix now resolves to its target definition (#2661, thanks @ousamabenyounes).
  • Fix: an unresolved local JS/TS import (to a file absent from the scan) emits a stable, portable ref target id instead of a per-checkout absolute-path slug (#2457, thanks @rohit-jsfreaky).
  • Fix: graphify benchmark no longer crashes on a node whose label is None (#2674, thanks @Arthuro0103).

v0.9.40

Choose a tag to compare

@safishamsi safishamsi released this 11 Aug 14:30

Correctness and determinism release: fixes a TypeScript false-warning regression, several node-id / path / cache determinism bugs, a Python crash, a Go phantom-reference, and more — with fixes from many community contributors.

  • Fix: the 0.9.37 partial-parse warning no longer fires on valid TypeScript/TSX (#2610, #2599, thanks @Sid-AutoWisdom and @atlasplatformu-ai). The warning now fires only when recovery plausibly cost symbols, so valid TS with a & in a JSX string attribute or a semicolon-less in_* interface member is silent, while the genuine Kotlin one-line-body and Luau cases still warn.
  • Fix: file_hash()'s stat fastpath no longer serves a stale digest when a file is rewritten to the same size within one mtime tick (#2612, thanks @rajarshidattapy).
  • Fix: stored-path absoluteness is detected cross-platform, so a POSIX-absolute source_file from a Linux/CI-built graph no longer leaks into node ids on Windows (#2618, thanks @rajarshidattapy).
  • Fix: normalize_id() is idempotent for Turkish İ and similar codepoints; no ASCII identifier ids change (#2614, thanks @rajarshidattapy).
  • Fix: graph.json collection order is deterministic across runs (#2582, thanks @hjotha).
  • Fix: explain / _find_node resolve node ids containing punctuation or non-ASCII characters (#2467, thanks @sean-soomgo).
  • Fix: .graphifyignore patterns match regardless of Unicode NFC/NFD normalization, so an accented ignore rule works on macOS (#2544, thanks @bruno-growthsales).
  • Fix: Obsidian vault metadata directories (.obsidian, .smart-env) are skipped during detection (#2493, thanks @rohit-jsfreaky).
  • Fix: a single unparenthesised arrow parameter (x => f(x)) is shadowed, so it no longer fabricates an indirect_call edge to an unrelated same-named callable (thanks @imagineers-tyler); follows the 0.9.38 sibling-closure fix (#2568).
  • Fix: Python extraction no longer crashes resolving an over-deep relative import above the package root (#2605, thanks @SinghAman21).
  • Fix: a Go qualified type (pkg.Type) resolves by import path instead of binding by bare name to an unrelated same-named symbol (#2608, thanks @gnukeno).
  • Fix: graph.html's document title no longer embeds the generator's absolute host path (#2598, thanks @michaelxer).

v0.9.39

Choose a tag to compare

@safishamsi safishamsi released this 10 Aug 19:32

Correctness release: fixes an affected gap for in-function dynamic imports, stops a Python name-only member-call fabrication, tightens fuzzy dedup, closes a watcher deletion residual, and fixes three Objective-C resolution bugs.

  • Fix: affected now traverses a dynamic import('…') made inside a function or at module scope (#2584, thanks @phudayyy). The 0.9.38 dedupe keyed only on the target, so an in-function dynamic import (whose symbol-level edge is anchored on the enclosing function) suppressed the file-level edge affected follows; the dedupe now keys on the importing file, emitting one file-level dynamic_import edge per file/target while keeping the call-site edge.
  • Fix: a Python member call on an untyped receiver (x.get(...)) no longer binds by name alone to a same-named module-level function, fabricating a false high-confidence calls edge and a god node (#2417, #2586, thanks @EZZEASY). Such a call is now resolved only with receiver-type, import, or self/cls/super evidence, matching the TypeScript fix from 0.9.37; super().method() still resolves.
  • Fix: fuzzy dedup no longer over-merges two distinct entities in the same file whose long labels differ by a content word (#2576, thanks @wilyan09007). A one-token difference is judged on the differing tokens rather than the prefix-weighted whole-label similarity, so asset contribution flow and asset consumption flow stay separate while genuine typo and whitespace/case variants still collapse.
  • Fix: graphify watch now rebuilds on a documentation-only deletion batch instead of only flagging it (#2580, thanks @angmeng), so a deleted doc's nodes are evicted immediately rather than waiting for the next code-file event. (The general deleted-file leak was already fixed in 0.9.10; this closes the live-watcher residual.)
  • Fix: Objective-C member-call resolution (#2589, #2590, #2591, thanks @xiongjianxu). A @protocol declaration is no longer treated as a receiver type (it collided with a same-named class); a category or class-extension interface (@interface Foo (Bar)) now folds into the base class instead of minting a duplicate node; and a message send to a @property or ivar receiver ([self.svc run], [_svc run]) now resolves through the property/ivar's declared type.