github Graphify-Labs/graphify v0.9.8

6 hours ago

graphify 0.9.8 — Windows hook fix + reliability round.

Highlights: the graph-nudge hooks now work on Windows (Claude Code, Codebuddy, Gemini CLI); plus fixes for CLAUDE.md section-write data loss, a tiktoken special-token crash, an Ollama hang, community-labeling robustness + token accounting, discovery-layer file drops, and the deepseek thinking default.

Install: uv tool install "graphifyy==0.9.8" or pip install graphifyy==0.9.8

Changelog

  • Fix: the Claude Code / Codebuddy PreToolUse and Gemini CLI BeforeTool graph-nudge hooks now work on Windows (#522). The hooks were inline POSIX bash (case/esac, [ -f ], single-quoted echo), which Windows cmd.exe/PowerShell cannot parse — so on Windows the hook failed silently, no "run graphify query before grepping/reading raw files" context was injected, and users had to invoke /graphify by hand. The detection logic (grep-command match, source-file extension match, skip-if-under-output-dir, graph-exists check) moved into a shell-agnostic graphify hook-guard <search|read> subcommand invoked via the absolute exe path (the same pattern the codex hook already uses), so the hook parses and runs identically on Windows, macOS, and Linux. Behavior on macOS/Linux is unchanged (byte-identical nudge payload); the graph path now also honors GRAPHIFY_OUT. The Gemini BeforeTool hook got the same treatment (graphify hook-guard gemini), which also removes its dependency on a bare python being on PATH. Codex stays a no-op there because Codex Desktop rejects additionalContext.
  • Fix: --update-style section writes to CLAUDE.md/AGENTS.md no longer corrupt or drop content (#1688, thanks @bdfinst). _replace_or_append_section located its managed block by substring (marker in content) and next(... if marker in line), so a heading that appeared as a substring of another line (or duplicate headings) matched the wrong offset and the rewrite could truncate the file. It now matches the section heading exactly (line.strip() == marker), appends when absent, and prefers the last exact match when several exist, so unrelated content is preserved.
  • Fix: token estimation no longer crashes on files containing tiktoken special-token text like <|endoftext|> (#1685, thanks @Kyzcreig). _TOKENIZER.encode(content) raises ValueError by default when the text contains a special token, which aborted packing on docs/corpora that merely mention these strings. Both encode sites now pass disallowed_special=() so such text is tokenized as ordinary bytes.
  • Fix: the Ollama backend no longer multiplies a hang by the retry count (#1686, thanks @Kyzcreig). A stalled local model would wedge for timeout * (max_retries + 1), which with the default 6 retries turned one long stall into a very long one. Ollama now defaults to zero client-side retries (a local model that stalls will not un-stall on retry); set GRAPHIFY_MAX_RETRIES to opt back in. Other backends are unchanged. Note: the underlying stall is non-deterministic and driven by the model server, so this bounds the wait rather than eliminating the hang.
  • Fix: a truncated or slightly malformed community-labeling reply no longer discards the whole batch (#1690, thanks @vdgbcrypto). _parse_label_response now salvages the complete "id": "name" pairs from a reply that failed a strict json.loads (e.g. a reply truncated mid-object), raising only when no pairs can be recovered. The per-batch token budget was also raised (256 + 48*n, was 64 + 24*n) to give models that prepend a short preamble enough headroom to finish the JSON. The exact provider truncation in the report could not be reproduced without a live key; the parser and budget fixes address the mechanism.
  • Fix: cluster-only mode now reports the real token cost of community labeling instead of a hardcoded zero (#1694, thanks @sub4biz). The labeling LLM calls were never accounted for, so GRAPH_REPORT.md's "Token cost" line always read 0 input · 0 output in cluster-only runs. _call_llm now accumulates per-response usage into an optional accumulator that is threaded through the labeling path and surfaced in the report. Backends that do not return usage (the Claude Code CLI) still contribute nothing, which is honest rather than estimated.
  • Docs/Feat: deepseek-v4-flash (and v4-pro) have thinking ENABLED by default; graphify no longer implies otherwise and adds an opt-in GRAPHIFY_DISABLE_THINKING=1 toggle (#1621, thanks @sub4biz for the empirical testing). Disabling thinking removes a rare reasoning-leak failure mode (which the adaptive extraction/labeling retry already recovers from) but, measured on real corpora, trades it for more frequent benign truncation and measurably lower extraction quality and file coverage — so it stays a documented user choice rather than a forced default. The stale "non-thinking" comment on the built-in deepseek config is corrected. The moonshot (kimi) branch is unchanged (it must disable thinking or content comes back empty).
  • Fix: source files are no longer silently dropped during discovery by two over-broad filters (#1666, thanks @krishnateja7 for the precise root-cause). (a) A bare snapshots/ directory was pruned as a Jest/Vitest artifact, which killed legitimate code namespaces like a Rails app/services/snapshots/; it is now pruned only when it actually contains .snap files or sits directly under a JS test root (__snapshots__ stays unconditionally pruned). (b) _is_sensitive dropped files on a bare name-keyword hit (device_token.rb, passwords_controller.rb) even when classify_file had already resolved them to source code; a genuine programming-language source file is now exempt from the weak keyword heuristic, while real secret stores in data/config formats (credentials.json, secrets.yaml, .env, .pem, ...) are still caught. This is the discovery-layer fix; the 0.9.7 no-cache-on-empty change could not surface these because the files never reached extraction.

Don't miss a new graphify release

NewReleases is sending notifications on new releases.