github safishamsi/graphify v0.9.2

7 hours ago

Bug-fix and feature release: Ruby type-aware call resolution, workspace exports-map resolution, the alias/workspace import-edge regression fix (#1529), tsconfig paths fallbacks, semantic-cache pruning, three Objective-C extractor fixes, Swift static-call confidence, and security dep bumps.

  • Feat: type-aware Ruby member-call resolution (#1499, thanks @vamsipavanmahesh). p.run is now resolved by the inferred type of the receiver (p = Processor.newProcessor#run) instead of by globally-unique method name, so the edge survives name collisions (an unrelated Worker#run no longer makes it ambiguous) and never points at the wrong method. Introduces a small resolver-registry framework that the existing Swift (#1356) and Python (#1446) cross-file passes register into. Receiver types are inferred only from unambiguous local var = ClassName.new bindings; a call whose receiver type can't be proven resolves to nothing rather than to a guess — a deliberate precision-over-recall change for Ruby member calls.
  • Feat: resolve workspace imports through the package's exports map (#1308, thanks @guyoron1). A subpath import like import { x } from "@scope/pkg/browser" now resolves through the package.json exports map (string values, condition objects, nested conditions, and ./* wildcard patterns) instead of falling back to a bare path string, falling back to the existing bare-path/index resolution when there's no exports map or no match. default is consulted last (Node's catch-all), and an export target that escapes the package directory is rejected.
  • Fix: import edges silently dropped on codebases using tsconfig path aliases or workspace packages (#1529), a regression from the 0.9.0 full-repo-relative node-ID change. Relative imports resolve to repo-relative paths and matched fine, but alias (@/lib/utils) and workspace imports resolve to absolute paths, so the import-target ID baked in the on-disk prefix and no longer matched the repo-relative definition node — the edge was dropped at build (common on Next.js/SvelteKit). The id-remap post-pass now also registers the absolute-resolved form, so alias/workspace import targets land on the real node again.
  • Fix: tsconfig compilerOptions.paths fallback targets are now honored (#1531, thanks @oleksii-tumanov). A paths value is an ordered list ("@app/*": ["src/app/*", "lib/app/*"]) that tsc tries in turn; graphify kept only the first entry, so an import whose file lived at a later target was dropped or misresolved. Each target is now tried in order and the first that resolves to a real file wins (no false edge when none exist).
  • Fix: the semantic (LLM) extraction cache is now pruned (#1527, thanks @mwolter805). The AST cache was version-swept but the content-hash-keyed semantic cache had no cleanup, so every content change or file deletion left an orphan entry and graphify-out/cache/semantic/ grew unbounded. Orphan entries are now removed at the end of extract, computed against the full live document set (not the incremental changed subset, which would have evicted still-valid entries) and only touching cache/semantic/; the cache stays unversioned so releases never re-bill LLM extraction.
  • Fix: three Objective-C extractor bugs (#1475, thanks @JabberYQ for the detailed report and test repo). (1) .h headers using NS_ASSUME_NONNULL_BEGIN before @interface produced no class node — tree-sitter-objc can't expand the argument-less macro and fails to emit a class_interface node at all, so the macro is now blanked (offset-preserving) before parsing. (2) Quoted #import "X.h" edges dangled once a .h/.m pair existed (the bare-stem target was salted away during id-disambiguation); imports now resolve to the real header file node, fixing the equivalent latent C #include bug too. (3) [[Foo alloc] init] now emits a references edge to the allocated class, resolved only to an unambiguous class (no false edges). Dot-syntax property accesses and @selector(...) target-action edges remain follow-ups.
  • Fix: Swift type-qualified static calls now resolve as EXTRACTED rather than INFERRED (#1533, thanks @JabberYQ). SessionType.staticMethod() / Singleton.shared.method() name the receiver type explicitly in source, so the resolved edge is an exact reference, matching the Python qualified-class-method pass; instance calls typed via local inference (obj.method()) stay INFERRED.
  • Fix: enforce the API timeout in the secondary LLM dispatch path (#1442, thanks @DhruvTilva). _call_llm (used by the dedup LLM tiebreaker) built its Anthropic/OpenAI clients without timeout, so requests there ignored GRAPHIFY_API_TIMEOUT and could hang — it now passes the timeout like the primary extraction paths.
  • Fix: to_graphml no longer raises ValueError on a node/edge with a None attribute value — null fields are coerced to "" before writing (#1502, thanks @antonioscarinci).
  • Feat: graphify save-result accepts --answer-file as an alternative to --answer, so a long or multi-line answer can be read from a file instead of an inline shell argument (#1502, thanks @antonioscarinci).
  • Fix: generated install/skill guidance is now host-generic (#1530, thanks @ari-mitophane). The wording no longer tells agents to invoke a literal skill tool with skill: "graphify" (host-specific and invalid in many environments); it now points to the installed graphify skill or instructions.
  • Security: bump msgpack to 1.2.1 (GHSA-6v7p-g79w-8964) and pydantic-settings to 2.14.2 (GHSA-4xgf-cpjx-pc3j), and drop the unused safety dev dependency, which only pulled in nltk (an unpatched HIGH advisory). All transitive; the two HIGH-severity ones were dev-tooling only and never in the published wheel. pip-audit (already run in CI) continues to provide dependency-CVE scanning.

Don't miss a new graphify release

NewReleases is sending notifications on new releases.