codeboarding 0.10.3__tar.gz → 0.11.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. {codeboarding-0.10.3/codeboarding.egg-info → codeboarding-0.11.0}/PKG-INFO +21 -9
  2. {codeboarding-0.10.3 → codeboarding-0.11.0}/PYPI.md +10 -8
  3. {codeboarding-0.10.3 → codeboarding-0.11.0}/README.md +8 -8
  4. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/abstraction_agent.py +14 -2
  5. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/agent.py +108 -118
  6. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/agent_responses.py +13 -16
  7. codeboarding-0.11.0/agents/analysis_patcher.py +206 -0
  8. codeboarding-0.11.0/agents/cluster_budget.py +21 -0
  9. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/cluster_methods_mixin.py +170 -17
  10. codeboarding-0.11.0/agents/constants.py +38 -0
  11. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/details_agent.py +12 -2
  12. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/llm_config.py +83 -43
  13. codeboarding-0.11.0/agents/model_capabilities.py +217 -0
  14. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/prompts/abstract_prompt_factory.py +8 -0
  15. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/prompts/claude_prompts.py +37 -0
  16. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/prompts/deepseek_prompts.py +37 -0
  17. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/prompts/gemini_flash_prompts.py +37 -0
  18. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/prompts/glm_prompts.py +37 -0
  19. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/prompts/gpt_prompts.py +37 -0
  20. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/prompts/kimi_prompts.py +37 -0
  21. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/prompts/prompt_factory.py +8 -0
  22. codeboarding-0.11.0/agents/retry.py +118 -0
  23. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/toolkit.py +0 -8
  24. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/validation.py +12 -29
  25. {codeboarding-0.10.3 → codeboarding-0.11.0/codeboarding.egg-info}/PKG-INFO +21 -9
  26. {codeboarding-0.10.3 → codeboarding-0.11.0}/codeboarding.egg-info/SOURCES.txt +32 -6
  27. {codeboarding-0.10.3 → codeboarding-0.11.0}/codeboarding.egg-info/requires.txt +10 -0
  28. {codeboarding-0.10.3 → codeboarding-0.11.0}/codeboarding.egg-info/top_level.txt +2 -0
  29. codeboarding-0.11.0/codeboarding_cli/__init__.py +1 -0
  30. codeboarding-0.11.0/codeboarding_cli/bootstrap.py +53 -0
  31. codeboarding-0.11.0/codeboarding_cli/commands/__init__.py +1 -0
  32. codeboarding-0.11.0/codeboarding_cli/commands/full_analysis.py +197 -0
  33. codeboarding-0.11.0/codeboarding_cli/commands/incremental_analysis.py +137 -0
  34. codeboarding-0.11.0/codeboarding_cli/commands/partial_analysis.py +69 -0
  35. codeboarding-0.11.0/codeboarding_workflows/__init__.py +14 -0
  36. codeboarding-0.11.0/codeboarding_workflows/analysis.py +144 -0
  37. codeboarding-0.11.0/codeboarding_workflows/orchestration.py +48 -0
  38. codeboarding-0.11.0/codeboarding_workflows/rendering.py +92 -0
  39. codeboarding-0.11.0/codeboarding_workflows/sources/__init__.py +12 -0
  40. codeboarding-0.11.0/codeboarding_workflows/sources/local.py +23 -0
  41. codeboarding-0.11.0/codeboarding_workflows/sources/remote.py +71 -0
  42. codeboarding-0.11.0/diagram_analysis/__init__.py +22 -0
  43. {codeboarding-0.10.3 → codeboarding-0.11.0}/diagram_analysis/analysis_json.py +146 -115
  44. {codeboarding-0.10.3 → codeboarding-0.11.0}/diagram_analysis/diagram_generator.py +293 -69
  45. codeboarding-0.11.0/diagram_analysis/ease.py +68 -0
  46. codeboarding-0.10.3/diagram_analysis/incremental_types.py → codeboarding-0.11.0/diagram_analysis/incremental/delta.py +23 -9
  47. codeboarding-0.11.0/diagram_analysis/incremental/models.py +220 -0
  48. codeboarding-0.11.0/diagram_analysis/incremental/payload.py +129 -0
  49. codeboarding-0.11.0/diagram_analysis/incremental/pipeline.py +264 -0
  50. codeboarding-0.11.0/diagram_analysis/incremental/semantic_diff.py +557 -0
  51. codeboarding-0.11.0/diagram_analysis/incremental/trace_planner.py +435 -0
  52. codeboarding-0.11.0/diagram_analysis/incremental/tracer.py +458 -0
  53. codeboarding-0.10.3/diagram_analysis/incremental_updater.py → codeboarding-0.11.0/diagram_analysis/incremental/updater.py +174 -125
  54. {codeboarding-0.10.3 → codeboarding-0.11.0}/diagram_analysis/io_utils.py +3 -2
  55. {codeboarding-0.10.3 → codeboarding-0.11.0}/diagram_analysis/run_context.py +3 -0
  56. codeboarding-0.11.0/diagram_analysis/run_metadata.py +146 -0
  57. codeboarding-0.11.0/github_action.py +129 -0
  58. {codeboarding-0.10.3 → codeboarding-0.11.0}/install.py +1 -1
  59. {codeboarding-0.10.3 → codeboarding-0.11.0}/logging_config.py +1 -1
  60. codeboarding-0.11.0/main.py +99 -0
  61. codeboarding-0.11.0/output_generators/__init__.py +0 -0
  62. {codeboarding-0.10.3 → codeboarding-0.11.0}/pyproject.toml +45 -3
  63. {codeboarding-0.10.3 → codeboarding-0.11.0}/repo_utils/__init__.py +2 -5
  64. codeboarding-0.11.0/repo_utils/change_detector.py +311 -0
  65. codeboarding-0.11.0/repo_utils/diff_parser.py +277 -0
  66. codeboarding-0.11.0/repo_utils/git_ops.py +283 -0
  67. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/__init__.py +2 -2
  68. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/analysis_result.py +38 -0
  69. codeboarding-0.11.0/static_analyzer/cfg_skip_planner.py +207 -0
  70. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/constants.py +31 -0
  71. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/adapters/csharp_adapter.py +3 -3
  72. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/adapters/go_adapter.py +3 -2
  73. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/adapters/java_adapter.py +3 -3
  74. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/adapters/php_adapter.py +3 -3
  75. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/adapters/python_adapter.py +3 -2
  76. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/adapters/rust_adapter.py +5 -4
  77. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/adapters/typescript_adapter.py +5 -4
  78. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/language_adapter.py +15 -2
  79. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/graph.py +85 -27
  80. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/incremental_orchestrator.py +11 -11
  81. codeboarding-0.11.0/tests/test_cli_parser.py +70 -0
  82. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_github_action.py +11 -11
  83. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_main.py +212 -218
  84. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_tool_registry.py +10 -2
  85. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_user_config.py +34 -1
  86. {codeboarding-0.10.3 → codeboarding-0.11.0}/tool_registry/registry.py +4 -3
  87. {codeboarding-0.10.3 → codeboarding-0.11.0}/user_config.py +23 -6
  88. {codeboarding-0.10.3 → codeboarding-0.11.0}/utils.py +14 -1
  89. {codeboarding-0.10.3 → codeboarding-0.11.0}/vscode_constants.py +2 -2
  90. codeboarding-0.10.3/agents/constants.py +0 -13
  91. codeboarding-0.10.3/agents/tools/read_git_diff.py +0 -131
  92. codeboarding-0.10.3/diagram_analysis/__init__.py +0 -3
  93. codeboarding-0.10.3/github_action.py +0 -173
  94. codeboarding-0.10.3/main.py +0 -567
  95. codeboarding-0.10.3/repo_utils/change_detector.py +0 -294
  96. codeboarding-0.10.3/repo_utils/git_diff.py +0 -74
  97. codeboarding-0.10.3/repo_utils/method_diff.py +0 -177
  98. codeboarding-0.10.3/static_analyzer/git_diff_analyzer.py +0 -224
  99. {codeboarding-0.10.3 → codeboarding-0.11.0}/LICENSE +0 -0
  100. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/__init__.py +0 -0
  101. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/change_status.py +0 -0
  102. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/dependency_discovery.py +0 -0
  103. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/meta_agent.py +0 -0
  104. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/planner_agent.py +0 -0
  105. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/prompts/__init__.py +0 -0
  106. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/__init__.py +0 -0
  107. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/base.py +0 -0
  108. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/get_external_deps.py +0 -0
  109. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/get_method_invocations.py +0 -0
  110. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/read_cfg.py +0 -0
  111. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/read_docs.py +0 -0
  112. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/read_file.py +0 -0
  113. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/read_file_structure.py +0 -0
  114. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/read_packages.py +0 -0
  115. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/read_source.py +0 -0
  116. {codeboarding-0.10.3 → codeboarding-0.11.0}/agents/tools/read_structure.py +0 -0
  117. {codeboarding-0.10.3 → codeboarding-0.11.0}/caching/__init__.py +0 -0
  118. {codeboarding-0.10.3 → codeboarding-0.11.0}/caching/cache.py +0 -0
  119. {codeboarding-0.10.3 → codeboarding-0.11.0}/caching/details_cache.py +0 -0
  120. {codeboarding-0.10.3 → codeboarding-0.11.0}/caching/meta_cache.py +0 -0
  121. {codeboarding-0.10.3 → codeboarding-0.11.0}/codeboarding.egg-info/dependency_links.txt +0 -0
  122. {codeboarding-0.10.3 → codeboarding-0.11.0}/codeboarding.egg-info/entry_points.txt +0 -0
  123. {codeboarding-0.10.3 → codeboarding-0.11.0}/constants.py +0 -0
  124. {codeboarding-0.10.3 → codeboarding-0.11.0}/core/__init__.py +0 -0
  125. {codeboarding-0.10.3 → codeboarding-0.11.0}/core/plugin_loader.py +0 -0
  126. {codeboarding-0.10.3 → codeboarding-0.11.0}/core/protocols.py +0 -0
  127. {codeboarding-0.10.3 → codeboarding-0.11.0}/core/registry.py +0 -0
  128. {codeboarding-0.10.3 → codeboarding-0.11.0}/diagram_analysis/file_coverage.py +1 -1
  129. {codeboarding-0.10.3/output_generators → codeboarding-0.11.0/diagram_analysis/incremental}/__init__.py +0 -0
  130. {codeboarding-0.10.3 → codeboarding-0.11.0}/diagram_analysis/version.py +0 -0
  131. {codeboarding-0.10.3 → codeboarding-0.11.0}/duckdb_crud.py +0 -0
  132. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/__init__.py +0 -0
  133. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/checks/__init__.py +0 -0
  134. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/checks/circular_deps.py +0 -0
  135. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/checks/cohesion.py +0 -0
  136. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/checks/coupling.py +0 -0
  137. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/checks/function_size.py +0 -0
  138. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/checks/god_class.py +0 -0
  139. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/checks/inheritance.py +0 -0
  140. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/checks/instability.py +0 -0
  141. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/checks/unused_code_diagnostics.py +0 -0
  142. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/config.py +0 -0
  143. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/constants.py +0 -0
  144. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/models.py +0 -0
  145. {codeboarding-0.10.3 → codeboarding-0.11.0}/health/runner.py +0 -0
  146. {codeboarding-0.10.3 → codeboarding-0.11.0}/health_main.py +0 -0
  147. {codeboarding-0.10.3 → codeboarding-0.11.0}/monitoring/__init__.py +0 -0
  148. {codeboarding-0.10.3 → codeboarding-0.11.0}/monitoring/callbacks.py +0 -0
  149. {codeboarding-0.10.3 → codeboarding-0.11.0}/monitoring/context.py +0 -0
  150. {codeboarding-0.10.3 → codeboarding-0.11.0}/monitoring/mixin.py +0 -0
  151. {codeboarding-0.10.3 → codeboarding-0.11.0}/monitoring/paths.py +0 -0
  152. {codeboarding-0.10.3 → codeboarding-0.11.0}/monitoring/stats.py +0 -0
  153. {codeboarding-0.10.3 → codeboarding-0.11.0}/monitoring/writers.py +0 -0
  154. {codeboarding-0.10.3 → codeboarding-0.11.0}/output_generators/html.py +0 -0
  155. {codeboarding-0.10.3 → codeboarding-0.11.0}/output_generators/html_template.py +0 -0
  156. {codeboarding-0.10.3 → codeboarding-0.11.0}/output_generators/markdown.py +0 -0
  157. {codeboarding-0.10.3 → codeboarding-0.11.0}/output_generators/mdx.py +0 -0
  158. {codeboarding-0.10.3 → codeboarding-0.11.0}/output_generators/sphinx.py +0 -0
  159. {codeboarding-0.10.3 → codeboarding-0.11.0}/repo_utils/errors.py +0 -0
  160. {codeboarding-0.10.3 → codeboarding-0.11.0}/repo_utils/ignore.py +0 -0
  161. {codeboarding-0.10.3 → codeboarding-0.11.0}/setup.cfg +0 -0
  162. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/analysis_cache.py +0 -0
  163. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/cluster_change_analyzer.py +0 -0
  164. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/cluster_helpers.py +0 -0
  165. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/cluster_relations.py +0 -0
  166. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/csharp_config_scanner.py +0 -0
  167. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/__init__.py +0 -0
  168. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/adapters/__init__.py +0 -0
  169. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/call_graph_builder.py +0 -0
  170. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/edge_build_context.py +0 -0
  171. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/edge_builder.py +0 -0
  172. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/hierarchy_builder.py +0 -0
  173. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/lsp_client.py +0 -0
  174. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/lsp_constants.py +0 -0
  175. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/models.py +0 -0
  176. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/progress.py +0 -0
  177. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/protocols.py +0 -0
  178. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/result_converter.py +0 -0
  179. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/source_inspector.py +0 -0
  180. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/symbol_table.py +0 -0
  181. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/engine/utils.py +0 -0
  182. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/java_config_scanner.py +0 -0
  183. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/java_utils.py +0 -0
  184. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/lsp_client/__init__.py +0 -0
  185. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/lsp_client/diagnostics.py +0 -0
  186. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/node.py +0 -0
  187. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/programming_language.py +0 -0
  188. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/reference_resolve_mixin.py +0 -0
  189. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/scanner.py +0 -0
  190. {codeboarding-0.10.3 → codeboarding-0.11.0}/static_analyzer/typescript_config_scanner.py +0 -0
  191. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_install.py +0 -0
  192. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_logging_config.py +0 -0
  193. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_pyproject_packages.py +0 -0
  194. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_registry_coverage.py +0 -0
  195. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_vscode_constants.py +0 -0
  196. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_windows_compatibility.py +0 -0
  197. {codeboarding-0.10.3 → codeboarding-0.11.0}/tests/test_windows_encoding.py +0 -0
  198. {codeboarding-0.10.3 → codeboarding-0.11.0}/tool_registry/__init__.py +0 -0
  199. {codeboarding-0.10.3 → codeboarding-0.11.0}/tool_registry/installers.py +0 -0
  200. {codeboarding-0.10.3 → codeboarding-0.11.0}/tool_registry/manifest.py +0 -0
  201. {codeboarding-0.10.3 → codeboarding-0.11.0}/tool_registry/paths.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeboarding
3
- Version: 0.10.3
3
+ Version: 0.11.0
4
4
  Summary: Interactive Diagrams for Code
5
5
  Author: CodeBoarding Team
6
6
  License-Expression: MIT
@@ -24,6 +24,7 @@ Requires-Dist: fastapi>=0.115
24
24
  Requires-Dist: filelock>=3.12
25
25
  Requires-Dist: gitpython>=3.1
26
26
  Requires-Dist: google-api-core>=2.10
27
+ Requires-Dist: jsonpatch>=1.33
27
28
  Requires-Dist: jsonschema>=4.25
28
29
  Requires-Dist: langchain>=1.2
29
30
  Requires-Dist: langchain-anthropic>=1.3
@@ -42,6 +43,15 @@ Requires-Dist: pathspec>=0.12
42
43
  Requires-Dist: pyyaml>=6.0
43
44
  Requires-Dist: regex>=2024.11
44
45
  Requires-Dist: rich>=12.6
46
+ Requires-Dist: tree-sitter>=0.23
47
+ Requires-Dist: tree-sitter-c-sharp>=0.23
48
+ Requires-Dist: tree-sitter-go>=0.23
49
+ Requires-Dist: tree-sitter-java>=0.23
50
+ Requires-Dist: tree-sitter-javascript>=0.23
51
+ Requires-Dist: tree-sitter-php>=0.23
52
+ Requires-Dist: tree-sitter-python>=0.23
53
+ Requires-Dist: tree-sitter-rust>=0.23
54
+ Requires-Dist: tree-sitter-typescript>=0.23
45
55
  Requires-Dist: trustcall>=0.0.39
46
56
  Requires-Dist: uvicorn>=0.23
47
57
  Provides-Extra: dev
@@ -110,10 +120,10 @@ codeboarding-setup
110
120
 
111
121
  ```bash
112
122
  # Analyze a local repository (output goes to /path/to/repo/.codeboarding/)
113
- codeboarding --local /path/to/repo
123
+ codeboarding full --local /path/to/repo
114
124
 
115
125
  # Analyze a remote GitHub repository (cloned to cwd/repo_name/, output to cwd/repo_name/.codeboarding/)
116
- codeboarding https://github.com/user/repo
126
+ codeboarding full https://github.com/user/repo
117
127
  ```
118
128
 
119
129
  ### Python API
@@ -188,19 +198,21 @@ Shell environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.) always
188
198
  ## CLI Reference
189
199
 
190
200
  ```
191
- codeboarding [REPO_URL ...] # remote: clone + analyze
192
- codeboarding --local PATH # local: analyze in-place
201
+ codeboarding full [REPO_URL ...] # remote: clone + analyze
202
+ codeboarding full --local PATH # local: analyze in-place
203
+ codeboarding incremental --local PATH # re-analyze only changed parts
204
+ codeboarding partial --local PATH --component-id ID # update one component
193
205
  ```
194
206
 
195
207
  | Option | Description |
196
208
  |---|---|
197
209
  | `--local PATH` | Analyze a local repository (output: `PATH/.codeboarding/`) |
198
210
  | `--depth-level INT` | Diagram depth (default: 1) |
199
- | `--incremental` | Smart incremental update (only re-analyze changed files) |
200
- | `--full` | Force full reanalysis, skip incremental detection |
201
- | `--partial-component-id ID` | Update a single component by its ID |
211
+ | `--force` | (full only) Force full reanalysis, skip cached static analysis |
212
+ | `--base-ref REF` / `--target-ref REF` | (incremental only) Git refs to diff |
213
+ | `--component-id ID` | (partial only) ID of the component to update |
202
214
  | `--binary-location PATH` | Custom path to language server binaries (overrides `~/.codeboarding/servers/`) |
203
- | `--upload` | Upload results to GeneratedOnBoardings repo (remote only) |
215
+ | `--upload` | (full, remote only) Upload results to GeneratedOnBoardings repo |
204
216
  | `--enable-monitoring` | Enable run monitoring |
205
217
 
206
218
  ---
@@ -50,10 +50,10 @@ codeboarding-setup
50
50
 
51
51
  ```bash
52
52
  # Analyze a local repository (output goes to /path/to/repo/.codeboarding/)
53
- codeboarding --local /path/to/repo
53
+ codeboarding full --local /path/to/repo
54
54
 
55
55
  # Analyze a remote GitHub repository (cloned to cwd/repo_name/, output to cwd/repo_name/.codeboarding/)
56
- codeboarding https://github.com/user/repo
56
+ codeboarding full https://github.com/user/repo
57
57
  ```
58
58
 
59
59
  ### Python API
@@ -128,19 +128,21 @@ Shell environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.) always
128
128
  ## CLI Reference
129
129
 
130
130
  ```
131
- codeboarding [REPO_URL ...] # remote: clone + analyze
132
- codeboarding --local PATH # local: analyze in-place
131
+ codeboarding full [REPO_URL ...] # remote: clone + analyze
132
+ codeboarding full --local PATH # local: analyze in-place
133
+ codeboarding incremental --local PATH # re-analyze only changed parts
134
+ codeboarding partial --local PATH --component-id ID # update one component
133
135
  ```
134
136
 
135
137
  | Option | Description |
136
138
  |---|---|
137
139
  | `--local PATH` | Analyze a local repository (output: `PATH/.codeboarding/`) |
138
140
  | `--depth-level INT` | Diagram depth (default: 1) |
139
- | `--incremental` | Smart incremental update (only re-analyze changed files) |
140
- | `--full` | Force full reanalysis, skip incremental detection |
141
- | `--partial-component-id ID` | Update a single component by its ID |
141
+ | `--force` | (full only) Force full reanalysis, skip cached static analysis |
142
+ | `--base-ref REF` / `--target-ref REF` | (incremental only) Git refs to diff |
143
+ | `--component-id ID` | (partial only) ID of the component to update |
142
144
  | `--binary-location PATH` | Custom path to language server binaries (overrides `~/.codeboarding/servers/`) |
143
- | `--upload` | Upload results to GeneratedOnBoardings repo (remote only) |
145
+ | `--upload` | (full, remote only) Upload results to GeneratedOnBoardings repo |
144
146
  | `--enable-monitoring` | Enable run monitoring |
145
147
 
146
148
  ---
@@ -70,7 +70,7 @@ For a deeper architecture walkthrough, see [`.codeboarding/overview.md`](.codebo
70
70
  uv sync --frozen
71
71
  source .venv/bin/activate # On Windows: .venv\Scripts\activate
72
72
  python install.py
73
- python main.py --local /path/to/repo
73
+ python main.py full --local /path/to/repo
74
74
  ```
75
75
 
76
76
  ### Use the packaged CLI
@@ -80,7 +80,7 @@ Requires **Python 3.12 or 3.13**. The recommended install method is [pipx](https
80
80
  ```bash
81
81
  pipx install codeboarding --python python3.12
82
82
  codeboarding-setup
83
- codeboarding --local /path/to/repo
83
+ codeboarding full --local /path/to/repo
84
84
  ```
85
85
 
86
86
  Or, if you prefer pip, install into a virtual environment (not the global Python):
@@ -88,7 +88,7 @@ Or, if you prefer pip, install into a virtual environment (not the global Python
88
88
  ```bash
89
89
  pip install codeboarding
90
90
  codeboarding-setup
91
- codeboarding --local /path/to/repo
91
+ codeboarding full --local /path/to/repo
92
92
  ```
93
93
 
94
94
  Output is written to `/path/to/repo/.codeboarding/`.
@@ -120,19 +120,19 @@ Shell environment variables such as `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOG
120
120
 
121
121
  ```bash
122
122
  # Analyze a local repository
123
- python main.py --local ./my-project
123
+ python main.py full --local ./my-project
124
124
 
125
125
  # Increase diagram depth
126
- python main.py --local ./my-project --depth-level 2
126
+ python main.py full --local ./my-project --depth-level 2
127
127
 
128
128
  # Re-analyze only changed parts when possible
129
- python main.py --local ./my-project --incremental
129
+ python main.py incremental --local ./my-project
130
130
 
131
131
  # Update a single component by ID
132
- python main.py --local ./my-project --partial-component-id "1.2"
132
+ python main.py partial --local ./my-project --component-id "1.2"
133
133
 
134
134
  # Analyze a remote GitHub repository
135
- python main.py https://github.com/pytorch/pytorch
135
+ python main.py full https://github.com/pytorch/pytorch
136
136
  ```
137
137
 
138
138
  ## Where to use it
@@ -70,8 +70,20 @@ class AbstractionAgent(ClusterMethodsMixin, CodeBoardingAgent):
70
70
 
71
71
  programming_langs = self.static_analysis.get_languages()
72
72
 
73
- # Build cluster string using the pre-computed cluster results
74
- cluster_str = self._build_cluster_string(programming_langs, cluster_results)
73
+ # Measure everything that wraps cfg_clusters (system message + rendered
74
+ # template with an empty slot) so the skip planner can back it out of
75
+ # the input window before budgeting the cluster string.
76
+ overhead_chars = len(str(self.system_message.content)) + len(
77
+ self.prompts["group_clusters"].format(
78
+ project_name=self.project_name,
79
+ cfg_clusters="",
80
+ meta_context=meta_context_str,
81
+ project_type=project_type,
82
+ )
83
+ )
84
+ cluster_str = self._build_cluster_string(
85
+ programming_langs, cluster_results, prompt_overhead_chars=overhead_chars
86
+ )
75
87
 
76
88
  prompt = self.prompts["group_clusters"].format(
77
89
  project_name=self.project_name,
@@ -1,6 +1,5 @@
1
1
  import json
2
2
  import logging
3
- import time
4
3
  from pathlib import Path
5
4
 
6
5
  from google.api_core.exceptions import ResourceExhausted
@@ -15,6 +14,7 @@ from pydantic import ValidationError
15
14
  from trustcall import create_extractor
16
15
 
17
16
  from agents.prompts import get_validation_feedback_message
17
+ from agents.retry import RetryAction, RetryDecision, default_backoff, with_retries
18
18
  from agents.tools.base import RepoContext
19
19
  from agents.tools.toolkit import CodeBoardingToolkit
20
20
  from agents.validation import ValidationResult, score_validation_results, VALIDATOR_WEIGHTS, DEFAULT_VALIDATOR_WEIGHT
@@ -96,86 +96,69 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
96
96
  def _invoke(self, prompt, callbacks: list | None = None) -> str:
97
97
  """Unified agent invocation method with timeout and exponential backoff.
98
98
 
99
- Uses exponential backoff based on total attempts, with different multipliers
100
- for different error types. This ensures backoff increases appropriately even
101
- when errors alternate between types.
99
+ Classification applied per exception:
100
+ - ``TimeoutError``: backoff ``min(10·2^n, 120)``, raise on exhaustion.
101
+ - ``ResourceExhausted``: backoff ``min(30·2^n, 300)``, raise on exhaustion.
102
+ - ``status_code == 404``: raise immediately (retired model ID, etc.).
103
+ - Other exceptions: backoff ``min(10·2^n, 120)``, return fallback string
104
+ on exhaustion (non-raising — callers treat the fallback as a failed run).
102
105
  """
103
- max_retries = 5
104
-
105
- for attempt in range(max_retries):
106
+ max_attempts = 5
107
+ # Counter captured by the closure so we can vary the per-attempt timeout
108
+ # without reaching into the retry helper.
109
+ attempt_counter = [0]
110
+
111
+ def call_once() -> str:
112
+ attempt = attempt_counter[0]
113
+ attempt_counter[0] += 1
106
114
  timeout_seconds = 300 if attempt == 0 else 600
107
- try:
108
- callback_list = callbacks or []
109
- # Always append monitoring callback - logging config controls output
110
- callback_list.append(MONITORING_CALLBACK)
111
- callback_list.append(self.agent_monitoring_callback)
112
-
113
- logger.info(
114
- f"Starting agent.invoke() [attempt {attempt + 1}/{max_retries}] with prompt length: {len(prompt)}, timeout: {timeout_seconds}s"
115
- )
116
-
117
- response = self._invoke_with_timeout(
118
- timeout_seconds=timeout_seconds, callback_list=callback_list, prompt=prompt
119
- )
120
-
121
- logger.info(
122
- f"Completed agent.invoke() - message count: {len(response['messages'])}, last message type: {type(response['messages'][-1])}"
115
+ callback_list = (callbacks or []) + [MONITORING_CALLBACK, self.agent_monitoring_callback]
116
+ logger.info(
117
+ f"Starting agent.invoke() [attempt {attempt + 1}/{max_attempts}] with prompt length: {len(prompt)}, timeout: {timeout_seconds}s"
118
+ )
119
+ response = self._invoke_with_timeout(
120
+ timeout_seconds=timeout_seconds, callback_list=callback_list, prompt=prompt
121
+ )
122
+ logger.info(
123
+ f"Completed agent.invoke() - message count: {len(response['messages'])}, last message type: {type(response['messages'][-1])}"
124
+ )
125
+ agent_response = response["messages"][-1]
126
+ assert isinstance(agent_response, AIMessage), f"Expected AIMessage, but got {type(agent_response)}"
127
+ if isinstance(agent_response.content, str):
128
+ return agent_response.content
129
+ if isinstance(agent_response.content, list):
130
+ return "".join(str(m) if not isinstance(m, str) else m for m in agent_response.content)
131
+ return "" # unreachable for AIMessage but satisfies typing
132
+
133
+ def classify(exc: Exception, attempt: int) -> RetryDecision:
134
+ if getattr(exc, "status_code", None) == 404:
135
+ logger.error(f"Permanent HTTP 404 — not retrying: {type(exc).__name__}: {exc}")
136
+ return RetryDecision(action=RetryAction.GIVE_UP)
137
+ if isinstance(exc, ResourceExhausted):
138
+ return RetryDecision(
139
+ action=RetryAction.RETRY,
140
+ backoff_s=default_backoff(attempt, initial_s=30.0, multiplier=2.0, max_s=300.0),
123
141
  )
142
+ # TimeoutError + generic Exception share the same backoff.
143
+ return RetryDecision(
144
+ action=RetryAction.RETRY,
145
+ backoff_s=default_backoff(attempt, initial_s=10.0, multiplier=2.0, max_s=120.0),
146
+ )
124
147
 
125
- agent_response = response["messages"][-1]
126
- assert isinstance(agent_response, AIMessage), f"Expected AIMessage, but got {type(agent_response)}"
127
- if isinstance(agent_response.content, str):
128
- return agent_response.content
129
- if isinstance(agent_response.content, list):
130
- return "".join(
131
- [
132
- str(message) if not isinstance(message, str) else message
133
- for message in agent_response.content
134
- ]
135
- )
136
-
137
- except TimeoutError as e:
138
- if attempt < max_retries - 1:
139
- # Exponential backoff: 10s * 2^attempt (10s, 20s, 40s, 80s)
140
- delay = min(10 * (2**attempt), 120)
141
- logger.warning(
142
- f"Agent invocation timed out after {timeout_seconds}s, retrying in {delay}s... (attempt {attempt + 1}/{max_retries})"
143
- )
144
- time.sleep(delay)
145
- else:
146
- logger.error(f"Agent invocation timed out after {timeout_seconds}s on final attempt")
147
- raise
148
-
149
- except ResourceExhausted as e:
150
- if attempt < max_retries - 1:
151
- # Longer backoff for rate limits: 30s * 2^attempt (30s, 60s, 120s, 240s)
152
- delay = min(30 * (2**attempt), 300)
153
- logger.warning(
154
- f"ResourceExhausted (rate limit): {e}\n"
155
- f"Retrying in {delay}s... (attempt {attempt + 1}/{max_retries})"
156
- )
157
- time.sleep(delay)
158
- else:
159
- logger.error(f"Max retries ({max_retries}) reached. ResourceExhausted: {e}")
160
- raise
161
-
162
- except Exception as e:
163
- # HTTP 404 (e.g. retired model ID) is permanent — retrying won't help.
164
- if getattr(e, "status_code", None) == 404:
165
- logger.error(f"Permanent HTTP 404 — not retrying: {type(e).__name__}: {e}")
166
- raise
167
-
168
- # Other errors (network, parsing, etc.) get standard exponential backoff
169
- if attempt < max_retries - 1:
170
- delay = min(10 * (2**attempt), 120)
171
- logger.warning(
172
- f"Agent error: {type(e).__name__}: {e}, retrying in {delay}s... (attempt {attempt + 1}/{max_retries})"
173
- )
174
- time.sleep(delay)
175
- # On final attempt, fall through to return error message below
176
-
177
- logger.error("Max retries reached. Failed to get response from the agent.")
178
- return "Could not get response from the agent."
148
+ def on_exhausted(exc: Exception) -> str:
149
+ # Typed exceptions surface the original error; only generic falls through
150
+ # to the historic fallback string that callers have long relied on.
151
+ if isinstance(exc, (TimeoutError, ResourceExhausted)):
152
+ raise exc
153
+ return "Could not get response from the agent."
154
+
155
+ return with_retries(
156
+ call_once,
157
+ max_attempts=max_attempts,
158
+ classify=classify,
159
+ on_exhausted=on_exhausted,
160
+ log_prefix="Agent invocation",
161
+ )
179
162
 
180
163
  def _invoke_with_timeout(self, timeout_seconds: int, callback_list: list, prompt: str):
181
164
  """Invoke agent with a timeout using threading."""
@@ -336,18 +319,27 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
336
319
  return best_result
337
320
 
338
321
  def _parse_response(self, prompt, response, return_type, max_retries=5, attempt=0):
339
- if attempt >= max_retries:
340
- logger.error(f"Max retries ({max_retries}) reached for parsing response: {response}")
341
- raise Exception(f"Max retries reached for parsing response: {response}")
342
-
343
- extractor = create_extractor(self.parsing_llm, tools=[return_type], tool_choice=return_type.__name__)
344
322
  if response is None or response.strip() == "":
345
323
  logger.error(f"Empty response for prompt: {prompt}")
346
- try:
347
- result = extractor.invoke(
348
- return_type.extractor_str() + response,
349
- config={"callbacks": [MONITORING_CALLBACK, self.agent_monitoring_callback]},
350
- )
324
+
325
+ def call_once():
326
+ # Extractor is rebuilt on every attempt — previous trustcall state
327
+ # may have corrupted attributes (see the tool_call_id bug below).
328
+ extractor = create_extractor(self.parsing_llm, tools=[return_type], tool_choice=return_type.__name__)
329
+ try:
330
+ result = extractor.invoke(
331
+ return_type.extractor_str() + response,
332
+ config={"callbacks": [MONITORING_CALLBACK, self.agent_monitoring_callback]},
333
+ )
334
+ except AttributeError as e:
335
+ # Trustcall bug: https://github.com/hinthornw/trustcall/issues/47
336
+ # 'ExtractionState' object has no attribute 'tool_call_id' during validation retry.
337
+ # Treat as a non-retriable fallback to the Pydantic parser.
338
+ if "tool_call_id" in str(e):
339
+ logger.warning(f"Trustcall bug encountered, falling back to Pydantic parser: {e}")
340
+ parser = PydanticOutputParser(pydantic_object=return_type)
341
+ return self._try_parse(response, parser)
342
+ raise
351
343
  if "responses" in result and len(result["responses"]) != 0:
352
344
  return return_type.model_validate(result["responses"][0])
353
345
  if "messages" in result and len(result["messages"]) != 0:
@@ -358,38 +350,36 @@ class CodeBoardingAgent(ReferenceResolverMixin, MonitoringMixin):
358
350
  return self._try_parse(message, parser)
359
351
  parser = PydanticOutputParser(pydantic_object=return_type)
360
352
  return self._try_parse(response, parser)
361
- except EmptyExtractorMessageError as e:
362
- logger.warning(f"{e} (attempt {attempt + 1}/{max_retries})")
363
- return self._parse_response(prompt, response, return_type, max_retries, attempt + 1)
364
- except AttributeError as e:
365
- # Workaround for trustcall bug: https://github.com/hinthornw/trustcall/issues/47
366
- # 'ExtractionState' object has no attribute 'tool_call_id' occurs during validation retry
367
- if "tool_call_id" in str(e):
368
- logger.warning(f"Trustcall bug encountered, falling back to Pydantic parser: {e}")
369
- parser = PydanticOutputParser(pydantic_object=return_type)
370
- return self._try_parse(response, parser)
371
- raise
372
- except IndexError as e:
373
- # try to parse with the json parser if possible
374
- logger.warning(f"IndexError while parsing response (attempt {attempt + 1}/{max_retries}): {e}")
375
- return self._parse_response(prompt, response, return_type, max_retries, attempt + 1)
376
- except (json.JSONDecodeError, ValueError) as e:
377
- logger.warning(f"Parse error (attempt {attempt + 1}/{max_retries}): {e}")
378
- return self._parse_response(prompt, response, return_type, max_retries, attempt + 1)
379
- except ResourceExhausted as e:
380
- # Parsing uses exponential backoff for rate limits
381
- if attempt < max_retries - 1:
382
- # Exponential backoff: 30s * 2^attempt, capped at 300s
383
- delay = min(30 * (2**attempt), 300)
384
- logger.warning(
385
- f"ResourceExhausted during parsing (rate limit): {e}\n"
386
- f"Retrying in {delay}s... (attempt {attempt + 1}/{max_retries})"
353
+
354
+ def classify(exc: Exception, attempt: int) -> RetryDecision:
355
+ if isinstance(exc, ResourceExhausted):
356
+ return RetryDecision(
357
+ action=RetryAction.RETRY,
358
+ backoff_s=default_backoff(attempt, initial_s=30.0, multiplier=2.0, max_s=300.0),
387
359
  )
388
- time.sleep(delay)
389
- return self._parse_response(prompt, response, return_type, max_retries, attempt + 1)
390
- else:
391
- logger.error(f"Resource exhausted on final parsing attempt: {e}")
392
- raise
360
+ if isinstance(exc, (EmptyExtractorMessageError, IndexError, json.JSONDecodeError, ValueError)):
361
+ return RetryDecision(action=RetryAction.RETRY_NOW)
362
+ # AttributeError (non-tool_call_id) and any other exception: give up.
363
+ return RetryDecision(action=RetryAction.GIVE_UP)
364
+
365
+ def on_exhausted(exc: Exception):
366
+ # Preserve historic shape: ResourceExhausted surfaces the original exception;
367
+ # parse-error exhaustion wraps with a descriptive message naming the response.
368
+ if isinstance(exc, ResourceExhausted):
369
+ logger.error(f"Resource exhausted on final parsing attempt: {exc}")
370
+ raise exc
371
+ logger.error(f"Max retries ({max_retries}) reached for parsing response: {response}")
372
+ raise Exception(f"Max retries reached for parsing response: {response}")
373
+
374
+ # ``attempt`` kwarg kept for backwards-compat with callers that passed it;
375
+ # the effective attempt count is ``max_retries - attempt``.
376
+ return with_retries(
377
+ call_once,
378
+ max_attempts=max(1, max_retries - attempt),
379
+ classify=classify,
380
+ on_exhausted=on_exhausted,
381
+ log_prefix="Parse response",
382
+ )
393
383
 
394
384
  def _try_parse(self, message_content, parser):
395
385
  try:
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import abc
2
4
  import logging
3
5
  from abc import abstractmethod
@@ -6,8 +8,6 @@ from typing import get_origin, Optional
6
8
 
7
9
  from pydantic import BaseModel, Field
8
10
 
9
- from agents.change_status import ChangeStatus
10
-
11
11
  logger = logging.getLogger(__name__)
12
12
 
13
13
 
@@ -142,10 +142,6 @@ class MethodEntry(BaseModel):
142
142
  start_line: int = Field(description="Starting line number in the file.")
143
143
  end_line: int = Field(description="Ending line number in the file.")
144
144
  node_type: str = Field(description="Node type name matching NodeType enum (e.g. METHOD, FUNCTION, CLASS).")
145
- status: ChangeStatus = Field(
146
- default=ChangeStatus.UNCHANGED,
147
- description="Diff status of this method: added, modified, deleted, or unchanged.",
148
- )
149
145
 
150
146
  def __hash__(self) -> int:
151
147
  return hash(self.qualified_name)
@@ -156,13 +152,22 @@ class MethodEntry(BaseModel):
156
152
  return self.qualified_name == other.qualified_name
157
153
 
158
154
  @classmethod
159
- def from_method_change(cls, method_change, *, status_override: ChangeStatus | None = None) -> "MethodEntry":
155
+ def from_method_change(cls, method_change) -> MethodEntry:
160
156
  return cls(
161
157
  qualified_name=method_change.qualified_name,
162
158
  start_line=method_change.start_line,
163
159
  end_line=method_change.end_line,
164
160
  node_type=method_change.node_type,
165
- status=status_override or method_change.change_type,
161
+ )
162
+
163
+ @classmethod
164
+ def from_node(cls, node) -> MethodEntry:
165
+ """Build from a ``static_analyzer.Node``. Accepts ``Any`` to avoid a hard dep."""
166
+ return cls(
167
+ qualified_name=node.fully_qualified_name,
168
+ start_line=node.line_start,
169
+ end_line=node.line_end,
170
+ node_type=node.type.name,
166
171
  )
167
172
 
168
173
 
@@ -170,10 +175,6 @@ class FileMethodGroup(BaseModel):
170
175
  """All methods/functions belonging to a component within a single file."""
171
176
 
172
177
  file_path: str = Field(description="Relative path to the source file.")
173
- file_status: ChangeStatus = Field(
174
- default=ChangeStatus.UNCHANGED,
175
- description="Diff status of this file: added, modified, deleted, renamed, or unchanged.",
176
- )
177
178
  methods: list[MethodEntry] = Field(
178
179
  default_factory=list,
179
180
  description="Methods and functions in this file that belong to the component, sorted by start_line.",
@@ -183,10 +184,6 @@ class FileMethodGroup(BaseModel):
183
184
  class FileEntry(BaseModel):
184
185
  """Single source of truth for methods in one file."""
185
186
 
186
- file_status: ChangeStatus = Field(
187
- default=ChangeStatus.UNCHANGED,
188
- description="Diff status of this file: added, modified, deleted, renamed, or unchanged.",
189
- )
190
187
  methods: list[MethodEntry] = Field(
191
188
  default_factory=list,
192
189
  description="Methods and functions in this file, sorted by start line.",