codeboarding 0.15.2__tar.gz → 0.15.3__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 (197) hide show
  1. {codeboarding-0.15.2/codeboarding.egg-info → codeboarding-0.15.3}/PKG-INFO +2 -2
  2. {codeboarding-0.15.2 → codeboarding-0.15.3}/PYPI.md +1 -1
  3. {codeboarding-0.15.2 → codeboarding-0.15.3}/README.md +3 -2
  4. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/llm_config.py +13 -0
  5. {codeboarding-0.15.2 → codeboarding-0.15.3/codeboarding.egg-info}/PKG-INFO +2 -2
  6. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding.egg-info/SOURCES.txt +1 -0
  7. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_cli/bootstrap.py +4 -2
  8. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/install.py +192 -141
  9. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/tool_registry/__init__.py +4 -2
  10. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/tool_registry/manifest.py +50 -57
  11. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/tool_registry/registry.py +8 -3
  12. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/user_config.py +4 -0
  13. {codeboarding-0.15.2 → codeboarding-0.15.3}/pyproject.toml +1 -1
  14. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/__init__.py +2 -0
  15. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/analysis_cache.py +2 -2
  16. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/adapters/csharp_adapter.py +76 -2
  17. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/edge_builder.py +23 -4
  18. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/language_adapter.py +5 -0
  19. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/lsp_constants.py +3 -0
  20. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/protocols.py +3 -0
  21. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/source_inspector.py +113 -0
  22. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/symbol_table.py +9 -0
  23. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/graph_definitions.py +23 -10
  24. codeboarding-0.15.3/tests/test_devcontainer.py +251 -0
  25. {codeboarding-0.15.2 → codeboarding-0.15.3}/LICENSE +0 -0
  26. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/__init__.py +0 -0
  27. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/agent_responses.py +0 -0
  28. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/change_status.py +0 -0
  29. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/component_ownership.py +0 -0
  30. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/constants.py +0 -0
  31. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/content_hash.py +0 -0
  32. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/file_index_models.py +0 -0
  33. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/incremental_results.py +0 -0
  34. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/llm_errors.py +0 -0
  35. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/llm_renderers/__init__.py +0 -0
  36. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/llm_renderers/call_graph.py +0 -0
  37. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/llm_renderers/scope.py +0 -0
  38. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/model_capabilities.py +0 -0
  39. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/planner_agent.py +0 -0
  40. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/prompts/__init__.py +0 -0
  41. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/prompts/profiles.py +0 -0
  42. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/prompts/shared.py +0 -0
  43. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/relation_edges.py +0 -0
  44. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/retry.py +0 -0
  45. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/scope_analysis_agent.py +0 -0
  46. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/scope_ids.py +0 -0
  47. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/tools/__init__.py +0 -0
  48. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/tools/base.py +0 -0
  49. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/tools/get_method_calls.py +0 -0
  50. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/tools/read_file.py +0 -0
  51. {codeboarding-0.15.2 → codeboarding-0.15.3}/agents/tree_planner_agent.py +0 -0
  52. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding.egg-info/dependency_links.txt +0 -0
  53. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding.egg-info/entry_points.txt +0 -0
  54. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding.egg-info/requires.txt +0 -0
  55. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding.egg-info/top_level.txt +0 -0
  56. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_cli/__init__.py +0 -0
  57. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_cli/commands/__init__.py +0 -0
  58. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_cli/commands/full_analysis.py +0 -0
  59. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_cli/commands/incremental_analysis.py +0 -0
  60. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_cli/commands/partial_analysis.py +0 -0
  61. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_cli/github_action.py +0 -0
  62. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_cli/main.py +0 -0
  63. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_cli/render.py +0 -0
  64. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_cli/view_instructions.py +0 -0
  65. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_workflows/__init__.py +0 -0
  66. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_workflows/analysis.py +0 -0
  67. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_workflows/fingerprint_diff.py +0 -0
  68. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_workflows/orchestration.py +0 -0
  69. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_workflows/sources/__init__.py +0 -0
  70. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_workflows/sources/local.py +0 -0
  71. {codeboarding-0.15.2 → codeboarding-0.15.3}/codeboarding_workflows/sources/remote.py +0 -0
  72. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/__init__.py +0 -0
  73. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/analysis_json.py +0 -0
  74. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/cluster_relations.py +0 -0
  75. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/diagram_generator.py +0 -0
  76. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/exceptions.py +0 -0
  77. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/file_coverage.py +0 -0
  78. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/file_index.py +0 -0
  79. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/incremental_changes.py +0 -0
  80. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/incremental_update.py +0 -0
  81. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/io_utils.py +0 -0
  82. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/reference_resolver.py +0 -0
  83. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/run_context.py +0 -0
  84. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/run_mode.py +0 -0
  85. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/scope_assembly.py +0 -0
  86. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/scope_plan.py +0 -0
  87. {codeboarding-0.15.2 → codeboarding-0.15.3}/diagram_analysis/tree_shape.py +0 -0
  88. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/__init__.py +0 -0
  89. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/checks/__init__.py +0 -0
  90. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/checks/circular_deps.py +0 -0
  91. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/checks/function_size.py +0 -0
  92. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/checks/unused_code_diagnostics.py +0 -0
  93. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/config.py +0 -0
  94. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/main.py +0 -0
  95. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/models.py +0 -0
  96. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/plugins/__init__.py +0 -0
  97. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/plugins/plugin_loader.py +0 -0
  98. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/plugins/protocols.py +0 -0
  99. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/plugins/registry.py +0 -0
  100. {codeboarding-0.15.2 → codeboarding-0.15.3}/health/runner.py +0 -0
  101. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/__init__.py +0 -0
  102. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/clustering_ids.py +0 -0
  103. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/constants.py +0 -0
  104. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/logging_config.py +0 -0
  105. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/__init__.py +0 -0
  106. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/callbacks.py +0 -0
  107. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/context.py +0 -0
  108. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/mixin.py +0 -0
  109. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/paths.py +0 -0
  110. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/stats.py +0 -0
  111. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/telemetry/__init__.py +0 -0
  112. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/telemetry/device_id.py +0 -0
  113. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/telemetry/events.py +0 -0
  114. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/telemetry/schemas.py +0 -0
  115. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/telemetry/service.py +0 -0
  116. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/monitoring/writers.py +0 -0
  117. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/repo_utils/__init__.py +0 -0
  118. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/repo_utils/change_detector.py +0 -0
  119. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/repo_utils/errors.py +0 -0
  120. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/repo_utils/git_ops.py +0 -0
  121. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/repo_utils/ignore.py +0 -0
  122. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/repo_utils/path_utils.py +0 -0
  123. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/tool_registry/grammars.py +0 -0
  124. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/tool_registry/installers.py +0 -0
  125. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/tool_registry/java.py +0 -0
  126. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/tool_registry/paths.py +0 -0
  127. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/utils.py +0 -0
  128. {codeboarding-0.15.2 → codeboarding-0.15.3}/infra/vscode_constants.py +0 -0
  129. {codeboarding-0.15.2 → codeboarding-0.15.3}/output_generators/__init__.py +0 -0
  130. {codeboarding-0.15.2 → codeboarding-0.15.3}/output_generators/html.py +0 -0
  131. {codeboarding-0.15.2 → codeboarding-0.15.3}/output_generators/html_template.py +0 -0
  132. {codeboarding-0.15.2 → codeboarding-0.15.3}/output_generators/markdown.py +0 -0
  133. {codeboarding-0.15.2 → codeboarding-0.15.3}/output_generators/mdx.py +0 -0
  134. {codeboarding-0.15.2 → codeboarding-0.15.3}/output_generators/rendering.py +0 -0
  135. {codeboarding-0.15.2 → codeboarding-0.15.3}/output_generators/sphinx.py +0 -0
  136. {codeboarding-0.15.2 → codeboarding-0.15.3}/setup.cfg +0 -0
  137. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/analysis_result.py +0 -0
  138. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/cfg/__init__.py +0 -0
  139. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/cfg/call_graph.py +0 -0
  140. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/cfg/edge.py +0 -0
  141. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/cfg/location_key.py +0 -0
  142. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/__init__.py +0 -0
  143. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/exceptions.py +0 -0
  144. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/models.py +0 -0
  145. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/names/__init__.py +0 -0
  146. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/names/draft.py +0 -0
  147. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/names/frontier.py +0 -0
  148. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/names/inventory.py +0 -0
  149. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/names/replay.py +0 -0
  150. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/names/spec.py +0 -0
  151. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/names/tokens.py +0 -0
  152. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/clustering/service.py +0 -0
  153. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/config.py +0 -0
  154. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/csharp_config_scanner.py +0 -0
  155. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/dotnet_sdk.py +0 -0
  156. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/dotnet_solution.py +0 -0
  157. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/__init__.py +0 -0
  158. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/adapters/__init__.py +0 -0
  159. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/adapters/go_adapter.py +0 -0
  160. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/adapters/java_adapter.py +0 -0
  161. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/adapters/kotlin_adapter.py +0 -0
  162. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/adapters/php_adapter.py +0 -0
  163. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/adapters/python_adapter.py +0 -0
  164. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/adapters/rust_adapter.py +0 -0
  165. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/adapters/typescript_adapter.py +0 -0
  166. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/call_graph_builder.py +0 -0
  167. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/edge_build_context.py +0 -0
  168. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/hierarchy_builder.py +0 -0
  169. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/lsp_client.py +0 -0
  170. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/lsp_recycler.py +0 -0
  171. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/models.py +0 -0
  172. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/process_memory.py +0 -0
  173. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/progress.py +0 -0
  174. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/result_converter.py +0 -0
  175. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/engine/utils.py +0 -0
  176. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/exceptions.py +0 -0
  177. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/external_calls.py +0 -0
  178. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/incremental_orchestrator.py +0 -0
  179. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/internal_references.py +0 -0
  180. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/java_config_scanner.py +0 -0
  181. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/java_utils.py +0 -0
  182. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/language_results.py +0 -0
  183. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/lsp_client/__init__.py +0 -0
  184. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/lsp_client/diagnostics.py +0 -0
  185. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/node.py +0 -0
  186. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/programming_language.py +0 -0
  187. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/scanner.py +0 -0
  188. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/typescript_config_scanner.py +0 -0
  189. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/typescript_plugin/node_modules/codeboarding-workspace-sources/index.js +0 -0
  190. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/typescript_plugin/node_modules/codeboarding-workspace-sources/package.json +0 -0
  191. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/typescript_workspace.py +0 -0
  192. {codeboarding-0.15.2 → codeboarding-0.15.3}/static_analyzer/workspace_dependencies.py +0 -0
  193. {codeboarding-0.15.2 → codeboarding-0.15.3}/tests/test_persisted_analysis_artifacts.py +0 -0
  194. {codeboarding-0.15.2 → codeboarding-0.15.3}/tests/test_pyproject_packages.py +0 -0
  195. {codeboarding-0.15.2 → codeboarding-0.15.3}/tests/test_view_instructions.py +0 -0
  196. {codeboarding-0.15.2 → codeboarding-0.15.3}/tests/test_windows_compatibility.py +0 -0
  197. {codeboarding-0.15.2 → codeboarding-0.15.3}/tests/test_windows_encoding.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeboarding
3
- Version: 0.15.2
3
+ Version: 0.15.3
4
4
  Summary: Interactive Diagrams for Code
5
5
  Author: CodeBoarding Team
6
6
  License-Expression: MIT
@@ -112,7 +112,7 @@ pip install codeboarding --extra-index-url https://pip.codeboarding.org/simple/
112
112
 
113
113
  > Installing into the global Python environment with `pip` is not recommended — it can cause dependency conflicts and will fail if the system Python is not 3.12 or 3.13.
114
114
 
115
- Language server binaries are downloaded automatically on first use. To pre-install them explicitly (useful in CI or restricted environments):
115
+ Each analysis downloads the language servers its repository's languages need, on first use. To pre-install all of them (useful in CI or restricted environments):
116
116
 
117
117
  ```bash
118
118
  codeboarding-setup
@@ -38,7 +38,7 @@ pip install codeboarding --extra-index-url https://pip.codeboarding.org/simple/
38
38
 
39
39
  > Installing into the global Python environment with `pip` is not recommended — it can cause dependency conflicts and will fail if the system Python is not 3.12 or 3.13.
40
40
 
41
- Language server binaries are downloaded automatically on first use. To pre-install them explicitly (useful in CI or restricted environments):
41
+ Each analysis downloads the language servers its repository's languages need, on first use. To pre-install all of them (useful in CI or restricted environments):
42
42
 
43
43
  ```bash
44
44
  codeboarding-setup
@@ -153,7 +153,7 @@ codeboarding-render /path/to/analysis.json --format mdx --output-dir /path/to/do
153
153
  python codeboarding_cli/render.py ../../demo/markitdown/.codeboarding/analysis.json --format md
154
154
  ```
155
155
 
156
- `python -m infra.install` and `codeboarding-setup` download language server binaries to `~/.codeboarding/servers/`, shared across projects. Node.js (and its bundled `npm`) is required for the Python, TypeScript, JavaScript, and PHP language servers; if neither `node` nor `CODEBOARDING_NODE_PATH` is set, setup downloads a pinned Node.js runtime into `~/.codeboarding/servers/nodeenv/` automatically.
156
+ An analysis downloads the language servers its repository's languages need into `~/.codeboarding/servers/` the first time it needs them, shared across projects; `python -m infra.install` and `codeboarding-setup` download all of them up front. Node.js (and its bundled `npm`) is required for the Python, TypeScript, JavaScript, and PHP language servers; if neither `node` nor `CODEBOARDING_NODE_PATH` is set, setup downloads a pinned Node.js runtime into `~/.codeboarding/servers/nodeenv/` automatically.
157
157
 
158
158
  ## Configuration
159
159
 
@@ -172,6 +172,7 @@ On first run, CodeBoarding creates `~/.codeboarding/config.toml`. Set one provid
172
172
  # openrouter_api_key = "sk-..."
173
173
  # orcarouter_api_key = "sk-orca-..." # model routing gateway (https://www.orcarouter.ai)
174
174
  # requesty_api_key = "rqsty-..." # LLM gateway (https://www.requesty.ai)
175
+ # opper_api_key = "..." # EU-hosted AI gateway (https://opper.ai)
175
176
  # litellm_base_url = "http://localhost:4000" # LiteLLM proxy server URL (required)
176
177
  # litellm_api_key = "sk-..." # LiteLLM proxy server key (optional)
177
178
 
@@ -249,7 +250,7 @@ for the actual depth. Existing baseline loading behavior is unchanged: prefer
249
250
  - Languages: Python, TypeScript, JavaScript, Java, Kotlin, Go, PHP, Rust, C#.
250
251
  - Java needs a JDK (21 recommended).
251
252
  - Kotlin is analysed with JetBrains' [kotlin-lsp](https://github.com/Kotlin/kotlin-lsp) (about 360 MB with its own Java runtime, under the [JetBrains Free Plugin License](https://github.com/Kotlin/kotlin-lsp/blob/main/kotlin-vscode/LICENSE.txt)) and needs glibc 2.34 or newer on Linux. Kotlin calls into Java are not drawn yet.
252
- - LLM providers: OpenAI, Anthropic, Google, Vercel AI Gateway, AWS Bedrock, Ollama, OpenRouter, OrcaRouter, Requesty, LiteLLM proxy, and more.
253
+ - LLM providers: OpenAI, Anthropic, Google, Vercel AI Gateway, AWS Bedrock, Ollama, OpenRouter, OrcaRouter, Requesty, Opper, LiteLLM proxy, and more.
253
254
 
254
255
  ## Examples
255
256
 
@@ -302,6 +302,19 @@ LLM_PROVIDERS = {
302
302
  "max_retries": 0,
303
303
  },
304
304
  ),
305
+ "opper": LLMConfig(
306
+ chat_class=ChatOpenAI,
307
+ selection_envs=["OPPER_API_KEY"],
308
+ api_key_env="OPPER_API_KEY",
309
+ agent_model="gemini-3.8-flash",
310
+ base_url_env="OPPER_BASE_URL",
311
+ default_base_url="https://api.opper.ai/v3/compat",
312
+ extra_args={
313
+ "max_tokens": None,
314
+ "timeout": LLMDefaults.REQUEST_TIMEOUT_SECONDS,
315
+ "max_retries": 0,
316
+ },
317
+ ),
305
318
  "litellm": LLMConfig(
306
319
  chat_class=ChatOpenAI,
307
320
  # Base URL only: a key alone must not select litellm, since there is no
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeboarding
3
- Version: 0.15.2
3
+ Version: 0.15.3
4
4
  Summary: Interactive Diagrams for Code
5
5
  Author: CodeBoarding Team
6
6
  License-Expression: MIT
@@ -112,7 +112,7 @@ pip install codeboarding --extra-index-url https://pip.codeboarding.org/simple/
112
112
 
113
113
  > Installing into the global Python environment with `pip` is not recommended — it can cause dependency conflicts and will fail if the system Python is not 3.12 or 3.13.
114
114
 
115
- Language server binaries are downloaded automatically on first use. To pre-install them explicitly (useful in CI or restricted environments):
115
+ Each analysis downloads the language servers its repository's languages need, on first use. To pre-install all of them (useful in CI or restricted environments):
116
116
 
117
117
  ```bash
118
118
  codeboarding-setup
@@ -187,6 +187,7 @@ static_analyzer/lsp_client/__init__.py
187
187
  static_analyzer/lsp_client/diagnostics.py
188
188
  static_analyzer/typescript_plugin/node_modules/codeboarding-workspace-sources/index.js
189
189
  static_analyzer/typescript_plugin/node_modules/codeboarding-workspace-sources/package.json
190
+ tests/test_devcontainer.py
190
191
  tests/test_persisted_analysis_artifacts.py
191
192
  tests/test_pyproject_packages.py
192
193
  tests/test_view_instructions.py
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
  from agents.llm_config import configure_models, validate_api_key_provided
6
6
  from health.plugins import get_registries, load_plugins
7
7
  from diagram_analysis.run_context import RunPaths
8
- from infra.install import ensure_tools
8
+ from infra.install import enable_on_demand_install, ensure_tools
9
9
  from infra.logging_config import setup_logging
10
10
  from infra.user_config import ensure_config_template, load_user_config
11
11
  from infra.utils import CODEBOARDING_DIR_NAME
@@ -41,4 +41,6 @@ def bootstrap_environment(output_dir: Path, binary_location: Path | None) -> Non
41
41
  # repairs a deleted ~/.codeboarding/servers/nodeenv/ independently of the
42
42
  # fingerprint check. Pre-gating with needs_install() here would skip that
43
43
  # repair when binaries look current but the runtime directory was deleted.
44
- ensure_tools(auto_install_npm=True, auto_install_vcpp=True)
44
+ # Only the base tools here: the analysis installs its languages' servers once it has found them.
45
+ ensure_tools(auto_install_npm=True, auto_install_vcpp=True, languages=())
46
+ enable_on_demand_install(auto_install_npm=True, auto_install_vcpp=True)
@@ -1,21 +1,24 @@
1
1
  import argparse
2
2
  import io
3
+ import logging
3
4
  import os
4
5
  import platform
5
6
  import shutil
6
7
  import subprocess
7
8
  import sys
8
9
  import tarfile
9
- from collections.abc import Callable
10
+ from collections.abc import Callable, Collection
10
11
  from dataclasses import dataclass
11
12
  from enum import StrEnum
12
13
  from pathlib import Path
13
14
 
14
15
  import requests
15
16
  from infra.tool_registry import (
17
+ ALL_LANGUAGE_KEYS,
16
18
  PINNED_NODE_VERSION,
17
19
  TOOL_REGISTRY,
18
20
  ProgressCallback,
21
+ ToolDependency,
19
22
  ToolKind,
20
23
  acquire_lock,
21
24
  archive_launcher_path,
@@ -32,6 +35,7 @@ from infra.tool_registry import (
32
35
  platform_bin_dir,
33
36
  preferred_node_path,
34
37
  preferred_npm_command,
38
+ required_tools,
35
39
  write_manifest,
36
40
  )
37
41
  from infra.tool_registry.java import find_java_21_or_later
@@ -39,6 +43,8 @@ from infra.tool_registry.registry import ConfigSection, PackageManagerToolSource
39
43
  from infra.vscode_constants import VSCODE_CONFIG
40
44
  from infra.user_config import ensure_config_template
41
45
 
46
+ logger = logging.getLogger(__name__)
47
+
42
48
 
43
49
  @dataclass(frozen=True, slots=True)
44
50
  class LanguageSupportCheck:
@@ -294,25 +300,26 @@ def get_platform_bin_dir(servers_dir: Path) -> Path:
294
300
  return platform_bin_dir(servers_dir)
295
301
 
296
302
 
297
- def install_node_servers(target_dir: Path, on_progress: ProgressCallback | None = None):
298
- """Install Node.js based servers (TypeScript, Pyright) using npm in target_dir."""
303
+ def install_node_servers(
304
+ target_dir: Path, on_progress: ProgressCallback | None = None, deps: Collection[ToolDependency] = TOOL_REGISTRY
305
+ ):
306
+ """Install the selected Node.js servers using npm; defaults to every registered Node.js server."""
299
307
  print("Step: Node.js servers installation started")
300
308
  target_dir.mkdir(parents=True, exist_ok=True)
301
309
 
302
- node_deps = [d for d in TOOL_REGISTRY if d.kind is ToolKind.NODE]
310
+ node_deps = [d for d in deps if d.kind is ToolKind.NODE]
303
311
  install_node_tools(target_dir, node_deps, on_progress=on_progress)
304
312
 
305
313
  # Verify the installation
306
- ts_lsp_path = target_dir / "node_modules" / ".bin" / "typescript-language-server"
307
- py_lsp_path = target_dir / "node_modules" / ".bin" / "pyright-langserver"
308
- php_lsp_path = target_dir / "node_modules" / ".bin" / "intelephense"
314
+ names = {
315
+ "typescript": "TypeScript Language Server",
316
+ "python": "Pyright Language Server",
317
+ "php": "Intelephense",
318
+ }
309
319
 
310
320
  success = True
311
- for name, path in [
312
- ("TypeScript Language Server", ts_lsp_path),
313
- ("Pyright Language Server", py_lsp_path),
314
- ("Intelephense", php_lsp_path),
315
- ]:
321
+ for dep in node_deps:
322
+ name, path = names.get(dep.key, dep.binary_name), target_dir / "node_modules" / ".bin" / dep.binary_name
316
323
  if path.exists():
317
324
  print(f"Step: {name} installation finished: success")
318
325
  else:
@@ -456,10 +463,15 @@ def resolve_missing_vcpp(auto_install_vcpp: bool = False) -> bool:
456
463
  return False
457
464
 
458
465
 
459
- def download_binaries(target_dir: Path, auto_install_vcpp: bool = False, on_progress: ProgressCallback | None = None):
460
- """Download tokei and gopls binaries from the latest GitHub release."""
466
+ def download_binaries(
467
+ target_dir: Path,
468
+ auto_install_vcpp: bool = False,
469
+ on_progress: ProgressCallback | None = None,
470
+ deps: Collection[ToolDependency] = TOOL_REGISTRY,
471
+ ):
472
+ """Download the selected native binaries; defaults to every registered native tool."""
461
473
  print("Step: Binary download started")
462
- native_deps = [d for d in TOOL_REGISTRY if d.kind is ToolKind.NATIVE]
474
+ native_deps = [d for d in deps if d.kind is ToolKind.NATIVE]
463
475
  install_native_tools(target_dir, native_deps, on_progress=on_progress)
464
476
 
465
477
  # Verify downloaded binaries actually work (catch missing DLL issues on Windows)
@@ -498,10 +510,12 @@ def download_binaries(target_dir: Path, auto_install_vcpp: bool = False, on_prog
498
510
  print("Step: Binary download finished")
499
511
 
500
512
 
501
- def download_jdtls(target_dir: Path, on_progress: ProgressCallback | None = None):
502
- """Download and extract the archive tools: JDTLS and kotlin-lsp."""
513
+ def download_jdtls(
514
+ target_dir: Path, on_progress: ProgressCallback | None = None, deps: Collection[ToolDependency] = TOOL_REGISTRY
515
+ ):
516
+ """Download the selected archive tools; defaults to every registered archive tool."""
503
517
  print("Step: JDTLS download started")
504
- archive_deps = [d for d in TOOL_REGISTRY if d.kind is ToolKind.ARCHIVE]
518
+ archive_deps = [d for d in deps if d.kind is ToolKind.ARCHIVE]
505
519
  for dep in archive_deps:
506
520
  install_archive_tool(target_dir, dep, on_progress=on_progress)
507
521
 
@@ -509,13 +523,15 @@ def download_jdtls(target_dir: Path, on_progress: ProgressCallback | None = None
509
523
  return True
510
524
 
511
525
 
512
- def install_package_manager_lsp_servers(target_dir: Path, on_progress: ProgressCallback | None = None) -> None:
513
- """Install LSP servers distributed via user-provided package managers.
526
+ def install_package_manager_lsp_servers(
527
+ target_dir: Path, on_progress: ProgressCallback | None = None, deps: Collection[ToolDependency] = TOOL_REGISTRY
528
+ ) -> None:
529
+ """Install the selected package-manager servers; defaults to every registered package-manager server.
514
530
 
515
531
  Skips cleanly when the package manager itself is absent — the adapter raises a meaningful error
516
532
  at analysis time.
517
533
  """
518
- pm_deps = [d for d in TOOL_REGISTRY if d.kind is ToolKind.PACKAGE_MANAGER]
534
+ pm_deps = [d for d in deps if d.kind is ToolKind.PACKAGE_MANAGER]
519
535
  if not pm_deps:
520
536
  return
521
537
  print("Step: Package-manager tool installation started")
@@ -588,101 +604,6 @@ def install_pre_commit_hooks():
588
604
  pass
589
605
 
590
606
 
591
- def _language_checks_from_registry(target_dir: Path) -> list[LanguageSupportCheck]:
592
- """Build one LanguageSupportCheck per LSP language listed in VSCODE_CONFIG.
593
-
594
- Data-driven so adding a language to TOOL_REGISTRY + VSCODE_CONFIG
595
- automatically flows to the setup summary — no second list to keep in sync.
596
- Each tool dep can surface multiple display languages (e.g. the
597
- ``typescript`` entry covers both TypeScript and JavaScript).
598
- """
599
- target_dir = target_dir.resolve()
600
- # native/package-manager installers already warn-and-skip on unsupported
601
- # hosts; the summary should mirror that instead of crashing when the host has
602
- # no ``platform_bin_dir`` layout.
603
- try:
604
- platform_bin_dir: Path | None = get_platform_bin_dir(target_dir)
605
- except RuntimeError:
606
- platform_bin_dir = None
607
- is_win = platform.system() == "Windows"
608
- node_ext = ".cmd" if is_win else ""
609
- native_ext = ".exe" if is_win else ""
610
- npm_missing = "npm not available"
611
-
612
- checks: list[LanguageSupportCheck] = []
613
- lsp_servers = VSCODE_CONFIG.get("lsp_servers", {})
614
-
615
- for dep in TOOL_REGISTRY:
616
- if dep.config_section != ConfigSection.LSP_SERVERS:
617
- continue
618
- config_entry = lsp_servers.get(dep.key)
619
- if config_entry is None:
620
- continue
621
- languages: list[str] = list(config_entry.get("languages", []) or [dep.key])
622
-
623
- paths: list[Path] = []
624
- fallback_available = False
625
- requires_npm = False
626
- reason_requirement = f"{dep.binary_name} not installed"
627
- reason_binary = f"{dep.binary_name} binary not found"
628
-
629
- if dep.kind is ToolKind.NATIVE:
630
- if platform_bin_dir is not None:
631
- paths.append(platform_bin_dir / f"{dep.binary_name}{native_ext}")
632
- else:
633
- reason_requirement = f"{dep.binary_name} unavailable on this platform"
634
- reason_binary = reason_requirement
635
- elif dep.kind is ToolKind.NODE:
636
- requires_npm = True
637
- reason_requirement = npm_missing
638
- paths.append(target_dir / "node_modules" / ".bin" / f"{dep.binary_name}{node_ext}")
639
- # Python pyright also resolves from the active environment.
640
- if dep.key == "python":
641
- env_path = shutil.which("pyright-langserver") or shutil.which("pyright-python-langserver")
642
- fallback_available = bool(env_path)
643
- reason_requirement = "pyright-langserver not found in node_modules or active environment"
644
- reason_binary = reason_requirement
645
- elif dep.kind is ToolKind.ARCHIVE:
646
- # An archive is validated by its launcher, or for JDTLS by directory
647
- # presence (+ plugins/ subdir), mirroring has_required_tools.
648
- subdir = dep.archive_subdir or dep.key
649
- paths.append(archive_launcher_path(target_dir, dep) or target_dir / "bin" / subdir)
650
- reason_requirement = f"{subdir} installation not found"
651
- reason_binary = reason_requirement
652
- # Java analysis can still proceed when a system Java 21+ is available
653
- # (jdtls's bundled JRE is the default, but not strictly required).
654
- if dep.key == "java":
655
- fallback_available = bool(find_java_21_or_later())
656
- reason_binary = "jdtls or Java 21+ not found"
657
- elif dep.kind is ToolKind.PACKAGE_MANAGER:
658
- pm_path = package_manager_tool_path(target_dir, dep)
659
- if pm_path is not None:
660
- paths.append(pm_path)
661
- manager = (
662
- dep.source.manager_binary if isinstance(dep.source, PackageManagerToolSource) else "package manager"
663
- )
664
- if pm_path is None:
665
- reason_requirement = f"{dep.binary_name} unavailable on this platform"
666
- else:
667
- reason_requirement = f"{dep.binary_name} not installed ({manager} unavailable or install failed)"
668
- reason_binary = reason_requirement
669
-
670
- health_check = check_rust_toolchain if dep.key == "rust" else None
671
- for lang in languages:
672
- checks.append(
673
- LanguageSupportCheck(
674
- language=lang,
675
- paths=list(paths),
676
- requires_npm=requires_npm,
677
- fallback_available=fallback_available,
678
- reason_if_requirement_missing=reason_requirement,
679
- reason_if_binary_missing=reason_binary,
680
- health_check=health_check,
681
- )
682
- )
683
- return checks
684
-
685
-
686
607
  def print_language_support_summary(npm_available: bool, target_dir: Path):
687
608
  """Print which language analyses are currently available based on installed tools."""
688
609
  print("Step: Language support summary")
@@ -697,9 +618,12 @@ def ensure_tools(
697
618
  auto_install_npm: bool = False,
698
619
  auto_install_vcpp: bool = False,
699
620
  on_progress: ProgressCallback | None = None,
621
+ languages: Collection[str] = ALL_LANGUAGE_KEYS,
700
622
  ) -> None:
701
623
  """Install tools to ~/.codeboarding/servers/ if needed. No-op if already current.
702
624
 
625
+ *languages* are ``lsp_servers`` keys. Defaults to all languages; an empty collection installs only base tools.
626
+
703
627
  Uses a file lock so that concurrent instances (multiple VSCode windows)
704
628
  don't corrupt binaries by downloading simultaneously.
705
629
 
@@ -709,24 +633,48 @@ def ensure_tools(
709
633
  servers_dir = get_servers_dir()
710
634
  servers_dir.mkdir(parents=True, exist_ok=True)
711
635
  lock_path = servers_dir / ".download.lock"
636
+ deps = required_tools(languages)
712
637
 
713
638
  with open(lock_path, "w") as lock_fd:
714
639
  acquire_lock(lock_fd)
715
-
716
- # Run above needs_install() so a deleted nodeenv/ is always repaired
717
- # (fingerprints alone wouldn't detect it).
718
- ensure_node_runtime(target_dir=servers_dir, auto_install_npm=auto_install_npm)
719
-
720
- if not needs_install():
640
+ if any(dep.kind is ToolKind.NODE for dep in deps):
641
+ # Run above needs_install() so a deleted nodeenv/ is always repaired
642
+ # (fingerprints alone wouldn't detect it).
643
+ ensure_node_runtime(target_dir=servers_dir, auto_install_npm=auto_install_npm)
644
+ if not needs_install(languages):
721
645
  return
722
-
723
646
  run_install(
724
647
  target_dir=servers_dir,
725
648
  auto_install_npm=auto_install_npm,
726
649
  auto_install_vcpp=auto_install_vcpp,
727
650
  on_progress=on_progress,
651
+ deps=deps,
728
652
  )
729
- write_manifest()
653
+ write_manifest(deps)
654
+
655
+
656
+ _on_demand_install: dict[str, bool] = {}
657
+
658
+
659
+ def enable_on_demand_install(auto_install_npm: bool = False, auto_install_vcpp: bool = False) -> None:
660
+ """Let each analysis install the language servers its repository needs once it has found its languages.
661
+
662
+ Why: a run with ``--binary-location`` brings its own servers, so installing stays off unless the
663
+ entry point turns it on.
664
+ """
665
+ _on_demand_install.update(auto_install_npm=auto_install_npm, auto_install_vcpp=auto_install_vcpp)
666
+
667
+
668
+ def ensure_language_servers(languages: Collection[str]) -> None:
669
+ """Install the servers of *languages* (``lsp_servers`` keys) when on-demand installs are enabled."""
670
+ if not _on_demand_install:
671
+ return
672
+ logger.info("Language servers needed: %s", ", ".join(sorted(languages)) or "none")
673
+ ensure_tools(
674
+ auto_install_npm=_on_demand_install["auto_install_npm"],
675
+ auto_install_vcpp=_on_demand_install["auto_install_vcpp"],
676
+ languages=languages,
677
+ )
730
678
 
731
679
 
732
680
  def run_install(
@@ -734,30 +682,34 @@ def run_install(
734
682
  auto_install_npm: bool = False,
735
683
  auto_install_vcpp: bool = False,
736
684
  on_progress: ProgressCallback | None = None,
685
+ deps: Collection[ToolDependency] = TOOL_REGISTRY,
737
686
  ) -> None:
738
687
  """Core installation logic — callable programmatically or via CLI.
739
688
 
740
- Downloads language server binaries to target_dir (defaults to ~/.codeboarding/servers/).
741
- Safe to call multiple times; already-installed tools are skipped.
689
+ Downloads the selected tools (every registered tool by default) to
690
+ target_dir (defaults to ~/.codeboarding/servers/). Safe to call multiple times;
691
+ already-installed tools are skipped.
742
692
 
743
693
  The ``on_progress`` callback receives ``(tool_name, step, total)`` where
744
694
  step/total count across *all* tool categories (native, node, archive).
745
695
  """
746
696
  target = (target_dir or get_servers_dir()).resolve()
747
697
  target.mkdir(parents=True, exist_ok=True)
698
+ selected = list(deps)
748
699
 
749
700
  ensure_config_template()
750
701
 
751
- # Covers the codeboarding-setup -> run_install path, which bypasses ensure_tools().
752
- ensure_node_runtime(target_dir=target, auto_install_npm=auto_install_npm)
702
+ node_deps = [d for d in selected if d.kind is ToolKind.NODE]
703
+ if node_deps:
704
+ # Covers the codeboarding-setup -> run_install path, which bypasses ensure_tools().
705
+ ensure_node_runtime(target_dir=target, auto_install_npm=auto_install_npm)
753
706
 
754
707
  # Compute a unified total so the caller sees a single progress stream.
755
- native_count = sum(1 for d in TOOL_REGISTRY if d.kind is ToolKind.NATIVE and d.source)
756
- node_deps = [d for d in TOOL_REGISTRY if d.kind is ToolKind.NODE]
757
- archive_count = sum(1 for d in TOOL_REGISTRY if d.kind is ToolKind.ARCHIVE)
758
- pm_count = sum(1 for d in TOOL_REGISTRY if d.kind is ToolKind.PACKAGE_MANAGER)
759
- npm_available = resolve_npm_availability(auto_install_npm=auto_install_npm, target_dir=target)
760
- total_steps = native_count + (1 if npm_available and node_deps else 0) + archive_count + pm_count
708
+ native_count = sum(1 for d in selected if d.kind is ToolKind.NATIVE and d.source)
709
+ archive_count = sum(1 for d in selected if d.kind is ToolKind.ARCHIVE)
710
+ pm_count = sum(1 for d in selected if d.kind is ToolKind.PACKAGE_MANAGER)
711
+ npm_available = bool(node_deps) and resolve_npm_availability(auto_install_npm=auto_install_npm, target_dir=target)
712
+ total_steps = native_count + (1 if npm_available else 0) + archive_count + pm_count
761
713
 
762
714
  step = 0
763
715
 
@@ -770,14 +722,18 @@ def run_install(
770
722
  tracker = unified_progress if on_progress else None
771
723
 
772
724
  if npm_available:
773
- install_node_servers(target, on_progress=tracker)
774
-
775
- download_binaries(target, auto_install_vcpp=auto_install_vcpp, on_progress=tracker)
776
- download_jdtls(target, on_progress=tracker)
777
- install_package_manager_lsp_servers(target, on_progress=tracker)
778
- download_grammars(target)
725
+ install_node_servers(target, on_progress=tracker, deps=selected)
726
+
727
+ if native_count:
728
+ download_binaries(target, auto_install_vcpp=auto_install_vcpp, on_progress=tracker, deps=selected)
729
+ if archive_count:
730
+ download_jdtls(target, on_progress=tracker, deps=selected)
731
+ install_package_manager_lsp_servers(target, on_progress=tracker, deps=selected)
732
+ if any(d.key == "kotlin" for d in selected):
733
+ download_grammars(target)
779
734
  install_pre_commit_hooks()
780
- print_language_support_summary(npm_available, target)
735
+ if all(dep in selected for dep in TOOL_REGISTRY):
736
+ print_language_support_summary(npm_available, target)
781
737
 
782
738
 
783
739
  def main() -> None:
@@ -808,5 +764,100 @@ def main() -> None:
808
764
  print(" codeboarding full --local /path/to/repo")
809
765
 
810
766
 
767
+ def _language_checks_from_registry(target_dir: Path) -> list[LanguageSupportCheck]:
768
+ """Build one LanguageSupportCheck per LSP language listed in VSCODE_CONFIG.
769
+
770
+ Data-driven so adding a language to TOOL_REGISTRY + VSCODE_CONFIG
771
+ automatically flows to the setup summary — no second list to keep in sync.
772
+ Each tool dep can surface multiple display languages (e.g. the
773
+ ``typescript`` entry covers both TypeScript and JavaScript).
774
+ """
775
+ target_dir = target_dir.resolve()
776
+ # native/package-manager installers already warn-and-skip on unsupported
777
+ # hosts; the summary should mirror that instead of crashing when the host has
778
+ # no ``platform_bin_dir`` layout.
779
+ try:
780
+ platform_bin_dir: Path | None = get_platform_bin_dir(target_dir)
781
+ except RuntimeError:
782
+ platform_bin_dir = None
783
+ is_win = platform.system() == "Windows"
784
+ node_ext = ".cmd" if is_win else ""
785
+ native_ext = ".exe" if is_win else ""
786
+ npm_missing = "npm not available"
787
+
788
+ checks: list[LanguageSupportCheck] = []
789
+ lsp_servers = VSCODE_CONFIG.get("lsp_servers", {})
790
+
791
+ for dep in TOOL_REGISTRY:
792
+ if dep.config_section != ConfigSection.LSP_SERVERS:
793
+ continue
794
+ config_entry = lsp_servers.get(dep.key)
795
+ if config_entry is None:
796
+ continue
797
+ languages: list[str] = list(config_entry.get("languages", []) or [dep.key])
798
+
799
+ paths: list[Path] = []
800
+ fallback_available = False
801
+ requires_npm = False
802
+ reason_requirement = f"{dep.binary_name} not installed"
803
+ reason_binary = f"{dep.binary_name} binary not found"
804
+
805
+ if dep.kind is ToolKind.NATIVE:
806
+ if platform_bin_dir is not None:
807
+ paths.append(platform_bin_dir / f"{dep.binary_name}{native_ext}")
808
+ else:
809
+ reason_requirement = f"{dep.binary_name} unavailable on this platform"
810
+ reason_binary = reason_requirement
811
+ elif dep.kind is ToolKind.NODE:
812
+ requires_npm = True
813
+ reason_requirement = npm_missing
814
+ paths.append(target_dir / "node_modules" / ".bin" / f"{dep.binary_name}{node_ext}")
815
+ # Python pyright also resolves from the active environment.
816
+ if dep.key == "python":
817
+ env_path = shutil.which("pyright-langserver") or shutil.which("pyright-python-langserver")
818
+ fallback_available = bool(env_path)
819
+ reason_requirement = "pyright-langserver not found in node_modules or active environment"
820
+ reason_binary = reason_requirement
821
+ elif dep.kind is ToolKind.ARCHIVE:
822
+ # An archive is validated by its launcher, or for JDTLS by directory
823
+ # presence (+ plugins/ subdir), mirroring has_required_tools.
824
+ subdir = dep.archive_subdir or dep.key
825
+ paths.append(archive_launcher_path(target_dir, dep) or target_dir / "bin" / subdir)
826
+ reason_requirement = f"{subdir} installation not found"
827
+ reason_binary = reason_requirement
828
+ # Java analysis can still proceed when a system Java 21+ is available
829
+ # (jdtls's bundled JRE is the default, but not strictly required).
830
+ if dep.key == "java":
831
+ fallback_available = bool(find_java_21_or_later())
832
+ reason_binary = "jdtls or Java 21+ not found"
833
+ elif dep.kind is ToolKind.PACKAGE_MANAGER:
834
+ pm_path = package_manager_tool_path(target_dir, dep)
835
+ if pm_path is not None:
836
+ paths.append(pm_path)
837
+ manager = (
838
+ dep.source.manager_binary if isinstance(dep.source, PackageManagerToolSource) else "package manager"
839
+ )
840
+ if pm_path is None:
841
+ reason_requirement = f"{dep.binary_name} unavailable on this platform"
842
+ else:
843
+ reason_requirement = f"{dep.binary_name} not installed ({manager} unavailable or install failed)"
844
+ reason_binary = reason_requirement
845
+
846
+ health_check = check_rust_toolchain if dep.key == "rust" else None
847
+ for lang in languages:
848
+ checks.append(
849
+ LanguageSupportCheck(
850
+ language=lang,
851
+ paths=list(paths),
852
+ requires_npm=requires_npm,
853
+ fallback_available=fallback_available,
854
+ reason_if_requirement_missing=reason_requirement,
855
+ reason_if_binary_missing=reason_binary,
856
+ health_check=health_check,
857
+ )
858
+ )
859
+ return checks
860
+
861
+
811
862
  if __name__ == "__main__":
812
863
  main()
@@ -4,6 +4,8 @@ Layered: registry (data) -> paths -> manifest / installers -> __init__ (re-expor
4
4
  """
5
5
 
6
6
  from .registry import ( # noqa: F401
7
+ ALL_LANGUAGE_KEYS,
8
+ BASE_TOOL_KEYS,
7
9
  JDTLS_BUILD,
8
10
  JDTLS_URL_TEMPLATE,
9
11
  JDTLS_VERSION,
@@ -50,11 +52,11 @@ from .manifest import ( # noqa: F401
50
52
  installed_version,
51
53
  manifest_path,
52
54
  needs_install,
53
- npm_specs_fingerprint,
54
55
  read_manifest,
55
56
  resolve_config,
56
57
  resolve_config_from_path,
57
- tools_fingerprint,
58
+ required_tools,
59
+ tool_fingerprint,
58
60
  write_manifest,
59
61
  )
60
62
  from .installers import ( # noqa: F401