codeboarding 0.14.2__tar.gz → 0.14.4__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 (205) hide show
  1. {codeboarding-0.14.2/codeboarding.egg-info → codeboarding-0.14.4}/PKG-INFO +1 -1
  2. {codeboarding-0.14.2 → codeboarding-0.14.4}/README.md +7 -0
  3. {codeboarding-0.14.2 → codeboarding-0.14.4/codeboarding.egg-info}/PKG-INFO +1 -1
  4. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding.egg-info/SOURCES.txt +4 -0
  5. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/diagram_generator.py +3 -3
  6. {codeboarding-0.14.2 → codeboarding-0.14.4}/pyproject.toml +1 -1
  7. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/__init__.py +132 -46
  8. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/analysis_cache.py +6 -1
  9. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/clustering/names/__init__.py +1 -6
  10. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/clustering/names/draft.py +334 -145
  11. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/clustering/names/frontier.py +18 -48
  12. codeboarding-0.14.4/static_analyzer/clustering/names/inventory.py +139 -0
  13. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/clustering/names/replay.py +4 -5
  14. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/clustering/names/spec.py +3 -13
  15. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/clustering/names/tokens.py +0 -6
  16. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/clustering/service.py +20 -13
  17. codeboarding-0.14.4/static_analyzer/dotnet_solution.py +62 -0
  18. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/adapters/csharp_adapter.py +108 -46
  19. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/adapters/go_adapter.py +6 -13
  20. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/adapters/java_adapter.py +9 -21
  21. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/adapters/php_adapter.py +0 -10
  22. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/adapters/rust_adapter.py +0 -6
  23. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/adapters/typescript_adapter.py +6 -0
  24. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/call_graph_builder.py +69 -61
  25. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/edge_build_context.py +5 -5
  26. codeboarding-0.14.4/static_analyzer/engine/edge_builder.py +612 -0
  27. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/language_adapter.py +32 -41
  28. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/lsp_client.py +153 -123
  29. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/lsp_constants.py +11 -15
  30. codeboarding-0.14.4/static_analyzer/engine/lsp_recycler.py +46 -0
  31. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/models.py +25 -0
  32. codeboarding-0.14.4/static_analyzer/engine/progress.py +59 -0
  33. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/protocols.py +0 -9
  34. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/result_converter.py +8 -3
  35. codeboarding-0.14.4/static_analyzer/engine/source_inspector.py +1285 -0
  36. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/symbol_table.py +75 -78
  37. codeboarding-0.14.4/static_analyzer/exceptions.py +5 -0
  38. codeboarding-0.14.4/static_analyzer/external_calls.py +142 -0
  39. codeboarding-0.14.4/static_analyzer/graph_definitions.py +369 -0
  40. codeboarding-0.14.4/static_analyzer/incremental_orchestrator.py +284 -0
  41. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/internal_references.py +5 -0
  42. codeboarding-0.14.2/static_analyzer/clustering/names/inventory.py +0 -116
  43. codeboarding-0.14.2/static_analyzer/engine/edge_builder.py +0 -794
  44. codeboarding-0.14.2/static_analyzer/engine/lsp_recycler.py +0 -158
  45. codeboarding-0.14.2/static_analyzer/engine/progress.py +0 -83
  46. codeboarding-0.14.2/static_analyzer/engine/source_inspector.py +0 -765
  47. codeboarding-0.14.2/static_analyzer/incremental_orchestrator.py +0 -632
  48. {codeboarding-0.14.2 → codeboarding-0.14.4}/LICENSE +0 -0
  49. {codeboarding-0.14.2 → codeboarding-0.14.4}/PYPI.md +0 -0
  50. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/__init__.py +0 -0
  51. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/agent_responses.py +0 -0
  52. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/change_status.py +0 -0
  53. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/component_ownership.py +0 -0
  54. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/constants.py +0 -0
  55. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/content_hash.py +0 -0
  56. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/file_index_models.py +0 -0
  57. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/incremental_results.py +0 -0
  58. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/llm_config.py +0 -0
  59. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/llm_errors.py +0 -0
  60. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/llm_renderers/__init__.py +0 -0
  61. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/llm_renderers/call_graph.py +0 -0
  62. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/llm_renderers/scope.py +0 -0
  63. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/model_capabilities.py +0 -0
  64. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/planner_agent.py +0 -0
  65. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/prompts/__init__.py +0 -0
  66. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/prompts/profiles.py +0 -0
  67. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/prompts/shared.py +0 -0
  68. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/relation_edges.py +0 -0
  69. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/retry.py +0 -0
  70. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/scope_analysis_agent.py +0 -0
  71. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/scope_ids.py +0 -0
  72. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/tools/__init__.py +0 -0
  73. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/tools/base.py +0 -0
  74. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/tools/get_method_calls.py +0 -0
  75. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/tools/read_file.py +0 -0
  76. {codeboarding-0.14.2 → codeboarding-0.14.4}/agents/tree_planner_agent.py +0 -0
  77. {codeboarding-0.14.2 → codeboarding-0.14.4}/clustering_ids.py +0 -0
  78. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding.egg-info/dependency_links.txt +0 -0
  79. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding.egg-info/entry_points.txt +0 -0
  80. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding.egg-info/requires.txt +0 -0
  81. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding.egg-info/top_level.txt +0 -0
  82. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_cli/__init__.py +0 -0
  83. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_cli/bootstrap.py +0 -0
  84. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_cli/commands/__init__.py +0 -0
  85. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_cli/commands/full_analysis.py +0 -0
  86. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_cli/commands/incremental_analysis.py +0 -0
  87. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_cli/commands/partial_analysis.py +0 -0
  88. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_cli/render.py +0 -0
  89. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_cli/view_instructions.py +0 -0
  90. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_workflows/__init__.py +0 -0
  91. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_workflows/analysis.py +0 -0
  92. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_workflows/orchestration.py +0 -0
  93. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_workflows/sources/__init__.py +0 -0
  94. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_workflows/sources/local.py +0 -0
  95. {codeboarding-0.14.2 → codeboarding-0.14.4}/codeboarding_workflows/sources/remote.py +0 -0
  96. {codeboarding-0.14.2 → codeboarding-0.14.4}/constants.py +0 -0
  97. {codeboarding-0.14.2 → codeboarding-0.14.4}/core/__init__.py +0 -0
  98. {codeboarding-0.14.2 → codeboarding-0.14.4}/core/plugin_loader.py +0 -0
  99. {codeboarding-0.14.2 → codeboarding-0.14.4}/core/protocols.py +0 -0
  100. {codeboarding-0.14.2 → codeboarding-0.14.4}/core/registry.py +0 -0
  101. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/__init__.py +0 -0
  102. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/analysis_json.py +0 -0
  103. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/exceptions.py +0 -0
  104. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/file_coverage.py +0 -0
  105. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/file_index.py +0 -0
  106. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/incremental_changes.py +0 -0
  107. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/incremental_update.py +0 -0
  108. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/io_utils.py +0 -0
  109. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/run_context.py +0 -0
  110. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/run_mode.py +0 -0
  111. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/scope_assembly.py +0 -0
  112. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/scope_plan.py +0 -0
  113. {codeboarding-0.14.2 → codeboarding-0.14.4}/diagram_analysis/tree_shape.py +0 -0
  114. {codeboarding-0.14.2 → codeboarding-0.14.4}/github_action.py +0 -0
  115. {codeboarding-0.14.2 → codeboarding-0.14.4}/health/__init__.py +0 -0
  116. {codeboarding-0.14.2 → codeboarding-0.14.4}/health/checks/__init__.py +0 -0
  117. {codeboarding-0.14.2 → codeboarding-0.14.4}/health/checks/circular_deps.py +0 -0
  118. {codeboarding-0.14.2 → codeboarding-0.14.4}/health/checks/function_size.py +0 -0
  119. {codeboarding-0.14.2 → codeboarding-0.14.4}/health/checks/unused_code_diagnostics.py +0 -0
  120. {codeboarding-0.14.2 → codeboarding-0.14.4}/health/config.py +0 -0
  121. {codeboarding-0.14.2 → codeboarding-0.14.4}/health/models.py +0 -0
  122. {codeboarding-0.14.2 → codeboarding-0.14.4}/health/runner.py +0 -0
  123. {codeboarding-0.14.2 → codeboarding-0.14.4}/health_main.py +0 -0
  124. {codeboarding-0.14.2 → codeboarding-0.14.4}/install.py +0 -0
  125. {codeboarding-0.14.2 → codeboarding-0.14.4}/logging_config.py +0 -0
  126. {codeboarding-0.14.2 → codeboarding-0.14.4}/main.py +0 -0
  127. {codeboarding-0.14.2 → codeboarding-0.14.4}/monitoring/__init__.py +0 -0
  128. {codeboarding-0.14.2 → codeboarding-0.14.4}/monitoring/callbacks.py +0 -0
  129. {codeboarding-0.14.2 → codeboarding-0.14.4}/monitoring/context.py +0 -0
  130. {codeboarding-0.14.2 → codeboarding-0.14.4}/monitoring/mixin.py +0 -0
  131. {codeboarding-0.14.2 → codeboarding-0.14.4}/monitoring/paths.py +0 -0
  132. {codeboarding-0.14.2 → codeboarding-0.14.4}/monitoring/stats.py +0 -0
  133. {codeboarding-0.14.2 → codeboarding-0.14.4}/monitoring/writers.py +0 -0
  134. {codeboarding-0.14.2 → codeboarding-0.14.4}/output_generators/__init__.py +0 -0
  135. {codeboarding-0.14.2 → codeboarding-0.14.4}/output_generators/html.py +0 -0
  136. {codeboarding-0.14.2 → codeboarding-0.14.4}/output_generators/html_template.py +0 -0
  137. {codeboarding-0.14.2 → codeboarding-0.14.4}/output_generators/markdown.py +0 -0
  138. {codeboarding-0.14.2 → codeboarding-0.14.4}/output_generators/mdx.py +0 -0
  139. {codeboarding-0.14.2 → codeboarding-0.14.4}/output_generators/rendering.py +0 -0
  140. {codeboarding-0.14.2 → codeboarding-0.14.4}/output_generators/sphinx.py +0 -0
  141. {codeboarding-0.14.2 → codeboarding-0.14.4}/repo_utils/__init__.py +0 -0
  142. {codeboarding-0.14.2 → codeboarding-0.14.4}/repo_utils/change_detector.py +0 -0
  143. {codeboarding-0.14.2 → codeboarding-0.14.4}/repo_utils/errors.py +0 -0
  144. {codeboarding-0.14.2 → codeboarding-0.14.4}/repo_utils/fingerprint_diff.py +0 -0
  145. {codeboarding-0.14.2 → codeboarding-0.14.4}/repo_utils/git_ops.py +0 -0
  146. {codeboarding-0.14.2 → codeboarding-0.14.4}/repo_utils/ignore.py +0 -0
  147. {codeboarding-0.14.2 → codeboarding-0.14.4}/repo_utils/path_utils.py +0 -0
  148. {codeboarding-0.14.2 → codeboarding-0.14.4}/setup.cfg +0 -0
  149. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/analysis_result.py +0 -0
  150. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/cfg/__init__.py +0 -0
  151. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/cfg/call_graph.py +0 -0
  152. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/cfg/edge.py +0 -0
  153. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/cfg/location_key.py +0 -0
  154. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/cluster_relations.py +0 -0
  155. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/clustering/__init__.py +0 -0
  156. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/clustering/exceptions.py +0 -0
  157. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/clustering/models.py +0 -0
  158. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/config.py +0 -0
  159. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/csharp_config_scanner.py +0 -0
  160. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/dotnet_sdk.py +0 -0
  161. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/__init__.py +0 -0
  162. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/adapters/__init__.py +0 -0
  163. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/adapters/python_adapter.py +0 -0
  164. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/hierarchy_builder.py +0 -0
  165. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/process_memory.py +0 -0
  166. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/engine/utils.py +0 -0
  167. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/java_config_scanner.py +0 -0
  168. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/java_utils.py +0 -0
  169. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/language_results.py +0 -0
  170. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/lsp_client/__init__.py +0 -0
  171. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/lsp_client/diagnostics.py +0 -0
  172. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/node.py +0 -0
  173. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/programming_language.py +0 -0
  174. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/reference_resolver.py +0 -0
  175. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/scanner.py +0 -0
  176. {codeboarding-0.14.2 → codeboarding-0.14.4}/static_analyzer/typescript_config_scanner.py +0 -0
  177. {codeboarding-0.14.2 → codeboarding-0.14.4}/telemetry/__init__.py +0 -0
  178. {codeboarding-0.14.2 → codeboarding-0.14.4}/telemetry/device_id.py +0 -0
  179. {codeboarding-0.14.2 → codeboarding-0.14.4}/telemetry/events.py +0 -0
  180. {codeboarding-0.14.2 → codeboarding-0.14.4}/telemetry/schemas.py +0 -0
  181. {codeboarding-0.14.2 → codeboarding-0.14.4}/telemetry/service.py +0 -0
  182. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_cli_parser.py +0 -0
  183. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_github_action.py +0 -0
  184. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_install.py +0 -0
  185. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_logging_config.py +0 -0
  186. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_main.py +0 -0
  187. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_persisted_analysis_artifacts.py +0 -0
  188. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_pyproject_packages.py +0 -0
  189. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_registry_coverage.py +0 -0
  190. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_telemetry_events.py +0 -0
  191. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_telemetry_service.py +0 -0
  192. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_tool_registry.py +0 -0
  193. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_user_config.py +0 -0
  194. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_view_instructions.py +0 -0
  195. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_vscode_constants.py +0 -0
  196. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_windows_compatibility.py +0 -0
  197. {codeboarding-0.14.2 → codeboarding-0.14.4}/tests/test_windows_encoding.py +0 -0
  198. {codeboarding-0.14.2 → codeboarding-0.14.4}/tool_registry/__init__.py +0 -0
  199. {codeboarding-0.14.2 → codeboarding-0.14.4}/tool_registry/installers.py +0 -0
  200. {codeboarding-0.14.2 → codeboarding-0.14.4}/tool_registry/manifest.py +0 -0
  201. {codeboarding-0.14.2 → codeboarding-0.14.4}/tool_registry/paths.py +0 -0
  202. {codeboarding-0.14.2 → codeboarding-0.14.4}/tool_registry/registry.py +0 -0
  203. {codeboarding-0.14.2 → codeboarding-0.14.4}/user_config.py +0 -0
  204. {codeboarding-0.14.2 → codeboarding-0.14.4}/utils.py +0 -0
  205. {codeboarding-0.14.2 → codeboarding-0.14.4}/vscode_constants.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeboarding
3
- Version: 0.14.2
3
+ Version: 0.14.4
4
4
  Summary: Interactive Diagrams for Code
5
5
  Author: CodeBoarding Team
6
6
  License-Expression: MIT
@@ -147,6 +147,13 @@ On first run, CodeBoarding creates `~/.codeboarding/config.toml`. Set one provid
147
147
 
148
148
  Shell environment variables such as `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, and `OLLAMA_BASE_URL` take precedence over the config file. For private repositories, set `GITHUB_TOKEN` in your environment.
149
149
 
150
+ Two environment variables tune the static analysis itself:
151
+
152
+ | Variable | Effect |
153
+ |---|---|
154
+ | `CODEBOARDING_LSP_REQUEST_TIMEOUT` | Seconds a language-server request may block *when it uses the per-language default* (120s for C#, 60s elsewhere). Not a hard cap on every request: the indexing and didOpen-drain probes pass their own scaled budget (60s plus 2s per file, capped at 1800s) and are unaffected. Applies to every language in the run. Unset or empty leaves the defaults alone; any other unusable value fails the run rather than falling back. |
155
+ | `CODEBOARDING_MAX_CONCURRENT_ENGINES` | How many language servers may be resident at once. `0` (the default) leaves the bound off. |
156
+
150
157
  ## Common commands
151
158
 
152
159
  ```bash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeboarding
3
- Version: 0.14.2
3
+ Version: 0.14.4
4
4
  Summary: Interactive Diagrams for Code
5
5
  Author: CodeBoarding Team
6
6
  License-Expression: MIT
@@ -113,6 +113,10 @@ static_analyzer/cluster_relations.py
113
113
  static_analyzer/config.py
114
114
  static_analyzer/csharp_config_scanner.py
115
115
  static_analyzer/dotnet_sdk.py
116
+ static_analyzer/dotnet_solution.py
117
+ static_analyzer/exceptions.py
118
+ static_analyzer/external_calls.py
119
+ static_analyzer/graph_definitions.py
116
120
  static_analyzer/incremental_orchestrator.py
117
121
  static_analyzer/internal_references.py
118
122
  static_analyzer/java_config_scanner.py
@@ -675,7 +675,7 @@ class DiagramGenerator:
675
675
  self.tree_spec = self._stored_tree_spec()
676
676
  self._incremental_preparation = self._prepare_incremental_clustering(root_analysis, sub_analyses, depth)
677
677
  elif target_component is None:
678
- service = ClusteringService(self._grouper())
678
+ service = ClusteringService(self._grouper(), self.repo_location)
679
679
  self.clustering_hierarchy = service.build_full_hierarchy(static_analysis, depth)
680
680
  self.tree_spec = service.spec
681
681
  else:
@@ -1031,7 +1031,7 @@ class DiagramGenerator:
1031
1031
  assert self.tree_spec is not None
1032
1032
  persisted = {ROOT_SCOPE_ID: root_analysis, **sub_analyses}
1033
1033
  # Scopes the specification never reached are drafted deterministically here.
1034
- service = ClusteringService()
1034
+ service = ClusteringService(repo_dir=self.repo_location)
1035
1035
  self.clustering_hierarchy = service.build_incremental_hierarchy(
1036
1036
  self.static_analysis,
1037
1037
  hierarchy_depth,
@@ -1252,7 +1252,7 @@ class DiagramGenerator:
1252
1252
  raise ClusteringScopeUnavailableError(component.component_id, "no owned CFG nodes")
1253
1253
  remaining_depth = max(1, hierarchy_depth - _component_depth(component.component_id))
1254
1254
  assert self.tree_spec is not None
1255
- service = ClusteringService(self._grouper())
1255
+ service = ClusteringService(self._grouper(), self.repo_location)
1256
1256
  # The service replays the specification from the root down to this component, so the
1257
1257
  # scope holds the units a full run placed in it, data-only files included.
1258
1258
  scope = service.build_scope_hierarchy(graphs, remaining_depth, component.component_id, self.tree_spec)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codeboarding"
7
- version = "0.14.2"
7
+ version = "0.14.4"
8
8
  description = "Interactive Diagrams for Code"
9
9
  readme = "PYPI.md"
10
10
  license = "MIT"
@@ -12,15 +12,20 @@ from static_analyzer.analysis_cache import StaticAnalysisCache
12
12
  from static_analyzer.analysis_result import StaticAnalysisResults
13
13
  from static_analyzer.cfg import CallGraph
14
14
  from static_analyzer.config import AdapterName, Language
15
- from static_analyzer.csharp_config_scanner import CSharpConfigScanner
15
+ from static_analyzer.csharp_config_scanner import SOLUTION_GLOBS, CSharpConfigScanner, CSharpProjectConfig
16
+ from static_analyzer.dotnet_solution import solution_projects
16
17
  from static_analyzer.engine.adapters import get_adapter
17
18
  from static_analyzer.engine.call_graph_builder import CallGraphBuilder
18
19
  from static_analyzer.engine.language_adapter import LanguageAdapter
19
20
  from static_analyzer.engine.lsp_client import LSPClient
20
21
  from static_analyzer.engine.lsp_recycler import default_memory_budget, per_engine_memory_budget
22
+ from static_analyzer.engine.models import ExternalCallSite
21
23
  from static_analyzer.engine.result_converter import convert_to_codeboarding_format
22
24
  from static_analyzer.engine.source_inspector import SourceInspector
23
25
  from static_analyzer.engine.utils import uri_to_path
26
+ from static_analyzer.exceptions import StaticAnalysisFatalError
27
+ from static_analyzer.external_calls import link_external_call_sites, record_package_imports
28
+ from static_analyzer.graph_definitions import GraphIndex
24
29
  from static_analyzer.incremental_orchestrator import update_cfg_for_changed_files
25
30
  from static_analyzer.java_config_scanner import JavaConfigScanner
26
31
  from static_analyzer.lsp_client.diagnostics import FileDiagnosticsMap
@@ -39,20 +44,21 @@ class EngineConfig:
39
44
  """One adapter + project root the engine should run.
40
45
 
41
46
  ``source_files`` is non-empty only when a scanner has authoritatively
42
- resolved file membership (currently TypeScript via ``tsc --showConfig``);
43
- otherwise the adapter walks ``project_path`` itself in ``_run_full_analysis``.
47
+ resolved file membership (TypeScript via ``tsc --showConfig``, C# by what
48
+ each root's solution lists); otherwise the adapter walks ``project_path``
49
+ itself in ``_run_full_analysis``. A config whose membership resolved to no
50
+ file is not created at all.
44
51
  """
45
52
 
46
53
  adapter: LanguageAdapter
47
54
  project_path: Path
48
55
  source_files: list[Path] = field(default_factory=list)
49
-
50
-
51
- class StaticAnalysisFatalError(RuntimeError):
52
- """Raised when continuing would produce misleading cached analysis."""
56
+ # Retain discovery exclusions for incremental edits, including deleted files.
57
+ excluded_roots: list[Path] = field(default_factory=list)
53
58
 
54
59
 
55
60
  MAX_CONCURRENT_ENGINES_ENV_VAR = "CODEBOARDING_MAX_CONCURRENT_ENGINES"
61
+ LSP_REQUEST_TIMEOUT_ENV_VAR = "CODEBOARDING_LSP_REQUEST_TIMEOUT"
56
62
 
57
63
 
58
64
  # An engine costs ~3 cores: the server's own peak (~1.9, measured on csharp-ls),
@@ -84,6 +90,24 @@ def max_concurrent_engines() -> int:
84
90
  return value
85
91
 
86
92
 
93
+ def lsp_request_timeout_override() -> int | None:
94
+ """Seconds from ``CODEBOARDING_LSP_REQUEST_TIMEOUT``, or None when it is unset.
95
+
96
+ Why it raises rather than falling back: the fallback is the adapter ceiling the
97
+ operator set the variable to escape, so the run would time out as if ignored.
98
+ """
99
+ raw = os.environ.get(LSP_REQUEST_TIMEOUT_ENV_VAR, "").strip()
100
+ if not raw:
101
+ return None
102
+ try:
103
+ seconds = int(raw)
104
+ except ValueError:
105
+ raise ValueError(f"{LSP_REQUEST_TIMEOUT_ENV_VAR} must be a whole number of seconds, got {raw!r}") from None
106
+ if seconds <= 0:
107
+ raise ValueError(f"{LSP_REQUEST_TIMEOUT_ENV_VAR} must be a positive number of seconds, got {raw!r}")
108
+ return seconds
109
+
110
+
87
111
  def recommended_engine_concurrency(engine_count: int) -> int:
88
112
  """Engine concurrency bounded by available work, cores, and memory.
89
113
 
@@ -121,6 +145,15 @@ def _adapter_names_for(programming_languages: list[ProgrammingLanguage]) -> list
121
145
  return names
122
146
 
123
147
 
148
+ def _csharp_solution_members(csharp_projects: list[CSharpProjectConfig]) -> dict[Path, list[Path]]:
149
+ """Per C# root, the project files its solution files list."""
150
+ members: dict[Path, list[Path]] = {}
151
+ for config in csharp_projects:
152
+ solutions = [path for pattern in SOLUTION_GLOBS for path in config.root.glob(pattern)]
153
+ members[config.root] = [project for solution in solutions for project in solution_projects(solution)]
154
+ return members
155
+
156
+
124
157
  def _create_engine_configs(
125
158
  programming_languages: list[ProgrammingLanguage],
126
159
  repository_path: Path,
@@ -198,12 +231,32 @@ def _create_engine_configs(
198
231
  csharp_projects = csharp_scanner.scan()
199
232
 
200
233
  if csharp_projects:
234
+ members = _csharp_solution_members(csharp_projects)
201
235
  for csharp_config in csharp_projects:
202
236
  logger.info(
203
237
  f"Creating engine config for CSharp ({csharp_config.project_type}) at: "
204
238
  f"{csharp_config.root.relative_to(repository_path)}"
205
239
  )
206
- configs.append(EngineConfig(adapter, csharp_config.root))
240
+ # A project a nested root's solution lists, and this root's solutions do not,
241
+ # is that engine's; naming it here as well would put it in the graph twice.
242
+ elsewhere = [
243
+ project.parent
244
+ for root, projects in members.items()
245
+ if root != csharp_config.root and root.is_relative_to(csharp_config.root)
246
+ for project in projects
247
+ if project not in members[csharp_config.root]
248
+ ]
249
+ source_files = adapter.discover_source_files(csharp_config.root, ignore_manager, elsewhere)
250
+ if not source_files:
251
+ logger.info(
252
+ f"Every C# file under {csharp_config.root} belongs to a nested solution; skipping"
253
+ )
254
+ continue
255
+ configs.append(
256
+ EngineConfig(
257
+ adapter, csharp_config.root, source_files=source_files, excluded_roots=elsewhere
258
+ )
259
+ )
207
260
  else:
208
261
  logger.info("No C# projects detected")
209
262
 
@@ -254,6 +307,11 @@ class StaticAnalyzer:
254
307
  # e.g. the incremental fingerprint diff. ``None`` means "detect via git"
255
308
  # (the legacy CLI-on-a-real-checkout path); an empty set re-LSPs nothing.
256
309
  self.changed_files = changed_files
310
+ # Resolved here so an unusable value fails before any engine starts, where
311
+ # the per-language handlers would turn it into "skip this language".
312
+ self._request_timeout_override = lsp_request_timeout_override()
313
+ if self._request_timeout_override is not None:
314
+ logger.info("Per-request LSP timeout overridden to %ds", self._request_timeout_override)
257
315
 
258
316
  def __enter__(self) -> "StaticAnalyzer":
259
317
  self.start_clients()
@@ -402,7 +460,7 @@ class StaticAnalyzer:
402
460
  command=command,
403
461
  project_root=project_path,
404
462
  init_options=init_options,
405
- default_timeout=adapter.get_lsp_default_timeout(),
463
+ default_timeout=self._request_timeout_override or adapter.get_lsp_default_timeout(),
406
464
  collect_diagnostics=True,
407
465
  extra_env=extra_env,
408
466
  workspace_settings=adapter.get_workspace_settings(),
@@ -580,7 +638,7 @@ class StaticAnalyzer:
580
638
 
581
639
  Returns:
582
640
  Deduplicated list of absolute file paths that the file depends on.
583
- Returns an empty list if no matching client is found or on failure.
641
+ Returns an empty list if no client serves the file; a failed query raises.
584
642
  """
585
643
  suffix = file_path.suffix
586
644
  client = next(
@@ -594,34 +652,30 @@ class StaticAnalyzer:
594
652
  if client is None:
595
653
  return []
596
654
 
597
- try:
598
- call_sites = SourceInspector().find_call_sites(file_path)
599
- if not call_sites:
600
- return []
601
-
602
- queries = [(file_path, site.lsp_line, site.lsp_column) for site in call_sites]
603
- results, _ = client.send_definition_batch(queries)
604
-
605
- resolved = file_path.resolve()
606
- unique_paths: set[str] = set()
607
- for definitions in results:
608
- for defn in definitions:
609
- uri = defn.get("targetUri", defn.get("uri", ""))
610
- if not uri.startswith("file://"):
611
- continue
612
- dep_path_obj = uri_to_path(uri)
613
- if dep_path_obj is None:
614
- continue
615
- dep_path = str(dep_path_obj)
616
- if dep_path != str(resolved):
617
- unique_paths.add(dep_path)
618
-
619
- logger.debug(f"Discovered {len(unique_paths)} dependencies for {file_path}")
620
- return list(unique_paths)
621
- except Exception:
622
- logger.warning(f"Failed to discover dependencies for {file_path}", exc_info=True)
655
+ call_sites = SourceInspector().find_call_sites(file_path)
656
+ if not call_sites:
623
657
  return []
624
658
 
659
+ queries = [(file_path, site.lsp_line, site.lsp_column) for site in call_sites]
660
+ results = client.send_definition_batch(queries)
661
+
662
+ resolved = file_path.resolve()
663
+ unique_paths: set[str] = set()
664
+ for definitions in results:
665
+ for defn in definitions:
666
+ uri = defn.get("targetUri", defn.get("uri", ""))
667
+ if not uri.startswith("file://"):
668
+ continue
669
+ dep_path_obj = uri_to_path(uri)
670
+ if dep_path_obj is None:
671
+ continue
672
+ dep_path = str(dep_path_obj)
673
+ if dep_path != str(resolved):
674
+ unique_paths.add(dep_path)
675
+
676
+ logger.debug(f"Discovered {len(unique_paths)} dependencies for {file_path}")
677
+ return list(unique_paths)
678
+
625
679
  def analyze(
626
680
  self,
627
681
  cache_dir: Path,
@@ -702,7 +756,7 @@ class StaticAnalyzer:
702
756
  # order. The merges replace on key collision, and overlapping configs
703
757
  # (nested solution roots) do collide, so completion order would let two
704
758
  # identical runs keep different nodes and produce different component IDs.
705
- completed: dict[int, tuple[Language, dict]] = {}
759
+ completed: dict[int, tuple[LanguageAdapter, dict]] = {}
706
760
 
707
761
  def run_one(engine_config: EngineConfig, engine_client: LSPClient | None, order: int = 0) -> None:
708
762
  """Analyze one engine. Owns the client's lifetime when given none."""
@@ -726,7 +780,7 @@ class StaticAnalyzer:
726
780
  duration_ms = round((time.monotonic() - t_lang_start) * 1000)
727
781
  logger.info(f"Engine analysis for {adapter.language} completed in {duration_ms / 1000:.1f}s")
728
782
  with absorb_lock:
729
- completed[order] = (language, analysis)
783
+ completed[order] = (adapter, analysis)
730
784
  self._collect_diagnostics_for(adapter, engine_client, analysis)
731
785
  track_lsp_result(
732
786
  language=adapter.language_enum.value,
@@ -783,9 +837,7 @@ class StaticAnalyzer:
783
837
  f"(attempted: {', '.join(cfg.adapter.language for cfg in pending)}){details}"
784
838
  )
785
839
 
786
- for order in sorted(completed):
787
- language, analysis = completed[order]
788
- self._absorb_into_results(results, language, analysis)
840
+ self._absorb_and_link(results, [completed[order] for order in sorted(completed)])
789
841
 
790
842
  summaries = []
791
843
  for language in results.get_languages():
@@ -829,6 +881,8 @@ class StaticAnalyzer:
829
881
  # so a config that took its own copy of the cache would hand back the nodes another
830
882
  # config had just invalidated, and the merge would resurrect deleted files.
831
883
  carried: dict[Language, dict] = {}
884
+ carried_adapters: dict[Language, LanguageAdapter] = {}
885
+ rebuilt: list[tuple[LanguageAdapter, dict]] = []
832
886
  for engine_config, engine_client in self._live_clients("warm-start"):
833
887
  adapter, project_path = engine_config.adapter, engine_config.project_path
834
888
  language = adapter.results_language
@@ -837,14 +891,26 @@ class StaticAnalyzer:
837
891
 
838
892
  if changed_files is None:
839
893
  analysis = self._run_full_analysis(engine_config, engine_client)
840
- self._absorb_into_results(results, language, analysis)
894
+ rebuilt.append((adapter, analysis))
841
895
  else:
896
+ changed_files = {
897
+ path
898
+ for path in changed_files
899
+ if not any(path.is_relative_to(root) for root in engine_config.excluded_roots)
900
+ }
842
901
  logger.info(f"warmstart {adapter.language}: re-LSPing {len(changed_files)} changed file(s)")
843
902
  cached_lang_dict = carried.get(language) or self._extract_language_dict(cached_results, language)
844
903
  analysis = update_cfg_for_changed_files(
845
- cached_lang_dict, changed_files, adapter, project_path, engine_client, self.ignore_manager
904
+ cached_lang_dict,
905
+ changed_files,
906
+ adapter,
907
+ project_path,
908
+ self.repository_path,
909
+ engine_client,
910
+ self.ignore_manager,
846
911
  )
847
912
  carried[language] = analysis
913
+ carried_adapters[language] = adapter
848
914
 
849
915
  self._collect_diagnostics_for(adapter, engine_client, analysis)
850
916
  track_lsp_result(
@@ -855,8 +921,9 @@ class StaticAnalyzer:
855
921
  analysis=analysis,
856
922
  diagnostics=self.collected_diagnostics.get(adapter.results_language, {}),
857
923
  )
858
- for language, analysis in carried.items():
859
- self._absorb_into_results(results, language, analysis)
924
+ self._absorb_and_link(
925
+ results, rebuilt + [(carried_adapters[language], analysis) for language, analysis in carried.items()]
926
+ )
860
927
  results.incremental_base_results = cached_results
861
928
  return results
862
929
 
@@ -905,6 +972,25 @@ class StaticAnalyzer:
905
972
  "diagnostics": cached_results.diagnostics.get(language, {}),
906
973
  }
907
974
 
975
+ def _absorb_and_link(self, results: StaticAnalysisResults, analyses: list[tuple[LanguageAdapter, dict]]) -> None:
976
+ """Absorb every engine's analysis, then finish the calls whose definitions lie in another engine's files.
977
+
978
+ Why after all of them: only the merged graph holds every engine's nodes, so a call into
979
+ another solution's project can find its target, whichever engine ran first.
980
+ """
981
+ for adapter, analysis in analyses:
982
+ self._absorb_into_results(results, adapter.results_language, analysis)
983
+ inspector = SourceInspector()
984
+ for adapter, analysis in analyses:
985
+ sites: list[ExternalCallSite] = analysis.get("external_call_sites", [])
986
+ if not sites:
987
+ continue
988
+ language = adapter.results_language
989
+ analysed_files = {str(path) for path in analysis.get("source_files", [])}
990
+ index = GraphIndex(results.get_cfg(language))
991
+ linked = link_external_call_sites(index, sites, adapter, inspector, analysed_files)
992
+ record_package_imports(results.get_package_dependencies(language), adapter, linked.edges)
993
+
908
994
  def _absorb_into_results(self, results: StaticAnalysisResults, language: Language, analysis: dict) -> None:
909
995
  """Stuff one language's analysis-dict into the shared ``StaticAnalysisResults``."""
910
996
  results.add_references(language, analysis.get("references", []))
@@ -988,7 +1074,7 @@ class StaticAnalyzer:
988
1074
  engine_client,
989
1075
  adapter,
990
1076
  project_path,
991
- memory_budget_bytes=per_engine_memory_budget(max(max_concurrent_engines(), 1)),
1077
+ self.repository_path,
992
1078
  )
993
1079
  engine_result = builder.build(source_files)
994
1080
  logger.info(f"CallGraphBuilder.build() for {adapter.language}: {time.monotonic() - t_build_start:.1f}s")
@@ -67,8 +67,13 @@ _LEGACY_CACHE_SUBDIR = "cache"
67
67
  # named after.
68
68
  # v7: the pickle no longer carries cluster lineage; components come from the tree
69
69
  # specification in analysis.json, replayed over the graph.
70
+ # v8: a C# file compiled into several projects has symbols and callers, and a
71
+ # nested solution's files are named by its own engine only.
72
+ # v9: a call into a project of another solution root is an edge.
73
+ # v10: every name is spelled from the repository root; C# keeps ``src`` and Java its source root.
74
+ # v11: every language builds its call edges from definitions at the call site.
70
75
  # Older pickles are treated as cache misses and re-run.
71
- _TAG_VERSION = "v7"
76
+ _TAG_VERSION = "v11"
72
77
 
73
78
 
74
79
  class StaticAnalysisCache:
@@ -17,17 +17,14 @@ from static_analyzer.clustering.names.frontier import Candidate, Frontier, walk
17
17
  from static_analyzer.clustering.names.inventory import (
18
18
  Trie,
19
19
  Unit,
20
- unit_key,
21
- unit_position,
22
20
  units_from_graph,
23
21
  units_from_graphs,
24
22
  )
25
23
  from static_analyzer.clustering.names.replay import Partition, replay
26
24
  from static_analyzer.clustering.names.spec import ComponentRule, ScopeSpec, TreeSpec
27
- from static_analyzer.clustering.names.tokens import LAYOUT_WORDS, ROLE_WORDS, stem, tokenize
25
+ from static_analyzer.clustering.names.tokens import ROLE_WORDS, stem, tokenize
28
26
 
29
27
  __all__ = [
30
- "LAYOUT_WORDS",
31
28
  "ROLE_WORDS",
32
29
  "AffinityGrouper",
33
30
  "Candidate",
@@ -48,8 +45,6 @@ __all__ = [
48
45
  "role_words_for",
49
46
  "stem",
50
47
  "tokenize",
51
- "unit_key",
52
- "unit_position",
53
48
  "units_from_graph",
54
49
  "units_from_graphs",
55
50
  "walk",