codeboarding 0.12.2__tar.gz → 0.12.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 (192) hide show
  1. {codeboarding-0.12.2/codeboarding.egg-info → codeboarding-0.12.4}/PKG-INFO +1 -1
  2. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/llm_config.py +20 -4
  3. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/read_file_structure.py +1 -1
  4. {codeboarding-0.12.2 → codeboarding-0.12.4/codeboarding.egg-info}/PKG-INFO +1 -1
  5. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding.egg-info/SOURCES.txt +0 -1
  6. {codeboarding-0.12.2 → codeboarding-0.12.4}/pyproject.toml +1 -1
  7. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/analysis_cache.py +77 -83
  8. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/analysis_result.py +44 -0
  9. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/graph.py +8 -2
  10. codeboarding-0.12.4/static_analyzer/incremental_orchestrator.py +338 -0
  11. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/scanner.py +1 -1
  12. codeboarding-0.12.2/health_main.py +0 -152
  13. codeboarding-0.12.2/static_analyzer/incremental_orchestrator.py +0 -125
  14. {codeboarding-0.12.2 → codeboarding-0.12.4}/LICENSE +0 -0
  15. {codeboarding-0.12.2 → codeboarding-0.12.4}/PYPI.md +0 -0
  16. {codeboarding-0.12.2 → codeboarding-0.12.4}/README.md +0 -0
  17. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/__init__.py +0 -0
  18. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/abstraction_agent.py +0 -0
  19. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/agent.py +0 -0
  20. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/agent_responses.py +0 -0
  21. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/change_status.py +0 -0
  22. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/cluster_budget.py +0 -0
  23. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/cluster_methods_mixin.py +0 -0
  24. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/constants.py +0 -0
  25. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/dependency_discovery.py +0 -0
  26. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/details_agent.py +0 -0
  27. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/incremental_agent.py +0 -0
  28. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/meta_agent.py +0 -0
  29. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/model_capabilities.py +0 -0
  30. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/planner_agent.py +0 -0
  31. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/prompts/__init__.py +0 -0
  32. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/prompts/abstract_prompt_factory.py +0 -0
  33. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/prompts/claude_prompts.py +0 -0
  34. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/prompts/deepseek_prompts.py +0 -0
  35. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/prompts/gemini_flash_prompts.py +0 -0
  36. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/prompts/glm_prompts.py +0 -0
  37. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/prompts/gpt_prompts.py +0 -0
  38. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/prompts/kimi_prompts.py +0 -0
  39. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/prompts/prompt_factory.py +0 -0
  40. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/retry.py +0 -0
  41. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/__init__.py +0 -0
  42. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/base.py +0 -0
  43. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/get_external_deps.py +0 -0
  44. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/get_method_invocations.py +0 -0
  45. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/read_cfg.py +0 -0
  46. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/read_docs.py +0 -0
  47. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/read_file.py +0 -0
  48. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/read_packages.py +0 -0
  49. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/read_source.py +0 -0
  50. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/read_structure.py +0 -0
  51. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/tools/toolkit.py +0 -0
  52. {codeboarding-0.12.2 → codeboarding-0.12.4}/agents/validation.py +0 -0
  53. {codeboarding-0.12.2 → codeboarding-0.12.4}/caching/__init__.py +0 -0
  54. {codeboarding-0.12.2 → codeboarding-0.12.4}/caching/cache.py +0 -0
  55. {codeboarding-0.12.2 → codeboarding-0.12.4}/caching/details_cache.py +0 -0
  56. {codeboarding-0.12.2 → codeboarding-0.12.4}/caching/meta_cache.py +0 -0
  57. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding.egg-info/dependency_links.txt +0 -0
  58. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding.egg-info/entry_points.txt +0 -0
  59. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding.egg-info/requires.txt +0 -0
  60. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding.egg-info/top_level.txt +0 -0
  61. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_cli/__init__.py +0 -0
  62. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_cli/bootstrap.py +0 -0
  63. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_cli/commands/__init__.py +0 -0
  64. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_cli/commands/full_analysis.py +0 -0
  65. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_cli/commands/incremental_analysis.py +0 -0
  66. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_cli/commands/partial_analysis.py +0 -0
  67. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_workflows/__init__.py +0 -0
  68. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_workflows/analysis.py +0 -0
  69. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_workflows/orchestration.py +0 -0
  70. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_workflows/rendering.py +0 -0
  71. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_workflows/sources/__init__.py +0 -0
  72. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_workflows/sources/local.py +0 -0
  73. {codeboarding-0.12.2 → codeboarding-0.12.4}/codeboarding_workflows/sources/remote.py +0 -0
  74. {codeboarding-0.12.2 → codeboarding-0.12.4}/constants.py +0 -0
  75. {codeboarding-0.12.2 → codeboarding-0.12.4}/core/__init__.py +0 -0
  76. {codeboarding-0.12.2 → codeboarding-0.12.4}/core/plugin_loader.py +0 -0
  77. {codeboarding-0.12.2 → codeboarding-0.12.4}/core/protocols.py +0 -0
  78. {codeboarding-0.12.2 → codeboarding-0.12.4}/core/registry.py +0 -0
  79. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/__init__.py +0 -0
  80. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/analysis_json.py +0 -0
  81. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/cluster_delta.py +0 -0
  82. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/cluster_snapshot.py +0 -0
  83. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/diagram_generator.py +0 -0
  84. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/exceptions.py +0 -0
  85. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/file_coverage.py +0 -0
  86. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/io_utils.py +0 -0
  87. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/run_context.py +0 -0
  88. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/run_mode.py +0 -0
  89. {codeboarding-0.12.2 → codeboarding-0.12.4}/diagram_analysis/version.py +0 -0
  90. {codeboarding-0.12.2 → codeboarding-0.12.4}/github_action.py +0 -0
  91. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/__init__.py +0 -0
  92. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/checks/__init__.py +0 -0
  93. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/checks/circular_deps.py +0 -0
  94. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/checks/cohesion.py +0 -0
  95. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/checks/coupling.py +0 -0
  96. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/checks/function_size.py +0 -0
  97. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/checks/god_class.py +0 -0
  98. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/checks/inheritance.py +0 -0
  99. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/checks/instability.py +0 -0
  100. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/checks/unused_code_diagnostics.py +0 -0
  101. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/config.py +0 -0
  102. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/models.py +0 -0
  103. {codeboarding-0.12.2 → codeboarding-0.12.4}/health/runner.py +0 -0
  104. {codeboarding-0.12.2 → codeboarding-0.12.4}/install.py +0 -0
  105. {codeboarding-0.12.2 → codeboarding-0.12.4}/logging_config.py +0 -0
  106. {codeboarding-0.12.2 → codeboarding-0.12.4}/main.py +0 -0
  107. {codeboarding-0.12.2 → codeboarding-0.12.4}/monitoring/__init__.py +0 -0
  108. {codeboarding-0.12.2 → codeboarding-0.12.4}/monitoring/callbacks.py +0 -0
  109. {codeboarding-0.12.2 → codeboarding-0.12.4}/monitoring/context.py +0 -0
  110. {codeboarding-0.12.2 → codeboarding-0.12.4}/monitoring/mixin.py +0 -0
  111. {codeboarding-0.12.2 → codeboarding-0.12.4}/monitoring/paths.py +0 -0
  112. {codeboarding-0.12.2 → codeboarding-0.12.4}/monitoring/stats.py +0 -0
  113. {codeboarding-0.12.2 → codeboarding-0.12.4}/monitoring/writers.py +0 -0
  114. {codeboarding-0.12.2 → codeboarding-0.12.4}/output_generators/__init__.py +0 -0
  115. {codeboarding-0.12.2 → codeboarding-0.12.4}/output_generators/html.py +0 -0
  116. {codeboarding-0.12.2 → codeboarding-0.12.4}/output_generators/html_template.py +0 -0
  117. {codeboarding-0.12.2 → codeboarding-0.12.4}/output_generators/markdown.py +0 -0
  118. {codeboarding-0.12.2 → codeboarding-0.12.4}/output_generators/mdx.py +0 -0
  119. {codeboarding-0.12.2 → codeboarding-0.12.4}/output_generators/sphinx.py +0 -0
  120. {codeboarding-0.12.2 → codeboarding-0.12.4}/repo_utils/__init__.py +0 -0
  121. {codeboarding-0.12.2 → codeboarding-0.12.4}/repo_utils/change_detector.py +0 -0
  122. {codeboarding-0.12.2 → codeboarding-0.12.4}/repo_utils/diff_parser.py +0 -0
  123. {codeboarding-0.12.2 → codeboarding-0.12.4}/repo_utils/errors.py +0 -0
  124. {codeboarding-0.12.2 → codeboarding-0.12.4}/repo_utils/git_ops.py +0 -0
  125. {codeboarding-0.12.2 → codeboarding-0.12.4}/repo_utils/ignore.py +0 -0
  126. {codeboarding-0.12.2 → codeboarding-0.12.4}/setup.cfg +0 -0
  127. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/__init__.py +0 -0
  128. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/cfg_skip_planner.py +0 -0
  129. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/cluster_helpers.py +0 -0
  130. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/cluster_relations.py +0 -0
  131. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/constants.py +0 -0
  132. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/csharp_config_scanner.py +0 -0
  133. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/dotnet_sdk.py +0 -0
  134. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/__init__.py +0 -0
  135. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/adapters/__init__.py +0 -0
  136. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/adapters/csharp_adapter.py +0 -0
  137. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/adapters/go_adapter.py +0 -0
  138. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/adapters/java_adapter.py +0 -0
  139. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/adapters/php_adapter.py +0 -0
  140. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/adapters/python_adapter.py +0 -0
  141. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/adapters/rust_adapter.py +0 -0
  142. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/adapters/typescript_adapter.py +0 -0
  143. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/call_graph_builder.py +0 -0
  144. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/edge_build_context.py +0 -0
  145. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/edge_builder.py +0 -0
  146. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/hierarchy_builder.py +0 -0
  147. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/language_adapter.py +0 -0
  148. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/lsp_client.py +0 -0
  149. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/lsp_constants.py +0 -0
  150. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/models.py +0 -0
  151. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/progress.py +0 -0
  152. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/protocols.py +0 -0
  153. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/result_converter.py +0 -0
  154. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/source_inspector.py +0 -0
  155. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/symbol_table.py +0 -0
  156. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/engine/utils.py +0 -0
  157. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/java_config_scanner.py +0 -0
  158. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/java_utils.py +0 -0
  159. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/language_results.py +0 -0
  160. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/leiden_utils.py +0 -0
  161. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/lsp_client/__init__.py +0 -0
  162. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/lsp_client/diagnostics.py +0 -0
  163. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/node.py +0 -0
  164. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/programming_language.py +0 -0
  165. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/reference_resolve_mixin.py +0 -0
  166. {codeboarding-0.12.2 → codeboarding-0.12.4}/static_analyzer/typescript_config_scanner.py +0 -0
  167. {codeboarding-0.12.2 → codeboarding-0.12.4}/telemetry/__init__.py +0 -0
  168. {codeboarding-0.12.2 → codeboarding-0.12.4}/telemetry/device_id.py +0 -0
  169. {codeboarding-0.12.2 → codeboarding-0.12.4}/telemetry/events.py +0 -0
  170. {codeboarding-0.12.2 → codeboarding-0.12.4}/telemetry/schemas.py +0 -0
  171. {codeboarding-0.12.2 → codeboarding-0.12.4}/telemetry/service.py +0 -0
  172. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_cli_parser.py +0 -0
  173. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_github_action.py +0 -0
  174. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_install.py +0 -0
  175. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_logging_config.py +0 -0
  176. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_main.py +0 -0
  177. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_pyproject_packages.py +0 -0
  178. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_registry_coverage.py +0 -0
  179. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_telemetry_events.py +0 -0
  180. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_tool_registry.py +0 -0
  181. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_user_config.py +0 -0
  182. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_vscode_constants.py +0 -0
  183. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_windows_compatibility.py +0 -0
  184. {codeboarding-0.12.2 → codeboarding-0.12.4}/tests/test_windows_encoding.py +0 -0
  185. {codeboarding-0.12.2 → codeboarding-0.12.4}/tool_registry/__init__.py +0 -0
  186. {codeboarding-0.12.2 → codeboarding-0.12.4}/tool_registry/installers.py +0 -0
  187. {codeboarding-0.12.2 → codeboarding-0.12.4}/tool_registry/manifest.py +0 -0
  188. {codeboarding-0.12.2 → codeboarding-0.12.4}/tool_registry/paths.py +0 -0
  189. {codeboarding-0.12.2 → codeboarding-0.12.4}/tool_registry/registry.py +0 -0
  190. {codeboarding-0.12.2 → codeboarding-0.12.4}/user_config.py +0 -0
  191. {codeboarding-0.12.2 → codeboarding-0.12.4}/utils.py +0 -0
  192. {codeboarding-0.12.2 → codeboarding-0.12.4}/vscode_constants.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeboarding
3
- Version: 0.12.2
3
+ Version: 0.12.4
4
4
  Summary: Interactive Diagrams for Code
5
5
  Author: CodeBoarding Team
6
6
  License-Expression: MIT
@@ -25,6 +25,23 @@ logger = logging.getLogger(__name__)
25
25
 
26
26
  _OPENROUTER_FALLBACK_CONTEXT_WINDOW = ContextWindow(1_048_576, 65_536, is_fallback=True)
27
27
 
28
+ # Model families that reject sampling params (temperature/top_p/top_k) with HTTP 400.
29
+ # Why: Anthropic removed them starting with Opus 4.7; sending temperature (even 0) 400s.
30
+ # Substrings match bare IDs and Bedrock-prefixed forms (e.g. us.anthropic.claude-opus-4-8-v1:0).
31
+ _SAMPLING_PARAM_FREE_MODEL_SUBSTRINGS = (
32
+ "claude-opus-4-7",
33
+ "claude-opus-4-8",
34
+ "claude-fable-5",
35
+ "claude-mythos-5",
36
+ )
37
+
38
+
39
+ def _model_accepts_temperature(model_name: str) -> bool:
40
+ """False for models that reject sampling params; temperature must be omitted for those."""
41
+ lowered = model_name.lower()
42
+ return not any(s in lowered for s in _SAMPLING_PARAM_FREE_MODEL_SUBSTRINGS)
43
+
44
+
28
45
  # ---------------------------------------------------------------------------
29
46
  # Module-level model overrides – set once by the orchestrator (main.py) and
30
47
  # consumed by initialize_llms() without needing to thread the values through
@@ -341,10 +358,9 @@ def _initialize_llm(
341
358
 
342
359
  logger.info(f"Using {name.title()} {log_prefix}LLM with model: {model_name}")
343
360
 
344
- kwargs = {
345
- "model": model_name,
346
- "temperature": getattr(config, temperature_attr),
347
- }
361
+ kwargs: dict[str, Any] = {"model": model_name}
362
+ if _model_accepts_temperature(model_name):
363
+ kwargs["temperature"] = getattr(config, temperature_attr)
348
364
  kwargs.update(config.get_resolved_extra_args())
349
365
 
350
366
  # ChatBedrockConverse and ChatOllama take no api_key kwarg; their SDKs read
@@ -36,7 +36,7 @@ class FileStructureTool(BaseRepoTool):
36
36
  # Ensure they are sorted by depth for is_subsequence logic
37
37
  return sorted(dirs, key=lambda x: len(x.parts))
38
38
 
39
- def _run(self, dir: str) -> str:
39
+ def _run(self, dir: str = ".") -> str:
40
40
  """
41
41
  Run the tool with the given input.
42
42
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeboarding
3
- Version: 0.12.2
3
+ Version: 0.12.4
4
4
  Summary: Interactive Diagrams for Code
5
5
  Author: CodeBoarding Team
6
6
  License-Expression: MIT
@@ -3,7 +3,6 @@ PYPI.md
3
3
  README.md
4
4
  constants.py
5
5
  github_action.py
6
- health_main.py
7
6
  install.py
8
7
  logging_config.py
9
8
  main.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codeboarding"
7
- version = "0.12.2"
7
+ version = "0.12.4"
8
8
  description = "Interactive Diagrams for Code"
9
9
  readme = "PYPI.md"
10
10
  license = "MIT"
@@ -29,7 +29,8 @@ from typing import TYPE_CHECKING, Any
29
29
 
30
30
  from filelock import FileLock
31
31
 
32
- from static_analyzer.graph import CallGraph
32
+ from static_analyzer.analysis_result import AnalysisData, InvalidatedAnalysis, InvalidatedEdge
33
+ from static_analyzer.graph import Edge
33
34
  from static_analyzer.lsp_client.diagnostics import FileDiagnosticsMap
34
35
  from static_analyzer.node import Node
35
36
  from utils import to_absolute_path, to_relative_path
@@ -332,67 +333,67 @@ def _atomic_copy(src: Path, dest: Path) -> None:
332
333
  raise
333
334
 
334
335
 
335
- def invalidate_files(analysis_result: dict[str, Any], changed_files: set[Path]) -> dict[str, Any]:
336
+ def invalidate_files(analysis_result: dict[str, Any], changed_files: set[Path]) -> InvalidatedAnalysis:
336
337
  """Return a copy of *analysis_result* with every entry from *changed_files* removed.
337
338
 
338
339
  Drops nodes whose ``file_path`` is in the change set, cascades edges that
339
- reference dropped nodes, drops class hierarchies and references from the
340
- same files, prunes package relations to surviving files, and filters
341
- ``source_files`` / ``diagnostics`` accordingly. Raises ``ValueError`` if
342
- the result has dangling edges or references after filtering.
340
+ reference dropped nodes, remembers cross-boundary edges for later LSP
341
+ validation, drops class hierarchies and references from the same files,
342
+ prunes package relations to surviving files, and filters ``source_files`` /
343
+ ``diagnostics`` accordingly. Raises ``ValueError`` if the result has
344
+ dangling edges or references after filtering.
343
345
  """
344
346
  changed_file_strs = {str(path) for path in changed_files}
345
347
 
346
- call_graph: CallGraph = analysis_result["call_graph"]
347
- filtered_cg = call_graph.filter(lambda node: node.file_path not in changed_file_strs)
348
+ cached = AnalysisData.from_dict(analysis_result)
349
+ call_graph = cached.call_graph
350
+ invalidated_edges: list[InvalidatedEdge] = []
351
+ filtered_cg = call_graph.filter(
352
+ lambda node: node.file_path not in changed_file_strs,
353
+ on_dropped_edge=lambda edge: _collect_invalidated_edge(edge, changed_file_strs, invalidated_edges),
354
+ )
348
355
 
349
- updated_result: dict[str, Any] = {
350
- "call_graph": filtered_cg,
351
- "class_hierarchies": {},
352
- "package_relations": {},
353
- "references": [],
354
- "source_files": [],
355
- }
356
+ diagnostics = None
357
+ if cached.diagnostics is not None:
358
+ diagnostics = {fp: diags for fp, diags in cached.diagnostics.items() if fp not in changed_file_strs}
356
359
 
357
- if "diagnostics" in analysis_result:
358
- updated_result["diagnostics"] = {
359
- fp: diags for fp, diags in analysis_result["diagnostics"].items() if fp not in changed_file_strs
360
- }
360
+ class_hierarchies = {
361
+ class_name: class_info.copy()
362
+ for class_name, class_info in cached.class_hierarchies.items()
363
+ if class_info.get("file_path", "") not in changed_file_strs
364
+ }
361
365
 
362
- class_hierarchies: dict[str, Any] = analysis_result["class_hierarchies"]
363
- for class_name, class_info in class_hierarchies.items():
364
- if class_info.get("file_path", "") not in changed_file_strs:
365
- updated_result["class_hierarchies"][class_name] = class_info.copy()
366
-
367
- package_relations: dict[str, Any] = analysis_result["package_relations"]
368
- for package_name, package_info in package_relations.items():
369
- original_files = package_info.get("files", [])
370
- remaining = [f for f in original_files if f not in changed_file_strs]
371
- if remaining:
372
- updated_package_info = package_info.copy()
373
- updated_package_info["files"] = remaining
374
- updated_result["package_relations"][package_name] = updated_package_info
375
-
376
- references: list[Node] = analysis_result["references"]
377
- for ref in references:
378
- if ref.file_path not in changed_file_strs:
379
- updated_result["references"].append(ref)
380
-
381
- source_files: list[Path] = analysis_result["source_files"]
382
- for file_path in source_files:
383
- if str(file_path) not in changed_file_strs:
384
- updated_result["source_files"].append(file_path)
366
+ package_relations: dict[str, Any] = {}
367
+ for package_name, package_info in cached.package_relations.items():
368
+ remaining_files = [f for f in package_info.get("files", []) if f not in changed_file_strs]
369
+ if remaining_files:
370
+ package_relations[package_name] = {**package_info, "files": remaining_files}
371
+
372
+ references = [ref for ref in cached.references if ref.file_path not in changed_file_strs]
373
+ source_files = [file_path for file_path in cached.source_files if str(file_path) not in changed_file_strs]
374
+
375
+ updated_result = AnalysisData(
376
+ call_graph=filtered_cg,
377
+ class_hierarchies=class_hierarchies,
378
+ package_relations=package_relations,
379
+ references=references,
380
+ source_files=source_files,
381
+ diagnostics=diagnostics,
382
+ )
385
383
 
386
384
  _validate_no_dangling_references(updated_result)
387
385
 
388
386
  logger.info(
389
387
  f"Invalidated {len(changed_files)} files: kept {len(filtered_cg.nodes)} nodes, "
390
- f"{len(filtered_cg.edges)} edges, {len(updated_result['references'])} references"
388
+ f"{len(filtered_cg.edges)} edges, {len(updated_result.references)} references"
391
389
  )
392
- return updated_result
390
+ return InvalidatedAnalysis(updated_result, invalidated_edges, changed_file_strs)
393
391
 
394
392
 
395
- def merge_results(cached_result: dict[str, Any], new_result: dict[str, Any]) -> dict[str, Any]:
393
+ def merge_results(
394
+ cached_result: AnalysisData,
395
+ new_result: dict[str, Any],
396
+ ) -> AnalysisData:
396
397
  """Union ``cached_result`` (post-invalidation) with ``new_result`` (fresh re-LSP).
397
398
 
398
399
  For overlapping keys (same file appearing in both), the new result wins
@@ -400,51 +401,44 @@ def merge_results(cached_result: dict[str, Any], new_result: dict[str, Any]) ->
400
401
  nodes from both sides merge; edges from either side that reference
401
402
  nodes present in the merged graph are kept.
402
403
  """
403
- merged_result: dict[str, Any] = {
404
- "call_graph": cached_result["call_graph"].union(new_result["call_graph"]),
405
- "class_hierarchies": {},
406
- "package_relations": {},
407
- "references": [],
408
- "source_files": [],
409
- }
410
-
411
- merged_result["class_hierarchies"].update(cached_result["class_hierarchies"])
412
- merged_result["class_hierarchies"].update(new_result["class_hierarchies"])
413
-
414
- merged_result["package_relations"].update(cached_result["package_relations"])
415
- merged_result["package_relations"].update(new_result["package_relations"])
416
-
417
- new_source_files: list[Path] = new_result.get("source_files", [])
418
- new_file_paths = {str(path) for path in new_source_files}
419
-
420
- for ref in cached_result["references"]:
421
- if ref.file_path not in new_file_paths:
422
- merged_result["references"].append(ref)
423
- merged_result["references"].extend(new_result["references"])
424
-
425
- for file_path in cached_result["source_files"]:
426
- if str(file_path) not in new_file_paths:
427
- merged_result["source_files"].append(file_path)
428
- merged_result["source_files"].extend(new_source_files)
429
-
430
- cached_diagnostics: FileDiagnosticsMap = cached_result.get("diagnostics", {})
431
- new_diagnostics: FileDiagnosticsMap = new_result.get("diagnostics", {})
404
+ new = AnalysisData.from_dict(new_result)
405
+ new_file_paths = {str(path) for path in new.source_files}
406
+ cached_diagnostics = cached_result.diagnostics or {}
407
+ new_diagnostics = new.diagnostics or {}
432
408
  merged_diagnostics: FileDiagnosticsMap = {
433
409
  fp: diags for fp, diags in cached_diagnostics.items() if fp not in new_file_paths
434
410
  }
435
411
  merged_diagnostics.update(new_diagnostics)
436
- if merged_diagnostics:
437
- merged_result["diagnostics"] = merged_diagnostics
438
412
 
439
- return merged_result
413
+ merged = AnalysisData(
414
+ call_graph=cached_result.call_graph.union(new.call_graph),
415
+ class_hierarchies={**cached_result.class_hierarchies, **new.class_hierarchies},
416
+ package_relations={**cached_result.package_relations, **new.package_relations},
417
+ references=[ref for ref in cached_result.references if ref.file_path not in new_file_paths] + new.references,
418
+ source_files=[path for path in cached_result.source_files if str(path) not in new_file_paths]
419
+ + new.source_files,
420
+ diagnostics=merged_diagnostics or None,
421
+ )
422
+ return merged
423
+
424
+
425
+ def _collect_invalidated_edge(
426
+ edge: Edge, changed_file_strs: set[str], invalidated_edges: list[InvalidatedEdge]
427
+ ) -> None:
428
+ src_node = edge.src_node
429
+ dst_node = edge.dst_node
430
+ src_changed = src_node.file_path in changed_file_strs
431
+ dst_changed = dst_node.file_path in changed_file_strs
432
+ if src_changed != dst_changed:
433
+ invalidated_edges.append((edge.get_source(), edge.get_destination(), src_node, dst_node))
440
434
 
441
435
 
442
- def _validate_no_dangling_references(analysis_result: dict[str, Any]) -> None:
436
+ def _validate_no_dangling_references(analysis_result: AnalysisData) -> None:
443
437
  """Sanity-check: every edge reaches existing nodes, every reference / class /
444
438
  package points at a file in ``source_files``. Raises on violations."""
445
- call_graph: CallGraph = analysis_result["call_graph"]
439
+ call_graph = analysis_result.call_graph
446
440
  existing_nodes = set(call_graph.nodes.keys())
447
- source_file_strs = {str(path) for path in analysis_result["source_files"]}
441
+ source_file_strs = {str(path) for path in analysis_result.source_files}
448
442
  errors: list[str] = []
449
443
 
450
444
  for edge in call_graph.edges:
@@ -455,16 +449,16 @@ def _validate_no_dangling_references(analysis_result: dict[str, Any]) -> None:
455
449
  if dst_name not in existing_nodes:
456
450
  errors.append(f"Edge destination '{dst_name}' references non-existent node")
457
451
 
458
- for ref in analysis_result["references"]:
452
+ for ref in analysis_result.references:
459
453
  if ref.file_path not in source_file_strs:
460
454
  errors.append(f"Reference '{ref.fully_qualified_name}' from '{ref.file_path}' references non-existent file")
461
455
 
462
- for class_name, class_info in analysis_result["class_hierarchies"].items():
456
+ for class_name, class_info in analysis_result.class_hierarchies.items():
463
457
  class_file_path = class_info.get("file_path", "")
464
458
  if class_file_path and class_file_path not in source_file_strs:
465
459
  errors.append(f"Class hierarchy '{class_name}' references non-existent file '{class_file_path}'")
466
460
 
467
- for package_name, package_info in analysis_result["package_relations"].items():
461
+ for package_name, package_info in analysis_result.package_relations.items():
468
462
  for package_file in package_info.get("files", []):
469
463
  if package_file not in source_file_strs:
470
464
  errors.append(f"Package '{package_name}' references non-existent file '{package_file}'")
@@ -2,6 +2,8 @@ import logging
2
2
  import re
3
3
  from collections.abc import Iterator
4
4
  from dataclasses import dataclass, field
5
+ from pathlib import Path
6
+ from typing import Any
5
7
 
6
8
  from static_analyzer.constants import Language
7
9
  from static_analyzer.graph import CallGraph
@@ -32,6 +34,48 @@ _WORD_RE = re.compile(r"\b([a-z]+)\b")
32
34
  # Used to detect generic type params like T or E in lowercased method signatures.
33
35
  _STANDALONE_SINGLE_LETTER_RE = re.compile(r"(?<![a-z])([a-z])(?!\w)")
34
36
 
37
+ InvalidatedEdge = tuple[str, str, Node, Node]
38
+
39
+
40
+ @dataclass
41
+ class AnalysisData:
42
+ call_graph: CallGraph
43
+ class_hierarchies: dict[str, Any]
44
+ package_relations: dict[str, Any]
45
+ references: list[Node]
46
+ source_files: list[Path]
47
+ diagnostics: FileDiagnosticsMap | None = None
48
+
49
+ @classmethod
50
+ def from_dict(cls, analysis: dict[str, Any]) -> "AnalysisData":
51
+ return cls(
52
+ call_graph=analysis["call_graph"],
53
+ class_hierarchies=analysis["class_hierarchies"],
54
+ package_relations=analysis["package_relations"],
55
+ references=analysis["references"],
56
+ source_files=analysis["source_files"],
57
+ diagnostics=analysis.get("diagnostics"),
58
+ )
59
+
60
+ def to_dict(self) -> dict[str, Any]:
61
+ analysis: dict[str, Any] = {
62
+ "call_graph": self.call_graph,
63
+ "class_hierarchies": self.class_hierarchies,
64
+ "package_relations": self.package_relations,
65
+ "references": self.references,
66
+ "source_files": self.source_files,
67
+ }
68
+ if self.diagnostics is not None:
69
+ analysis["diagnostics"] = self.diagnostics
70
+ return analysis
71
+
72
+
73
+ @dataclass
74
+ class InvalidatedAnalysis:
75
+ analysis: AnalysisData
76
+ invalidated_edges: list[InvalidatedEdge]
77
+ invalidated_files: set[str]
78
+
35
79
 
36
80
  def _strip_java_generics(name: str) -> str:
37
81
  """Remove Java generic type params from a (already lowercased) qualified name.
@@ -171,13 +171,17 @@ class CallGraph:
171
171
 
172
172
  self.nodes[src_name].added_method_called_by_me(self.nodes[dst_name])
173
173
 
174
- def filter(self, keep_node: Callable[[Node], bool]) -> "CallGraph":
174
+ def filter(
175
+ self,
176
+ keep_node: Callable[[Node], bool],
177
+ on_dropped_edge: Callable[[Edge], None],
178
+ ) -> "CallGraph":
175
179
  """Return a new CallGraph keeping only nodes matching ``keep_node`` and connecting edges.
176
180
 
177
181
  ``_cluster_cache`` is preserved and pruned to the surviving qnames so
178
182
  a warm-start invalidation/filter step doesn't silently drop the prior
179
183
  clustering. Edges whose endpoints both survive are re-added; edges
180
- with a dropped endpoint are cascaded out.
184
+ with a dropped endpoint are cascaded out and optionally collected.
181
185
  """
182
186
  out = CallGraph(language=self.language)
183
187
  for node in self.nodes.values():
@@ -190,6 +194,8 @@ class CallGraph:
190
194
  out.add_edge(src, dst)
191
195
  except ValueError as e:
192
196
  logger.warning(f"Failed to add edge {src} -> {dst} during filter: {e}")
197
+ else:
198
+ on_dropped_edge(edge)
193
199
  out._cluster_cache = self._prune_cluster_cache(out.nodes)
194
200
  return out
195
201