cg-code-graph 0.21.1__tar.gz → 0.22.0__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 (392) hide show
  1. {cg_code_graph-0.21.1/cg_code_graph.egg-info → cg_code_graph-0.22.0}/PKG-INFO +4 -2
  2. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/README.md +3 -1
  3. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/__init__.py +1 -1
  4. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/cli.py +67 -24
  5. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/coverage.py +47 -10
  6. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/external.py +37 -0
  7. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/indexer.py +17 -1
  8. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/link.py +14 -1
  9. cg_code_graph-0.22.0/cg_code_graph/mcp_graphs.py +438 -0
  10. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/mcp_install.py +234 -14
  11. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/mcp_server.py +331 -56
  12. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/platforms.py +32 -3
  13. cg_code_graph-0.22.0/cg_code_graph/plugins/laravel/massassign.py +286 -0
  14. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/laravel/plugin.py +142 -20
  15. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/laravel/values.py +2 -0
  16. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/swift/plugin.py +55 -14
  17. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/query.py +407 -39
  18. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/routes.py +5 -4
  19. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0/cg_code_graph.egg-info}/PKG-INFO +4 -2
  20. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph.egg-info/SOURCES.txt +18 -0
  21. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph.egg-info/entry_points.txt +1 -0
  22. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/pyproject.toml +1 -0
  23. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_affected.py +3 -0
  24. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_bridges.py +3 -1
  25. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_coverage.py +4 -4
  26. cg_code_graph-0.22.0/tests/test_db_connections.py +280 -0
  27. cg_code_graph-0.22.0/tests/test_issue_192.py +60 -0
  28. cg_code_graph-0.22.0/tests/test_issue_202.py +79 -0
  29. cg_code_graph-0.22.0/tests/test_issue_203.py +52 -0
  30. cg_code_graph-0.22.0/tests/test_issue_205.py +36 -0
  31. cg_code_graph-0.22.0/tests/test_issue_206.py +32 -0
  32. cg_code_graph-0.22.0/tests/test_issue_207.py +58 -0
  33. cg_code_graph-0.22.0/tests/test_issue_208.py +45 -0
  34. cg_code_graph-0.22.0/tests/test_issue_209.py +42 -0
  35. cg_code_graph-0.22.0/tests/test_issue_210.py +51 -0
  36. cg_code_graph-0.22.0/tests/test_issue_211.py +45 -0
  37. cg_code_graph-0.22.0/tests/test_issue_211_used_at.py +71 -0
  38. cg_code_graph-0.22.0/tests/test_listing_files.py +45 -0
  39. cg_code_graph-0.22.0/tests/test_mass_assignment.py +164 -0
  40. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_mcp_install.py +11 -8
  41. cg_code_graph-0.22.0/tests/test_mcp_multi_graph.py +282 -0
  42. cg_code_graph-0.22.0/tests/test_readers_writers_specs.py +211 -0
  43. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_routes_search_explain.py +3 -2
  44. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/LICENSE +0 -0
  45. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/MANIFEST.in +0 -0
  46. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/__main__.py +0 -0
  47. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/affected.py +0 -0
  48. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/agent_rules.py +0 -0
  49. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/agents.py +0 -0
  50. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/aitools.py +0 -0
  51. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/android_ipc.py +0 -0
  52. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/apple_ipc.py +0 -0
  53. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/apps.py +0 -0
  54. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/blindspots.py +0 -0
  55. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/bridges.py +0 -0
  56. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/brokers.py +0 -0
  57. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/concepts.py +0 -0
  58. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/config.py +0 -0
  59. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/__init__.py +0 -0
  60. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/cache.py +0 -0
  61. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/detect.py +0 -0
  62. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/env.py +0 -0
  63. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/extractors.py +0 -0
  64. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/fsutil.py +0 -0
  65. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/generated.py +0 -0
  66. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/model.py +0 -0
  67. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/paths.py +0 -0
  68. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/plugin.py +0 -0
  69. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/redact.py +0 -0
  70. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/store.py +0 -0
  71. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/core/syntax_errors.py +0 -0
  72. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/dart_isolates.py +0 -0
  73. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/doctor.py +0 -0
  74. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/explore.py +0 -0
  75. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/graphql.py +0 -0
  76. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/guard_facts.py +0 -0
  77. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/hooks.py +0 -0
  78. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/insecure_transport.py +0 -0
  79. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/jobs.py +0 -0
  80. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/lint_async.py +0 -0
  81. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/local_ipc.py +0 -0
  82. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/local_sockets.py +0 -0
  83. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/parity.py +0 -0
  84. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/parity_structure.py +0 -0
  85. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/payload.py +0 -0
  86. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plans.py +0 -0
  87. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/platform_scan.py +0 -0
  88. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/__init__.py +0 -0
  89. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/astro/__init__.py +0 -0
  90. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/astro/content.py +0 -0
  91. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/astro/plugin.py +0 -0
  92. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/cfamily/__init__.py +0 -0
  93. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/cfamily/plugin.py +0 -0
  94. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/cfamily/syntax.py +0 -0
  95. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/dart/__init__.py +0 -0
  96. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/dart/bridges.py +0 -0
  97. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/dart/extractor/bin/extract.dart +0 -0
  98. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/dart/extractor/pubspec.lock +0 -0
  99. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/dart/extractor/pubspec.yaml +0 -0
  100. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/dart/http.py +0 -0
  101. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/dart/models.py +0 -0
  102. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/dart/plugin.py +0 -0
  103. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/dart/program.py +0 -0
  104. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/django/__init__.py +0 -0
  105. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/django/extras.py +0 -0
  106. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/django/models.py +0 -0
  107. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/django/plugin.py +0 -0
  108. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/django/schemas.py +0 -0
  109. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/django/shapes.py +0 -0
  110. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/django/urls.py +0 -0
  111. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/express/__init__.py +0 -0
  112. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/express/plugin.py +0 -0
  113. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/flutter/__init__.py +0 -0
  114. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/flutter/plugin.py +0 -0
  115. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/java/__init__.py +0 -0
  116. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/java/exact.py +0 -0
  117. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/java/plugin.py +0 -0
  118. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/jvm/__init__.py +0 -0
  119. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/jvm/interop.py +0 -0
  120. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/jvm/spring.py +0 -0
  121. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/kotlin/__init__.py +0 -0
  122. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/kotlin/exact.py +0 -0
  123. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/kotlin/plugin.py +0 -0
  124. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/kotlin/reparse.py +0 -0
  125. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/laravel/__init__.py +0 -0
  126. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/laravel/broadcast.py +0 -0
  127. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/laravel/tests.py +0 -0
  128. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/native/__init__.py +0 -0
  129. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/native/gates.py +0 -0
  130. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/native/runner.py +0 -0
  131. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/native/scipread.py +0 -0
  132. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/native/ts.py +0 -0
  133. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/nest/__init__.py +0 -0
  134. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/nest/plugin.py +0 -0
  135. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/nextjs/__init__.py +0 -0
  136. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/nextjs/plugin.py +0 -0
  137. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/nuxt/__init__.py +0 -0
  138. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/nuxt/plugin.py +0 -0
  139. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/php/__init__.py +0 -0
  140. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/php/external.py +0 -0
  141. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/php/extractor/composer.json +0 -0
  142. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/php/extractor/composer.lock +0 -0
  143. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/php/extractor/extract.php +0 -0
  144. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/php/gating.py +0 -0
  145. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/php/http_client.py +0 -0
  146. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/php/inline_guards.py +0 -0
  147. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/php/plugin.py +0 -0
  148. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/php/strings.py +0 -0
  149. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/__init__.py +0 -0
  150. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/aitools.py +0 -0
  151. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/external.py +0 -0
  152. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/fields.py +0 -0
  153. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/plugin.py +0 -0
  154. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/refs.py +0 -0
  155. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/roots.py +0 -0
  156. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/socketio.py +0 -0
  157. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/subproc.py +0 -0
  158. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/tests.py +0 -0
  159. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/python/values.py +0 -0
  160. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/pyweb/__init__.py +0 -0
  161. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/pyweb/plugin.py +0 -0
  162. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/pyweb/values.py +0 -0
  163. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/rust/__init__.py +0 -0
  164. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/rust/cargo.py +0 -0
  165. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/rust/plugin.py +0 -0
  166. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/rust/syntax.py +0 -0
  167. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/scip/__init__.py +0 -0
  168. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/scip/importer.py +0 -0
  169. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/scip/scip.proto +0 -0
  170. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/scip/scip_pb2.py +0 -0
  171. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/stubs/__init__.py +0 -0
  172. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/stubs/plugins.py +0 -0
  173. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/swift/__init__.py +0 -0
  174. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/swift/baseurl.py +0 -0
  175. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/swift/exact.py +0 -0
  176. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/swift/indexstore.py +0 -0
  177. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/swift/packages.py +0 -0
  178. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/ts/__init__.py +0 -0
  179. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/ts/baseurl.py +0 -0
  180. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/ts/extractor/extract.mjs +0 -0
  181. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/ts/extractor/fw.mjs +0 -0
  182. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/ts/extractor/package-lock.json +0 -0
  183. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/ts/extractor/package.json +0 -0
  184. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/ts/extractor/rr.mjs +0 -0
  185. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/ts/nav.py +0 -0
  186. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/ts/plugin.py +0 -0
  187. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/ts/react_router.py +0 -0
  188. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/tsweb/__init__.py +0 -0
  189. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/tsweb/common.py +0 -0
  190. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/plugins/tsweb/data.py +0 -0
  191. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/__init__.py +0 -0
  192. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/c_cpp.yaml +0 -0
  193. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/common.yaml +0 -0
  194. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/dart.yaml +0 -0
  195. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/django-ninja.yaml +0 -0
  196. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/django.yaml +0 -0
  197. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/djangorestframework.yaml +0 -0
  198. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/express.yaml +0 -0
  199. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/java.yaml +0 -0
  200. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/kotlin.yaml +0 -0
  201. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/laravel.yaml +0 -0
  202. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/nest.yaml +0 -0
  203. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/nextjs.yaml +0 -0
  204. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/nuxt.yaml +0 -0
  205. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/php.yaml +0 -0
  206. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/python.yaml +0 -0
  207. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/react-router.yaml +0 -0
  208. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/rust.yaml +0 -0
  209. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/spring.yaml +0 -0
  210. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/swift.yaml +0 -0
  211. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/presets/typescript.yaml +0 -0
  212. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/process_runs.py +0 -0
  213. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/protocols/__init__.py +0 -0
  214. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/protocols/builtin.py +0 -0
  215. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/protocols/matchers.py +0 -0
  216. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/protocols/view.py +0 -0
  217. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/realtime.py +0 -0
  218. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/realtime_events.py +0 -0
  219. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/realtime_native.py +0 -0
  220. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/realtime_ws.py +0 -0
  221. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/roundtrip.py +0 -0
  222. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/rpc.py +0 -0
  223. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/sdk_systems.py +0 -0
  224. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/sockets.py +0 -0
  225. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/ssrf_input.py +0 -0
  226. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/starters.py +0 -0
  227. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/surface.py +0 -0
  228. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/tests_index.py +0 -0
  229. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/udp_apps.py +0 -0
  230. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/__init__.py +0 -0
  231. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/graph.py +0 -0
  232. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/server.py +0 -0
  233. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/static/app.css +0 -0
  234. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/static/app.js +0 -0
  235. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/static/index.html +0 -0
  236. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/static/layered.js +0 -0
  237. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/static/vendor/VERSIONS.txt +0 -0
  238. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/static/vendor/cose-base.js +0 -0
  239. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/static/vendor/cytoscape-fcose.js +0 -0
  240. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/static/vendor/cytoscape.min.js +0 -0
  241. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/static/vendor/layout-base.js +0 -0
  242. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/tools/package-lock.json +0 -0
  243. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/tools/package.json +0 -0
  244. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/viz/tools/shoot.mjs +0 -0
  245. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/watch.py +0 -0
  246. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/webhook_subs.py +0 -0
  247. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/webhooks.py +0 -0
  248. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph/xcode.py +0 -0
  249. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph.egg-info/dependency_links.txt +0 -0
  250. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph.egg-info/requires.txt +0 -0
  251. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/cg_code_graph.egg-info/top_level.txt +0 -0
  252. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/setup.cfg +0 -0
  253. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_agents_install.py +0 -0
  254. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_agents_md.py +0 -0
  255. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_ai_tools.py +0 -0
  256. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_android_ipc.py +0 -0
  257. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_apple_ipc.py +0 -0
  258. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_astro.py +0 -0
  259. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_astro_content.py +0 -0
  260. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_astro_server.py +0 -0
  261. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_bridges_pigeon_monorepo.py +0 -0
  262. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_brokers.py +0 -0
  263. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_cache_clean.py +0 -0
  264. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_cache_invalidation.py +0 -0
  265. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_cache_migration.py +0 -0
  266. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_channels_tests.py +0 -0
  267. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_cli_output.py +0 -0
  268. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_completeness.py +0 -0
  269. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_config_extras.py +0 -0
  270. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_coverage_ts_hint.py +0 -0
  271. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_dart_isolates.py +0 -0
  272. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_dispatch_interfaces.py +0 -0
  273. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_django_flutter.py +0 -0
  274. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_docs_languages.py +0 -0
  275. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_docs_links.py +0 -0
  276. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_echo_ref.py +0 -0
  277. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_echo_wrapper.py +0 -0
  278. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_elysia.py +0 -0
  279. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_enum_constants.py +0 -0
  280. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_env.py +0 -0
  281. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_explore.py +0 -0
  282. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_external.py +0 -0
  283. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_fp_batch.py +0 -0
  284. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_gating.py +0 -0
  285. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_generated.py +0 -0
  286. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_graphql.py +0 -0
  287. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_hooks.py +0 -0
  288. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_impact_overrides.py +0 -0
  289. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_index_isolation.py +0 -0
  290. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_inherited_overrides.py +0 -0
  291. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_inline_guards.py +0 -0
  292. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_java.py +0 -0
  293. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_java_exact.py +0 -0
  294. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_jobs.py +0 -0
  295. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_js_refs.py +0 -0
  296. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_js_runtime.py +0 -0
  297. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_kotlin.py +0 -0
  298. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_kotlin_binding.py +0 -0
  299. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_kotlin_exact.py +0 -0
  300. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_kotlin_fields.py +0 -0
  301. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_kotlin_mixed.py +0 -0
  302. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_kotlin_nav_consts.py +0 -0
  303. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_kotlin_props.py +0 -0
  304. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_laravel_determinism.py +0 -0
  305. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_laravel_resources.py +0 -0
  306. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_lint_async.py +0 -0
  307. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_local_ipc.py +0 -0
  308. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_local_sockets.py +0 -0
  309. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_mcp.py +0 -0
  310. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_mcp_index.py +0 -0
  311. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_native_cfamily.py +0 -0
  312. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_native_concurrency.py +0 -0
  313. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_native_rust.py +0 -0
  314. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_nuxt_import_dirs.py +0 -0
  315. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_nuxt_rootsrc.py +0 -0
  316. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_packaging.py +0 -0
  317. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_parity.py +0 -0
  318. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_parity_structure.py +0 -0
  319. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_php_http.py +0 -0
  320. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_php_props.py +0 -0
  321. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_plain_js.py +0 -0
  322. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_plans.py +0 -0
  323. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_platform_gaps.py +0 -0
  324. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_platform_type_variants.py +0 -0
  325. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_platforms.py +0 -0
  326. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_platforms_apple.py +0 -0
  327. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_presets_config.py +0 -0
  328. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_process_ipc.py +0 -0
  329. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_process_runs.py +0 -0
  330. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_protocols.py +0 -0
  331. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_app_tests_module.py +0 -0
  332. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_collection_chains.py +0 -0
  333. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_compat.py +0 -0
  334. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_entry_refs.py +0 -0
  335. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_fields.py +0 -0
  336. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_infer_cycles.py +0 -0
  337. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_roots.py +0 -0
  338. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_subprocess_tests.py +0 -0
  339. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_tests.py +0 -0
  340. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_web_hosts.py +0 -0
  341. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_web_routes.py +0 -0
  342. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_python_web_routes_more.py +0 -0
  343. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_react_router.py +0 -0
  344. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_realtime.py +0 -0
  345. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_realtime_native.py +0 -0
  346. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_redact_salt.py +0 -0
  347. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_request_keys.py +0 -0
  348. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_roundtrip.py +0 -0
  349. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_route_tables.py +0 -0
  350. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_rpc.py +0 -0
  351. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_scip_health.py +0 -0
  352. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_sdk_external.py +0 -0
  353. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_sdk_external_infra.py +0 -0
  354. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_sdk_external_mail.py +0 -0
  355. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_snippet.py +0 -0
  356. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_sockets.py +0 -0
  357. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_spring.py +0 -0
  358. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_ssrf.py +0 -0
  359. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_starters_budget.py +0 -0
  360. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_surface.py +0 -0
  361. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_surface_transport.py +0 -0
  362. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift.py +0 -0
  363. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_binding.py +0 -0
  364. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_exact.py +0 -0
  365. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_facts.py +0 -0
  366. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_fields.py +0 -0
  367. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_modules.py +0 -0
  368. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_more.py +0 -0
  369. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_nav_values.py +0 -0
  370. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_parse_errors.py +0 -0
  371. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_props.py +0 -0
  372. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_sdk.py +0 -0
  373. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_testing.py +0 -0
  374. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_swift_values.py +0 -0
  375. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_tests_scope.py +0 -0
  376. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_ts_fields.py +0 -0
  377. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_ts_fields_js_vue.py +0 -0
  378. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_ts_file_specs.py +0 -0
  379. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_ts_frameworks.py +0 -0
  380. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_ts_node_http.py +0 -0
  381. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_ts_nuxt.py +0 -0
  382. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_ts_src_tsconfig.py +0 -0
  383. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_unguarded.py +0 -0
  384. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_values.py +0 -0
  385. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_viz.py +0 -0
  386. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_vue_nav.py +0 -0
  387. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_watch.py +0 -0
  388. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_webhooks.py +0 -0
  389. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_webhooks_152.py +0 -0
  390. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_webhooks_152b.py +0 -0
  391. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_websockets.py +0 -0
  392. {cg_code_graph-0.21.1 → cg_code_graph-0.22.0}/tests/test_workspace_link.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cg-code-graph
3
- Version: 0.21.1
3
+ Version: 0.22.0
4
4
  Summary: cg: a code graph of routes, calls, data access and platform boundaries for agents and humans (CLI + MCP server)
5
5
  Author: cyberchronos00
6
6
  License-Expression: MIT
@@ -33,6 +33,8 @@ Dynamic: license-file
33
33
 
34
34
  For you and your AI agent (CLI + MCP). No LLM in the graph.
35
35
 
36
+ <!-- mcp-name: io.github.cyberchronos00/cg-code-graph -->
37
+
36
38
  [![PyPI](https://img.shields.io/pypi/v/cg-code-graph)](https://pypi.org/project/cg-code-graph/) [![Downloads](https://img.shields.io/pepy/dt/cg-code-graph)](https://pepy.tech/project/cg-code-graph) [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-3776AB)](https://pypi.org/project/cg-code-graph/) [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/cyberchronos00/code-graph/blob/main/LICENSE) [![Docs](https://img.shields.io/badge/docs-code--graph-93b600)](https://code-graph.cyberchronos00.workers.dev/)
37
39
 
38
40
  [Docs](https://code-graph.cyberchronos00.workers.dev/) · [Quick start](https://code-graph.cyberchronos00.workers.dev/docs/quickstart) · [CLI](https://code-graph.cyberchronos00.workers.dev/docs/cli) · [MCP](https://code-graph.cyberchronos00.workers.dev/docs/mcp) · [Changelog](https://github.com/cyberchronos00/code-graph/blob/main/CHANGELOG.md)
@@ -91,7 +93,7 @@ Go and others: import a SCIP index with `cg index --scip FILE`. Run `cg doctor`
91
93
  - Across repos: `cg link` matches client HTTP calls (frontends and Laravel / Guzzle backends) to server routes, and checks request keys (Laravel FormRequest, Elysia schemas, Nuxt / TypeScript bodies).
92
94
  - Route guards: `cg routes --writes --unguarded` lists routes that write with no auth guard. Laravel checks inside the action count as inline guards unless `--strict`.
93
95
  - Security views: `cg surface` lists the inbound / outbound attack surface and findings (hardcoded credentials, plaintext protocols, unverified webhooks, unguarded handlers, listeners on all interfaces, disabled TLS / SSH verification, plaintext gRPC, exposed IPC, outbound URLs built from request input) as text, JSON or SARIF, with `--fail-on` for CI.
94
- - MCP server (`cg-mcp`) so agents query the graph instead of grepping.
96
+ - MCP server (`cg-mcp`) so agents query the graph instead of grepping. One server can serve many graphs (`graphs` plus an optional `graph` argument); a single `--db` stays one graph. See [MCP](https://code-graph.cyberchronos00.workers.dev/docs/mcp).
95
97
  - Visual view (`cg serve`) and plan checks (`cg plan check`) for planned changes.
96
98
 
97
99
  ## Docs
@@ -6,6 +6,8 @@
6
6
 
7
7
  For you and your AI agent (CLI + MCP). No LLM in the graph.
8
8
 
9
+ <!-- mcp-name: io.github.cyberchronos00/cg-code-graph -->
10
+
9
11
  [![PyPI](https://img.shields.io/pypi/v/cg-code-graph)](https://pypi.org/project/cg-code-graph/) [![Downloads](https://img.shields.io/pepy/dt/cg-code-graph)](https://pepy.tech/project/cg-code-graph) [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-3776AB)](https://pypi.org/project/cg-code-graph/) [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/cyberchronos00/code-graph/blob/main/LICENSE) [![Docs](https://img.shields.io/badge/docs-code--graph-93b600)](https://code-graph.cyberchronos00.workers.dev/)
10
12
 
11
13
  [Docs](https://code-graph.cyberchronos00.workers.dev/) · [Quick start](https://code-graph.cyberchronos00.workers.dev/docs/quickstart) · [CLI](https://code-graph.cyberchronos00.workers.dev/docs/cli) · [MCP](https://code-graph.cyberchronos00.workers.dev/docs/mcp) · [Changelog](https://github.com/cyberchronos00/code-graph/blob/main/CHANGELOG.md)
@@ -64,7 +66,7 @@ Go and others: import a SCIP index with `cg index --scip FILE`. Run `cg doctor`
64
66
  - Across repos: `cg link` matches client HTTP calls (frontends and Laravel / Guzzle backends) to server routes, and checks request keys (Laravel FormRequest, Elysia schemas, Nuxt / TypeScript bodies).
65
67
  - Route guards: `cg routes --writes --unguarded` lists routes that write with no auth guard. Laravel checks inside the action count as inline guards unless `--strict`.
66
68
  - Security views: `cg surface` lists the inbound / outbound attack surface and findings (hardcoded credentials, plaintext protocols, unverified webhooks, unguarded handlers, listeners on all interfaces, disabled TLS / SSH verification, plaintext gRPC, exposed IPC, outbound URLs built from request input) as text, JSON or SARIF, with `--fail-on` for CI.
67
- - MCP server (`cg-mcp`) so agents query the graph instead of grepping.
69
+ - MCP server (`cg-mcp`) so agents query the graph instead of grepping. One server can serve many graphs (`graphs` plus an optional `graph` argument); a single `--db` stays one graph. See [MCP](https://code-graph.cyberchronos00.workers.dev/docs/mcp).
68
70
  - Visual view (`cg serve`) and plan checks (`cg plan check`) for planned changes.
69
71
 
70
72
  ## Docs
@@ -1,2 +1,2 @@
1
1
  """code-graph: deterministic code knowledge graph (beta)."""
2
- __version__ = "0.21.1"
2
+ __version__ = "0.22.0"
@@ -73,6 +73,27 @@ def _add_install_parser(sub, uninstall: bool):
73
73
  p.add_argument("--uninstall", action="store_true", help="remove the entry instead of writing it")
74
74
  p.add_argument("--dry-run", action="store_true", help="print the exact changes and write nothing")
75
75
  p.add_argument("--yes", action="store_true", help="skip the confirmation prompt")
76
+ if not uninstall:
77
+ p.add_argument("--registry", action="store_true",
78
+ help="global single entry: cg-mcp with no --db; graphs come from the registry")
79
+ p.add_argument("--migrate", action="store_true",
80
+ help="fold per-repo cg-* host entries into the graph registry (dry run unless --apply)")
81
+ p.add_argument("--apply", action="store_true", help="with --migrate, write the registry and the host config")
82
+ p.add_argument("--graphs", help="registry file for --migrate (default: ~/.config/cg/graphs.toml)")
83
+
84
+
85
+ def _impact_labels(rows: list[dict], name_of, group_of) -> list[str]:
86
+ """Display names for one impact listing. Same-name rows in one group gain a file suffix."""
87
+ from collections import defaultdict
88
+ groups = defaultdict(list)
89
+ for i, row in enumerate(rows):
90
+ groups[group_of(row)].append(i)
91
+ labels = [name_of(row) for row in rows]
92
+ for idxs in groups.values():
93
+ named = Q.disambiguated([labels[i] for i in idxs], [rows[i].get("file") for i in idxs])
94
+ for i, lab in zip(idxs, named):
95
+ labels[i] = lab
96
+ return labels
76
97
 
77
98
 
78
99
  def main(argv=None):
@@ -321,8 +342,8 @@ def main(argv=None):
321
342
  p.add_argument("--quiet", action="store_true", help="pass --quiet to each refresh")
322
343
  helps = {"reaches": "everything that depends on the targets, grouped by entry classification",
323
344
  "siblings": "code related to a symbol: class hierarchy, the same method in sibling classes, shared resources, co-callers",
324
- "writers": "code that writes a table (or column), or a stored property `Type.prop`",
325
- "readers": "code that reads a stored property `Type.prop` (READS_PROP)",
345
+ "writers": "code that writes a table, a column (`table.column`) or a stored property `Type.prop`",
346
+ "readers": "code that reads a table, a column (`table.column`) or a stored property `Type.prop`",
326
347
  "impact": "callers of a method up to their entry points (reverse walk), overrides listed apart",
327
348
  "stats": "node / edge counts of a graph DB",
328
349
  "node": "one node's details and its incoming / outgoing edges",
@@ -342,6 +363,9 @@ def main(argv=None):
342
363
  p.add_argument("--no-paths", action="store_true")
343
364
  p.add_argument("--max-depth", type=int, default=30)
344
365
  p.add_argument("--gate", default="auto", help="gate scenario for live/gated split (default: the one indexed; 'none' to disable)")
366
+ if name in ("reaches", "writers", "readers"):
367
+ p.add_argument("--connection", help="only the table / column edges that go to this database connection "
368
+ "(a dynamic name by its pattern, e.g. 'legacy_{store.id}'; docs/cli.md)")
345
369
  if name in ("reaches", "impact", "downstream"):
346
370
  p.add_argument("--platform", help="only code built for this target (windows, linux, macos, ios, android, web; see docs/platforms.md)")
347
371
  if name == "impact":
@@ -489,9 +513,14 @@ def main(argv=None):
489
513
  mcp_file=a.mcp_file, dry_run=a.dry_run, assume_yes=a.yes)
490
514
  if a.cmd in ("install", "uninstall"):
491
515
  from .mcp_install import run as run_install
516
+ registry = bool(getattr(a, "registry", False))
517
+ migrate = bool(getattr(a, "migrate", False))
518
+ if (registry or migrate) and not a.project:
519
+ a.glob = True
492
520
  return run_install(a.host, scope="global" if a.glob else "project", root=a.dir, db=a.db,
493
521
  tools=a.tools, portable=a.portable, remove=a.cmd == "uninstall" or a.uninstall,
494
- dry_run=a.dry_run, assume_yes=a.yes)
522
+ dry_run=a.dry_run, assume_yes=a.yes, registry=registry, migrate=migrate,
523
+ apply=bool(getattr(a, "apply", False)), graphs=getattr(a, "graphs", None))
495
524
  if a.cmd == "hooks":
496
525
  from . import hooks as HK
497
526
  return HK.run(a.action, root=a.dir, db=a.db, dry_run=a.dry_run, assume_yes=a.yes)
@@ -646,7 +675,7 @@ def main(argv=None):
646
675
  return
647
676
  if a.cmd == "reaches":
648
677
  res = Q.reaches(st, a.specs, min_conf=a.min_confidence, max_depth=a.max_depth, gate=None if a.gate == "none" else a.gate,
649
- platform=a.platform)
678
+ platform=a.platform, connection=a.connection)
650
679
  res["completeness"] = _completeness(st, [x for t in res["targets"].values() for x in t] + [i["id"] for i in res["items"]])
651
680
  print(json.dumps(res, indent=1, default=str) if a.json else Q.render_reaches(res, show_paths=not a.no_paths))
652
681
  _note(res["completeness"], a.json)
@@ -658,26 +687,35 @@ def main(argv=None):
658
687
  if res.get("platform"):
659
688
  from .platforms import render_filter
660
689
  print(render_filter(res["platform"]))
661
- if res["platform"].get("targets_not_built"):
662
- print(f"not built for {a.platform}: {', '.join(res['platform']['targets_not_built'][:6])}")
690
+ dropped = res["platform"].get("targets_not_built") or []
691
+ if dropped and res["targets"]:
692
+ print(f"not built for {a.platform}: {', '.join(dropped[:6])}")
693
+ if dropped and not res["targets"]:
694
+ msg = f"{a.spec} is not built for {a.platform}: nothing calls it there"
695
+ if Q.divergence_lists(st, a.platform, dropped):
696
+ msg += (f" (`cg platforms divergence --target {a.platform}` lists references to it that would not build)")
697
+ print(msg); return
663
698
  print(f"targets: {res['targets'][:5]}")
664
699
  if not res["targets"]:
665
700
  print(f"no method matches {a.spec!r}; try `search` with part of the name"); return
666
- if a.platform and res["targets"] and set(res["platform"]["targets_not_built"]) == set(res["targets"]):
667
- print(f"{a.spec} is not built for {a.platform}: nothing calls it there (`cg platforms divergence --target "
668
- f"{a.platform}` lists references to it that would not build)"); return
701
+ for d in res.get("default_arguments") or []:
702
+ print(f"via default argument: {d['src']} @ {d['file']}:{d['line']}")
669
703
  for line in Q.override_lines(res):
670
704
  print(line)
705
+ if res.get("connections"):
706
+ print(Q.connections_line(res["connections"]))
671
707
  if not res["callers"] and not res["entry_points"]:
672
708
  print(Q.explain_no_callers(st, a.spec, res["targets"], a.min_confidence)); return
673
709
  print(f"callers (transitive): {len(res['callers'])}")
674
- for c in res["callers"]:
710
+ caller_names = _impact_labels(res["callers"], lambda c: c.get("fqn") or c["id"], lambda c: c.get("module") or c.get("kind"))
711
+ for c, nm in zip(res["callers"], caller_names):
675
712
  loc = f" @ {c['file']}:{c['line']}" if Q.NATIVE_FILE_RE.search(c.get("file") or "") else ""
676
- print(f" d={c['depth']} [{c['module'] or c['kind']}] {c['fqn']}{loc}{Q.caller_label(c)}")
713
+ print(f" d={c['depth']} [{c['module'] or c['kind']}] {nm}{loc}{Q.caller_label(c)}")
677
714
  print(f"entry points: {len(res['entry_points'])}")
678
- for e in res["entry_points"]:
715
+ entry_names = _impact_labels(res["entry_points"], lambda e: e["name"], lambda e: e.get("entry_kind"))
716
+ for e, nm0 in zip(res["entry_points"], entry_names):
679
717
  native = Q.NATIVE_FILE_RE.search(e.get("file") or "")
680
- nm = f"{e.get('fqn') or e['name']} @ {e['file']}:{e['line']}" if native else e["name"]
718
+ nm = f"{e.get('fqn') or e['name']} @ {e['file']}:{e['line']}" if native else nm0
681
719
  print(f" {e['entry_kind']:16} {nm} conf={e['path_confidence']}{Q.CANDIDATE_LABEL if e.get('candidate') else ''}"
682
720
  f"{Q.generated_label(e)}{Q.platform_label(e)}")
683
721
  if not a.no_paths:
@@ -702,17 +740,14 @@ def main(argv=None):
702
740
  res = RT.roundtrip(st, a.spec, include_tests=a.tests)
703
741
  print(RT.to_json(res) if a.json else RT.render(res))
704
742
  elif a.cmd in ("writers", "readers"):
705
- rows = Q.writers(st, a.spec) if a.cmd == "writers" else Q.readers(st, a.spec)
743
+ groups = Q.access(st, a.spec, a.cmd, a.connection)
744
+ rows = [r for g in groups for r in g["rows"]]
706
745
  if a.json:
707
746
  print(json.dumps(rows, indent=1)); return
708
747
  if not rows:
709
- print(Q.explain_no_writers(st, a.spec, a.cmd)); return
710
- for r in rows:
711
- ek = ",".join(sorted(r["entry_kinds"]))
712
- ex = {k: v for k, v in (r.get("attrs") or {}).items() if k in ("receiver", "accessor", "storage")}
713
- extra = (" " + " ".join(f"{k}={v}" for k, v in ex.items())) if ex else ""
714
- kind = f"{r['kind']}({r['attrs']['orig']})" if r.get("test") else r['kind']
715
- print(f"[{r['module']}] {r['fqn']} {kind} {r['dst']} @{r['file']}:{r['line']} ({r['confidence']}){extra} entries: {ek}")
748
+ print(Q.explain_no_connection(st, a.spec, a.connection, a.cmd) if a.connection else Q.explain_no_writers(st, a.spec, a.cmd))
749
+ return
750
+ print("\n".join(Q.render_access(groups)))
716
751
  verb = "write" if a.cmd == "writers" else "read"
717
752
  nt = sum(1 for r in rows if r.get("test"))
718
753
  print(f"{len(rows)} {verb} edges, {len({r['src'] for r in rows})} {a.cmd}" + (f" ({nt} from test code)" if nt else ""))
@@ -773,6 +808,14 @@ def node_doc(st, nid: str) -> dict:
773
808
  return {"node": n, "out": edges("src", "dst"), "in": edges("dst", "src")}
774
809
 
775
810
 
811
+ def _mass_note(e: dict) -> str:
812
+ """` via update(validated()) keys from UpdateBookRequest::rules ∩ Book::$fillable` for a mass-assignment column write (#177)."""
813
+ at = e.get("attrs") or {}
814
+ if e["kind"] != "WRITES_COLUMN" or not at.get("mass_assignment"):
815
+ return ""
816
+ return f" via {at.get('via')}" + (f" keys from {' ∩ '.join(at['keys_from'])}" if at.get("keys_from") else "")
817
+
818
+
776
819
  def render_node(d: dict) -> str:
777
820
  """`cg node` text: the node's location, kind and attrs, then its edges; the same edge from one site (one row per
778
821
  gate scenario or accessor) is listed once with a count."""
@@ -801,10 +844,10 @@ def render_node(d: dict) -> str:
801
844
  out.append(f" {'outgoing' if key == 'out' else 'incoming'} ({len(es)}):" if es else f" {'outgoing' if key == 'out' else 'incoming'}: none")
802
845
  seen: dict = {}
803
846
  for e in es:
804
- k = (e["kind"], e[other], e["file"], e["line"], e["confidence"])
847
+ k = (e["kind"], e[other], e["file"], e["line"], e["confidence"], _mass_note(e))
805
848
  seen[k] = seen.get(k, 0) + 1
806
- for (kind, o, f, line, conf), c in seen.items():
807
- out.append(f" {arrow} {kind} {o} @{f}:{line} {conf}" + (f" ×{c}" if c > 1 else ""))
849
+ for (kind, o, f, line, conf, note), c in seen.items():
850
+ out.append(f" {arrow} {kind} {o} @{f}:{line} {conf}{note}" + (f" ×{c}" if c > 1 else ""))
808
851
  return "\n".join(out)
809
852
 
810
853
 
@@ -233,9 +233,20 @@ def file_completeness(discovered: list[str], report: dict) -> dict:
233
233
  for b in BUCKETS:
234
234
  res[b] = len(out[b])
235
235
  res["paths"] = {b: v[:MAX_PATHS] for b, v in out.items() if v}
236
+ if out["excluded"]:
237
+ res["exclude_reasons"] = {f: _exclude_reason(f) for f in out["excluded"]}
236
238
  return res
237
239
 
238
240
 
241
+ def _exclude_reason(path: str) -> str:
242
+ """Why a discovered file was left out of the graph: tool config, a test file, or a `.cg.yaml` exclude."""
243
+ if _TOOL_FILE.search(path):
244
+ return "tool config"
245
+ if _TEST_NAME.search(path):
246
+ return "test file"
247
+ return ".cg.yaml exclude"
248
+
249
+
239
250
  def missing_files(e: dict) -> int:
240
251
  """Discovered files of a language entry that are not in the graph for a reason other than a deliberate exclusion."""
241
252
  return sum(e.get(b, 0) for b in ("parse_failed", "skipped_oversize", "unmapped"))
@@ -308,6 +319,8 @@ def compute(root: str | Path, plugins: dict, scip_imported: bool = False, report
308
319
  status, reason = _status(lang, st)
309
320
  e = {"language": lang, "files": n, "status": status,
310
321
  "by_ext": {x: files[x] for x in exts if files[x]}}
322
+ if lang == "typescript" and not any(x in e["by_ext"] for x in (".ts", ".tsx", ".mts", ".cts")):
323
+ e["label"] = "javascript"
311
324
  if reason:
312
325
  e["reason"] = reason
313
326
  if status in ("skipped", "heuristic", "not_indexed"):
@@ -495,16 +508,22 @@ def blind_spots(cov: dict | None) -> list[dict]:
495
508
  return list((cov or {}).get("blind_spots") or [])
496
509
 
497
510
 
511
+ def _shown_lang(e: dict) -> str:
512
+ """Display name. A TypeScript-plugin entry with no .ts/.tsx/.mts/.cts files reads as javascript; JSON keeps `language`."""
513
+ return e.get("label") or e["language"]
514
+
515
+
498
516
  def _entry_text(e: dict) -> str:
499
517
  """'php 18 exact' / 'python 4 discovered, 2 indexed (exact parser): 1 parse failed, 1 unmapped' /
500
- 'swift 101 heuristic, 8 parsed with syntax errors'."""
518
+ 'javascript 12 discovered, 2 indexed (exact parser): 9 unmapped, 1 excluded'."""
501
519
  pe = e.get("parsed_with_errors")
502
520
  errs = f", {pe} parsed with syntax errors" if pe else ""
521
+ name = _shown_lang(e)
503
522
  if e.get("files_complete") is False:
504
- parts = [f"{e[b]} {BUCKET_SHORT[b]}" for b in ("parse_failed", "skipped_oversize", "unmapped") if e.get(b)]
505
- return (f"{e['language']} {e['files']} discovered, {e['indexed']} indexed ({e['status'].replace('_', ' ')} parser)"
523
+ parts = [f"{e[b]} {BUCKET_SHORT[b]}" for b in BUCKETS if e.get(b)]
524
+ return (f"{name} {e['files']} discovered, {e['indexed']} indexed ({e['status'].replace('_', ' ')} parser)"
506
525
  + (": " + ", ".join(parts) if parts else "") + errs)
507
- return f"{e['language']} {e['files']} {e['status'].replace('_', ' ')}{errs}"
526
+ return f"{name} {e['files']} {e['status'].replace('_', ' ')}{errs}"
508
527
 
509
528
 
510
529
  def realtime_unresolved_lines(e: dict, indent: str = " ") -> list[str]:
@@ -579,11 +598,16 @@ def _paths_lines(e: dict, all_files: bool, indent: str = " ") -> list[str]:
579
598
  out = []
580
599
  for b in BUCKETS:
581
600
  ps = (e.get("paths") or {}).get(b) or []
582
- if not ps or (b == "excluded" and not all_files):
601
+ if not ps:
583
602
  continue
584
603
  show = ps if all_files else ps[:SHOW_PATHS]
585
604
  more = e.get(b, len(ps)) - len(show)
586
- out.append(f"{indent}{BUCKET_SHORT[b]}: " + ", ".join(show) + (f" … +{more} more (--all-files)" if more > 0 else ""))
605
+ if b == "excluded":
606
+ reasons = e.get("exclude_reasons") or {}
607
+ body = ", ".join(f"{p} ({reasons.get(p) or _exclude_reason(p)})" for p in show)
608
+ else:
609
+ body = ", ".join(show)
610
+ out.append(f"{indent}{BUCKET_SHORT[b]}: " + body + (f" … +{more} more (--all-files)" if more > 0 else ""))
587
611
  return out
588
612
 
589
613
 
@@ -720,11 +744,12 @@ def render(covs: dict[str, dict | None], all_files: bool = False) -> str:
720
744
  for e in gaps(cov):
721
745
  any_gap = True
722
746
  exts = ", ".join(f"{k} {v}" for k, v in sorted(e["by_ext"].items()))
747
+ shown = _shown_lang(e)
723
748
  if e.get("files_complete") is False:
724
749
  parts = [f"{e[b]} {BUCKET_SHORT[b]}" for b in BUCKETS if e.get(b)]
725
- line = f" {e['language']}: {e['files']} files ({exts}) {e['indexed']} indexed, " + ", ".join(parts)
750
+ line = f" {shown}: {e['files']} files ({exts}) {e['indexed']} indexed, " + ", ".join(parts)
726
751
  else:
727
- line = f" {e['language']}: {e['files']} files ({exts}) {e['status'].replace('_', ' ')}"
752
+ line = f" {shown}: {e['files']} files ({exts}) {e['status'].replace('_', ' ')}"
728
753
  if e.get("reason"):
729
754
  line += f": {e['reason']}"
730
755
  out.append(line)
@@ -795,7 +820,7 @@ def note(covs: dict[str, dict | None]) -> str:
795
820
  for e in gaps(c):
796
821
  what = (f"{e['indexed']} of {e['files']} files indexed" if e.get("files_complete") is False
797
822
  else f"{e['files']} files, {e['status'].replace('_', ' ')}")
798
- bad.append(f"{e['language']} ({what}{', ' + r if len(covs) > 1 else ''})")
823
+ bad.append(f"{_shown_lang(e)} ({what}{', ' + r if len(covs) > 1 else ''})")
799
824
  bs = [b for c in covs.values() for b in blind_spots(c)]
800
825
  bs_txt = f"; blind spots: {_bs_count(bs)} (see coverage)" if bs else ""
801
826
  nerr = sum(e.get("syntax_error_files", 0) for c in covs.values() for e in (c or {}).get("languages", []))
@@ -956,6 +981,18 @@ def completeness_for(store, node_ids=None, categories=("route", "handler"), whol
956
981
  categories=categories, ids=set(node_ids), files=sc["files"], **kw)
957
982
 
958
983
 
984
+ def _unsupported_text(uns: dict) -> str:
985
+ """`unsupported: sh 3`. A single-repo DB drops a `shop/sh` prefix; a combined graph keeps `api/sh`."""
986
+ keys = list(uns)
987
+ prefixes = {k.partition("/")[0] for k in keys if "/" in k}
988
+ drop = len(prefixes) <= 1
989
+ parts = []
990
+ for k, v in sorted(uns.items()):
991
+ label = k.partition("/")[2] if drop and "/" in k else k
992
+ parts.append(f"{label} {v}")
993
+ return "unsupported: " + ", ".join(parts)
994
+
995
+
959
996
  def possibly_more(comp: dict) -> str:
960
997
  """'1 unmodelled route registration, 2 Python files not indexed' for an incomplete answer, '' when complete."""
961
998
  if comp.get("complete"):
@@ -990,7 +1027,7 @@ def possibly_more(comp: dict) -> str:
990
1027
  parts.append(f"{len(se)} file{'s' if len(se) != 1 else ''} with syntax errors ({x['file']}{pos}"
991
1028
  + (f" +{len(se) - 1}" if len(se) > 1 else "") + ")")
992
1029
  if comp.get("unsupported"):
993
- parts.append("unsupported: " + ", ".join(f"{k} {v}" for k, v in sorted(comp["unsupported"].items())))
1030
+ parts.append(_unsupported_text(comp["unsupported"]))
994
1031
  if comp.get("recorded") is False:
995
1032
  parts.append("coverage not recorded")
996
1033
  return ", ".join(parts)
@@ -292,8 +292,35 @@ def read_spring_datasources(root: Path) -> list:
292
292
  out.append(fact)
293
293
  return out
294
294
 
295
+
296
+ CONNECTION_EDGE_KINDS = ("READS_TABLE", "WRITES_TABLE", "READS_COLUMN", "WRITES_COLUMN", "MENTIONS_COLUMN")
297
+
298
+
299
+ def table_connections(builder) -> int:
300
+ """`attrs.connections` of a table node: `{connection: edge count}` over its table / column edges that carry
301
+ `attrs.connection` (most used first). One table name used on several connections stays one node. Not tied to an ORM:
302
+ a plugin only has to set `attrs.connection` on its table / column edges."""
303
+ counts: dict[str, dict[str, int]] = {}
304
+ for e in builder.edges.values():
305
+ conn = (e.attrs or {}).get("connection")
306
+ if e.kind not in CONNECTION_EDGE_KINDS or not conn:
307
+ continue
308
+ key = e.dst.split(":", 1)[1]
309
+ tid = f"table:{key}" if e.dst.startswith("table:") else f"table:{key.rsplit('.', 1)[0]}"
310
+ counts.setdefault(tid, {})
311
+ counts[tid][conn] = counts[tid].get(conn, 0) + 1
312
+ n = 0
313
+ for tid, c in counts.items():
314
+ node = builder.nodes.get(tid)
315
+ if node is not None:
316
+ node.attrs["connections"] = dict(sorted(c.items(), key=lambda kv: (-kv[1], kv[0])))
317
+ n += 1
318
+ return n
319
+
320
+
295
321
  def attach(builder, root: Path) -> dict:
296
322
  root = Path(root)
323
+ table_connections(builder)
297
324
  envs = {n.id[len("env:"):]: n for n in builder.nodes.values() if n.kind == "env"}
298
325
  conns = [n for n in builder.nodes.values() if n.kind == "connection"]
299
326
  # Spring datasources before the empty-check so a repo with only application*.properties still indexes (#41)
@@ -646,6 +673,16 @@ def attach(builder, root: Path) -> dict:
646
673
  (builder.nodes[nid].attrs or {}).get("confidence") or "resolved",
647
674
  op="table", via="laravel connection")
648
675
  st["tables"] = st.get("tables", 0) + 1
676
+ # a table used on several connections: one CONNECTS_TO per connection that maps to a system, not only the first
677
+ for n in builder.nodes.values():
678
+ if n.kind != "table":
679
+ continue
680
+ for cname in (n.attrs or {}).get("connections") or {}:
681
+ nid = conn_ext.get(f"connection:{cname}")
682
+ if nid:
683
+ builder.add_edge(n.id, nid, "CONNECTS_TO", n.file, n.line,
684
+ (builder.nodes[nid].attrs or {}).get("confidence") or "resolved",
685
+ op="table", via=f"connection {cname}")
649
686
 
650
687
  # ---- ORM tables without a datasource block: attach to the sole SQL system of the project (#41 step 2)
651
688
  # Immich (Kysely + DB_URL), Nest TypeORM (@Entity + DATABASE_URL) when no DataSource fact named them.
@@ -28,6 +28,19 @@ from .plugins.flutter.plugin import FlutterPlugin
28
28
  from .plugins.nest.plugin import NestPlugin
29
29
  from .plugins.nextjs.plugin import NextPlugin
30
30
  from .plugins.express.plugin import ExpressPlugin
31
+ import subprocess
32
+
33
+
34
+ def _git_head(root) -> str | None:
35
+ """Commit indexed for this root, when it is a git work tree."""
36
+ try:
37
+ proc = subprocess.run(["git", "-C", str(root), "rev-parse", "HEAD"],
38
+ capture_output=True, text=True, timeout=8)
39
+ except (OSError, subprocess.TimeoutExpired):
40
+ return None
41
+ if proc.returncode != 0:
42
+ return None
43
+ return (proc.stdout or "").strip() or None
31
44
  from .plugins.astro.plugin import AstroPlugin
32
45
  from .plugins.kotlin.plugin import KotlinPlugin
33
46
  from .plugins.java.plugin import JavaPlugin
@@ -464,6 +477,9 @@ def index_project(root: str | Path, db_path: str | Path, name: str | None = None
464
477
  stats["nodes"] = len(builder.nodes)
465
478
  stats["edges"] = len(builder.edges)
466
479
  stats["index_seconds"] = round(time.time() - t0, 2)
467
- store.set_meta(project=project.name, root=str(project.root), stats=stats, indexed_at=time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()))
480
+ head = _git_head(project.root)
481
+ extra = {"commit": head} if head else {}
482
+ store.set_meta(project=project.name, root=str(project.root), stats=stats,
483
+ indexed_at=time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()), **extra)
468
484
  store.db.close()
469
485
  return stats
@@ -497,10 +497,23 @@ def link_many(repos: list[tuple[str, str, str]], out_db: str, *, allow: dict | N
497
497
  for name, path, _role in repos:
498
498
  got = for_graph(GraphStore(path))
499
499
  cov.update({name: next(iter(got.values()))} if len(got) == 1 else got)
500
+ commits = {}
501
+ for name, path, _role in repos:
502
+ try:
503
+ member_st = GraphStore(path)
504
+ try:
505
+ member = member_st.meta()
506
+ finally:
507
+ member_st.db.close()
508
+ except Exception:
509
+ member = {}
510
+ if member.get("commit"):
511
+ commits[name] = member["commit"]
500
512
  store.set_meta(project="+".join(names), stats=stats, repos=names, coverage=cov,
501
513
  sources={name: str(Path(path).resolve()) for name, path, _role in repos},
502
514
  repo_roles={name: role for name, _path, role in repos}, link_clients=clients, link_allow=allow,
503
- indexed_at=time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()))
515
+ indexed_at=time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()),
516
+ **({"commit": commits} if commits else {}))
504
517
  db.close()
505
518
  return {"stats": stats, "results": results, "payload_issues": issues, "uncalled_routes": uncalled,
506
519
  "webhook_subscriptions": subscriptions, "external_callbacks": callbacks}