Skip to content

Releases: Graphify-Labs/graphify

0.8.45

Choose a tag to compare

@safishamsi safishamsi released this 22 Jun 21:40

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, so graphify --update matches cached files after a clone or move instead of re-extracting the whole corpus. (#1417)
  • Hyperedge source_file relativizedbuild_from_json now relativizes graph.hyperedges[] like nodes and edges, so a semantic subagent's absolute path no longer leaks into graph.json. (#1418)
  • Report header shows the scan root — the split-skill runbook passed '.' to report.generate; it now passes the real scan path, so GRAPH_REPORT.md no longer titles itself .. (#1419)
  • GRAPHIFY_OUT honoured end-to-end — the custom output-dir override was only respected by some readers, so GRAPHIFY_OUT=custom-out graphify extract still wrote to graphify-out/. It's now resolved through a single graphify.paths module across extract, cluster-only, query/affected/benchmark, save-result, uninstall --purge, cache-check, the manifest/transcripts/memory/converted paths, the build_merge/serve/prs defaults, and the detect scan-exclude. Default behaviour is unchanged. (#1423)

Native-backend extraction

  • Hyperedges are now requested on the native pathgraphify 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

Choose a tag to compare

@safishamsi safishamsi released this 19 Jun 14:46

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.json before Part C, fixing a FileNotFoundError.
  • --cluster-only relies on the self-contained graphify cluster-only CLI 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 when to_json actually persisted the graph (respects the #479 shrink-guard).

Remaining correctness tier:

  • --directed is propagated as directed= into build_from_json (build + rebuild) and build_merge (--update merge + diff), so a --directed / --directed --update run no longer collapses reciprocal edges into an undirected graph.
  • Semantic extraction scopes to document/paper/image only (code is covered by the AST pass), so subagents stop re-reading every source file.
  • .graphify_cached.json is deleted on a cache miss, so a stale cache from a prior run is never merged.
  • --update transcribes changed video files into documents before the semantic pipeline.
  • Transcription writes via write_text, honours GRAPHIFY_WHISPER_MODEL/GRAPHIFY_WHISPER_PROMPT, prints status to stderr.
  • add-watch/exports use the resolved interpreter explicitly; MCP Desktop config documents the absolute interpreter path.

Library

  • The semantic extract entry points no longer crash on FileSlice units. The 0.8.43 #1386 fix coerced every item with Path(f), which raised TypeError on the FileSlice objects from the oversized-text slicing path (#1369). Items are coerced only when not already a Path/FileSlice (#1397, #1399).

Full changelog: https://github.com/safishamsi/graphify/blob/v8/CHANGELOG.md

v0.8.43

Choose a tag to compare

@safishamsi safishamsi released this 19 Jun 09:41

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, and pom.xml are now parsed deterministically into one canonical package node per package (keyed by name) plus depends_on edges, routed to the AST path so the LLM never sees them. Previously apm.yml was 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 as references edges between docs, so hub docs like index.md / table-of-contents.md actually become hubs. (#1376)

Security

  • Bumped vulnerable dependencies to patched versions: pypdf 6.11.0→6.13.3 (CVE-2026-48155/48156), yt-dlp 2026.3.17→2026.6.9, pyjwt 2.12.1→2.13.0, cryptography 48.0.0→49.0.0, python-multipart 0.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 on str paths instead of pathlib.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 install no longer creates a backslash-named junk directory and reports false success when core.hooksPath is 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.ids module; 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

Choose a tag to compare

@safishamsi safishamsi released this 18 Jun 20:22

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 --update no longer deletes a changed file's freshly re-extracted nodes. The 0.8.41 root= fix (#1361) made build_merge's prune actually match relative source_file values — which then matched the just-re-extracted nodes of changed files (still in prune_sources) and removed them, so --update on a changed file could wipe its nodes. The update runbook now prunes only genuinely deleted files; changed files are reconciled by build_merge's replace-on-re-extract (#1344). The full build also passes root= to build_from_json, and the extraction-spec source_file is pinned to the verbatim path, so full build and incremental updates never drift. (#1366 — thanks @RelywOo)

  • 🔒 Security: .graphifyignore and .gitignore are now merged per directory instead of .graphifyignore silently replacing the dir's .gitignore. Previously, adding a .graphifyignore disabled that directory's .gitignore entirely, so a file excluded only by .gitignore — including neutrally-named secrets like prod-dump.sql or customer-data.json — got indexed into the graph (whose artifacts embed file contents and are routinely committed). .gitignore is read first and .graphifyignore last, so it still wins on conflict (including ! negations); adding one can only ever exclude more. (#1363)

  • Java record declarations are now first-class type nodes, and new Foo(...) constructor calls produce a calls edge. Previously a record appeared only as its file node (degree 0). (#1373)

  • Large text documents are no longer silently truncated during semantic extraction. _read_files capped 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 parent source_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

Choose a tag to compare

@safishamsi safishamsi released this 17 Jun 18:34

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(), and self.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 tagged INFERRED. (#1356)

  • /graphify <path> --update prunes stale nodes correctly. The update runbook called build_merge without root=, so absolute prune paths never matched the graph's relative source_file values — nothing was pruned and changed/deleted files left ghost nodes that compounded every run. (#1361)

  • export obsidian no longer writes an empty 32-byte graph.canvas on a populated graph. to_canvas now falls back to one synthetic community covering every node when no community data is present (e.g. --no-cluster builds). (#1324)

  • Edges missing source_file (occasionally emitted by the semantic/LLM extractor) are now backfilled from their endpoint nodes in build_from_json and the --no-cluster path, so they no longer reach graph.json without a file reference. (#1279)

  • Fuzzy dedup no longer over-merges distinct nodes in three cases: numbered/versioned siblings (ADR 0011 vs ADR 0013), cross-file rationale/document boilerplate (file-anchored like code), and cross-file labels sharing a long prefix but diverging in a distinguishing token (testing-library jest-native vs react-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 configured 16384 output-token cap instead of silently falling back to 8192, which had been truncating deep-mode JSON mid-string. GRAPHIFY_MAX_OUTPUT_TOKENS still overrides. (#1365)

Full changelog: https://github.com/safishamsi/graphify/blob/v8/CHANGELOG.md

v0.8.40

Choose a tag to compare

@safishamsi safishamsi released this 16 Jun 01:39

What's changed

Features

  • Feat: custom OpenAI- and Anthropic-compatible endpoints. OPENAI_BASE_URL/OPENAI_MODEL and ANTHROPIC_BASE_URL/ANTHROPIC_MODEL point either backend at a self-hosted or proxy server (vLLM, llama.cpp, LM Studio, LiteLLM, gateways); defaults preserve api.openai.com/api.anthropic.com and GRAPHIFY_OPENAI_MODEL still wins over OPENAI_MODEL. Wired through extraction and community labeling (#1273).
  • Feat: PowerShell .psm1 modules indexed. .psm1 was missing from CODE_EXTENSIONS and the dispatch table, so modules and their dependents were silently absent from the graph (#1315). .psd1 manifests and Import-Module/dot-source edges remain a follow-up.
  • Feat: JS/TS AST extracts assignment-form symbolsthis.X = () => {} / this.X = function(){}, exports.X/module.exports.X, Foo.prototype.X, class arrow/function fields, and const f = function(){} expressions. Constructor-style and CommonJS codebases (DAOs, route handlers, services) previously lost most callable symbols. Arbitrary obj.x = fn stays excluded (preserves the #1077 god-node guard) (#1322).

Fixes

  • Fix: Swift import edges no longer silently dropped. The import target previously had no backing node, so build.py pruned 100% of Swift imports; the module node is now synthesized so the edge survives (#1327).
  • Fix: --no-cluster / incremental update no longer accumulate duplicate edges. These paths bypass the DiGraph that collapses parallel edges, so repeated update grew 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-mcp and graphify.serve accept --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 ProcessPoolExecutor cap 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 in try/except OSError (#1299).
  • Fix: prs.py claude-cli backend resolves claude.cmd on Windows (WinError 2 on npm installs) (#1288).

Performance

  • Perf: save_manifest file hashing parallelized with a ThreadPoolExecutor (#1295).
  • Perf: _walk_js_tree converted from recursive generator to iterative walk (#1294).

CI / Docs

  • CI: bandit + pip-audit security-scan job added as non-blocking advisory signal (continue-on-error) (#1209).
  • Docs: RFC for file-level node summaries (docs/node-summaries-rfc.md) (#1166).

Install: `uv tool install graphifyy` · PyPI · CHANGELOG

v0.8.39

Choose a tag to compare

@safishamsi safishamsi released this 12 Jun 19:38

What's changed

Performance

  • Perf: O(n²)→O(n) LSH neighbor lookup in dedup. candidates_by_id dict replaces inner O(n) scan per neighbor; norm_cache avoids re-normalising labels on every comparison.

Fixes

  • Fix: affected handles "edges"-keyed graph.json (KeyError: 'links' crash). Normalised using the established pattern in __main__.py/serve.py.
  • Fix: .graphifyignore single ! 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-chunks prints node count instead of raw list object.
  • Fix: manifest data-loss on corrupt JSON. Silent except: pass replaced 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: --model flag for graphify cluster-only and graphify label. Override the model used for community naming independently of the backend.

Docs

  • Persian (فارسی) README translation added.
  • README: --model examples added to cluster-only/label command section.

Install: uv tool install graphifyy · PyPI · CHANGELOG

v0.8.38

Choose a tag to compare

@safishamsi safishamsi released this 11 Jun 22:54

What's changed

13 fixes and improvements merged in this release.

Correctness

  • Fix: LLM calls edges 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 a source_location. Post-fix annotation: calls precision 100% (n=6), overall INFERRED precision 94% (n=16).
  • Fix: JS/TS default imports/exports now produce symbol-level edges. export default class Foo imported as import Foo from './foo' previously got only a file→file edge. The class node now receives a proper imports edge and call resolution through renamed bindings works correctly.
  • Fix: tsconfig paths aliases resolved relative to baseUrl. NestJS, monorepo, and similar layouts using baseUrl: "./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. two render functions) no longer get mis-merged.
  • Fix: resolve_seed matches bare names against callable-decorated labels. A query for render now finds nodes labelled .render().

Performance

  • Fix: collect_files is now a single pruned os.walk. Replaces ~85 redundant rglob passes; 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 --out root. graphify extract ./src --out /tmp/out no longer leaks graphify-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-cli spawns claude.cmd on Windows headless installs. Adds CREATE_NO_WINDOW to both subprocess spawn sites.
  • Fix: claude-cli handles JSON-array envelope from Claude Code CLI ≥ 2.1.

Features

  • Feat: Cargo workspace dependency extraction. graphify extract ./my-workspace --cargo maps Rust workspace crates and internal crate_depends_on edges.
  • Fix: SystemVerilog class-level extraction improved. Dart with MyMixin now correctly emits mixes_in (not implements).

Docs

  • README grammar count updated: 28 → 36 tree-sitter grammars.

Install: uv tool install graphifyy · PyPI · CHANGELOG

0.8.37

Choose a tag to compare

@safishamsi safishamsi released this 10 Jun 20:46

Security

  • SSRF guard thread-safety: Replaced the global socket.getaddrinfo monkey-patch with per-connection _SSRFGuardedHTTPConnection/_SSRFGuardedHTTPSConnection subclasses. 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: KeyError when 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 --update re-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=0 is now auto-omitted for o1/o3/o4/gpt-5 series. Override with GRAPHIFY_LLM_TEMPERATURE.
  • Corporate Windows / EDR hang: datasketch and scipy removed. Replaced with a self-contained pure-numpy MinHash/MinHashLSH implementation — eliminates the numpy.testing → platform.machine() subprocess spawn at import time that EDR software (CrowdStrike, SentinelOne) was intercepting.
  • Dedup merges distinct same-named symbols: Config class in app.py and Config class in db.py are 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 fnmatch calls on 2k+ file corpora.

Features

  • GRAPHIFY_MAX_GRAPH_BYTES: Override the 512 MiB graph.json size cap (e.g. 700MB, 2GB). The cap error message now cites this env var. graphify export html auto-falls back to community-aggregation view when over cap.
  • Stronger CLAUDE.md instructions: "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 (none to omit entirely).

CI

  • Self-graph release asset: Every release now ships graphify-self-graph.tar.gzgraph.json + graph.html + GRAPH_REPORT.md from Graphify analysing its own source. Download and open graph.html locally to see what Graphify produces, no install required.

Upgrade

uv tool upgrade graphifyy
# or
pip install --upgrade graphifyy

v0.8.36

Choose a tag to compare

@safishamsi safishamsi released this 08 Jun 22:58

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. Explicit extra_body also bypasses Ollama num_ctx auto-derive. Thanks @EirikWolf (#1197)
  • Multi-batch community labelinglabel_communities now chunks in groups of 100 (configurable batch_size=) for 16k-context models; partial batch failures no longer drop the whole pass. Thanks @EirikWolf (#1197)
  • .slnx solution file support — modern VS 2022 XML solution format, with contains and imports edges. Thanks @bakgaard (#1189)
  • graphify-mcp console script — MCP stdio server directly invocable from uv tool install / pipx. Thanks @jr2804 (#1190)

Fixes

  • Community label token budget — raised from min(40+16n,4096) to min(64+24n,8192); GRAPHIFY_MAX_OUTPUT_TOKENS now honoured for labeling path (#1200)
  • Import cycle hang on large graphsnx.simple_cycles() now receives length_bound=max_cycle_length, dropping report generation from never-returns to ~0.1s (#1196)
  • Fuzzy dedup prefix-extension false mergesgetActiveSession/getActiveSessions, parseConfig/parseConfigFile etc. no longer silently merged by prefix-extension guard in Pass 2 and LLM tiebreaker (#1201)
  • None node label crash_norm, _norm_label, _strip_diacritics now guard against null label fields. Thanks @freiit (#1195)
  • Skill trigger: field removed — not part of Agent Skills spec; agentskills validate CI no longer flags graphify installs (#1180)

Upgrade

uv tool upgrade graphifyy