graphifyy 0.9.77__tar.gz → 0.9.78__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (589) hide show
  1. {graphifyy-0.9.77/graphifyy.egg-info → graphifyy-0.9.78}/PKG-INFO +5 -1
  2. {graphifyy-0.9.77 → graphifyy-0.9.78}/README.md +4 -0
  3. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/analyze.py +26 -11
  4. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/export.py +19 -0
  5. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extract.py +110 -23
  6. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/apex.py +2 -2
  7. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/base.py +68 -0
  8. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/bash.py +2 -2
  9. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/blade.py +2 -2
  10. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/cobol.py +2 -2
  11. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/commonlisp.py +2 -2
  12. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/dart.py +2 -2
  13. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/dm.py +19 -6
  14. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/elixir.py +7 -2
  15. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/engine.py +42 -4
  16. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/erlang.py +2 -2
  17. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/fortran.py +3 -3
  18. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/go.py +8 -2
  19. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/julia.py +2 -2
  20. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/markdown.py +2 -2
  21. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/objc.py +2 -2
  22. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/ocaml.py +2 -2
  23. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/pascal.py +3 -3
  24. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/pascal_forms.py +2 -2
  25. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/powershell.py +37 -18
  26. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/r.py +17 -5
  27. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/razor.py +2 -2
  28. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/resolution.py +8 -6
  29. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/rust.py +8 -2
  30. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/sln.py +2 -2
  31. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/solidity.py +2 -2
  32. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/sql.py +2 -2
  33. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/terraform.py +2 -2
  34. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/vbnet.py +2 -2
  35. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/verilog.py +2 -2
  36. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/zig.py +2 -2
  37. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/file_slice.py +6 -4
  38. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/llm.py +3 -2
  39. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-aider.md +2 -2
  40. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-devin.md +2 -2
  41. {graphifyy-0.9.77/graphify/skills/claude → graphifyy-0.9.78/graphify/skills/agents}/references/update.md +2 -2
  42. {graphifyy-0.9.77/graphify/skills/agents → graphifyy-0.9.78/graphify/skills/amp}/references/update.md +2 -2
  43. {graphifyy-0.9.77/graphify/skills/claw → graphifyy-0.9.78/graphify/skills/claude}/references/update.md +2 -2
  44. {graphifyy-0.9.77/graphify/skills/amp → graphifyy-0.9.78/graphify/skills/claw}/references/update.md +2 -2
  45. graphifyy-0.9.78/graphify/skills/codex/references/update.md +210 -0
  46. graphifyy-0.9.78/graphify/skills/copilot/references/update.md +210 -0
  47. graphifyy-0.9.78/graphify/skills/droid/references/update.md +210 -0
  48. graphifyy-0.9.78/graphify/skills/kilo/references/update.md +210 -0
  49. graphifyy-0.9.78/graphify/skills/kiro/references/update.md +210 -0
  50. graphifyy-0.9.78/graphify/skills/opencode/references/update.md +210 -0
  51. graphifyy-0.9.78/graphify/skills/pi/references/update.md +210 -0
  52. graphifyy-0.9.78/graphify/skills/trae/references/update.md +210 -0
  53. graphifyy-0.9.78/graphify/skills/vscode/references/update.md +210 -0
  54. graphifyy-0.9.78/graphify/skills/windows/references/update.md +210 -0
  55. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/symbol_resolution.py +2 -1
  56. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/watch.py +113 -1
  57. {graphifyy-0.9.77 → graphifyy-0.9.78/graphifyy.egg-info}/PKG-INFO +5 -1
  58. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphifyy.egg-info/SOURCES.txt +3 -0
  59. {graphifyy-0.9.77 → graphifyy-0.9.78}/pyproject.toml +1 -1
  60. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_analyze.py +33 -0
  61. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cpp_nested_and_cli.py +84 -0
  62. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_member_calls.py +47 -0
  63. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_type_resolution.py +54 -0
  64. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_export.py +12 -0
  65. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_extract.py +21 -6
  66. graphifyy-0.9.78/tests/test_incremental_unscanned_placeholders.py +126 -0
  67. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_js_import_resolution.py +31 -0
  68. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_languages.py +144 -0
  69. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_python_import_resolution.py +25 -0
  70. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_r_extractor.py +91 -0
  71. graphifyy-0.9.78/tests/test_source_file_encoding.py +259 -0
  72. graphifyy-0.9.78/tests/test_watch_identity_cache.py +59 -0
  73. graphifyy-0.9.77/graphify/skills/codex/references/update.md +0 -210
  74. graphifyy-0.9.77/graphify/skills/copilot/references/update.md +0 -210
  75. graphifyy-0.9.77/graphify/skills/droid/references/update.md +0 -210
  76. graphifyy-0.9.77/graphify/skills/kilo/references/update.md +0 -210
  77. graphifyy-0.9.77/graphify/skills/kiro/references/update.md +0 -210
  78. graphifyy-0.9.77/graphify/skills/opencode/references/update.md +0 -210
  79. graphifyy-0.9.77/graphify/skills/pi/references/update.md +0 -210
  80. graphifyy-0.9.77/graphify/skills/trae/references/update.md +0 -210
  81. graphifyy-0.9.77/graphify/skills/vscode/references/update.md +0 -210
  82. graphifyy-0.9.77/graphify/skills/windows/references/update.md +0 -210
  83. {graphifyy-0.9.77 → graphifyy-0.9.78}/LICENSE +0 -0
  84. {graphifyy-0.9.77 → graphifyy-0.9.78}/LICENSE-MIT +0 -0
  85. {graphifyy-0.9.77 → graphifyy-0.9.78}/NOTICE +0 -0
  86. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/__init__.py +0 -0
  87. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/__main__.py +0 -0
  88. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/_minhash.py +0 -0
  89. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/affected.py +0 -0
  90. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/always_on/agents-md.md +0 -0
  91. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/always_on/antigravity-rules.md +0 -0
  92. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/always_on/claude-md.md +0 -0
  93. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/always_on/gemini-md.md +0 -0
  94. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/always_on/kiro-steering.md +0 -0
  95. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/always_on/vscode-instructions.md +0 -0
  96. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/benchmark.py +0 -0
  97. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/build.py +0 -0
  98. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/cache.py +0 -0
  99. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/callflow_html.py +0 -0
  100. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/cargo_introspect.py +0 -0
  101. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/cli.py +0 -0
  102. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/cluster.py +0 -0
  103. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/command-kilo.md +0 -0
  104. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/cross_repo_calls.py +0 -0
  105. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/cross_repo_types.py +0 -0
  106. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/csharp_dispatch.py +0 -0
  107. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/dedup.py +0 -0
  108. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/detect.py +0 -0
  109. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/diagnostics.py +0 -0
  110. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/exporters/__init__.py +0 -0
  111. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/exporters/base.py +0 -0
  112. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/exporters/graphdb.py +0 -0
  113. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/exporters/html.py +0 -0
  114. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/__init__.py +0 -0
  115. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/csharp.py +0 -0
  116. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/json_config.py +0 -0
  117. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/models.py +0 -0
  118. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/extractors/robot.py +0 -0
  119. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/global_graph.py +0 -0
  120. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/google_workspace.py +0 -0
  121. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/hooks.py +0 -0
  122. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/ids.py +0 -0
  123. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/ingest.py +0 -0
  124. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/install.py +0 -0
  125. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/interface_dispatch.py +0 -0
  126. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/manifest.py +0 -0
  127. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/manifest_ingest.py +0 -0
  128. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/markdown_resolution.py +0 -0
  129. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/mcp_ingest.py +0 -0
  130. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/multigraph_compat.py +0 -0
  131. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/pascal_resolution.py +0 -0
  132. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/paths.py +0 -0
  133. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/pg_introspect.py +0 -0
  134. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/prs.py +0 -0
  135. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/querylog.py +0 -0
  136. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/reflect.py +0 -0
  137. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/report.py +0 -0
  138. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/resolver_registry.py +0 -0
  139. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/ruby_resolution.py +0 -0
  140. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/scip_ingest.py +0 -0
  141. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/security.py +0 -0
  142. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/semantic_cleanup.py +0 -0
  143. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/serve.py +0 -0
  144. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-agents.md +0 -0
  145. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-amp.md +0 -0
  146. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-claw.md +0 -0
  147. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-codex.md +0 -0
  148. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-copilot.md +0 -0
  149. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-droid.md +0 -0
  150. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-kilo.md +0 -0
  151. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-kiro.md +0 -0
  152. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-opencode.md +0 -0
  153. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-pi.md +0 -0
  154. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-trae.md +0 -0
  155. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-vscode.md +0 -0
  156. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill-windows.md +0 -0
  157. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skill.md +0 -0
  158. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/agents/references/add-watch.md +0 -0
  159. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/agents/references/exports.md +0 -0
  160. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/agents/references/extraction-spec.md +0 -0
  161. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/agents/references/github-and-merge.md +0 -0
  162. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/agents/references/hooks.md +0 -0
  163. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/agents/references/query.md +0 -0
  164. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/agents/references/transcribe.md +0 -0
  165. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/amp/references/add-watch.md +0 -0
  166. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/amp/references/exports.md +0 -0
  167. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/amp/references/extraction-spec.md +0 -0
  168. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/amp/references/github-and-merge.md +0 -0
  169. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/amp/references/hooks.md +0 -0
  170. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/amp/references/query.md +0 -0
  171. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/amp/references/transcribe.md +0 -0
  172. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claude/references/add-watch.md +0 -0
  173. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claude/references/exports.md +0 -0
  174. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claude/references/extraction-spec.md +0 -0
  175. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claude/references/github-and-merge.md +0 -0
  176. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claude/references/hooks.md +0 -0
  177. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claude/references/query.md +0 -0
  178. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claude/references/transcribe.md +0 -0
  179. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claw/references/add-watch.md +0 -0
  180. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claw/references/exports.md +0 -0
  181. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claw/references/extraction-spec.md +0 -0
  182. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claw/references/github-and-merge.md +0 -0
  183. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claw/references/hooks.md +0 -0
  184. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claw/references/query.md +0 -0
  185. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/claw/references/transcribe.md +0 -0
  186. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/codex/references/add-watch.md +0 -0
  187. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/codex/references/exports.md +0 -0
  188. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/codex/references/extraction-spec.md +0 -0
  189. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/codex/references/github-and-merge.md +0 -0
  190. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/codex/references/hooks.md +0 -0
  191. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/codex/references/query.md +0 -0
  192. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/codex/references/transcribe.md +0 -0
  193. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/copilot/references/add-watch.md +0 -0
  194. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/copilot/references/exports.md +0 -0
  195. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/copilot/references/extraction-spec.md +0 -0
  196. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/copilot/references/github-and-merge.md +0 -0
  197. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/copilot/references/hooks.md +0 -0
  198. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/copilot/references/query.md +0 -0
  199. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/copilot/references/transcribe.md +0 -0
  200. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/droid/references/add-watch.md +0 -0
  201. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/droid/references/exports.md +0 -0
  202. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/droid/references/extraction-spec.md +0 -0
  203. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/droid/references/github-and-merge.md +0 -0
  204. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/droid/references/hooks.md +0 -0
  205. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/droid/references/query.md +0 -0
  206. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/droid/references/transcribe.md +0 -0
  207. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kilo/references/add-watch.md +0 -0
  208. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kilo/references/exports.md +0 -0
  209. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kilo/references/extraction-spec.md +0 -0
  210. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kilo/references/github-and-merge.md +0 -0
  211. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kilo/references/hooks.md +0 -0
  212. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kilo/references/query.md +0 -0
  213. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kilo/references/transcribe.md +0 -0
  214. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kiro/references/add-watch.md +0 -0
  215. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kiro/references/exports.md +0 -0
  216. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kiro/references/extraction-spec.md +0 -0
  217. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kiro/references/github-and-merge.md +0 -0
  218. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kiro/references/hooks.md +0 -0
  219. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kiro/references/query.md +0 -0
  220. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/kiro/references/transcribe.md +0 -0
  221. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/opencode/references/add-watch.md +0 -0
  222. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/opencode/references/exports.md +0 -0
  223. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/opencode/references/extraction-spec.md +0 -0
  224. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/opencode/references/github-and-merge.md +0 -0
  225. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/opencode/references/hooks.md +0 -0
  226. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/opencode/references/query.md +0 -0
  227. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/opencode/references/transcribe.md +0 -0
  228. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/pi/references/add-watch.md +0 -0
  229. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/pi/references/exports.md +0 -0
  230. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/pi/references/extraction-spec.md +0 -0
  231. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/pi/references/github-and-merge.md +0 -0
  232. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/pi/references/hooks.md +0 -0
  233. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/pi/references/query.md +0 -0
  234. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/pi/references/transcribe.md +0 -0
  235. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/trae/references/add-watch.md +0 -0
  236. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/trae/references/exports.md +0 -0
  237. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/trae/references/extraction-spec.md +0 -0
  238. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/trae/references/github-and-merge.md +0 -0
  239. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/trae/references/hooks.md +0 -0
  240. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/trae/references/query.md +0 -0
  241. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/trae/references/transcribe.md +0 -0
  242. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/vscode/references/add-watch.md +0 -0
  243. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/vscode/references/exports.md +0 -0
  244. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/vscode/references/extraction-spec.md +0 -0
  245. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/vscode/references/github-and-merge.md +0 -0
  246. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/vscode/references/hooks.md +0 -0
  247. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/vscode/references/query.md +0 -0
  248. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/vscode/references/transcribe.md +0 -0
  249. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/windows/references/add-watch.md +0 -0
  250. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/windows/references/exports.md +0 -0
  251. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/windows/references/extraction-spec.md +0 -0
  252. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/windows/references/github-and-merge.md +0 -0
  253. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/windows/references/hooks.md +0 -0
  254. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/windows/references/query.md +0 -0
  255. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/skills/windows/references/transcribe.md +0 -0
  256. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/swift_dispatch.py +0 -0
  257. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/transcribe.py +0 -0
  258. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/tree_html.py +0 -0
  259. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/validate.py +0 -0
  260. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphify/wiki.py +0 -0
  261. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphifyy.egg-info/dependency_links.txt +0 -0
  262. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphifyy.egg-info/entry_points.txt +0 -0
  263. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphifyy.egg-info/requires.txt +0 -0
  264. {graphifyy-0.9.77 → graphifyy-0.9.78}/graphifyy.egg-info/top_level.txt +0 -0
  265. {graphifyy-0.9.77 → graphifyy-0.9.78}/setup.cfg +0 -0
  266. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_affected_cli.py +0 -0
  267. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_affected_member_seed.py +0 -0
  268. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_agents_platform.py +0 -0
  269. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_anthropic_custom_endpoint.py +0 -0
  270. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_antigravity_install.py +0 -0
  271. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_apm_fallback_version.py +0 -0
  272. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_architecture_doc.py +0 -0
  273. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_astro_extraction.py +0 -0
  274. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_astro_import_ids.py +0 -0
  275. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_atomic_canvas_export.py +0 -0
  276. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_atomic_version_stamp.py +0 -0
  277. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_atomic_writes.py +0 -0
  278. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_backend_env_isolation.py +0 -0
  279. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_backend_extras.py +0 -0
  280. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_benchmark.py +0 -0
  281. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_benchmark_raw_graph.py +0 -0
  282. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_blade_extractor.py +0 -0
  283. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_build.py +0 -0
  284. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_build_located_semantic_identity.py +0 -0
  285. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_build_merge_dedup_scope.py +0 -0
  286. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_build_merge_hyperedges_and_prune.py +0 -0
  287. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_build_merge_shrink_guard.py +0 -0
  288. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_builtin_global_type_refs.py +0 -0
  289. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cache.py +0 -0
  290. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_callflow_html.py +0 -0
  291. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cargo_introspect.py +0 -0
  292. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cargo_missing_manifest.py +0 -0
  293. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_carried_hyperedge_remap.py +0 -0
  294. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_case_sensitive_resolution.py +0 -0
  295. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_charmap_encoding.py +0 -0
  296. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_chunking.py +0 -0
  297. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cjs_module_extension.py +0 -0
  298. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_claude_cli_backend.py +0 -0
  299. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_claude_md.py +0 -0
  300. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cli_broken_pipe.py +0 -0
  301. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cli_export.py +0 -0
  302. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cli_help.py +0 -0
  303. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cluster.py +0 -0
  304. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cluster_exclude_hubs.py +0 -0
  305. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cobol_extractor.py +0 -0
  306. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_codebuddy.py +0 -0
  307. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_community_hub_labels.py +0 -0
  308. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_community_labels_skill.py +0 -0
  309. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_confidence.py +0 -0
  310. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_corrupt_graph_json.py +0 -0
  311. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cpp_method_declarations.py +0 -0
  312. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cpp_objc_cross_file_calls.py +0 -0
  313. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cpp_preprocess.py +0 -0
  314. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cross_extension_reexport_self_cycle.py +0 -0
  315. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cross_language_call_resolution.py +0 -0
  316. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cross_repo_external_call_guards.py +0 -0
  317. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cross_repo_member_calls.py +0 -0
  318. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_cross_repo_shared_types.py +0 -0
  319. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_call_site_generic_args.py +0 -0
  320. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_enum_members.py +0 -0
  321. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_field_generic_args.py +0 -0
  322. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_generic_callsites.py +0 -0
  323. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_interface_dispatch.py +0 -0
  324. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_member_nodes.py +0 -0
  325. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_object_creation.py +0 -0
  326. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_partial_classes.py +0 -0
  327. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_csharp_tuple_type_refs.py +0 -0
  328. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_dart.py +0 -0
  329. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_dedup.py +0 -0
  330. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_dedup_remaps_hyperedges.py +0 -0
  331. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_dedup_shrink_refuses_force_write.py +0 -0
  332. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_dedup_survivor_richness.py +0 -0
  333. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_definition_file_portability.py +0 -0
  334. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_detect.py +0 -0
  335. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_devin.py +0 -0
  336. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_docx_to_markdown.py +0 -0
  337. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_dotnet.py +0 -0
  338. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_duplicate_annotation_edges.py +0 -0
  339. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_elixir_import_resolution.py +0 -0
  340. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_elixir_unqualified_call_scope.py +0 -0
  341. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_erlang_extractor.py +0 -0
  342. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_evidence_binding.py +0 -0
  343. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_explain_cli.py +0 -0
  344. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_export_control_characters.py +0 -0
  345. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_export_direction.py +0 -0
  346. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_export_idempotent_writes.py +0 -0
  347. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_export_path_length.py +0 -0
  348. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_external_stub_endpoints.py +0 -0
  349. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_extract_cache_location.py +0 -0
  350. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_extract_cli.py +0 -0
  351. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_extract_code_only_cli.py +0 -0
  352. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_extract_php_closures.py +0 -0
  353. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_extraction_spec_ids.py +0 -0
  354. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_extractors_registry.py +0 -0
  355. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_falkordb_integration.py +0 -0
  356. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_file_label_disambiguation.py +0 -0
  357. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_file_node_id_spec.py +0 -0
  358. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_file_slice.py +0 -0
  359. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_file_truncation_warning.py +0 -0
  360. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_forwarding_review_findings.py +0 -0
  361. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_gemini_hook.py +0 -0
  362. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_global_add_tag_inference.py +0 -0
  363. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_global_graph.py +0 -0
  364. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_go_builtin_call_targets.py +0 -0
  365. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_go_import_repoint.py +0 -0
  366. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_go_interface_methods.py +0 -0
  367. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_go_qualified_resolution.py +0 -0
  368. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_god_nodes_cli.py +0 -0
  369. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_god_nodes_exclude_hubs.py +0 -0
  370. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_google_workspace.py +0 -0
  371. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_graphdb_push_indexes.py +0 -0
  372. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_hollow_chunks_arm_shrink_guard.py +0 -0
  373. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_home_sandbox.py +0 -0
  374. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_hook_chain_survives_skip.py +0 -0
  375. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_hook_guard.py +0 -0
  376. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_hook_guard_token_match.py +0 -0
  377. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_hook_out_of_project_paths.py +0 -0
  378. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_hook_strict.py +0 -0
  379. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_hooks.py +0 -0
  380. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_hyperedge_member_shapes.py +0 -0
  381. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_hyperedge_roundtrip.py +0 -0
  382. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_hypergraph.py +0 -0
  383. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_id_normalization_contract.py +0 -0
  384. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ignore_file_encoding.py +0 -0
  385. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_image_vision.py +0 -0
  386. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_import_extension_resolution.py +0 -0
  387. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_import_self_loops.py +0 -0
  388. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_imported_export_forwarding.py +0 -0
  389. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_incomplete_build_guard.py +0 -0
  390. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_incremental.py +0 -0
  391. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_incremental_mtime_collision.py +0 -0
  392. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_indirect_call_arrow_single_param_shadow.py +0 -0
  393. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_indirect_call_block_scoped_shadow.py +0 -0
  394. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_indirect_call_catch_binding_shadow.py +0 -0
  395. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_indirect_call_external_import_shadow.py +0 -0
  396. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_indirect_call_for_of_binding_shadow.py +0 -0
  397. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_indirect_call_function_expression_shadow.py +0 -0
  398. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_indirect_call_nested_closure_shadow.py +0 -0
  399. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_indirect_dispatch.py +0 -0
  400. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_indirect_dispatch_assign_return.py +0 -0
  401. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_indirect_dispatch_getattr.py +0 -0
  402. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_inferred_confidence_rubric.py +0 -0
  403. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ingest.py +0 -0
  404. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ingest_url_type.py +0 -0
  405. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_inherited_field_receivers.py +0 -0
  406. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_injection_sentinel_coverage.py +0 -0
  407. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_install.py +0 -0
  408. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_install_references.py +0 -0
  409. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_install_roundtrip.py +0 -0
  410. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_install_strings.py +0 -0
  411. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_install_upgrade.py +0 -0
  412. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_install_version_stamp.py +0 -0
  413. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_install_version_warning.py +0 -0
  414. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_issue_3405_python_resolution.py +0 -0
  415. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_issue_3472_source_file_collision.py +0 -0
  416. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_java_enum_members.py +0 -0
  417. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_java_member_calls.py +0 -0
  418. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_java_type_resolution.py +0 -0
  419. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_js_callback_calls.py +0 -0
  420. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_js_destructured_export.py +0 -0
  421. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_js_dynamic_import_affected.py +0 -0
  422. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_js_dynamic_imports.py +0 -0
  423. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_js_exported_fn_call_resolution.py +0 -0
  424. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_js_exported_scalar_bindings.py +0 -0
  425. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_js_module_level_anonymous_calls.py +0 -0
  426. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_js_this_member_expressions.py +0 -0
  427. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_jsconfig_baseurl.py +0 -0
  428. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_json_schema_config.py +0 -0
  429. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_jsx_component_usage_calls.py +0 -0
  430. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_kotlin_grammar.py +0 -0
  431. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_kotlin_object_literal.py +0 -0
  432. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_label_retry.py +0 -0
  433. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_labeling.py +0 -0
  434. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_language_resolvers.py +0 -0
  435. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_llm_backends.py +0 -0
  436. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_llm_parser.py +0 -0
  437. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_llm_parser_reasoning.py +0 -0
  438. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_long_path_hashing.py +0 -0
  439. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_loose_sibling_import_resolution.py +0 -0
  440. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_lua_import.py +0 -0
  441. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_lua_method_calls.py +0 -0
  442. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_manifest_ingest.py +0 -0
  443. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_manifest_tomli_required.py +0 -0
  444. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_markdown_code_spans.py +0 -0
  445. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_mcp_ingest.py +0 -0
  446. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_merge_chunks_validation.py +0 -0
  447. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_merge_graphs_cli.py +0 -0
  448. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_minhash.py +0 -0
  449. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_multigraph_compat.py +0 -0
  450. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_multigraph_diagnostics.py +0 -0
  451. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_multilang.py +0 -0
  452. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_no_dedup_flag.py +0 -0
  453. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_node_id_canonical.py +0 -0
  454. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_non_regular_files.py +0 -0
  455. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_non_string_node_ids.py +0 -0
  456. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_objc_category_interfaces.py +0 -0
  457. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_objc_enum_declarations.py +0 -0
  458. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_objc_field_table_remap.py +0 -0
  459. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_objc_member_calls.py +0 -0
  460. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_objc_property_ivar_receivers.py +0 -0
  461. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_obsidian_dangling_member.py +0 -0
  462. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_obsidian_filename_cap.py +0 -0
  463. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_obsidian_unicode_tags.py +0 -0
  464. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_obsidian_vault_migration.py +0 -0
  465. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ocaml.py +0 -0
  466. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_office_incremental.py +0 -0
  467. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_office_limits.py +0 -0
  468. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ollama.py +0 -0
  469. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ollama_retry_cap.py +0 -0
  470. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_openai_custom_endpoint.py +0 -0
  471. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_out_dir_evidence.py +0 -0
  472. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_oversized_document_slicing.py +0 -0
  473. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_owned_type_sources.py +0 -0
  474. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_package_json_subpath_imports.py +0 -0
  475. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_partial_cache.py +0 -0
  476. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_partial_extraction_warning.py +0 -0
  477. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_pascal.py +0 -0
  478. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_pascal_call_scoping.py +0 -0
  479. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_pascal_resolution.py +0 -0
  480. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_path_cli.py +0 -0
  481. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_paths.py +0 -0
  482. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_pdf_extra_warning.py +0 -0
  483. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_pdf_slicing.py +0 -0
  484. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_pdf_token_estimate.py +0 -0
  485. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_pg_introspect.py +0 -0
  486. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_phantom_cross_package_call.py +0 -0
  487. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_phantom_external_import.py +0 -0
  488. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_php_language_construct_calls.py +0 -0
  489. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_php_object_creation.py +0 -0
  490. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_php_type_resolution.py +0 -0
  491. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_pin_hash_seed.py +0 -0
  492. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_pipeline.py +0 -0
  493. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_provider_registry.py +0 -0
  494. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_prs.py +0 -0
  495. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_prune_sweeps_orphans.py +0 -0
  496. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_python_decorators.py +0 -0
  497. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_python_parse_memoization.py +0 -0
  498. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_python_underscore_resolution.py +0 -0
  499. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_query_cli.py +0 -0
  500. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_query_induced_edges.py +0 -0
  501. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_query_mcp_direction.py +0 -0
  502. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_query_names_its_graph.py +0 -0
  503. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_querylog.py +0 -0
  504. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_rationale.py +0 -0
  505. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_read_hook.py +0 -0
  506. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_reflect.py +0 -0
  507. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_relation_collapse_precedence.py +0 -0
  508. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_replace_or_append_section.py +0 -0
  509. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_report.py +0 -0
  510. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_report_gap_thresholds.py +0 -0
  511. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_resolve_memoization.py +0 -0
  512. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ruby_bare_self_send_calls.py +0 -0
  513. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ruby_resolution.py +0 -0
  514. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_rust_anonymous_const.py +0 -0
  515. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_rust_builtin_type_hubs.py +0 -0
  516. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_rust_macro_rules.py +0 -0
  517. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_rust_self_member_calls.py +0 -0
  518. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_scala_abstract_methods.py +0 -0
  519. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_scala_context_bounds.py +0 -0
  520. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_scala_self_type.py +0 -0
  521. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_scala_top_level_binding.py +0 -0
  522. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_scala_type_definition.py +0 -0
  523. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_scip_ingest.py +0 -0
  524. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_search_hook.py +0 -0
  525. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_security.py +0 -0
  526. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_semantic_cache_basename_recovery.py +0 -0
  527. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_semantic_cache_out_root.py +0 -0
  528. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_semantic_cleanup.py +0 -0
  529. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_semantic_fragment_sanitize.py +0 -0
  530. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_semantic_id_remap_root.py +0 -0
  531. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_semantic_similarity.py +0 -0
  532. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_serve.py +0 -0
  533. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_serve_http.py +0 -0
  534. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_settings_merge.py +0 -0
  535. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_skill_auto_refresh.py +0 -0
  536. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_skill_semantic_all_cached.py +0 -0
  537. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_skill_version_warning.py +0 -0
  538. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_skillgen.py +0 -0
  539. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_skillgen_input_path_injection.py +0 -0
  540. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_solidity_extractor.py +0 -0
  541. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_source_key_memoization.py +0 -0
  542. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_sourceless_call_targets.py +0 -0
  543. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_src_layout_import_resolution.py +0 -0
  544. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_stale_prune.py +0 -0
  545. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_stat_index_husk.py +0 -0
  546. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_stat_index_multi_root.py +0 -0
  547. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_stat_index_portability.py +0 -0
  548. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_svelte_extraction.py +0 -0
  549. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_swift_builtin_noise.py +0 -0
  550. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_swift_computed_properties.py +0 -0
  551. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_swift_cross_file_calls.py +0 -0
  552. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_swift_import_resolution.py +0 -0
  553. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_swift_protocol_dispatch.py +0 -0
  554. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_swift_protocol_requirements.py +0 -0
  555. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_symbol_resolution.py +0 -0
  556. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_terraform.py +0 -0
  557. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_terraform_modules.py +0 -0
  558. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_trailing_newline_not_a_syntax_error.py +0 -0
  559. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_transcribe.py +0 -0
  560. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_tree_html.py +0 -0
  561. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ts_abstract_methods.py +0 -0
  562. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ts_decorators.py +0 -0
  563. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ts_generators.py +0 -0
  564. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ts_import_require.py +0 -0
  565. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ts_import_type_arguments.py +0 -0
  566. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ts_inheritance.py +0 -0
  567. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ts_namespace.py +0 -0
  568. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ts_new_expression_calls.py +0 -0
  569. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ts_parse_warning.py +0 -0
  570. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_ts_receiver_member_calls.py +0 -0
  571. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_type_only_import_cycles.py +0 -0
  572. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_typescript_enum_members.py +0 -0
  573. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_typescript_module_extensions.py +0 -0
  574. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_uninstall_scope.py +0 -0
  575. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_uninstall_symlink.py +0 -0
  576. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_unmapped_at_alias_resolution.py +0 -0
  577. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_unverified_semantic_shrink.py +0 -0
  578. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_validate.py +0 -0
  579. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_vbnet_extractor.py +0 -0
  580. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_vue_extraction.py +0 -0
  581. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_walk_python_tree_iterative.py +0 -0
  582. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_watch.py +0 -0
  583. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_watch_manifest_location.py +0 -0
  584. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_wheel_packaging.py +0 -0
  585. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_wiki.py +0 -0
  586. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_wiki_link_filename_parity.py +0 -0
  587. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_wiki_truncation_indicator.py +0 -0
  588. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_word_count_cache.py +0 -0
  589. {graphifyy-0.9.77 → graphifyy-0.9.78}/tests/test_zero_node_no_cache.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graphifyy
3
- Version: 0.9.77
3
+ Version: 0.9.78
4
4
  Summary: AI coding assistant skill (Claude Code, CodeBuddy, Codex, OpenCode, Kilo Code, Cursor, Gemini CLI, Aider, OpenClaw, Factory Droid, Trae, Hermes, Kiro, Pi, Devin CLI, Google Antigravity) - turn any folder of code, docs, papers, images, or videos into a queryable knowledge graph
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Homepage, https://graphify.com
@@ -219,6 +219,10 @@ graphify-out/
219
219
  └── graph.json the full graph — query it anytime without re-reading your files
220
220
  ```
221
221
 
222
+ The persisted graph includes `graph.schema_version` so integrations can detect
223
+ incompatible format changes, plus `graph.graphify_version` identifying the
224
+ Graphify release that produced it.
225
+
222
226
  **Works in** Claude Code, Cursor, Codex, Gemini CLI, GitHub Copilot, and 15+ more — [pick your platform](#install).
223
227
 
224
228
  ---
@@ -68,6 +68,10 @@ graphify-out/
68
68
  └── graph.json the full graph — query it anytime without re-reading your files
69
69
  ```
70
70
 
71
+ The persisted graph includes `graph.schema_version` so integrations can detect
72
+ incompatible format changes, plus `graph.graphify_version` identifying the
73
+ Graphify release that produced it.
74
+
71
75
  **Works in** Claude Code, Cursor, Codex, Gemini CLI, GitHub Copilot, and 15+ more — [pick your platform](#install).
72
76
 
73
77
  ---
@@ -630,24 +630,39 @@ def graph_diff(G_old: nx.Graph, G_new: nx.Graph) -> dict:
630
630
  return (u, v, data.get("relation", ""))
631
631
  return (min(u, v), max(u, v), data.get("relation", ""))
632
632
 
633
- old_edge_keys = {
634
- edge_key(G_old, u, v, d)
633
+ def edge_dir(data: dict) -> tuple | None:
634
+ # Stored direction of an undirected edge (build_from_json, load_node_link_graph).
635
+ if "_src" in data and "_tgt" in data:
636
+ return (data["_src"], data["_tgt"])
637
+ return None
638
+
639
+ old_edge_dirs = {
640
+ edge_key(G_old, u, v, d): edge_dir(d)
635
641
  for u, v, d in G_old.edges(data=True)
636
642
  }
637
- new_edge_keys = {
638
- edge_key(G_new, u, v, d)
643
+ new_edge_dirs = {
644
+ edge_key(G_new, u, v, d): edge_dir(d)
639
645
  for u, v, d in G_new.edges(data=True)
640
646
  }
641
-
642
- added_edge_keys = new_edge_keys - old_edge_keys
643
- removed_edge_keys = old_edge_keys - new_edge_keys
647
+ old_edge_keys = set(old_edge_dirs)
648
+ new_edge_keys = set(new_edge_dirs)
649
+
650
+ # A reversed edge (a->b became b->a) keeps its undirected key. Report it as
651
+ # removed + added only when both snapshots carry _src/_tgt and they differ,
652
+ # so graphs without the markers diff exactly as before (#4067).
653
+ reversed_edge_keys = {
654
+ k for k in old_edge_keys & new_edge_keys
655
+ if old_edge_dirs[k] and new_edge_dirs[k] and old_edge_dirs[k] != new_edge_dirs[k]
656
+ }
657
+ added_edge_keys = (new_edge_keys - old_edge_keys) | reversed_edge_keys
658
+ removed_edge_keys = (old_edge_keys - new_edge_keys) | reversed_edge_keys
644
659
 
645
660
  new_edges_list = []
646
661
  for u, v, d in G_new.edges(data=True):
647
662
  if edge_key(G_new, u, v, d) in added_edge_keys:
648
663
  new_edges_list.append({
649
- "source": u,
650
- "target": v,
664
+ "source": d.get("_src", u),
665
+ "target": d.get("_tgt", v),
651
666
  "relation": d.get("relation", ""),
652
667
  "confidence": d.get("confidence", ""),
653
668
  })
@@ -656,8 +671,8 @@ def graph_diff(G_old: nx.Graph, G_new: nx.Graph) -> dict:
656
671
  for u, v, d in G_old.edges(data=True):
657
672
  if edge_key(G_old, u, v, d) in removed_edge_keys:
658
673
  removed_edges_list.append({
659
- "source": u,
660
- "target": v,
674
+ "source": d.get("_src", u),
675
+ "target": d.get("_tgt", v),
661
676
  "relation": d.get("relation", ""),
662
677
  "confidence": d.get("confidence", ""),
663
678
  })
@@ -172,6 +172,20 @@ from graphify.exporters.base import COMMUNITY_COLORS # noqa: E402,F401
172
172
  from graphify.exporters.html import to_html # noqa: E402,F401
173
173
 
174
174
 
175
+ # Increment when the persisted graph structure changes incompatibly for consumers.
176
+ GRAPH_SCHEMA_VERSION = 1
177
+
178
+
179
+ def _graphify_version() -> str | None:
180
+ """Return the installed graphify version for graph provenance."""
181
+ try:
182
+ from importlib.metadata import version
183
+
184
+ return version("graphifyy")
185
+ except Exception:
186
+ return None
187
+
188
+
175
189
  # Fallback scores for an edge that carries a confidence tier but no
176
190
  # confidence_score. The INFERRED default was 0.5, which references/extraction-spec.md
177
191
  # rules out in as many words — "never omit it, never use 0.5 as a default" — and
@@ -433,6 +447,11 @@ def to_json(G: nx.Graph, communities: dict[int, list[str]], output_path: str, *,
433
447
  if isinstance(data.get("graph"), dict) and "hyperedges" in data["graph"]:
434
448
  data["graph"]["hyperedges"] = hyperedges
435
449
  data["hyperedges"] = hyperedges
450
+ graph_metadata = data.setdefault("graph", {})
451
+ graph_metadata["schema_version"] = GRAPH_SCHEMA_VERSION
452
+ graphify_version = _graphify_version()
453
+ if graphify_version is not None:
454
+ graph_metadata["graphify_version"] = graphify_version
436
455
  # Fallback provenance comes from the repo the graph is being written INTO
437
456
  # (output_path lives in <target>/graphify-out/), never the shell's cwd —
438
457
  # the same cwd-anchoring mistake #2316 fixed for `update`.
@@ -33,6 +33,8 @@ from graphify.extractors.base import ( # noqa: F401
33
33
  _LANGUAGE_BUILTIN_GLOBALS,
34
34
  _file_stem,
35
35
  _make_id,
36
+ _read_source_bytes,
37
+ _read_source_text,
36
38
  _read_text,
37
39
  )
38
40
  from graphify.extractors.apex import extract_apex # noqa: F401
@@ -674,6 +676,23 @@ def _python_import_bindings(node, source: bytes) -> list[tuple[str, str]]:
674
676
  return bindings
675
677
 
676
678
 
679
+ def _missing_relative_module_name(attempted: Path, root: Path, raw: str) -> str:
680
+ """Dotted module name, relative to ``root``, of an unresolved relative import.
681
+
682
+ ``attempted`` is the file the import would have named (``pkg/missing.py`` or
683
+ ``pkg/__init__.py``). Falls back to ``ref:`` + the raw specifier when the
684
+ import climbs above the scan root, so the id never carries an absolute path.
685
+ """
686
+ try:
687
+ rel = attempted.relative_to(root)
688
+ except ValueError:
689
+ return f"ref:{raw}"
690
+ parts = list(rel.with_suffix("").parts)
691
+ if parts and parts[-1] == "__init__":
692
+ parts.pop()
693
+ return ".".join(parts) if parts else f"ref:{raw}"
694
+
695
+
677
696
  def _import_python(
678
697
  node, source: bytes, file_nid: str, stem: str, edges: list, str_path: str,
679
698
  scope_stack: list[str] | None = None, scan_root: Path | None = None,
@@ -749,13 +768,24 @@ def _import_python(
749
768
  target_path = _resolve_python_module_path(
750
769
  module_name, current_path, root, level=dots
751
770
  )
771
+ missing = False
752
772
  if target_path is None:
753
773
  base = current_path.parent
754
774
  for _ in range(dots - 1):
755
775
  base = base.parent
756
776
  rel = (module_name.replace(".", "/") + ".py") if module_name else "__init__.py"
757
777
  target_path = base / rel
758
- tgt_nid = _make_id(str(target_path))
778
+ missing = not target_path.is_file()
779
+ if missing:
780
+ # No file behind the import: minting the id from the
781
+ # attempted absolute path bakes the checkout location (and
782
+ # OS username) into the graph, and the root-relative id
783
+ # remap never rewrites it. Use the dotted module name
784
+ # relative to the scan root, the id an unresolved absolute
785
+ # import of the same module already gets.
786
+ tgt_nid = _make_id(_missing_relative_module_name(target_path, root, raw))
787
+ else:
788
+ tgt_nid = _make_id(str(target_path))
759
789
  else:
760
790
  # Use the shared scan-root-aware resolver for absolute imports.
761
791
  # It stops at the corpus boundary and handles package roots the
@@ -1672,7 +1702,7 @@ def _extract_python_rationale(path: Path, result: dict) -> None:
1672
1702
  from tree_sitter import Language, Parser
1673
1703
  language = Language(tspython.language())
1674
1704
  parser = Parser(language)
1675
- source = path.read_bytes()
1705
+ source = _read_source_bytes(path, warn=False)
1676
1706
  tree = parser.parse(source)
1677
1707
  root = tree.root_node
1678
1708
  except Exception:
@@ -2053,7 +2083,7 @@ def extract_js(path: Path) -> dict:
2053
2083
  source_override = None
2054
2084
  if is_ts:
2055
2085
  try:
2056
- source = path.read_bytes()
2086
+ source = _read_source_bytes(path)
2057
2087
  source_override = _normalize_ts_import_types(source, tsx=suffix == ".tsx")
2058
2088
  except OSError:
2059
2089
  pass
@@ -2091,7 +2121,7 @@ def _rescue_js_dynamic_imports(path: Path, result: dict) -> None:
2091
2121
  """
2092
2122
  try:
2093
2123
  import re as _re
2094
- src = path.read_text(encoding="utf-8", errors="replace")
2124
+ src = _read_source_text(path, warn=False)
2095
2125
  if not _re.search(r"(?<!\w)import\s*\(", src): # cheap bail — most files have none
2096
2126
  return
2097
2127
  existing_ids = {n["id"] for n in result.get("nodes", [])}
@@ -2207,7 +2237,7 @@ def _extract_js_rationale(path: Path, result: dict) -> None:
2207
2237
  Mutates result in-place by appending to result['nodes'] and result['edges'].
2208
2238
  """
2209
2239
  try:
2210
- source_text = path.read_text(encoding="utf-8", errors="replace")
2240
+ source_text = _read_source_text(path, warn=False)
2211
2241
  except Exception:
2212
2242
  return
2213
2243
 
@@ -2446,7 +2476,7 @@ def extract_svelte(path: Path) -> dict:
2446
2476
  dynamic imports.
2447
2477
  """
2448
2478
  try:
2449
- src = path.read_text(encoding="utf-8", errors="replace")
2479
+ src = _read_source_text(path)
2450
2480
  except OSError as e:
2451
2481
  return {"nodes": [], "edges": [], "error": str(e)}
2452
2482
 
@@ -2555,7 +2585,7 @@ def extract_astro(path: Path) -> dict:
2555
2585
  file flagged every template as a syntax error and dropped frontmatter symbols.
2556
2586
  """
2557
2587
  try:
2558
- src = path.read_text(encoding="utf-8", errors="replace")
2588
+ src = _read_source_text(path)
2559
2589
  except OSError:
2560
2590
  return {"nodes": [], "edges": []}
2561
2591
  masked = _astro_mask_non_script(src).encode("utf-8")
@@ -2563,7 +2593,7 @@ def extract_astro(path: Path) -> dict:
2563
2593
  result = _extract_generic(path, _TS_CONFIG, source_override=masked)
2564
2594
  try:
2565
2595
  import re as _re
2566
- src = path.read_text(encoding="utf-8", errors="replace")
2596
+ src = _read_source_text(path, warn=False)
2567
2597
  existing_ids = {n["id"] for n in result.get("nodes", [])}
2568
2598
  file_node_id = _make_id(str(path))
2569
2599
  aliases = _load_tsconfig_aliases(path.parent)
@@ -2624,7 +2654,7 @@ def extract_vue(path: Path) -> dict:
2624
2654
  ``import('…')`` dynamic imports the AST does not edge.
2625
2655
  """
2626
2656
  try:
2627
- src = path.read_text(encoding="utf-8", errors="replace")
2657
+ src = _read_source_text(path)
2628
2658
  except OSError:
2629
2659
  return {"nodes": [], "edges": []}
2630
2660
 
@@ -2674,7 +2704,7 @@ def _is_spock_file(path: Path, ts_result: dict) -> bool:
2674
2704
  import re as _re
2675
2705
  _SPOCK_FEATURE_RE = _re.compile(r"""^\s*def\s+[\"']""", _re.MULTILINE)
2676
2706
  try:
2677
- return bool(_SPOCK_FEATURE_RE.search(path.read_text(errors="replace")))
2707
+ return bool(_SPOCK_FEATURE_RE.search(_read_source_text(path, warn=False)))
2678
2708
  except OSError:
2679
2709
  return False
2680
2710
 
@@ -2685,7 +2715,7 @@ def _extract_spock_fallback(path: Path, ts_result: dict) -> dict:
2685
2715
  (which survive reliably) with class and feature-method nodes extracted via regex.
2686
2716
  """
2687
2717
  import re as _re
2688
- source = path.read_text(errors="replace")
2718
+ source = _read_source_text(path, warn=False)
2689
2719
  str_path = str(path)
2690
2720
  stem = _file_stem(path)
2691
2721
 
@@ -2825,7 +2855,7 @@ def _augment_cpp_string_tests(path: Path, result: dict) -> dict:
2825
2855
  commented Spock ``def "feature"()`` would).
2826
2856
  """
2827
2857
  try:
2828
- source = path.read_text(errors="replace")
2858
+ source = _read_source_text(path, warn=False)
2829
2859
  except OSError:
2830
2860
  return result
2831
2861
  matches = list(_CPP_STRING_TEST_RE.finditer(source))
@@ -2962,6 +2992,60 @@ def _normalize_cpp_cli(source: bytes) -> bytes | None:
2962
2992
  return _CPP_CLI_ATTR_RE.sub(_blank_keeping_newlines, out)
2963
2993
 
2964
2994
 
2995
+ _CPP_EXPORT_MACRO_RE = re.compile(
2996
+ # Match `class` or `struct`
2997
+ rb"(\b(?:class|struct)[ \t\r\n]+)"
2998
+ # Match one or more ALL-CAPS macros (stacked or single)
2999
+ rb"((?:[A-Z][A-Z0-9_]*[ \t\r\n]+)+)"
3000
+ # Negative lookahead: the matched class name cannot be exactly "final"
3001
+ rb"(?!final[ \t\r\n]*[:{])"
3002
+ # Match the actual class name (and optional final), ending with `:` or `{`
3003
+ rb"([A-Za-z_][A-Za-z0-9_]*(?:[ \t\r\n]+final)?[ \t\r\n]*)([:{])"
3004
+ )
3005
+
3006
+
3007
+ def _normalize_cpp_export_macros(source: bytes) -> bytes:
3008
+ """Blank an export macro between ``class``/``struct`` and the type name.
3009
+
3010
+ Single ALL-CAPS identifier macros (and stacked ones) are handled.
3011
+ Note: paren-attribute macros like ``__declspec(dllexport)`` and
3012
+ ``__attribute__((visibility("default")))`` are currently unsupported,
3013
+ as well as template specializations (e.g. ``template<> class MODULE_API Widget<int>``).
3014
+ """
3015
+
3016
+ def repl(m: re.Match[bytes]) -> bytes:
3017
+ start = m.start()
3018
+
3019
+ # Check if preceded by `(` (e.g. range-based for loop `for(class MACRO var: items)`)
3020
+ idx = start - 1
3021
+ while idx >= 0 and source[idx] in b" \t\r\n":
3022
+ idx -= 1
3023
+ if idx >= 0 and source[idx] == ord(b"("):
3024
+ return m.group(0)
3025
+
3026
+ # Check for brace initialization like `var{1}`
3027
+ if m.group(4) == b"{":
3028
+ end = m.end()
3029
+ idx_after = end
3030
+ while idx_after < len(source) and source[idx_after] in b" \t\r\n":
3031
+ idx_after += 1
3032
+ if idx_after < len(source):
3033
+ next_char = source[idx_after : idx_after + 1]
3034
+ if next_char in b"0123456789\"'-":
3035
+ return m.group(0)
3036
+
3037
+ # Blank the macro with spaces to preserve offsets, but KEEP NEWLINES!
3038
+ macro_text = m.group(2)
3039
+ blanked_macro = bytearray(macro_text)
3040
+ for i in range(len(blanked_macro)):
3041
+ if blanked_macro[i] not in b"\r\n":
3042
+ blanked_macro[i] = ord(b" ")
3043
+
3044
+ return m.group(1) + bytes(blanked_macro) + m.group(3) + m.group(4)
3045
+
3046
+ return _CPP_EXPORT_MACRO_RE.sub(repl, source)
3047
+
3048
+
2965
3049
  def extract_cpp(path: Path) -> dict:
2966
3050
  """Extract functions, classes, and includes from a .cpp/.cc/.cxx/.hpp file.
2967
3051
 
@@ -2972,12 +3056,14 @@ def extract_cpp(path: Path) -> dict:
2972
3056
  drops as ERROR nodes (issue #2594), mirroring the Spock fallback for Groovy.
2973
3057
  """
2974
3058
  try:
2975
- source = path.read_bytes()
3059
+ source = _read_source_bytes(path)
2976
3060
  except OSError:
2977
3061
  # Let _extract_generic report the read failure in its usual shape.
2978
3062
  return _augment_cpp_string_tests(path, _extract_generic(path, _CPP_CONFIG))
3063
+ source_override = _normalize_cpp_cli(source) or source
3064
+ source_override = _normalize_cpp_export_macros(source_override)
2979
3065
  result = _extract_generic(
2980
- path, _CPP_CONFIG, source_override=_normalize_cpp_cli(source) or source
3066
+ path, _CPP_CONFIG, source_override=source_override
2981
3067
  )
2982
3068
  return _augment_cpp_string_tests(path, result)
2983
3069
 
@@ -3055,7 +3141,7 @@ def extract_php(path: Path) -> dict:
3055
3141
  """
3056
3142
  result = _extract_generic(path, _PHP_CONFIG)
3057
3143
  try:
3058
- src = path.read_text(encoding="utf-8", errors="replace")
3144
+ src = _read_source_text(path, warn=False)
3059
3145
  masked, had_script = _php_mask_to_script_blocks(src)
3060
3146
  if not had_script:
3061
3147
  return result
@@ -4679,13 +4765,14 @@ def _resolve_csharp_member_calls(
4679
4765
  type_nid = bases[0]
4680
4766
  type_qualified = True
4681
4767
  elif receiver[:1].isupper():
4682
- # Type.M() — the type is named explicitly (also covers a Pascal-cased
4683
- # local whose name equals its type, resolved via the table below if the
4684
- # explicit-type lookup misses).
4685
- type_nid = _resolve_type_name_nid(receiver, caller_node, src_file)
4768
+ # Type.M() — the type is named explicitly. A field/property/local of
4769
+ # that name in scope shadows the type (`IStore Store => ...;
4770
+ # Store.Save()`, #3797), so the receiver's declared type from the
4771
+ # table wins when known; otherwise fall back to the type name.
4772
+ type_name = rc.get("receiver_type")
4773
+ type_nid = _resolve_type_name_nid(type_name, caller_node, src_file)
4686
4774
  if not type_nid:
4687
- type_name = rc.get("receiver_type")
4688
- type_nid = _resolve_type_name_nid(type_name, caller_node, src_file)
4775
+ type_nid = _resolve_type_name_nid(receiver, caller_node, src_file)
4689
4776
  if not type_nid:
4690
4777
  _park_if_absent(type_name or receiver, caller_node, rc)
4691
4778
  continue
@@ -6337,7 +6424,7 @@ def _xaml_codebehind_symbols(
6337
6424
  # parameter list on method nodes, so we read it from the code-behind source
6338
6425
  # at the method's recorded line.
6339
6426
  try:
6340
- cb_lines = codebehind.read_text(encoding="utf-8", errors="replace").splitlines()
6427
+ cb_lines = _read_source_text(codebehind, warn=False).splitlines()
6341
6428
  except OSError:
6342
6429
  cb_lines = []
6343
6430
 
@@ -6523,7 +6610,7 @@ def _xaml_communitytoolkit_members(vm_node: dict) -> tuple[dict[str, dict], list
6523
6610
  try:
6524
6611
  # errors="replace" so a non-UTF8 code-behind can't raise UnicodeDecodeError
6525
6612
  # and abort the whole extract_xaml (matches every other reader here).
6526
- lines = Path(source_file).read_text(encoding="utf-8", errors="replace").splitlines()
6613
+ lines = _read_source_text(Path(source_file), warn=False).splitlines()
6527
6614
  except OSError:
6528
6615
  return {}, []
6529
6616
 
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
 
4
4
 
5
5
  from pathlib import Path
6
- from graphify.extractors.base import _file_stem, _make_id
6
+ from graphify.extractors.base import _file_stem, _make_id, _read_source_text
7
7
 
8
8
 
9
9
  def extract_apex(path: Path) -> dict:
@@ -11,7 +11,7 @@ def extract_apex(path: Path) -> dict:
11
11
  Apex .cls and .trigger files using regex (no tree-sitter grammar on PyPI)."""
12
12
  import re as _re
13
13
  try:
14
- source = path.read_text(encoding="utf-8", errors="replace")
14
+ source = _read_source_text(path)
15
15
  except OSError:
16
16
  return {"nodes": [], "edges": []}
17
17
 
@@ -1,6 +1,7 @@
1
1
  # DO NOT import from graphify.extract here — direction is extract.py → extractors/ only.
2
2
  from __future__ import annotations
3
3
 
4
+ import sys
4
5
  from pathlib import Path
5
6
 
6
7
  from graphify.ids import make_id
@@ -83,3 +84,70 @@ def _file_stem(path: Path) -> str:
83
84
 
84
85
  def _read_text(node, source: bytes) -> str:
85
86
  return source[node.start_byte:node.end_byte].decode("utf-8", errors="replace")
87
+
88
+
89
+ _UTF32_BOMS = (b"\xff\xfe\x00\x00", b"\x00\x00\xfe\xff")
90
+ _UTF16_BOMS = (b"\xff\xfe", b"\xfe\xff")
91
+ # Fixed, not the host locale: extraction must give the same graph on every
92
+ # machine (CONTRIBUTING: deterministic extraction).
93
+ _SOURCE_FALLBACK_ENCODINGS = ("cp1252", "latin-1")
94
+ _warned_source_encodings: set[str] = set()
95
+
96
+
97
+ def _read_source_bytes(path: Path, *, warn: bool = True) -> bytes:
98
+ """Read a source file as the UTF-8 bytes tree-sitter parses.
99
+
100
+ tree-sitter treats its input as UTF-8. Raw bytes in another encoding were
101
+ parsed as-is: a UTF-16 file (interleaved NUL bytes) produced no nodes at
102
+ all, and a Windows-1252 identifier was cut at its first non-UTF-8 byte
103
+ (``CaféOrder`` came out as ``Order``) — both silently.
104
+
105
+ Valid UTF-8, with or without a BOM, is returned byte-for-byte, so node ids,
106
+ byte offsets and cache keys for those files are unchanged. A UTF-16 or
107
+ UTF-32 file with a BOM is decoded exactly and re-encoded as UTF-8. Anything
108
+ else is decoded as cp1252, then latin-1 (which cannot fail), so names come
109
+ out whole; line numbers are unchanged in every case. ``warn`` names the file
110
+ once per process on that last, guessed path — pass ``warn=False`` from
111
+ secondary passes that re-read a file the extractor already read.
112
+ """
113
+ raw = path.read_bytes()
114
+ if raw.isascii():
115
+ return raw
116
+ try:
117
+ raw.decode("utf-8")
118
+ return raw
119
+ except UnicodeDecodeError:
120
+ pass
121
+ for boms, encoding in ((_UTF32_BOMS, "utf-32"), (_UTF16_BOMS, "utf-16")):
122
+ if raw.startswith(boms):
123
+ try:
124
+ return raw.decode(encoding).encode("utf-8")
125
+ except UnicodeDecodeError:
126
+ continue # FF FE 00 00 is also UTF-16-LE: BOM + U+0000
127
+ for encoding in _SOURCE_FALLBACK_ENCODINGS:
128
+ try:
129
+ text = raw.decode(encoding)
130
+ except UnicodeDecodeError:
131
+ continue
132
+ key = str(path)
133
+ if warn and key not in _warned_source_encodings:
134
+ _warned_source_encodings.add(key)
135
+ print(
136
+ f"[graphify] WARNING: {path} is not valid UTF-8; read it as {encoding}. "
137
+ "Re-save it as UTF-8 if names in the graph look wrong.",
138
+ file=sys.stderr,
139
+ )
140
+ return text.encode("utf-8")
141
+ return raw # unreachable: latin-1 decodes every byte
142
+
143
+
144
+ def _read_source_text(path: Path, *, warn: bool = True) -> str:
145
+ """``_read_source_bytes`` as text, for extractors that work on ``str``.
146
+
147
+ Matches ``path.read_text(encoding="utf-8")`` exactly for every valid UTF-8
148
+ file, including its universal-newline translation (``\\r\\n`` and a lone
149
+ ``\\r`` both become ``\\n``) and a kept BOM, so regex extractors see the same
150
+ text they always did.
151
+ """
152
+ text = _read_source_bytes(path, warn=warn).decode("utf-8")
153
+ return text.replace("\r\n", "\n").replace("\r", "\n")
@@ -6,7 +6,7 @@ import re
6
6
  from pathlib import Path
7
7
  from typing import Any
8
8
 
9
- from graphify.extractors.base import _file_stem, _make_id, _read_text
9
+ from graphify.extractors.base import _file_stem, _make_id, _read_source_bytes, _read_text
10
10
 
11
11
 
12
12
  # Leading `${VAR}` / `$VAR` expansion segment(s) of a `source` path argument. The
@@ -113,7 +113,7 @@ def extract_bash(path: Path) -> dict:
113
113
  try:
114
114
  language = Language(tsbash.language())
115
115
  parser = Parser(language)
116
- source = path.read_bytes()
116
+ source = _read_source_bytes(path)
117
117
  tree = parser.parse(source)
118
118
  root = tree.root_node
119
119
  except Exception as e:
@@ -3,14 +3,14 @@ from __future__ import annotations
3
3
 
4
4
  from pathlib import Path
5
5
 
6
- from graphify.extractors.base import _make_id
6
+ from graphify.extractors.base import _make_id, _read_source_text
7
7
 
8
8
 
9
9
  def extract_blade(path: Path) -> dict:
10
10
  """Extract @extends, @include, <livewire:> components, and wire:click bindings from Blade templates."""
11
11
  import re
12
12
  try:
13
- src = path.read_text(encoding="utf-8", errors="replace")
13
+ src = _read_source_text(path)
14
14
  except OSError:
15
15
  return {"error": f"cannot read {path}"}
16
16
 
@@ -5,7 +5,7 @@ import re
5
5
  from pathlib import Path
6
6
  from typing import Any
7
7
 
8
- from graphify.extractors.base import _make_id
8
+ from graphify.extractors.base import _make_id, _read_source_text
9
9
 
10
10
 
11
11
  _NAME = r"[A-Z0-9][A-Z0-9-]*"
@@ -98,7 +98,7 @@ def _starts_inside_string(code: str, offset: int) -> bool:
98
98
 
99
99
  def extract_cobol(path: Path) -> dict:
100
100
  try:
101
- source = path.read_text(encoding="utf-8", errors="replace")
101
+ source = _read_source_text(path)
102
102
  except OSError as exc:
103
103
  return {"nodes": [], "edges": [], "error": str(exc)}
104
104
 
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
  import warnings
5
5
  from pathlib import Path
6
6
 
7
- from graphify.extractors.base import _file_stem, _make_id
7
+ from graphify.extractors.base import _file_stem, _make_id, _read_source_bytes
8
8
 
9
9
 
10
10
  # Standard CL definer forms that introduce data/type/variable bindings
@@ -78,7 +78,7 @@ def extract_commonlisp(path: Path) -> dict:
78
78
  )
79
79
  language = Language(tscl.language())
80
80
  parser = Parser(language)
81
- source = path.read_bytes()
81
+ source = _read_source_bytes(path)
82
82
  tree = parser.parse(source)
83
83
  root = tree.root_node
84
84
  except Exception as e:
@@ -4,13 +4,13 @@ from __future__ import annotations
4
4
  import re
5
5
 
6
6
  from pathlib import Path
7
- from graphify.extractors.base import _file_stem, _make_id
7
+ from graphify.extractors.base import _file_stem, _make_id, _read_source_text
8
8
 
9
9
 
10
10
  def extract_dart(path: Path) -> dict:
11
11
  """Extract classes, mixins, functions, imports, generic calls, and annotations from a .dart file using regex."""
12
12
  try:
13
- src = path.read_text(encoding="utf-8", errors="replace")
13
+ src = _read_source_text(path)
14
14
  except OSError:
15
15
  return {"error": f"cannot read {path}"}
16
16
 
@@ -5,7 +5,13 @@ import re
5
5
 
6
6
  from pathlib import Path
7
7
  from typing import Any
8
- from graphify.extractors.base import _file_stem, _make_id, _read_text
8
+ from graphify.extractors.base import (
9
+ _file_stem,
10
+ _make_id,
11
+ _read_source_bytes,
12
+ _read_source_text,
13
+ _read_text,
14
+ )
9
15
 
10
16
 
11
17
  def extract_dm(path: Path) -> dict:
@@ -18,7 +24,7 @@ def extract_dm(path: Path) -> dict:
18
24
  try:
19
25
  language = Language(tsdm.language())
20
26
  parser = Parser(language)
21
- source = path.read_bytes()
27
+ source = _read_source_bytes(path)
22
28
  tree = parser.parse(source)
23
29
  root = tree.root_node
24
30
  except Exception as e:
@@ -366,7 +372,7 @@ def extract_dmm(path: Path) -> dict:
366
372
  try:
367
373
  if path.stat().st_size > 50 * 1024 * 1024:
368
374
  return {"nodes": [], "edges": [], "error": "file too large (>50 MB)"}
369
- text = path.read_text(encoding="utf-8", errors="replace")
375
+ text = _read_source_text(path)
370
376
  except Exception as e:
371
377
  return {"nodes": [], "edges": [], "error": str(e)}
372
378
 
@@ -436,7 +442,7 @@ _DMF_TYPE_RE = re.compile(r'^\s*type\s*=\s*(\S+)\s*$')
436
442
  def extract_dmf(path: Path) -> dict:
437
443
  """Extract windows and controls from a .dmf interface file."""
438
444
  try:
439
- text = path.read_text(encoding="utf-8", errors="replace")
445
+ text = _read_source_text(path)
440
446
  except Exception as e:
441
447
  return {"nodes": [], "edges": [], "error": str(e)}
442
448
 
@@ -449,6 +455,7 @@ def extract_dmf(path: Path) -> dict:
449
455
  seen: set[str] = {file_nid}
450
456
 
451
457
  current_window_nid: str | None = None
458
+ current_window_name: str | None = None
452
459
  current_elem_nid: str | None = None
453
460
  current_elem_name: str | None = None
454
461
 
@@ -465,13 +472,19 @@ def extract_dmf(path: Path) -> dict:
465
472
  "confidence": "EXTRACTED", "source_file": str_path,
466
473
  "source_location": f"L{line_idx}", "weight": 1.0})
467
474
  current_window_nid = nid
475
+ current_window_name = name
468
476
  current_elem_nid = None
469
477
  current_elem_name = None
470
478
  continue
471
479
  m = _DMF_ELEM_RE.match(line)
472
- if m and current_window_nid is not None:
480
+ if m and current_window_nid is not None and current_window_name is not None:
473
481
  name = m.group(1)
474
- nid = _make_id(stem, "elem", current_window_nid, name)
482
+ # Key on the window NAME, not its node id: the id already embeds
483
+ # `stem`, which is absolute at extraction time. extract()'s
484
+ # id-remap only rewrites the leading stem, so the copy nested in
485
+ # the middle kept the checkout path (and OS username) in every
486
+ # element id.
487
+ nid = _make_id(stem, "elem", current_window_name, name)
475
488
  if nid not in seen:
476
489
  seen.add(nid)
477
490
  nodes.append({"id": nid, "label": f'elem "{name}"', "file_type": "code",