codeboarding 0.13.8__tar.gz → 0.13.11__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 (244) hide show
  1. {codeboarding-0.13.8/codeboarding.egg-info → codeboarding-0.13.11}/PKG-INFO +16 -5
  2. {codeboarding-0.13.8 → codeboarding-0.13.11}/PYPI.md +8 -2
  3. {codeboarding-0.13.8 → codeboarding-0.13.11}/README.md +30 -4
  4. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/abstraction_agent.py +70 -82
  5. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/agent_responses.py +25 -77
  6. codeboarding-0.13.11/agents/component_ownership.py +56 -0
  7. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/content_hash.py +3 -5
  8. codeboarding-0.13.11/agents/details_agent.py +244 -0
  9. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/file_index_models.py +14 -0
  10. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/incremental_agent.py +96 -151
  11. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/incremental_results.py +11 -3
  12. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/llm_config.py +70 -25
  13. codeboarding-0.13.11/agents/llm_renderers/__init__.py +22 -0
  14. codeboarding-0.13.11/agents/llm_renderers/call_graph.py +119 -0
  15. codeboarding-0.13.11/agents/llm_renderers/clustering.py +129 -0
  16. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/meta_agent.py +1 -3
  17. codeboarding-0.13.11/agents/planner_agent.py +95 -0
  18. codeboarding-0.13.11/agents/prompts/abstract_prompt_factory.py +63 -0
  19. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/prompts/claude_prompts.py +43 -0
  20. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/prompts/deepseek_prompts.py +86 -6
  21. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/prompts/gemini_flash_prompts.py +43 -0
  22. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/prompts/glm_prompts.py +43 -0
  23. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/prompts/gpt_prompts.py +43 -0
  24. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/prompts/kimi_prompts.py +88 -27
  25. codeboarding-0.13.11/agents/relation_edges.py +469 -0
  26. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/repair.py +27 -4
  27. codeboarding-0.13.11/agents/scope_ids.py +3 -0
  28. codeboarding-0.13.11/agents/static_analysis_enricher_mixin.py +225 -0
  29. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/base.py +4 -3
  30. codeboarding-0.13.11/agents/tools/component_bridge_edges.py +68 -0
  31. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/read_cfg.py +3 -2
  32. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/validation.py +40 -71
  33. {codeboarding-0.13.8 → codeboarding-0.13.11}/caching/__init__.py +0 -3
  34. {codeboarding-0.13.8 → codeboarding-0.13.11}/caching/cache.py +8 -63
  35. {codeboarding-0.13.8 → codeboarding-0.13.11}/caching/meta_cache.py +0 -3
  36. codeboarding-0.13.11/clustering_ids.py +54 -0
  37. {codeboarding-0.13.8 → codeboarding-0.13.11/codeboarding.egg-info}/PKG-INFO +16 -5
  38. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding.egg-info/SOURCES.txt +24 -15
  39. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding.egg-info/entry_points.txt +1 -0
  40. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding.egg-info/requires.txt +6 -0
  41. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding.egg-info/top_level.txt +1 -0
  42. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_cli/commands/full_analysis.py +10 -7
  43. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_cli/commands/incremental_analysis.py +1 -12
  44. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_cli/commands/partial_analysis.py +0 -1
  45. codeboarding-0.13.11/codeboarding_cli/render.py +72 -0
  46. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_workflows/__init__.py +0 -1
  47. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_workflows/analysis.py +30 -14
  48. codeboarding-0.13.11/codeboarding_workflows/orchestration.py +27 -0
  49. {codeboarding-0.13.8 → codeboarding-0.13.11}/core/__init__.py +1 -1
  50. {codeboarding-0.13.8 → codeboarding-0.13.11}/core/protocols.py +1 -1
  51. {codeboarding-0.13.8 → codeboarding-0.13.11}/diagram_analysis/analysis_json.py +1 -13
  52. {codeboarding-0.13.8 → codeboarding-0.13.11}/diagram_analysis/diagram_generator.py +467 -547
  53. codeboarding-0.13.11/diagram_analysis/exceptions.py +44 -0
  54. {codeboarding-0.13.8 → codeboarding-0.13.11}/diagram_analysis/file_index.py +53 -1
  55. codeboarding-0.13.11/diagram_analysis/incremental_changes.py +111 -0
  56. {codeboarding-0.13.8 → codeboarding-0.13.11}/diagram_analysis/io_utils.py +21 -0
  57. codeboarding-0.13.11/diagram_analysis/run_context.py +37 -0
  58. codeboarding-0.13.11/diagram_analysis/scope_plan.py +175 -0
  59. {codeboarding-0.13.8 → codeboarding-0.13.11}/diagram_analysis/tree_shape.py +8 -6
  60. {codeboarding-0.13.8 → codeboarding-0.13.11}/github_action.py +2 -2
  61. {codeboarding-0.13.8 → codeboarding-0.13.11}/health/checks/function_size.py +1 -1
  62. {codeboarding-0.13.8 → codeboarding-0.13.11}/health/config.py +0 -32
  63. {codeboarding-0.13.8 → codeboarding-0.13.11}/health/models.py +5 -36
  64. {codeboarding-0.13.8 → codeboarding-0.13.11}/health/runner.py +7 -29
  65. {codeboarding-0.13.8 → codeboarding-0.13.11}/install.py +1 -1
  66. {codeboarding-0.13.8 → codeboarding-0.13.11}/main.py +19 -0
  67. codeboarding-0.13.11/output_generators/__init__.py +5 -0
  68. {codeboarding-0.13.8 → codeboarding-0.13.11}/output_generators/html.py +13 -5
  69. {codeboarding-0.13.8 → codeboarding-0.13.11}/output_generators/markdown.py +4 -3
  70. {codeboarding-0.13.8 → codeboarding-0.13.11}/output_generators/mdx.py +15 -8
  71. codeboarding-0.13.11/output_generators/rendering.py +232 -0
  72. {codeboarding-0.13.8 → codeboarding-0.13.11}/output_generators/sphinx.py +6 -2
  73. {codeboarding-0.13.8 → codeboarding-0.13.11}/pyproject.toml +13 -3
  74. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/__init__.py +355 -177
  75. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/analysis_cache.py +63 -58
  76. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/analysis_result.py +50 -6
  77. codeboarding-0.13.11/static_analyzer/cfg/__init__.py +10 -0
  78. codeboarding-0.13.11/static_analyzer/cfg/call_graph.py +232 -0
  79. codeboarding-0.13.11/static_analyzer/cfg/edge.py +103 -0
  80. codeboarding-0.13.11/static_analyzer/cfg/location_key.py +12 -0
  81. codeboarding-0.13.11/static_analyzer/cluster_relations.py +229 -0
  82. codeboarding-0.13.11/static_analyzer/clustering/__init__.py +27 -0
  83. codeboarding-0.13.11/static_analyzer/clustering/cache.py +104 -0
  84. codeboarding-0.13.11/static_analyzer/clustering/delta.py +473 -0
  85. codeboarding-0.13.11/static_analyzer/clustering/engine.py +223 -0
  86. codeboarding-0.13.11/static_analyzer/clustering/exceptions.py +40 -0
  87. codeboarding-0.13.11/static_analyzer/clustering/expansion.py +30 -0
  88. codeboarding-0.13.11/static_analyzer/clustering/grouping.py +436 -0
  89. codeboarding-0.13.11/static_analyzer/clustering/models.py +220 -0
  90. codeboarding-0.13.11/static_analyzer/clustering/service.py +780 -0
  91. codeboarding-0.13.8/diagram_analysis/cluster_snapshot.py → codeboarding-0.13.11/static_analyzer/clustering/snapshot.py +27 -17
  92. codeboarding-0.13.8/static_analyzer/constants.py → codeboarding-0.13.11/static_analyzer/config.py +118 -20
  93. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/csharp_config_scanner.py +19 -6
  94. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/adapters/__init__.py +9 -8
  95. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/adapters/csharp_adapter.py +37 -2
  96. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/adapters/go_adapter.py +1 -1
  97. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/adapters/java_adapter.py +1 -1
  98. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/adapters/php_adapter.py +1 -1
  99. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/adapters/python_adapter.py +1 -1
  100. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/adapters/rust_adapter.py +1 -1
  101. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/adapters/typescript_adapter.py +5 -1
  102. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/call_graph_builder.py +11 -3
  103. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/edge_builder.py +245 -13
  104. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/language_adapter.py +36 -1
  105. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/lsp_client.py +18 -1
  106. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/lsp_constants.py +5 -2
  107. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/lsp_recycler.py +16 -0
  108. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/models.py +3 -0
  109. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/protocols.py +12 -0
  110. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/result_converter.py +8 -8
  111. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/source_inspector.py +291 -2
  112. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/symbol_table.py +61 -1
  113. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/incremental_orchestrator.py +257 -23
  114. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/internal_references.py +14 -1
  115. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/language_results.py +8 -7
  116. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/leiden_utils.py +3 -5
  117. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/node.py +10 -4
  118. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/programming_language.py +1 -1
  119. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/reference_resolver.py +20 -67
  120. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/typescript_config_scanner.py +54 -10
  121. codeboarding-0.13.11/tests/test_cli_parser.py +180 -0
  122. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_github_action.py +5 -5
  123. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_main.py +142 -55
  124. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_pyproject_packages.py +11 -0
  125. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_registry_coverage.py +2 -2
  126. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_user_config.py +50 -2
  127. {codeboarding-0.13.8 → codeboarding-0.13.11}/tool_registry/registry.py +1 -1
  128. {codeboarding-0.13.8 → codeboarding-0.13.11}/user_config.py +16 -4
  129. codeboarding-0.13.8/agents/cluster_ids.py +0 -49
  130. codeboarding-0.13.8/agents/cluster_methods_mixin.py +0 -752
  131. codeboarding-0.13.8/agents/details_agent.py +0 -300
  132. codeboarding-0.13.8/agents/planner_agent.py +0 -181
  133. codeboarding-0.13.8/agents/prompts/abstract_prompt_factory.py +0 -98
  134. codeboarding-0.13.8/agents/relation_edges.py +0 -291
  135. codeboarding-0.13.8/agents/scope_ids.py +0 -1
  136. codeboarding-0.13.8/agents/tools/component_bridge_edges.py +0 -84
  137. codeboarding-0.13.8/caching/details_cache.py +0 -38
  138. codeboarding-0.13.8/codeboarding_workflows/orchestration.py +0 -48
  139. codeboarding-0.13.8/codeboarding_workflows/rendering.py +0 -139
  140. codeboarding-0.13.8/diagram_analysis/cluster_delta.py +0 -940
  141. codeboarding-0.13.8/diagram_analysis/exceptions.py +0 -78
  142. codeboarding-0.13.8/diagram_analysis/run_context.py +0 -71
  143. codeboarding-0.13.8/diagram_analysis/scope_plan.py +0 -260
  144. codeboarding-0.13.8/health/checks/cohesion.py +0 -99
  145. codeboarding-0.13.8/health/checks/coupling.py +0 -140
  146. codeboarding-0.13.8/health/checks/god_class.py +0 -167
  147. codeboarding-0.13.8/health/checks/inheritance.py +0 -104
  148. codeboarding-0.13.8/health/checks/instability.py +0 -77
  149. codeboarding-0.13.8/output_generators/__init__.py +0 -0
  150. codeboarding-0.13.8/static_analyzer/cluster_helpers.py +0 -577
  151. codeboarding-0.13.8/static_analyzer/cluster_relations.py +0 -582
  152. codeboarding-0.13.8/static_analyzer/graph.py +0 -811
  153. codeboarding-0.13.8/static_analyzer/method_cluster_paths.py +0 -77
  154. codeboarding-0.13.8/tests/test_cli_parser.py +0 -70
  155. {codeboarding-0.13.8 → codeboarding-0.13.11}/LICENSE +0 -0
  156. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/__init__.py +0 -0
  157. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/agent.py +0 -0
  158. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/change_status.py +0 -0
  159. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/constants.py +0 -0
  160. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/dependency_discovery.py +0 -0
  161. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/llm_errors.py +0 -0
  162. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/model_capabilities.py +0 -0
  163. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/prompts/__init__.py +0 -0
  164. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/prompts/prompt_factory.py +0 -0
  165. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/retry.py +0 -0
  166. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/__init__.py +0 -0
  167. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/get_external_deps.py +0 -0
  168. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/get_method_invocations.py +0 -0
  169. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/list_git_changes.py +0 -0
  170. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/read_docs.py +0 -0
  171. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/read_file.py +0 -0
  172. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/read_file_structure.py +0 -0
  173. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/read_packages.py +0 -0
  174. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/read_source.py +0 -0
  175. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/read_structure.py +0 -0
  176. {codeboarding-0.13.8 → codeboarding-0.13.11}/agents/tools/toolkit.py +0 -0
  177. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding.egg-info/dependency_links.txt +0 -0
  178. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_cli/__init__.py +0 -0
  179. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_cli/bootstrap.py +0 -0
  180. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_cli/commands/__init__.py +0 -0
  181. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_cli/view_instructions.py +0 -0
  182. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_workflows/sources/__init__.py +0 -0
  183. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_workflows/sources/local.py +0 -0
  184. {codeboarding-0.13.8 → codeboarding-0.13.11}/codeboarding_workflows/sources/remote.py +0 -0
  185. {codeboarding-0.13.8 → codeboarding-0.13.11}/constants.py +0 -0
  186. {codeboarding-0.13.8 → codeboarding-0.13.11}/core/plugin_loader.py +0 -0
  187. {codeboarding-0.13.8 → codeboarding-0.13.11}/core/registry.py +0 -0
  188. {codeboarding-0.13.8 → codeboarding-0.13.11}/diagram_analysis/__init__.py +0 -0
  189. {codeboarding-0.13.8 → codeboarding-0.13.11}/diagram_analysis/file_coverage.py +0 -0
  190. {codeboarding-0.13.8 → codeboarding-0.13.11}/diagram_analysis/run_mode.py +0 -0
  191. {codeboarding-0.13.8 → codeboarding-0.13.11}/health/__init__.py +0 -0
  192. {codeboarding-0.13.8 → codeboarding-0.13.11}/health/checks/__init__.py +0 -0
  193. {codeboarding-0.13.8 → codeboarding-0.13.11}/health/checks/circular_deps.py +0 -0
  194. {codeboarding-0.13.8 → codeboarding-0.13.11}/health/checks/unused_code_diagnostics.py +0 -0
  195. {codeboarding-0.13.8 → codeboarding-0.13.11}/logging_config.py +0 -0
  196. {codeboarding-0.13.8 → codeboarding-0.13.11}/monitoring/__init__.py +0 -0
  197. {codeboarding-0.13.8 → codeboarding-0.13.11}/monitoring/callbacks.py +0 -0
  198. {codeboarding-0.13.8 → codeboarding-0.13.11}/monitoring/context.py +0 -0
  199. {codeboarding-0.13.8 → codeboarding-0.13.11}/monitoring/mixin.py +0 -0
  200. {codeboarding-0.13.8 → codeboarding-0.13.11}/monitoring/paths.py +0 -0
  201. {codeboarding-0.13.8 → codeboarding-0.13.11}/monitoring/stats.py +0 -0
  202. {codeboarding-0.13.8 → codeboarding-0.13.11}/monitoring/writers.py +0 -0
  203. {codeboarding-0.13.8 → codeboarding-0.13.11}/output_generators/html_template.py +0 -0
  204. {codeboarding-0.13.8 → codeboarding-0.13.11}/repo_utils/__init__.py +0 -0
  205. {codeboarding-0.13.8 → codeboarding-0.13.11}/repo_utils/change_detector.py +0 -0
  206. {codeboarding-0.13.8 → codeboarding-0.13.11}/repo_utils/errors.py +0 -0
  207. {codeboarding-0.13.8 → codeboarding-0.13.11}/repo_utils/fingerprint_diff.py +0 -0
  208. {codeboarding-0.13.8 → codeboarding-0.13.11}/repo_utils/git_ops.py +0 -0
  209. {codeboarding-0.13.8 → codeboarding-0.13.11}/repo_utils/ignore.py +0 -0
  210. {codeboarding-0.13.8 → codeboarding-0.13.11}/repo_utils/path_utils.py +0 -0
  211. {codeboarding-0.13.8 → codeboarding-0.13.11}/setup.cfg +0 -0
  212. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/dotnet_sdk.py +0 -0
  213. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/__init__.py +0 -0
  214. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/edge_build_context.py +0 -0
  215. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/hierarchy_builder.py +0 -0
  216. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/process_memory.py +0 -0
  217. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/progress.py +0 -0
  218. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/engine/utils.py +0 -0
  219. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/java_config_scanner.py +0 -0
  220. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/java_utils.py +0 -0
  221. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/lsp_client/__init__.py +0 -0
  222. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/lsp_client/diagnostics.py +0 -0
  223. {codeboarding-0.13.8 → codeboarding-0.13.11}/static_analyzer/scanner.py +0 -0
  224. {codeboarding-0.13.8 → codeboarding-0.13.11}/telemetry/__init__.py +0 -0
  225. {codeboarding-0.13.8 → codeboarding-0.13.11}/telemetry/device_id.py +0 -0
  226. {codeboarding-0.13.8 → codeboarding-0.13.11}/telemetry/events.py +0 -0
  227. {codeboarding-0.13.8 → codeboarding-0.13.11}/telemetry/schemas.py +0 -0
  228. {codeboarding-0.13.8 → codeboarding-0.13.11}/telemetry/service.py +0 -0
  229. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_install.py +0 -0
  230. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_logging_config.py +0 -0
  231. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_persisted_analysis_artifacts.py +0 -0
  232. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_telemetry_events.py +0 -0
  233. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_telemetry_service.py +0 -0
  234. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_tool_registry.py +0 -0
  235. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_view_instructions.py +0 -0
  236. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_vscode_constants.py +0 -0
  237. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_windows_compatibility.py +0 -0
  238. {codeboarding-0.13.8 → codeboarding-0.13.11}/tests/test_windows_encoding.py +0 -0
  239. {codeboarding-0.13.8 → codeboarding-0.13.11}/tool_registry/__init__.py +0 -0
  240. {codeboarding-0.13.8 → codeboarding-0.13.11}/tool_registry/installers.py +0 -0
  241. {codeboarding-0.13.8 → codeboarding-0.13.11}/tool_registry/manifest.py +0 -0
  242. {codeboarding-0.13.8 → codeboarding-0.13.11}/tool_registry/paths.py +0 -0
  243. {codeboarding-0.13.8 → codeboarding-0.13.11}/utils.py +0 -0
  244. {codeboarding-0.13.8 → codeboarding-0.13.11}/vscode_constants.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeboarding
3
- Version: 0.13.8
3
+ Version: 0.13.11
4
4
  Summary: Interactive Diagrams for Code
5
5
  Author: CodeBoarding Team
6
6
  License-Expression: MIT
@@ -10,10 +10,9 @@ Keywords: code-understanding,code-visualization,code-analysis,diagrams,documenta
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: Programming Language :: Python :: 3.12
13
- Classifier: Programming Language :: Python :: 3.13
14
13
  Classifier: Topic :: Software Development :: Documentation
15
14
  Classifier: Topic :: Software Development :: Code Generators
16
- Requires-Python: <3.14,>=3.12
15
+ Requires-Python: <3.13,>=3.12
17
16
  Description-Content-Type: text/markdown
18
17
  License-File: LICENSE
19
18
  Requires-Dist: docker>=7.1
@@ -23,6 +22,7 @@ Requires-Dist: fastapi>=0.115
23
22
  Requires-Dist: filelock>=3.12
24
23
  Requires-Dist: gitpython>=3.1
25
24
  Requires-Dist: google-api-core>=2.10
25
+ Requires-Dist: igraph>=1.0
26
26
  Requires-Dist: jsonpatch>=1.33
27
27
  Requires-Dist: jsonschema>=4.25
28
28
  Requires-Dist: langchain>=1.2
@@ -30,9 +30,11 @@ Requires-Dist: langchain-anthropic>=1.3
30
30
  Requires-Dist: langchain-aws>=1.1
31
31
  Requires-Dist: langchain-cerebras>=0.8
32
32
  Requires-Dist: langchain-community>=0.4
33
+ Requires-Dist: langchain-core>=1.2
33
34
  Requires-Dist: langchain-google-genai>=3.1
34
35
  Requires-Dist: langchain-ollama>=1.0
35
36
  Requires-Dist: langchain-openai>=1.1
37
+ Requires-Dist: langgraph>=1.0
36
38
  Requires-Dist: leidenalg>=0.10
37
39
  Requires-Dist: markdown>=3.8
38
40
  Requires-Dist: markdown-it-py>=3.0
@@ -44,7 +46,10 @@ Requires-Dist: posthog>=3.7
44
46
  Requires-Dist: pydantic>=2.0
45
47
  Requires-Dist: pyyaml>=6.0
46
48
  Requires-Dist: regex>=2024.11
49
+ Requires-Dist: requests>=2.32
47
50
  Requires-Dist: rich>=12.6
51
+ Requires-Dist: sqlalchemy>=2.0
52
+ Requires-Dist: tqdm>=4.67
48
53
  Requires-Dist: tree-sitter==0.25.2
49
54
  Requires-Dist: tree-sitter-c-sharp==0.23.5
50
55
  Requires-Dist: tree-sitter-go==0.25.0
@@ -191,10 +196,13 @@ LLM provider keys and model overrides are stored in `~/.codeboarding/config.toml
191
196
  # google_api_key = "AIza..."
192
197
  # ollama_base_url = "http://localhost:11434"
193
198
 
199
+ # Optional Anthropic endpoint override; requires anthropic_api_key above
200
+ # anthropic_base_url = "https://resource.services.ai.azure.com/anthropic"
201
+
194
202
  [llm]
195
203
  # Optional: override the default model for your active provider
196
- # agent_model = "gemini-3-flash"
197
- # parsing_model = "gemini-3-flash"
204
+ # agent_model = "gemini-3.7-flash"
205
+ # parsing_model = "gemini-3.1-flash-lite"
198
206
  ```
199
207
 
200
208
  Shell environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.) always take precedence over the config file, so CI/CD pipelines need no changes. For private repositories, set `GITHUB_TOKEN` in your environment.
@@ -208,13 +216,16 @@ Shell environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.) always
208
216
  ```
209
217
  codeboarding full [REPO_URL ...] # remote: clone + analyze
210
218
  codeboarding full --local PATH # local: analyze in-place
219
+ codeboarding full --local PATH --render FORMAT # render md/html/mdx/rst after analysis
211
220
  codeboarding incremental --local PATH # re-analyze only changed parts
212
221
  codeboarding partial --local PATH --component-id ID # update one component
222
+ codeboarding-render PATH/analysis.json --format FORMAT # render an existing analysis
213
223
  ```
214
224
 
215
225
  | Option | Description |
216
226
  |---|---|
217
227
  | `--local PATH` | Analyze a local repository (output: `PATH/.codeboarding/`) |
228
+ | `--render {md,html,mdx,rst}` | Render overview and component files from the local `analysis.json` for full, incremental, or partial commands |
218
229
  | `--depth-level INT` | Safety-valve depth cap (default: 3) on how deep auto-expansion goes; a component that outgrows the leaf ceiling is flagged expandable regardless, and can be expanded on demand |
219
230
  | `--force` | (full only) Force full reanalysis, skip cached static analysis |
220
231
  | `--base-ref REF` / `--target-ref REF` | (incremental only) Git refs to diff |
@@ -119,10 +119,13 @@ LLM provider keys and model overrides are stored in `~/.codeboarding/config.toml
119
119
  # google_api_key = "AIza..."
120
120
  # ollama_base_url = "http://localhost:11434"
121
121
 
122
+ # Optional Anthropic endpoint override; requires anthropic_api_key above
123
+ # anthropic_base_url = "https://resource.services.ai.azure.com/anthropic"
124
+
122
125
  [llm]
123
126
  # Optional: override the default model for your active provider
124
- # agent_model = "gemini-3-flash"
125
- # parsing_model = "gemini-3-flash"
127
+ # agent_model = "gemini-3.7-flash"
128
+ # parsing_model = "gemini-3.1-flash-lite"
126
129
  ```
127
130
 
128
131
  Shell environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.) always take precedence over the config file, so CI/CD pipelines need no changes. For private repositories, set `GITHUB_TOKEN` in your environment.
@@ -136,13 +139,16 @@ Shell environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.) always
136
139
  ```
137
140
  codeboarding full [REPO_URL ...] # remote: clone + analyze
138
141
  codeboarding full --local PATH # local: analyze in-place
142
+ codeboarding full --local PATH --render FORMAT # render md/html/mdx/rst after analysis
139
143
  codeboarding incremental --local PATH # re-analyze only changed parts
140
144
  codeboarding partial --local PATH --component-id ID # update one component
145
+ codeboarding-render PATH/analysis.json --format FORMAT # render an existing analysis
141
146
  ```
142
147
 
143
148
  | Option | Description |
144
149
  |---|---|
145
150
  | `--local PATH` | Analyze a local repository (output: `PATH/.codeboarding/`) |
151
+ | `--render {md,html,mdx,rst}` | Render overview and component files from the local `analysis.json` for full, incremental, or partial commands |
146
152
  | `--depth-level INT` | Safety-valve depth cap (default: 3) on how deep auto-expansion goes; a component that outgrows the leaf ceiling is flagged expandable regardless, and can be expanded on demand |
147
153
  | `--force` | (full only) Force full reanalysis, skip cached static analysis |
148
154
  | `--base-ref REF` / `--target-ref REF` | (incremental only) Git refs to diff |
@@ -95,6 +95,27 @@ Output is written to `/path/to/repo/.codeboarding/`. To explore it interactively
95
95
  [web platform](https://app.codeboarding.org) and drop in the generated `analysis.json` — it stays
96
96
  in your browser, nothing is uploaded. (The CLI prints this reminder after every successful run.)
97
97
 
98
+ To also generate `overview` and one file per expanded component, pass `--render` with one of
99
+ `md`, `html`, `mdx`, or `rst`. Rendering is available after full, incremental, and partial local analyses;
100
+ previously generated files for the selected format are reconciled from a renderer-owned manifest:
101
+
102
+ ```bash
103
+ codeboarding full --local /path/to/repo --render md
104
+ codeboarding incremental --local /path/to/repo --render html
105
+ codeboarding partial --local /path/to/repo --component-id "1.2" --render rst
106
+ ```
107
+
108
+ You can render an existing analysis without rerunning analysis or configuring an LLM:
109
+
110
+ ```bash
111
+ codeboarding-render /path/to/repo/.codeboarding/analysis.json
112
+ # Select a format or output directory:
113
+ codeboarding-render /path/to/analysis.json --format mdx --output-dir /path/to/docs
114
+
115
+ # From a source checkout in development:
116
+ python codeboarding_cli/render.py ../../demo/markitdown/.codeboarding/analysis.json --format md
117
+ ```
118
+
98
119
  `python install.py` 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.
99
120
 
100
121
  ## Configuration
@@ -106,21 +127,25 @@ On first run, CodeBoarding creates `~/.codeboarding/config.toml`. Set one provid
106
127
  # openai_api_key = "sk-..."
107
128
  # openai_base_url = "https://api.example.com/v1" # any OpenAI-compatible gateway
108
129
  # anthropic_api_key = "sk-ant-..."
130
+ # anthropic_base_url = "https://resource.services.ai.azure.com/anthropic" # Azure Foundry
109
131
  # google_api_key = "AIza..."
110
132
  # vercel_api_key = "vck_..."
111
133
  # aws_bearer_token_bedrock = "..."
112
134
  # ollama_base_url = "http://localhost:11434"
113
135
  # openrouter_api_key = "sk-..."
136
+ # orcarouter_api_key = "sk-orca-..." # model routing gateway (https://www.orcarouter.ai)
114
137
  # litellm_base_url = "http://localhost:4000" # LiteLLM proxy server URL (required)
115
138
  # litellm_api_key = "sk-..." # LiteLLM proxy server key (optional)
116
139
 
117
140
  [llm]
118
- # agent_model = "gemini-3-flash"
119
- # parsing_model = "gemini-3-flash"
141
+ # agent_model = "gemini-3.7-flash"
142
+ # parsing_model = "gemini-3.1-flash-lite"
120
143
  ```
121
144
 
122
145
  `openai_base_url` points CodeBoarding at any OpenAI-compatible gateway. Set the gateway API key as `openai_api_key`, then choose its model IDs with `agent_model` and `parsing_model`. The equivalent shell variables are `OPENAI_BASE_URL`, `OPENAI_API_KEY`, `AGENT_MODEL`, and `PARSING_MODEL`.
123
146
 
147
+ `anthropic_base_url` points the Anthropic client at a compatible Messages API, including Azure Foundry Claude deployments. Set the deployment key as `anthropic_api_key` and use canonical Anthropic model IDs such as `claude-sonnet-5` for `agent_model` and `parsing_model` so CodeBoarding selects the correct model capabilities and prompts.
148
+
124
149
  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.
125
150
 
126
151
  ## Common commands
@@ -150,7 +175,8 @@ python main.py full https://github.com/pytorch/pytorch
150
175
  > `.codeboarding/` only if you want the baseline to travel with the branch (so a teammate or a
151
176
  > fresh checkout can run incremental too). With no baseline at all — or one that predates content
152
177
  > versioning — `incremental` fails fast with "run a full analysis first" rather than silently
153
- > doing a full run.
178
+ > doing a full run. Static-analysis caches are versioned but not migrated; after a cache-version
179
+ > upgrade, run a full analysis once to reindex.
154
180
 
155
181
  ## Where to use it
156
182
 
@@ -162,7 +188,7 @@ python main.py full https://github.com/pytorch/pytorch
162
188
  ## Supported stack
163
189
 
164
190
  - Languages: Python, TypeScript, JavaScript, Java, Go, PHP, Rust, C#.
165
- - LLM providers: OpenAI, Anthropic, Google, Vercel AI Gateway, AWS Bedrock, Ollama, OpenRouter, LiteLLM proxy, and more.
191
+ - LLM providers: OpenAI, Anthropic, Google, Vercel AI Gateway, AWS Bedrock, Ollama, OpenRouter, OrcaRouter, LiteLLM proxy, and more.
166
192
 
167
193
  ## Examples
168
194
 
@@ -10,12 +10,16 @@ from agents.agent_responses import (
10
10
  ComponentApiSurfaces,
11
11
  ComponentArchitecture,
12
12
  ComponentRelations,
13
- ClusterAnalysis,
14
13
  MetaAnalysisInsights,
15
- assign_component_ids,
16
14
  assign_relation_ids,
17
15
  )
18
- from agents.cluster_methods_mixin import ClusterMethodsMixin
16
+ from agents.component_ownership import group_ids_by_name
17
+ from agents.llm_renderers import (
18
+ cluster_group_descriptions,
19
+ cluster_group_ids,
20
+ render_cluster_groups,
21
+ render_scope_connections,
22
+ )
19
23
  from agents.prompts import (
20
24
  get_final_analysis_message,
21
25
  get_api_surfaces_message,
@@ -24,7 +28,13 @@ from agents.prompts import (
24
28
  format_project_system_message,
25
29
  )
26
30
  from agents.relation_edges import index_relation_endpoints
27
- from agents.repair import ComponentRepairContext, repair_component_group_names, repair_key_entities
31
+ from agents.repair import (
32
+ ComponentRepairContext,
33
+ ensure_unique_key_entities,
34
+ repair_component_group_names,
35
+ repair_key_entities,
36
+ )
37
+ from agents.static_analysis_enricher_mixin import StaticAnalysisEnricherMixin
28
38
  from agents.validation import (
29
39
  ValidationContext,
30
40
  validate_group_name_coverage,
@@ -34,14 +44,12 @@ from agents.validation import (
34
44
  from monitoring import trace
35
45
  from static_analyzer import StaticAnalysisFatalError
36
46
  from static_analyzer.analysis_result import StaticAnalysisResults
37
- from static_analyzer.cluster_helpers import build_all_cluster_results
38
- from static_analyzer.constants import Language
39
- from static_analyzer.graph import ClusterResult
47
+ from static_analyzer.clustering import ClusterResult, ClusterScopeResult
40
48
 
41
49
  logger = logging.getLogger(__name__)
42
50
 
43
51
 
44
- class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
52
+ class AbstractionAgent(StaticAnalysisEnricherMixin, CodeBoardingAgent):
45
53
  def __init__(
46
54
  self,
47
55
  repo_dir: Path,
@@ -79,33 +87,40 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
79
87
  ),
80
88
  }
81
89
 
82
- @trace
83
- def step_clusters_grouping(self, cluster_results: dict[str, ClusterResult]) -> ClusterAnalysis:
84
- """Deterministically partition leaf clusters into the top-level component groups.
85
-
86
- Resolution-tuned Leiden picks both the count (modularity peak over
87
- ``[5, 8]``) and the membership, so the top-level structure is stable across
88
- re-runs the LLM no longer decides it; it only names them in the
89
- final-analysis step.
90
- """
91
- logger.info(f"[AbstractionAgent] Super-clustering leaf clusters for: {self.project_name}")
92
- cfg_graphs = {
93
- lang: self.static_analysis.get_cfg(Language(lang)).clustering_networkx() for lang in cluster_results
94
- }
95
- return self.deterministic_cluster_grouping(cluster_results, cfg_graphs)
90
+ def run(self, scope: ClusterScopeResult) -> AnalysisInsights:
91
+ if not scope.groups:
92
+ # No leaf clusters means the repo has no callable structure to abstract
93
+ # (unsupported/empty/no-code). Fail loudly instead of emptying the
94
+ # architecture and crashing downstream in populate_file_methods.
95
+ raise StaticAnalysisFatalError(
96
+ f"No component groups found for {self.project_name}: the static analysis produced "
97
+ "no callable structure to build an architecture from."
98
+ )
96
99
 
97
- @trace
98
- def step_final_analysis(
99
- self, llm_cluster_analysis: ClusterAnalysis, cluster_results: dict[str, ClusterResult]
100
- ) -> AnalysisInsights:
101
- logger.info(f"[AbstractionAgent] Generating final component analysis for: {self.project_name}")
100
+ analysis = self._step_llm_analysis(scope)
101
+ self.populate_file_methods(analysis, scope)
102
+
103
+ api_surfaces = self._step_api_surfaces(analysis, scope)
104
+ self._step_relation_analysis(analysis, api_surfaces, scope)
105
+
106
+ analysis = self.reference_resolver.fix_source_code_reference_lines(analysis)
107
+ index_relation_endpoints(analysis, self.repo_dir)
108
+ ensure_unique_key_entities(analysis)
102
109
 
103
- cluster_str = llm_cluster_analysis.llm_str() if llm_cluster_analysis else "No cluster analysis available."
110
+ return analysis
104
111
 
105
- group_names = [cc.name for cc in llm_cluster_analysis.cluster_components] if llm_cluster_analysis else []
112
+ @trace
113
+ def _step_llm_analysis(self, scope: ClusterScopeResult) -> AnalysisInsights:
114
+ logger.info(f"[AbstractionAgent] Generating final component analysis for: {self.project_name}")
115
+ cluster_results = scope.leaf_clusters_by_language
116
+ group_ids = cluster_group_ids(scope.groups)
117
+ group_names = list(group_ids)
106
118
 
107
119
  prompt = self.prompts["final_analysis"].format(
108
- cluster_analysis=cluster_str,
120
+ cluster_analysis=render_cluster_groups(
121
+ group_ids,
122
+ cluster_group_descriptions(scope),
123
+ ),
109
124
  )
110
125
 
111
126
  if group_names:
@@ -117,7 +132,7 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
117
132
  context = ValidationContext(
118
133
  cluster_results=cluster_results,
119
134
  static_analysis=self.static_analysis,
120
- llm_cluster_analysis=llm_cluster_analysis,
135
+ group_ids=group_ids,
121
136
  )
122
137
 
123
138
  architecture = self._invoke_repair_validate(
@@ -131,12 +146,12 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
131
146
  repair_context=ComponentRepairContext(
132
147
  reference_resolver=self.reference_resolver,
133
148
  cluster_results=cluster_results,
134
- llm_cluster_analysis=llm_cluster_analysis,
149
+ group_ids=group_ids,
135
150
  ),
136
151
  validation_context=context,
137
152
  max_validation_attempts=3,
138
153
  )
139
- self.assemble_one_component_per_group(architecture, llm_cluster_analysis, cluster_results)
154
+ self.assemble_one_component_per_group(architecture, scope)
140
155
  return AnalysisInsights(
141
156
  description=architecture.description,
142
157
  components=architecture.components,
@@ -144,9 +159,16 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
144
159
  )
145
160
 
146
161
  @trace
147
- def step_api_surfaces(self, analysis: AnalysisInsights) -> ComponentApiSurfaces:
162
+ def _step_api_surfaces(
163
+ self,
164
+ analysis: AnalysisInsights,
165
+ scope: ClusterScopeResult,
166
+ ) -> ComponentApiSurfaces:
148
167
  logger.info(f"[AbstractionAgent] Analyzing component API surfaces for: {self.project_name}")
149
- static_call_evidence = self.build_scope_cfg_string(analysis)
168
+ static_call_evidence = render_scope_connections(
169
+ scope,
170
+ {component.component_id: component.name for component in analysis.components},
171
+ )
150
172
  prompt = self.prompts["api_surfaces"].format(
151
173
  component_summaries=analysis.llm_str(),
152
174
  static_call_evidence=static_call_evidence,
@@ -154,17 +176,23 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
154
176
  return self._parse_invoke(prompt, ComponentApiSurfaces)
155
177
 
156
178
  @trace
157
- def step_relation_analysis(
179
+ def _step_relation_analysis(
158
180
  self,
159
181
  analysis: AnalysisInsights,
160
182
  api_surfaces: ComponentApiSurfaces,
161
- cluster_analysis: ClusterAnalysis,
162
- cluster_results: dict[str, ClusterResult],
183
+ scope: ClusterScopeResult,
163
184
  ) -> None:
164
185
  logger.info(f"[AbstractionAgent] Discovering component relations for: {self.project_name}")
165
- static_call_evidence = self.build_scope_cfg_string(analysis)
166
- cfg_graphs = self.static_analysis.available_cfgs()
167
- self.toolkit.context.cluster_analysis = cluster_analysis
186
+ cluster_results = scope.leaf_clusters_by_language
187
+ static_call_evidence = render_scope_connections(
188
+ scope,
189
+ {component.component_id: component.name for component in analysis.components},
190
+ )
191
+ cfg_graphs = scope.graphs_by_language
192
+ self.toolkit.context.clustering = scope
193
+ self.toolkit.context.group_ids_by_name = group_ids_by_name(
194
+ analysis.components, {group.group_id for group in scope.groups}
195
+ )
168
196
  self.toolkit.context.cluster_results = cluster_results
169
197
  self.toolkit.context.cfg_graphs = cfg_graphs
170
198
  prompt = self.prompts["relation_analysis"].format(
@@ -181,50 +209,10 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
181
209
  cfg_graphs=cfg_graphs,
182
210
  repo_dir=str(self.repo_dir),
183
211
  static_analysis=self.static_analysis,
184
- llm_cluster_analysis=cluster_analysis,
185
212
  components=analysis.components,
186
213
  ),
187
214
  max_validation_attempts=3,
188
215
  )
189
216
  analysis.components_relations = relation_result.components_relations
190
217
  assign_relation_ids(analysis)
191
- self.build_static_relations(analysis)
192
-
193
- def run(self):
194
- # Build full cluster results dict for all languages ONCE
195
- cluster_results = build_all_cluster_results(self.static_analysis)
196
-
197
- # Step 1: Deterministically partition leaf clusters into the top-level groups (Leiden, modularity-peak N)
198
- cluster_analysis = self.step_clusters_grouping(cluster_results)
199
- if not cluster_analysis.cluster_components:
200
- # No leaf clusters means the repo has no callable structure to abstract
201
- # (unsupported/empty/no-code). Fail loudly instead of emptying the
202
- # architecture and crashing downstream in populate_file_methods.
203
- raise StaticAnalysisFatalError(
204
- f"No component groups found for {self.project_name}: the static analysis produced "
205
- "no callable structure to build an architecture from."
206
- )
207
-
208
- # Step 2: Name and describe each fixed group into a component (LLM, one component per group)
209
- analysis = self.step_final_analysis(cluster_analysis, cluster_results)
210
- # Step 3: Assign hierarchical component IDs ("1", "2", "3", ...)
211
- assign_component_ids(analysis)
212
- # Step 4: Resolve cluster IDs deterministically from group names
213
- self._resolve_cluster_ids_from_groups(analysis, cluster_analysis)
214
- # Step 5: Populate file_methods deterministically from cluster results + orphan assignment
215
- self.populate_file_methods(analysis, cluster_results)
216
-
217
- # Step 6: Analyze component API surfaces
218
- api_surfaces = self.step_api_surfaces(analysis)
219
-
220
- # Step 7: Discover relations from API surfaces and attach deterministic all_edges
221
- self.step_relation_analysis(analysis, api_surfaces, cluster_analysis, cluster_results)
222
-
223
- # Step 8: Fix source code reference lines (resolves reference_file paths for key_entities and key_edges)
224
- analysis = self.reference_resolver.fix_source_code_reference_lines(analysis)
225
- # Step 9: Index relation endpoints after reference resolution
226
- index_relation_endpoints(analysis, self.repo_dir)
227
- # Step 10: Ensure unique key entities across components
228
- self._ensure_unique_key_entities(analysis)
229
-
230
- return analysis, cluster_results
218
+ self.merge_scope_relations(analysis, scope)
@@ -5,20 +5,17 @@ import logging
5
5
  from abc import abstractmethod
6
6
  from collections.abc import Hashable
7
7
  from enum import StrEnum
8
- from typing import TYPE_CHECKING, get_origin
8
+ from typing import get_origin
9
9
 
10
10
  from pydantic import BaseModel, Field
11
11
  from pydantic.fields import FieldInfo
12
12
 
13
- from agents.cluster_ids import CodeBoardingClusterId, GraphClusterId
14
- from agents.file_index_models import FileEntry, FileMethodGroup
13
+ from clustering_ids import ComponentId
14
+ from agents.file_index_models import FileEntry, FileMethodGroup, MethodIndexEntry
15
15
  from agents.scope_ids import ROOT_SCOPE_ID
16
16
 
17
17
  logger = logging.getLogger(__name__)
18
18
 
19
- if TYPE_CHECKING:
20
- from diagram_analysis.analysis_json import MethodIndexEntry
21
-
22
19
 
23
20
  class LLMBaseModel(BaseModel, abc.ABC):
24
21
  """Base model for LLM-parseable response types."""
@@ -317,7 +314,7 @@ class Relation(LLMBaseModel):
317
314
  src_id=src_id,
318
315
  dst_id=dst_id,
319
316
  is_static=is_static,
320
- all_edges=cls._unique_edges(edges),
317
+ all_edges=cls.unique_edges(edges),
321
318
  )
322
319
 
323
320
  def llm_str(self) -> str:
@@ -356,12 +353,12 @@ class Relation(LLMBaseModel):
356
353
  def _merge_edges(
357
354
  key_edges: list[RelationEdge], all_edges: list[RelationEdge]
358
355
  ) -> tuple[list[RelationEdge], list[RelationEdge]]:
359
- merged_key_edges = Relation._unique_edges(key_edges)
360
- merged_all_edges = Relation._unique_edges([*all_edges, *merged_key_edges])
356
+ merged_key_edges = Relation.unique_edges(key_edges)
357
+ merged_all_edges = Relation.unique_edges([*all_edges, *merged_key_edges])
361
358
  return merged_key_edges, merged_all_edges
362
359
 
363
360
  @staticmethod
364
- def _unique_edges(edges: list[RelationEdge]) -> list[RelationEdge]:
361
+ def unique_edges(edges: list[RelationEdge]) -> list[RelationEdge]:
365
362
  unique_edges: list[RelationEdge] = []
366
363
  seen: set[Hashable] = set()
367
364
  for edge in edges:
@@ -377,72 +374,6 @@ class Relation(LLMBaseModel):
377
374
  return len(self.all_edges)
378
375
 
379
376
 
380
- class ClustersComponent(LLMBaseModel):
381
- """A grouped component from cluster analysis - may contain multiple clusters."""
382
-
383
- name: str = Field(
384
- description="Short, descriptive name for this cluster group (e.g., 'Authentication', 'Data Pipeline', 'Request Handling')"
385
- )
386
- cluster_ids: list[GraphClusterId] = Field(
387
- description="List of cluster IDs from the CFG analysis that are grouped together (e.g., [1, 3, 5])"
388
- )
389
- description: str = Field(
390
- description="Explanation of what this component does, its main flow, WHY these clusters are grouped together, how it interacts with other cluster groups, and the most important classes/methods (by their exact qualified names from the clusters)"
391
- )
392
- existing_component_id: str | None = Field(
393
- default=None,
394
- description=(
395
- "Incremental routing: the exact component_id of the existing component "
396
- "this entry is routing clusters into (e.g. '1.3'). Set to null to create "
397
- "a brand-new component. Identity is by ID, not name — leaving this null "
398
- "while reusing an existing component's name forks a duplicate component. "
399
- "Ignored by the full-analysis flow."
400
- ),
401
- json_schema_extra={"hidden": True},
402
- )
403
- parent_id: str | None = Field(
404
- default=None,
405
- description=(
406
- "Incremental routing: when ``existing_component_id`` is null (brand-new "
407
- "component), the existing component_id under which the new component "
408
- "should attach (or null to attach at root). Ignored when "
409
- "``existing_component_id`` is set, and ignored by the full-analysis flow."
410
- ),
411
- json_schema_extra={"hidden": True},
412
- )
413
- redetail_needed: bool = Field(
414
- default=True,
415
- description=(
416
- "Incremental routing only: when routing clusters into an existing component "
417
- "(``existing_component_id`` is set), set False if the cluster delta is "
418
- "cosmetic (refactor, internal rename, small bug fix) and the component's "
419
- "high-level purpose is unchanged — the existing description stays. Default "
420
- "True forces a full redetail. Ignored for brand-new components (always "
421
- "redetailed) and by the full-analysis flow."
422
- ),
423
- json_schema_extra={"hidden": True},
424
- )
425
-
426
- def llm_str(self):
427
- ids_str = ", ".join(str(cid) for cid in self.cluster_ids)
428
- return f"**{self.name}** (cluster_ids: [{ids_str}])\n {self.description}"
429
-
430
-
431
- class ClusterAnalysis(LLMBaseModel):
432
- """Analysis results containing grouped cluster components."""
433
-
434
- cluster_components: list[ClustersComponent] = Field(
435
- description="Grouped clusters into logical components. Multiple cluster IDs can be grouped together if they work as a cohesive unit."
436
- )
437
-
438
- def llm_str(self):
439
- if not self.cluster_components:
440
- return "No clusters analyzed."
441
- title = "# Grouped Cluster Components\n"
442
- body = "\n".join(cc.llm_str() for cc in self.cluster_components)
443
- return title + body
444
-
445
-
446
377
  class Component(LLMBaseModel):
447
378
  """A software component with name, description, and key entities."""
448
379
 
@@ -459,7 +390,7 @@ class Component(LLMBaseModel):
459
390
  default_factory=list,
460
391
  )
461
392
 
462
- source_cluster_ids: list[CodeBoardingClusterId] = Field(
393
+ source_cluster_ids: list[ComponentId] = Field(
463
394
  description="List of cluster IDs from CFG analysis that this component encompasses (populated deterministically from source_group_names).",
464
395
  default_factory=list,
465
396
  exclude=True,
@@ -512,6 +443,23 @@ class AnalysisInsights(LLMBaseModel):
512
443
  components: list[Component] = Field(description="List of the components identified in the project.")
513
444
  components_relations: list[Relation] = Field(description="List of relations among the components.")
514
445
 
446
+ def component_by_id(self, component_id: str) -> Component | None:
447
+ """Return the component with this stable ID."""
448
+ return next((component for component in self.components if component.component_id == component_id), None)
449
+
450
+ def component_by_name(self, name: str) -> Component | None:
451
+ """Return the first component with this displayed name."""
452
+ return next((component for component in self.components if component.name == name), None)
453
+
454
+ def node_owners(self) -> dict[str, str]:
455
+ """Map each indexed symbol to its owning component ID."""
456
+ return {
457
+ method.qualified_name: component.component_id
458
+ for component in self.components
459
+ for file_methods in component.file_methods
460
+ for method in file_methods.methods
461
+ }
462
+
515
463
  def llm_str(self):
516
464
  if not self.components:
517
465
  return "No abstract components found."
@@ -0,0 +1,56 @@
1
+ """Derived component ownership data used to resolve relation endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Collection
6
+ from dataclasses import dataclass
7
+
8
+ from agents.agent_responses import AnalysisInsights, Component, SourceCodeReference
9
+
10
+
11
+ def group_ids_by_name(components: list[Component], valid_group_ids: Collection[str]) -> dict[str, str]:
12
+ """Map component source-group names to their authoritative live group IDs."""
13
+ valid_ids = set(valid_group_ids)
14
+ return {
15
+ name: component.component_id
16
+ for component in components
17
+ if component.component_id in valid_ids
18
+ for name in component.source_group_names
19
+ }
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class ComponentOwnershipIndex:
24
+ """Resolve source references to their unique owning component."""
25
+
26
+ candidates_by_leaf: dict[str, list[tuple[str, str]]]
27
+
28
+ @classmethod
29
+ def from_analysis(cls, analysis: AnalysisInsights) -> ComponentOwnershipIndex:
30
+ candidates_by_leaf: dict[str, list[tuple[str, str]]] = {}
31
+ for component in analysis.components:
32
+ for file_methods in component.file_methods:
33
+ for method in file_methods.methods:
34
+ candidates_by_leaf.setdefault(method.qualified_name.rsplit(".", 1)[-1], []).append(
35
+ (method.qualified_name, component.component_id)
36
+ )
37
+ return cls(candidates_by_leaf)
38
+
39
+ @classmethod
40
+ def from_node_owners(cls, node_owners: dict[str, str]) -> ComponentOwnershipIndex:
41
+ candidates_by_leaf: dict[str, list[tuple[str, str]]] = {}
42
+ for qualified_name, component_id in node_owners.items():
43
+ candidates_by_leaf.setdefault(qualified_name.rsplit(".", 1)[-1], []).append((qualified_name, component_id))
44
+ return cls(candidates_by_leaf)
45
+
46
+ def owner_of(self, reference: SourceCodeReference) -> str:
47
+ """Return the unique owner of a canonical or suffix-qualified reference."""
48
+ qualified_name = reference.qualified_name.replace(":", ".")
49
+ owners = {
50
+ component_id
51
+ for candidate, component_id in self.candidates_by_leaf.get(qualified_name.rsplit(".", 1)[-1], ())
52
+ if qualified_name == candidate.replace(":", ".")
53
+ or qualified_name.endswith(f".{candidate.replace(':', '.')}")
54
+ or candidate.replace(":", ".").endswith(f".{qualified_name}")
55
+ }
56
+ return owners.pop() if len(owners) == 1 else ""
@@ -1,10 +1,8 @@
1
1
  """Content fingerprinting helpers — a leaf module with no analysis imports.
2
2
 
3
- Standalone (no heavy analysis imports) so clustering, the JSON layer, and the
4
- wrapper can all import it without an ``analysis_json`` <-> ``cluster_methods_mixin``
5
- cycle. ``splitlines()`` folds line-ending-only edits accepted for change
6
- detection. Decode/encode both use ``surrogateescape`` so invalid UTF-8 bytes stay
7
- distinct (``replace`` would collapse them to U+FFFD and mask real changes).
3
+ Standalone (no heavy analysis imports) so clustering, static enrichment, and the
4
+ JSON layer can import it without cycles. ``splitlines()`` folds line-ending-only
5
+ edits. Decode/encode use ``surrogateescape`` so invalid UTF-8 bytes stay distinct.
8
6
  """
9
7
 
10
8
  import hashlib