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.runis now resolved by the inferred type of the receiver (p = Processor.new⇒Processor#run) instead of by globally-unique method name, so the edge survives name collisions (an unrelatedWorker#runno 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 localvar = ClassName.newbindings; 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
exportsmap (#1308, thanks @guyoron1). A subpath import likeimport { x } from "@scope/pkg/browser"now resolves through the package.jsonexportsmap (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.defaultis 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.pathsfallback targets are now honored (#1531, thanks @oleksii-tumanov). Apathsvalue is an ordered list ("@app/*": ["src/app/*", "lib/app/*"]) thattsctries 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 ofextract, computed against the full live document set (not the incremental changed subset, which would have evicted still-valid entries) and only touchingcache/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)
.hheaders usingNS_ASSUME_NONNULL_BEGINbefore@interfaceproduced no class node — tree-sitter-objc can't expand the argument-less macro and fails to emit aclass_interfacenode at all, so the macro is now blanked (offset-preserving) before parsing. (2) Quoted#import "X.h"edges dangled once a.h/.mpair 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#includebug too. (3)[[Foo alloc] init]now emits areferencesedge 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 withouttimeout, so requests there ignoredGRAPHIFY_API_TIMEOUTand could hang — it now passes the timeout like the primary extraction paths. - Fix:
to_graphmlno longer raisesValueErroron a node/edge with aNoneattribute value — null fields are coerced to""before writing (#1502, thanks @antonioscarinci). - Feat:
graphify save-resultaccepts--answer-fileas 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
skilltool withskill: "graphify"(host-specific and invalid in many environments); it now points to the installed graphify skill or instructions. - Security: bump
msgpackto 1.2.1 (GHSA-6v7p-g79w-8964) andpydantic-settingsto 2.14.2 (GHSA-4xgf-cpjx-pc3j), and drop the unusedsafetydev dependency, which only pulled innltk(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.