Releases: Graphify-Labs/graphify
Release list
v0.9.1
Patch release over 0.9.0 — node-ID hardening, incremental-update correctness, rate-limit resilience, and Java extraction. All non-breaking; no re-migration.
- Fix: rate-limited (HTTP 429) extraction chunks are now retried instead of dropped (#1523, thanks @bercedev). The provider SDKs back off and honor
Retry-After, but the SDK default of 2 retries was too low for strict per-org concurrency/RPM caps (e.g. Moonshot/kimi), so a parallelextract429'd, each chunk loggedchunk N failed, and was silently lost (incomplete graph + console spam). The OpenAI-compatible, Azure, and Anthropic clients are now built with a highermax_retries(default 6, override viaGRAPHIFY_MAX_RETRIES). For very tight accounts,--max-concurrency 1further reduces the concurrency that triggers org-level limits. - Fix:
graphify updatenow prunes the edges a re-extracted file no longer produces (#1521, thanks @UltronOfSpace). Old edges were preserved by endpoint-node membership alone, so a deleted import's edge survived forever as long as both endpoints still existed — driving phantom circular-dependency findings (and--forcedidn't help). Edges owned by a re-extracted file (source_file) are dropped before merging the fresh extraction; cross-file edges that merely point at the file are untouched. - Fix: residual node-ID collisions after the 0.9.0 full-path change (#1522, thanks @sub4biz).
normalize_idcollapses every separator to_, so distinct paths that differ only by a separator-vs-punctuation swap (foo/bar_baz.pyvsfoo_bar/baz.py) still merged. Colliders are now salted with a short stable path hash so they stay distinct; non-colliding IDs are byte-identical to 0.9.0 (no re-migration). - Fix: Java record component types now emit
referencesedges (#1519, thanks @oleksii-tumanov) — a record's data dependencies (record Order(Payload p, List<Item> items, …)) were invisible; primitives and the record's own type parameters are skipped. - Fix: same-label cross-file imported-type stubs now stay distinct in the six dedicated extractors too — Julia, Fortran, Go, Rust, PowerShell, ObjC (#1515, thanks @TPAteeq). The #1462 disambiguation previously only covered the generic extractor, so e.g. two Go files importing the same
ext.Widgetcollapsed into one conflated node; they're now kept distinct (whilesource_filestays empty so the #1402 rewire onto a real definition is unchanged). - Fix: Java type parameters no longer emit spurious
referencesedges (#1518, thanks @oleksii-tumanov). The generic-parent support (#1511) created a stray edge/stub for the bareTinclass Box<T> extends Container<T>; the extractor now collects in-scope type-parameter names (class/interface/record/method/constructor, incl. bounded/multiple) and skips them, while keeping every real type and theinherits/implementsedge to the base. - Fix: the internal
origin_filedisambiguation field (#1462) is no longer serialized into graph.json, where it had shipped (in 0.9.0) as an absolute, machine-specific path — it is dropped once the colliding-id pass consumes it, keeping output portable (#1516, thanks @TPAteeq; cf. #555, #932)._originstays (the incremental watcher needs it, #1116).
v0.9.0 — full-path node IDs (breaking)
⚠️ Breaking change — node IDs changed
Node IDs now include the full repo-relative path, fixing silent data loss when same-named files live in different directories. Existing graphs migrate automatically on the next build/update (no LLM re-bill). Run graphify extract --force to recover nodes that previously collided. If you push to a persisted Neo4j store, re-import after upgrading; GraphML/Gephi layouts go stale; query by label rather than persisting node IDs.
- Breaking — node IDs now include the full repo-relative path (#1504, #1509). The node-ID stem was the immediate parent dir + filename, so same-named files in different directories collided into one last-writer-wins node and silently dropped graph content (
docs/v1/api/README.mdanddocs/v2/api/README.mdboth →api_readme). The stem is now the full repo-relative path (docs_v1_api_readmevsdocs_v2_api_readme); top-level files are unchanged (setup.py→setup). The AST extractor, the LLM system prompt, the extraction-spec, and the two hand-copied stem helpers are all aligned to this one rule (fixing the #1509 AST↔LLM divergence that produced ghost duplicates), andbuild_from_jsondeterministically re-keys any cached/older semantic fragment onto the new IDs from itssource_fileso the unversioned semantic cache survives without ghosts or a re-bill. Existing graphs migrate to the new ID format automatically on the nextbuild/update(no re-bill). Note: same-named files in different directories that previously collided into one node are only recovered as distinct nodes by a fresh extraction — rungraphify extract --forceto rebuild and gain them (migrating an already-collided graph/cache can't resurrect the nodes that were already dropped). If you push to a persisted Neo4j store, re-import after upgrading (re-exported IDs change); saved Gephi/yEd (GraphML) layouts go stale; MCP/cypher consumers should query by label rather than persisting node IDs across rebuilds. - Feat:
--timingflag ongraphify extractandgraphify cluster-onlyprints per-stage wall-clock timings to stderr (#1490). Shows how long each pipeline stage takes —extract: detect → AST → semantic → build → cluster → analyze → export;cluster-only: load → cluster → analyze → label → report → export — plus a final total, so slow stages are visible on large corpora. Off by default (monotonicperf_counter, stderr-only); machine-read stdout /graph.jsonare unchanged.
v0.8.51
- Fix: the Obsidian export (
--obsidian/to_obsidian) no longer overwrites a user's own notes or.obsidian/config when pointed at an existing vault (#1506). It wrote one note per node straight into the target dir and unconditionally replaced.obsidian/graph.json, so--obsidian-dir ~/my-vaultcould clobber a same-named note (Database.md) and the user's graph-view settings — silently, no backup. graphify now records the files it owns in a.graphify_obsidian_manifest.jsonand refuses to overwrite any pre-existing file it didn't create (skipping it with one aggregated warning); a re-run still updates graphify's own notes. The defaultgraphify-out/obsidianoutput is unchanged. - Fix: Java enum and annotation (
@interface) declarations are now emitted as type nodes (#1512, thanks @oleksii-tumanov), so a field typed as an enum or a class annotated with a project annotation resolves to a real node instead of a dangling reference. - Fix: Java generic parent relationships are no longer dropped (#1510, thanks @oleksii-tumanov) —
class Foo extends Bar<T>/implements List<T>now emit theinherits/implementsedge to the base type, with the type arguments asgeneric_argreferences. - Fix: the
claude-clibackend no longer crashes withUnicodeDecodeErroron Windows systems whereclaude.cmdemits GBK/cp936 bytes (#1505, thanks @nuthalapativarun) — both subprocess calls decode witherrors="replace". - Fix:
graphify explainandgraphify affectednow resolve a query given as a source-file path even when the graph has multiple nodes from that file (#1503, thanks @behavio1). A path likeapp/api/route.tstokenized to terms that matched no node, so explain returned "No node matching"; source-file paths are now indexed and matched exactly, and when several nodes share the file the lookup prefers the file-level node (theL1node whose name matches the file). Trailing-separator handling is aligned between the two commands. - Docs: clearer install/PATH guidance for
uv tool install graphifyyon macOS (#1471, thanks @Patsch36). Two expected uv behaviors read as bugs: (1) afteruv tool install, thegraphifycommand lands in uv's tool bin dir (~/.local/bin), which a fresh macOS/zsh shell often doesn't have onPATH— the README now points touv tool update-shellinstead of implying uv always wiresPATH; (2)uvx graphify …/uv tool run graphify …resolve the first word as a package and fail, because the package isgraphifyyandgraphifyis only its console script — the docs now showuvx --from graphifyy graphify install. README install note + Troubleshooting only; no code change. - Fix: imported type stubs with the same label no longer falsely merge across files when there is no project definition to rewire onto (#1462, thanks @jiangyq9). Two files that both
from pathlib import Pathand usePathas a type previously collapsed into one node; the referencing file is now kept as an internal disambiguator (origin_file) used only when splitting colliding ids, whilesource_filestays empty so a real project definition can still be rewired onto (the #1402 path is unaffected). - Feat: resolve C# cross-file type references and extract
enum/struct/recorddeclarations (#1466, thanks @TheFedaikin). A new_resolve_csharp_type_references(the C# counterpart to the Java resolver) re-points danglinginherits/implements/referencesedges from no-source "shadow" stubs to their real definitions, disambiguating same-named types in different namespaces via the referencing file'susingdirectives and enclosing namespace; ambiguous matches are refused rather than guessed.enum/struct/recordtypes are now extracted as definitions so those references resolve too. Advances #1318 for C#. - Fix: the Go AST extractor no longer creates phantom duplicate nodes for cross-file type references — the Go copy of
ensure_named_nodestill used the older sourced-stub fallback; it now emits a sourceless stub like the other extractors, extending the #1402 fix to Go (#1500, thanks @TPAteeq).
v0.8.50
- Feat:
graphify label --missing-onlyrelabels only communities that are unnamed or still hold aCommunity Nplaceholder, preserving existing non-placeholder labels from.graphify_labels.json(#1481, thanks @jiangyq9; supersedes #1421 by @matiasduartee, who proposed the same flag). Lets a large graph be relabeled incrementally without re-naming (and paying for) communities that already have good names. - Feat: index Metal (
.metal) shader files — Metal Shading Language is C++14, so.metalis classified as code and routed through the existing C++ extractor, mirroring the CUDA.cu/.cuhreuse (#1480, thanks @jiangyq9; supersedes #1450 by @GoodOlClint). Also adds.cu/.cuh/.metalto the cross-language edge-filter family map (they were missing), so phantom cross-languagecallsedges between these and C++ are correctly suppressed. - Fix: pass
stream: Falseexplicitly on OpenAI-compatible chat-completion calls (#1223, thanks @jiangyq9). Some gateways default to SSE streaming whenstreamis omitted, but graphify always reads the result as a single response, so the call failed against those gateways. Applied to both the extraction dispatch path and the--dedup-llmtiebreaker path. - Fix: emit
referencesedges for Java field types (#1485) and for type-level annotations on Java classes/interfaces/records (#1487, both thanks @oleksii-tumanov). Field types (including thegeneric_argelement ofList<Handler>) and class annotations (@Service,@Entity) were missing from the graph even though parameter/return types and method annotations were already captured; primitives are still skipped. - Fix: the Objective-C extractor was silently dropping most code-level relationships (#1475, thanks @JabberYQ for the detailed report). Five fixes: (1) ObjC
.hheaders were parsed by the C extractor (1 node, 0 edges, losing every@interface/@protocol/@property/method) — a.his now routed to the ObjC extractor when it contains an ObjC-only directive (@interface/@protocol/@implementation/@import), which never hijacks a real C/C++ header; (2)[receiver selector]calls produced nocallsedges at all because the method-body pass looked forselector/keyword_argument_listnodes, but the grammar tags selector parts with the field namemethod(typeidentifier) — the selector is now read from themethodfields, skipping the receiver, which also makes compound sends like[self a:x b:y]resolve; (3) generic property types (NSArray<Product *> *) were invisible because the type was wrapped in ageneric_specifier— the element and container types are now both referenced; (4) class methods (+foo) were mislabeled-foo; (5)@import Foundation;now produces animportsedge. Property/dot-syntaxaccessesand@selector(...)target-action edges remain follow-ups. - Feat: link WPF/XAML views to their ViewModels and extract richer binding references (#1473, thanks @MikeKatsoulakis). Builds on the initial XAML support (#1460). Resolves a view to its ViewModel from an explicit
<Window.DataContext><vm:MainViewModel/>, a design-timed:DataContext="{d:DesignInstance Type=…}", theView→ViewModelnaming convention, or PrismViewModelLocator.AutoWireViewModel="True"— always against an actually-extracted C# class, so a name with no matching class (or an ambiguous one) emits no edge (explicit DataContext is EXTRACTED, conventions are INFERRED). Also extracts binding paths ({Binding User.Name},Path=Order.Total), commands (Command="{Binding SaveCommand}"), converters, and CommunityToolkit[ObservableProperty]/[RelayCommand]generated members. The event-handler resolution stays gated on the .NET handler signature (no spurious event edges), and ViewModel discovery is bounded to the extraction root. - Fix:
.vueSingle File Components now extract their<script>with the right grammar (#1468, thanks @papinto)..vuewas dispatched toextract_js, which selects a tree-sitter grammar by suffix;.vueis neither.tsnor.tsx, so the whole SFC —<template>markup,<script>, and<style>— was parsed as JavaScript, producing a top-level ERROR node and recovering no imports, symbols, or type references. A dedicatedextract_vuenow masks everything outside<script>(replacing it with spaces so line numbers stay accurate) and parses just the script with the grammar named bylang(tsdefault,tsx/js/jsxhonored). The open-tag scan tolerates>inside quoted attributes, so Vue 3.3+ generic components (generic="T extends Record<string, unknown>") parse correctly. - Fix:
graphify reflect --if-stalenow also checks the.graphify_analysis.jsonand.graphify_labels.jsonsidecars (and any custom--analysis/--labelspaths) when deciding whetherLESSONS.mdis up to date (#1470, thanks @oleksii-tumanov). It previously only stat'd the memory docs andgraph.json, so lessons could stay stale after community analysis or labels changed without the graph changing. A missing sidecar is treated as not-an-input, so no-cluster builds are unaffected. - Fix: the
Read|GlobPreToolUse hook (the "run graphify first" nudge installed for Claude Code and CodeBuddy) now matches the file's real trailing extension instead of substring-scanning the path (#1463, thanks @marketechniks). The old check askedany(ext in path), which had two opposite failures:.jsonfiles (package.json,tsconfig.json) spuriously fired because.jsis a substring of.json, and.astro/.vue/.sveltenever fired because they weren't in the set — so on Astro/Vue/Svelte projects, where those are the primary source type, reads and globs never surfaced the graph. The hook now compares the segment after the last/then after the last.against the extension set (with.astro/.vue/.svelteadded), sopackage.jsonstays silent,data.geojsonstays silent,**/*.astrofires, and an extension sitting on a directory component (my.ts/file) correctly doesn't. Thegraphify-out/suppression and fail-open behavior are unchanged. - Fix: make it unambiguous in the skill that graphify needs no API key, so terminal-style hosts stop looping on a missing one (#1461). Hermes (and the other AGENTS.md hosts: Codex, Aider, OpenClaw, Droid, Trae, …) run the
graphifyCLI directly and don't dispatch subagents, but the Step 3 extraction guidance framed the no-key path only as "fall through to subagent dispatch" — so on/graphify .those agents would spin for minutes insisting they needed an API key before eventually proceeding. Step 3 now opens with an explicit, hoisted "graphify needs no API key — never ask the user for one, never block on one" statement (code is AST-only; a code-only corpus skips semantic extraction entirely), and the fallback now spells out a non-subagent path for terminal hosts instead of assuming subagent dispatch. Applied across every generated skill body, including the aider/devin monoliths, with a regression test that pins the wording in place. - Feat: extract WPF/XAML structure from
.xamlfiles (#1460, thanks @MikeKatsoulakis). No new parser dependency (stdlib XML, with the same DOCTYPE/ENTITY and size guards as the.csprojextractor). Captures the root element, named controls (x:Name/Name) and their control types,{Binding ...}references, andx:Class, and bridges the view to its.xaml.cscode-behind by resolving event-handler attributes to the matching methods on the partial class. Event resolution is gated on the .NET handler signature(object sender, …EventArgs e)and skips free-form attributes (Content,Text,Tag, …), so a property value that merely matches a method name (e.g.Content="Save"next to a business methodSave()) can't fabricate a spurious event edge. - Fix:
to_canvas(Obsidian Canvas export) now lays out each community's node cards in the sameceil(sqrt(n))-column grid the group box is sized for. The box width assumed a roughly-squaresqrt(n)-column layout, but the placement loop hardcoded 3 columns, so any community larger than ~9 members rendered as a cramped 3-wide strip in an over-wide, mostly-empty box. The column count is now computed once per community and reused for the box width, box height, and card placement, so the cards fill the box. Cosmetic, no data change (#1452, thanks @TPAteeq). - Fix:
to_obsidian/to_canvas/to_wikino longer silently overwrite notes whose labels differ only by case (e.g. a classReferencesand a prose headingreferences). The filename dedup was keyed on the exact-case name, so two such labels counted as non-colliding and the second write clobbered the first on case-insensitive filesystems (macOS/APFS, Windows/NTFS) — no suffix, no warning. Dedup now folds case (keyed on the lowercased name) while still emitting the original-case filename, so any pair that would collide on disk gets a numeric suffix. The obsidian/canvas dedup is shared in one helper so they can't drift,wiki's slug dedup gets the matching fix, the_COMMUNITY_*.mdoverview notes (which had no dedup) are covered, and a generatedbase_1is itself re-checked so it can't overwrite a node literally labelledbase_1(#1453, thanks @TPAteeq). - Feat: the
kimi,gemini, anddeepseeksemantic-extraction backends now honorKIMI_BASE_URL,GEMINI_BASE_URL, andDEEPSEEK_BASE_URLto point at any OpenAI-compatible endpoint (a proxy, gateway, or self-hosted relay), matching the existingOLLAMA_BASE_URL/OPENAI_BASE_URLoverrides. Each falls back to its hardcoded official default when the variable is unset, so behavior is unchanged for everyone who doesn't set it (#1458, thanks @jc2shile). - Fix:
to_wiki(Wikipedia-style wiki export) now emits portable relative markdown links instead of Obsidian[[wikilinks]], so navigation works in every renderer — VS Code preview, GitHub, GitLab, a plain browser — not just Obsidian. Two defects: (1)[[Title]]resolves by note title only inside Obsid...
graphify 0.8.49
graphify 0.8.49
Fixes
get_communityMCP tool now shows the community name in its header (Community 12 — Auth & Sessions (8 nodes)), matchingget_node/ query output; skipped when it is only theCommunity Nplaceholder so it never doubles (#1448, thanks @rmart1308).graphify reflectno longer duplicates "known dead ends" / "corrections" lines when the same Q&A is saved more than once (dedup by question, most recent wins).- Work-memory works without the git hook: the skill runs
graphify reflect --if-staleat the start of graph work, so a skill-only install still refreshesLESSONS.md.--if-staleno-ops when the file is already newer than every input, so the post-commit hook is an optimization rather than a requirement.
Security
- Floor
starletteat>=1.3.1for CVE-2026-48818 and CVE-2026-54283 (both resolved by 1.3.1). starlette underpins the HTTP MCP transport (graphify-mcpover HTTP); stdio and the CLI are unaffected. Now declared in themcp/allextras and floored so end users installinggraphifyy[mcp]are covered, not just the dev lock (#1391, #1396, thanks @orbisai0security).
Refactor / Performance
- Begin splitting
extract.pyinto per-language modules undergraphify/extractors/(blade, elixir, razor, zig + sharedbase.py), behavior-neutral, withextract.pyre-exporting the moved names so all callers and the dispatch table are unchanged (#1212, thanks @TheFedaikin). - Parallel community labeling:
cluster-only/labeltake--max-concurrencyand--batch-size;ollama/claude-clistay serial unless opted in (#1390).
Install: uv tool install graphifyy==0.8.49
graphify 0.8.47
graphify 0.8.47
Self-improving "work memory" (#1441)
graphify save-result --outcome useful|dead_end|corrected [--correction TEXT]records how a saved Q&A turned out.graphify reflectaggregatesgraphify-out/memory/into a deterministicreflections/LESSONS.mdan agent loads next session. Source nodes are scored, not counted — signed and recency-decayed (configurable--half-life-days, default 30), so a fresh dead end outweighs a stale useful. A node is preferred only once corroborated by ≥--min-corroborationdistinct results (default 2); others are tentative; mixed-signal nodes render once as contested (recency-wins). Citations whose node no longer exists in the graph are dropped, so stale lessons don't linger. Deterministic, no LLM.- Zero-config adoption: the skill instructs the agent to read
LESSONS.mdat the start of graph work and to record outcomes, and the git post-commit/post-checkout hooks now auto-runreflectafter each rebuild — so lessons stay fresh without a manual command.
Fixes
- Python qualified class-method calls (
ClassName.method(...)) now produce an EXTRACTEDcallsedge to the class-qualified method node (#1446) — previously dropped, including the common case where the called method shares its name with the caller (a viewset action delegating to a same-named service action). validate_extraction/build_from_jsonno longer crash on a non-hashable nodeidor edge endpoint from a malformed extraction — a single bad node no longer aborts the whole build (#1447, thanks @dschwartzi).graphify updatenow prunes a function/symbol removed from a still-present file without--force— the shrink-guard is file-aware (allows shrinkage from re-extracted/deleted files, still refuses unexplained loss).
Install: uv tool install graphifyy==0.8.47
0.8.46
Faster queries, a graph-health gate in the skill runbook, CUDA support, and a batch of extraction/install fixes.
Performance
- Trigram query prefilter —
graphify query/path/explainand the MCP query tools now narrow candidates via a trigram index (built once per graph, rebuilt on hot-reload) before the IDF scorer, cutting the previous O(N) scan on large graphs. The prefilter is a strict superset of the exhaustive scorer, so results and ranking are unchanged; short/CJK/low-selectivity queries fall back to the full scan (#1431, thanks @papinto).
Skill runbook
- Step 4.5 graph-health gate — after building the graph, the runbook runs
diagnose_extractionand surfaces dangling/self-loop/collapsed-edge warnings before labeling (read-only; never aborts). It also anchors the semantic cache on the scan root so cache hits survive a non-cwd scan (#1437, thanks @bahcgscateringsa-design).
Languages
- CUDA (
.cu/.cuh) is now extracted via the existing C++ (tree-sitter-cpp) grammar — no new dependency. Kernels, host functions, structs, includes and host call edges are captured;<<<grid, block>>>launch syntax parses cleanly (#1411).
Extraction quality
- No more phantom duplicate nodes from cross-file type annotations. A class defined once but referenced via type annotations in N files produced 1+N nodes (the extras with the file path baked into the id, e.g.
pkg_a_py_thing). The cross-file reference now resolves to the single canonical definition — fixed across all six language extractors (#1402, thanks @ZedUserdesign).
Install & misc fixes
graphify install --platform hermesinstalls to%LOCALAPPDATA%\hermes\skillson Windows (where Hermes scans), not~/.hermes/skills(#1403, thanks @SHJordan).- The opencode plugin's search reminder no longer contains backticks that bash executed as a command substitution on every search (#1413, thanks @WSHAPER).
to_obsidian/to_canvasnever emit punctuation-only filenames (e.g.@.md) that break downstream re-sluggers — they fall back tounnamed(#1409, thanks @Mylock51).graphify extract --cargoexits with a clear error instead of a traceback whenCargo.tomlis missing/unreadable (#1428, thanks @DhruvTilva).- Internal: resolved an
F821inprs.pyvia aTYPE_CHECKINGimport (#1429, thanks @DhruvTilva).
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