Releases: Graphify-Labs/graphify
Release list
0.8.45
Path-portability fixes so graphify-out/ artifacts are portable across clones and machines, plus a native-backend extraction fix.
Path portability
- Portable
manifest.json— the skill runbooks now relativize manifest keys to the scan root, sographify --updatematches cached files after a clone or move instead of re-extracting the whole corpus. (#1417) - Hyperedge
source_filerelativized —build_from_jsonnow relativizesgraph.hyperedges[]like nodes and edges, so a semantic subagent's absolute path no longer leaks intograph.json. (#1418) - Report header shows the scan root — the split-skill runbook passed
'.'toreport.generate; it now passes the real scan path, soGRAPH_REPORT.mdno longer titles itself.. (#1419) GRAPHIFY_OUThonoured end-to-end — the custom output-dir override was only respected by some readers, soGRAPHIFY_OUT=custom-out graphify extractstill wrote tographify-out/. It's now resolved through a singlegraphify.pathsmodule acrossextract,cluster-only,query/affected/benchmark,save-result,uninstall --purge,cache-check, themanifest/transcripts/memory/convertedpaths, thebuild_merge/serve/prsdefaults, and thedetectscan-exclude. Default behaviour is unchanged. (#1423)
Native-backend extraction
- Hyperedges are now requested on the native path —
graphify extract --backend <gemini|claude|claude-cli|openai|kimi|…>never produced hyperedges (the prompt's schema showed an empty array and never described them), while the agent/skill path did. The native prompt now matches the skill spec, so both paths yield the same hyperedge behaviour. (#1430)
0.8.44
Skill-runbook correctness and a crash fix.
Generated skill (#1392)
Crash & data-loss tier:
- Semantic chunk files are derived from cwd (where Part C globs
graphify-out/), not the scanned dir, so a non-cwd scan no longer produces "no nodes". - Code-only corpora write an empty
.graphify_semantic.jsonbefore Part C, fixing aFileNotFoundError. --cluster-onlyrelies on the self-containedgraphify cluster-onlyCLI instead of re-running steps that read already-deleted intermediate files.- The zero-node guard runs before any write, and
GRAPH_REPORT.md/analysis are written only whento_jsonactually persisted the graph (respects the #479 shrink-guard).
Remaining correctness tier:
--directedis propagated asdirected=intobuild_from_json(build + rebuild) andbuild_merge(--updatemerge + diff), so a--directed/--directed --updaterun no longer collapses reciprocal edges into an undirected graph.- Semantic extraction scopes to
document/paper/imageonly (code is covered by the AST pass), so subagents stop re-reading every source file. .graphify_cached.jsonis deleted on a cache miss, so a stale cache from a prior run is never merged.--updatetranscribes changed video files into documents before the semantic pipeline.- Transcription writes via
write_text, honoursGRAPHIFY_WHISPER_MODEL/GRAPHIFY_WHISPER_PROMPT, prints status to stderr. add-watch/exportsuse the resolved interpreter explicitly; MCP Desktop config documents the absolute interpreter path.
Library
- The semantic extract entry points no longer crash on
FileSliceunits. The 0.8.43 #1386 fix coerced every item withPath(f), which raisedTypeErroron theFileSliceobjects from the oversized-text slicing path (#1369). Items are coerced only when not already aPath/FileSlice(#1397, #1399).
Full changelog: https://github.com/safishamsi/graphify/blob/v8/CHANGELOG.md
v0.8.43
pip install -U graphifyy / uv tool upgrade graphifyy.
Two new capabilities plus a security update and four fixes.
Features
- Package-manifest dependency graph.
apm.yml,pyproject.toml,go.mod, andpom.xmlare now parsed deterministically into one canonical package node per package (keyed by name) plusdepends_onedges, routed to the AST path so the LLM never sees them. Previouslyapm.ymlwas an LLM-handled document, so a package got a different file-anchored id from its own manifest than from each dependent's reference and split into duplicate nodes — now a package referenced from many manifests is a single hub. (#1377) - Markdown links become edges. Inline
[text](./other.md)links, reference-style links, and[[wikilinks]]in markdown are now resolved (relative to the source file; external URLs / anchors / images skipped) and emitted asreferencesedges between docs, so hub docs likeindex.md/table-of-contents.mdactually become hubs. (#1376)
Security
- Bumped vulnerable dependencies to patched versions:
pypdf6.11.0→6.13.3 (CVE-2026-48155/48156),yt-dlp2026.3.17→2026.6.9,pyjwt2.12.1→2.13.0,cryptography48.0.0→49.0.0,python-multipart0.0.28→0.0.32, with lower-bound floors for the direct deps so installs get the patched versions. (#1375 — thanks @hypnwtykvmpr)
Fixes
- The semantic extract entry points (
extract_corpus_parallel,extract_files_direct) crashed onstrpaths instead ofpathlib.Path; both now coerce at entry. (#1386) - Community labeling now recovers from a malformed-JSON batch by splitting and retrying each half (mirroring the extract path) instead of silently dropping ~100 names per failed batch on large graphs. (#1280, #1278 — thanks @CJdev232)
graphify hook installno longer creates a backslash-named junk directory and reports false success whencore.hooksPathis a Windows-style path under WSL — such paths are now rejected with a clear error. (#1385)
Internal
- Node-ID normalization unified into a single
graphify.idsmodule; the four hand-synced copies (extract,build,mcp_ingest,symbol_resolution) — root of the recurring ghost-node bug class — now share one guarded implementation. (#1378 — thanks @danielnguyenfinhub)
Full changelog: https://github.com/safishamsi/graphify/blob/v8/CHANGELOG.md
v0.8.42
Bug-fix release. pip install -U graphifyy / uv tool upgrade graphifyy.
Ships a live regression fix and a security fix from 0.8.41, plus two correctness/feature fixes.
Fixes
-
🔴
/graphify --updateno longer deletes a changed file's freshly re-extracted nodes. The 0.8.41root=fix (#1361) madebuild_merge's prune actually match relativesource_filevalues — which then matched the just-re-extracted nodes of changed files (still inprune_sources) and removed them, so--updateon a changed file could wipe its nodes. The update runbook now prunes only genuinely deleted files; changed files are reconciled bybuild_merge's replace-on-re-extract (#1344). The full build also passesroot=tobuild_from_json, and the extraction-specsource_fileis pinned to the verbatim path, so full build and incremental updates never drift. (#1366 — thanks @RelywOo) -
🔒 Security:
.graphifyignoreand.gitignoreare now merged per directory instead of.graphifyignoresilently replacing the dir's.gitignore. Previously, adding a.graphifyignoredisabled that directory's.gitignoreentirely, so a file excluded only by.gitignore— including neutrally-named secrets likeprod-dump.sqlorcustomer-data.json— got indexed into the graph (whose artifacts embed file contents and are routinely committed)..gitignoreis read first and.graphifyignorelast, so it still wins on conflict (including!negations); adding one can only ever exclude more. (#1363) -
Java
recorddeclarations are now first-class type nodes, andnew Foo(...)constructor calls produce acallsedge. Previously a record appeared only as its file node (degree 0). (#1373) -
Large text documents are no longer silently truncated during semantic extraction.
_read_filescapped every file at 20,000 chars and dropped the rest; oversized Markdown/text/rST docs are now sliced at heading/paragraph boundaries so the whole file is extracted (slices share the parentsource_file, so the graph isn't fragmented). Code files and PDFs are never sliced. (#1369)
Full changelog: https://github.com/safishamsi/graphify/blob/v8/CHANGELOG.md
v0.8.41
Bug-fix release. pip install -U graphifyy / uv tool install graphifyy@0.8.41.
Fixes
-
Swift cross-file class relationships now resolve through member calls and constructors. A per-file type table (from property/parameter declarations + constructor inference) types the receiver of
recv.method(),Type.staticMethod(),Singleton.shared.method(), andself.prop.method(), and property/field initializers (let vm = VM()) are walked for constructor calls. Edges emit only when the receiver's type resolves to exactly one definition (preserving the god-node guards) and are taggedINFERRED. (#1356) -
/graphify <path> --updateprunes stale nodes correctly. The update runbook calledbuild_mergewithoutroot=, so absolute prune paths never matched the graph's relativesource_filevalues — nothing was pruned and changed/deleted files left ghost nodes that compounded every run. (#1361) -
export obsidianno longer writes an empty 32-bytegraph.canvason a populated graph.to_canvasnow falls back to one synthetic community covering every node when no community data is present (e.g.--no-clusterbuilds). (#1324) -
Edges missing
source_file(occasionally emitted by the semantic/LLM extractor) are now backfilled from their endpoint nodes inbuild_from_jsonand the--no-clusterpath, so they no longer reachgraph.jsonwithout a file reference. (#1279) -
Fuzzy dedup no longer over-merges distinct nodes in three cases: numbered/versioned siblings (
ADR 0011vsADR 0013), cross-filerationale/documentboilerplate (file-anchored like code), and cross-file labels sharing a long prefix but diverging in a distinguishing token (testing-library jest-nativevsreact-native, now scored on plain Jaro). Same-file near-duplicates still merge. (#1284 — thanks @van4oza, #1243) -
OpenAI-compatible backends (
ollama,openai,deepseek,kimi) now honour their configured16384output-token cap instead of silently falling back to8192, which had been truncating deep-mode JSON mid-string.GRAPHIFY_MAX_OUTPUT_TOKENSstill overrides. (#1365)
Full changelog: https://github.com/safishamsi/graphify/blob/v8/CHANGELOG.md
v0.8.40
What's changed
Features
- Feat: custom OpenAI- and Anthropic-compatible endpoints.
OPENAI_BASE_URL/OPENAI_MODELandANTHROPIC_BASE_URL/ANTHROPIC_MODELpoint either backend at a self-hosted or proxy server (vLLM, llama.cpp, LM Studio, LiteLLM, gateways); defaults preserveapi.openai.com/api.anthropic.comandGRAPHIFY_OPENAI_MODELstill wins overOPENAI_MODEL. Wired through extraction and community labeling (#1273). - Feat: PowerShell
.psm1modules indexed..psm1was missing fromCODE_EXTENSIONSand the dispatch table, so modules and their dependents were silently absent from the graph (#1315)..psd1manifests andImport-Module/dot-source edges remain a follow-up. - Feat: JS/TS AST extracts assignment-form symbols —
this.X = () => {}/this.X = function(){},exports.X/module.exports.X,Foo.prototype.X, class arrow/function fields, andconst f = function(){}expressions. Constructor-style and CommonJS codebases (DAOs, route handlers, services) previously lost most callable symbols. Arbitraryobj.x = fnstays excluded (preserves the #1077 god-node guard) (#1322).
Fixes
- Fix: Swift
importedges no longer silently dropped. The import target previously had no backing node, sobuild.pypruned 100% of Swift imports; the module node is now synthesized so the edge survives (#1327). - Fix:
--no-cluster/ incrementalupdateno longer accumulate duplicate edges. These paths bypass theDiGraphthat collapses parallel edges, so repeatedupdategrew edge counts and diverged across build modes. Edges are now deduped by(source, target, relation)— deterministic and idempotent (#1317). - Fix: community names shown after
cluster-only.query/explain/MCP now surface the human-readable community name instead of a blank/numeric id; backward-compatible with old graphs (#1305). - Fix:
graphify-mcpandgraphify.serveaccept--graph <path>as an alias for the positional arg, consistent with other subcommands (#1304). - Fix: AST extraction no longer crashes on Windows with >61 logical cores — the 61-worker
ProcessPoolExecutorcap now applies to all worker-count paths (#1298). - Fix: ghost-merge skips ambiguous
(basename, label)collisions so same-named symbols in same-named files across directories no longer mis-point edges (#1257). - Fix: startup no longer crashes on unreadable
.graphify_version(restricted installs / network mounts) — FS probes wrapped intry/except OSError(#1299). - Fix:
prs.pyclaude-cli backend resolvesclaude.cmdon Windows (WinError 2 on npm installs) (#1288).
Performance
- Perf:
save_manifestfile hashing parallelized with aThreadPoolExecutor(#1295). - Perf:
_walk_js_treeconverted from recursive generator to iterative walk (#1294).
CI / Docs
- CI: bandit + pip-audit
security-scanjob added as non-blocking advisory signal (continue-on-error) (#1209). - Docs: RFC for file-level node summaries (
docs/node-summaries-rfc.md) (#1166).
v0.8.39
What's changed
Performance
- Perf: O(n²)→O(n) LSH neighbor lookup in dedup.
candidates_by_iddict replaces inner O(n) scan per neighbor;norm_cacheavoids re-normalising labels on every comparison.
Fixes
- Fix:
affectedhandles"edges"-keyed graph.json (KeyError: 'links'crash). Normalised using the established pattern in__main__.py/serve.py. - Fix:
.graphifyignoresingle!negation no longer disables all directory pruning. Pure performance fix — the descent was always wasted since gitignore can't rescue files beneath an excluded parent. - Fix:
merge-chunksprints node count instead of raw list object. - Fix: manifest data-loss on corrupt JSON. Silent
except: passreplaced with corrupt-file backup + stderr warning. - Fix: tree-sitter packages have pinned upper-bound version ranges to prevent silent breakage on grammar API changes.
Features
- Feat: FalkorDB export backend.
graphify export falkordb --push redis://localhost:6379(optional dep:uv tool install "graphifyy[falkordb]"). - Feat:
--modelflag forgraphify cluster-onlyandgraphify label. Override the model used for community naming independently of the backend.
Docs
- Persian (فارسی) README translation added.
- README:
--modelexamples added tocluster-only/labelcommand section.
v0.8.38
What's changed
13 fixes and improvements merged in this release.
Correctness
- Fix: LLM
callsedges now have correct direction. The extraction prompt never stated source = caller / target = callee — the LLM systematically emitted callee→caller. Explicit direction rule added to prompt. Ghost-node merge extended to collapse LLM bare-stem duplicates onto AST canonical nodes even when the LLM node has asource_location. Post-fix annotation:callsprecision 100% (n=6), overall INFERRED precision 94% (n=16). - Fix: JS/TS default imports/exports now produce symbol-level edges.
export default class Fooimported asimport Foo from './foo'previously got only a file→file edge. The class node now receives a properimportsedge and call resolution through renamed bindings works correctly. - Fix: tsconfig
pathsaliases resolved relative tobaseUrl. NestJS, monorepo, and similar layouts usingbaseUrl: "./src"now resolve@/*aliases correctly. - Fix: dedup pass 2 picks winner only from the verified pair. Unrelated same-named nodes from different files were being dragged into merges and supplanting the correct winner.
- Fix: global-graph edges rewired to deduplicated external nodes. Dangling edge references after external node dedup are now remapped before insertion.
- Fix: ghost-merge skips ambiguous
(basename, label)keys. Two same-named symbols in different files (e.g. tworenderfunctions) no longer get mis-merged. - Fix:
resolve_seedmatches bare names against callable-decorated labels. A query forrendernow finds nodes labelled.render().
Performance
- Fix:
collect_filesis now a single prunedos.walk. Replaces ~85 redundantrglobpasses; noise directories are pruned before descent.
Cache
- Fix: AST cache namespaced by graphify version. Stale cache from previous releases no longer silently produces wrong output after upgrade.
- Fix: extraction cache anchored at
--outroot.graphify extract ./src --out /tmp/outno longer leaksgraphify-out/into the scanned project. - Fix: frontmatter delimiter requires whole
---line. Thematic breaks (----) and YAML title lines (--- title: foo) no longer trigger false frontmatter parsing.
Platform
- Fix:
claude-clispawnsclaude.cmdon Windows headless installs. AddsCREATE_NO_WINDOWto both subprocess spawn sites. - Fix:
claude-clihandles JSON-array envelope from Claude Code CLI ≥ 2.1.
Features
- Feat: Cargo workspace dependency extraction.
graphify extract ./my-workspace --cargomaps Rust workspace crates and internalcrate_depends_onedges. - Fix: SystemVerilog class-level extraction improved. Dart
with MyMixinnow correctly emitsmixes_in(notimplements).
Docs
- README grammar count updated: 28 → 36 tree-sitter grammars.
0.8.37
Security
- SSRF guard thread-safety: Replaced the global
socket.getaddrinfomonkey-patch with per-connection_SSRFGuardedHTTPConnection/_SSRFGuardedHTTPSConnectionsubclasses. DNS is resolved once, IP validated, connection made to that exact address — closes the concurrent-thread race and the underlying TOCTOU gap. - Prompt injection mitigation: Untrusted source files are now wrapped in
<untrusted_source path="..." sha256="...">XML delimiters during LLM extraction. Jailbreak sentinel tokens neutralised; system prompt hardened.
Fixes
- Obsidian/Canvas export crash:
KeyErrorwhen a community contains a node absent from the graph — dangling members now skipped gracefully. - macOS NFC/NFD re-extraction loop: Office sidecar filenames now NFC-normalised before hashing, fixing the bug where
--updatere-extracted all Office files on every run on HFS+/APFS. - Data JSON orphan nodes: The JSON extractor now only processes config/manifest JSON (
package.json,tsconfig.json, etc.). Data JSON files (arrays, generic key/value) are skipped by the AST pass — eliminates 561+ orphan nodes on real repos. - OpenAI reasoning models temperature error:
temperature=0is now auto-omitted for o1/o3/o4/gpt-5 series. Override withGRAPHIFY_LLM_TEMPERATURE. - Corporate Windows / EDR hang:
datasketchandscipyremoved. Replaced with a self-contained pure-numpy MinHash/MinHashLSH implementation — eliminates thenumpy.testing → platform.machine()subprocess spawn at import time that EDR software (CrowdStrike, SentinelOne) was intercepting. - Dedup merges distinct same-named symbols:
Configclass inapp.pyandConfigclass indb.pyare no longer collapsed. Code nodes are now deduplicated by ID only.
Performance
- detect() 34% faster on large repos: Ignore-pattern checks memoized per scan. Each ancestor directory evaluated once across all sibling files, eliminating ~42M redundant
fnmatchcalls on 2k+ file corpora.
Features
GRAPHIFY_MAX_GRAPH_BYTES: Override the 512 MiBgraph.jsonsize cap (e.g.700MB,2GB). The cap error message now cites this env var.graphify export htmlauto-falls back to community-aggregation view when over cap.- Stronger
CLAUDE.mdinstructions: "MANDATORY: Before using Read/Grep/Glob/Bash you MUST run graphify first" — includes explicit instruction to forward the rule to every subagent prompt. GRAPHIFY_LLM_TEMPERATURE: Override LLM temperature for any backend (noneto omit entirely).
CI
- Self-graph release asset: Every release now ships
graphify-self-graph.tar.gz—graph.json+graph.html+GRAPH_REPORT.mdfrom Graphify analysing its own source. Download and opengraph.htmllocally to see what Graphify produces, no install required.
Upgrade
uv tool upgrade graphifyy
# or
pip install --upgrade graphifyyv0.8.36
What's new
Features
- extra_body for custom providers — pass model-specific request shapes (e.g. Qwen3 thinking disable, vLLM chat templates) via
providers.json. Explicitextra_bodyalso bypasses Ollamanum_ctxauto-derive. Thanks @EirikWolf (#1197) - Multi-batch community labeling —
label_communitiesnow chunks in groups of 100 (configurablebatch_size=) for 16k-context models; partial batch failures no longer drop the whole pass. Thanks @EirikWolf (#1197) .slnxsolution file support — modern VS 2022 XML solution format, withcontainsandimportsedges. Thanks @bakgaard (#1189)graphify-mcpconsole script — MCP stdio server directly invocable fromuv tool install/pipx. Thanks @jr2804 (#1190)
Fixes
- Community label token budget — raised from
min(40+16n,4096)tomin(64+24n,8192);GRAPHIFY_MAX_OUTPUT_TOKENSnow honoured for labeling path (#1200) - Import cycle hang on large graphs —
nx.simple_cycles()now receiveslength_bound=max_cycle_length, dropping report generation from never-returns to ~0.1s (#1196) - Fuzzy dedup prefix-extension false merges —
getActiveSession/getActiveSessions,parseConfig/parseConfigFileetc. no longer silently merged by prefix-extension guard in Pass 2 and LLM tiebreaker (#1201) Nonenode label crash —_norm,_norm_label,_strip_diacriticsnow guard againstnulllabel fields. Thanks @freiit (#1195)- Skill
trigger:field removed — not part of Agent Skills spec;agentskills validateCI no longer flags graphify installs (#1180)
Upgrade
uv tool upgrade graphifyy