codeboarding 0.13.7__tar.gz → 0.13.10__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.
- {codeboarding-0.13.7/codeboarding.egg-info → codeboarding-0.13.10}/PKG-INFO +16 -5
- {codeboarding-0.13.7 → codeboarding-0.13.10}/PYPI.md +8 -2
- {codeboarding-0.13.7 → codeboarding-0.13.10}/README.md +30 -4
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/abstraction_agent.py +70 -82
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/agent_responses.py +25 -77
- codeboarding-0.13.10/agents/component_ownership.py +56 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/content_hash.py +3 -5
- codeboarding-0.13.10/agents/details_agent.py +244 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/file_index_models.py +14 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/incremental_agent.py +96 -151
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/incremental_results.py +11 -3
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/llm_config.py +39 -15
- codeboarding-0.13.10/agents/llm_renderers/__init__.py +22 -0
- codeboarding-0.13.10/agents/llm_renderers/call_graph.py +119 -0
- codeboarding-0.13.10/agents/llm_renderers/clustering.py +129 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/meta_agent.py +1 -3
- codeboarding-0.13.10/agents/planner_agent.py +95 -0
- codeboarding-0.13.10/agents/prompts/abstract_prompt_factory.py +63 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/prompts/claude_prompts.py +43 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/prompts/deepseek_prompts.py +86 -6
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/prompts/gemini_flash_prompts.py +43 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/prompts/glm_prompts.py +43 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/prompts/gpt_prompts.py +43 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/prompts/kimi_prompts.py +88 -27
- codeboarding-0.13.10/agents/relation_edges.py +469 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/repair.py +27 -4
- codeboarding-0.13.10/agents/scope_ids.py +3 -0
- codeboarding-0.13.10/agents/static_analysis_enricher_mixin.py +225 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/base.py +4 -3
- codeboarding-0.13.10/agents/tools/component_bridge_edges.py +68 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/read_cfg.py +3 -2
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/validation.py +40 -71
- {codeboarding-0.13.7 → codeboarding-0.13.10}/caching/__init__.py +0 -3
- {codeboarding-0.13.7 → codeboarding-0.13.10}/caching/cache.py +8 -63
- {codeboarding-0.13.7 → codeboarding-0.13.10}/caching/meta_cache.py +0 -3
- codeboarding-0.13.10/clustering_ids.py +54 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10/codeboarding.egg-info}/PKG-INFO +16 -5
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding.egg-info/SOURCES.txt +26 -15
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding.egg-info/entry_points.txt +1 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding.egg-info/requires.txt +6 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding.egg-info/top_level.txt +1 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_cli/commands/full_analysis.py +10 -7
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_cli/commands/incremental_analysis.py +1 -12
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_cli/commands/partial_analysis.py +0 -1
- codeboarding-0.13.10/codeboarding_cli/render.py +72 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_workflows/__init__.py +0 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_workflows/analysis.py +30 -14
- codeboarding-0.13.10/codeboarding_workflows/orchestration.py +27 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/constants.py +12 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/core/__init__.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/core/protocols.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/diagram_analysis/analysis_json.py +1 -13
- {codeboarding-0.13.7 → codeboarding-0.13.10}/diagram_analysis/diagram_generator.py +467 -547
- codeboarding-0.13.10/diagram_analysis/exceptions.py +44 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/diagram_analysis/file_index.py +53 -1
- codeboarding-0.13.10/diagram_analysis/incremental_changes.py +111 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/diagram_analysis/io_utils.py +21 -0
- codeboarding-0.13.10/diagram_analysis/run_context.py +37 -0
- codeboarding-0.13.10/diagram_analysis/scope_plan.py +175 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/diagram_analysis/tree_shape.py +8 -6
- {codeboarding-0.13.7 → codeboarding-0.13.10}/github_action.py +2 -2
- {codeboarding-0.13.7 → codeboarding-0.13.10}/health/checks/function_size.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/health/config.py +0 -32
- {codeboarding-0.13.7 → codeboarding-0.13.10}/health/models.py +5 -36
- {codeboarding-0.13.7 → codeboarding-0.13.10}/health/runner.py +7 -29
- codeboarding-0.13.10/health_main.py +152 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/install.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/main.py +19 -0
- codeboarding-0.13.10/output_generators/__init__.py +5 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/output_generators/html.py +13 -5
- {codeboarding-0.13.7 → codeboarding-0.13.10}/output_generators/markdown.py +4 -3
- {codeboarding-0.13.7 → codeboarding-0.13.10}/output_generators/mdx.py +15 -8
- codeboarding-0.13.10/output_generators/rendering.py +232 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/output_generators/sphinx.py +6 -2
- {codeboarding-0.13.7 → codeboarding-0.13.10}/pyproject.toml +13 -3
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/__init__.py +355 -177
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/analysis_cache.py +65 -61
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/analysis_result.py +50 -6
- codeboarding-0.13.10/static_analyzer/cfg/__init__.py +10 -0
- codeboarding-0.13.10/static_analyzer/cfg/call_graph.py +232 -0
- codeboarding-0.13.10/static_analyzer/cfg/edge.py +103 -0
- codeboarding-0.13.10/static_analyzer/cfg/location_key.py +12 -0
- codeboarding-0.13.10/static_analyzer/cluster_relations.py +229 -0
- codeboarding-0.13.10/static_analyzer/clustering/__init__.py +27 -0
- codeboarding-0.13.10/static_analyzer/clustering/cache.py +104 -0
- codeboarding-0.13.10/static_analyzer/clustering/delta.py +473 -0
- codeboarding-0.13.10/static_analyzer/clustering/engine.py +223 -0
- codeboarding-0.13.10/static_analyzer/clustering/exceptions.py +40 -0
- codeboarding-0.13.10/static_analyzer/clustering/expansion.py +30 -0
- codeboarding-0.13.10/static_analyzer/clustering/grouping.py +436 -0
- codeboarding-0.13.10/static_analyzer/clustering/models.py +220 -0
- codeboarding-0.13.10/static_analyzer/clustering/service.py +780 -0
- codeboarding-0.13.7/diagram_analysis/cluster_snapshot.py → codeboarding-0.13.10/static_analyzer/clustering/snapshot.py +27 -17
- codeboarding-0.13.7/static_analyzer/constants.py → codeboarding-0.13.10/static_analyzer/config.py +118 -20
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/csharp_config_scanner.py +19 -6
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/adapters/__init__.py +9 -8
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/adapters/csharp_adapter.py +37 -2
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/adapters/go_adapter.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/adapters/java_adapter.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/adapters/php_adapter.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/adapters/python_adapter.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/adapters/rust_adapter.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/adapters/typescript_adapter.py +5 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/call_graph_builder.py +11 -3
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/edge_builder.py +245 -13
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/language_adapter.py +36 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/lsp_client.py +18 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/lsp_constants.py +5 -2
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/lsp_recycler.py +16 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/models.py +3 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/protocols.py +12 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/result_converter.py +8 -8
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/source_inspector.py +291 -2
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/symbol_table.py +61 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/incremental_orchestrator.py +257 -23
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/internal_references.py +14 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/language_results.py +23 -5
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/leiden_utils.py +3 -5
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/node.py +10 -4
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/programming_language.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/reference_resolver.py +20 -67
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/typescript_config_scanner.py +54 -10
- codeboarding-0.13.10/tests/test_cli_parser.py +180 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_github_action.py +5 -5
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_main.py +142 -55
- codeboarding-0.13.10/tests/test_persisted_analysis_artifacts.py +29 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_pyproject_packages.py +11 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_registry_coverage.py +2 -2
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_user_config.py +50 -2
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tool_registry/registry.py +1 -1
- {codeboarding-0.13.7 → codeboarding-0.13.10}/user_config.py +16 -4
- {codeboarding-0.13.7 → codeboarding-0.13.10}/utils.py +1 -3
- codeboarding-0.13.7/agents/cluster_ids.py +0 -49
- codeboarding-0.13.7/agents/cluster_methods_mixin.py +0 -752
- codeboarding-0.13.7/agents/details_agent.py +0 -300
- codeboarding-0.13.7/agents/planner_agent.py +0 -181
- codeboarding-0.13.7/agents/prompts/abstract_prompt_factory.py +0 -98
- codeboarding-0.13.7/agents/relation_edges.py +0 -291
- codeboarding-0.13.7/agents/scope_ids.py +0 -1
- codeboarding-0.13.7/agents/tools/component_bridge_edges.py +0 -84
- codeboarding-0.13.7/caching/details_cache.py +0 -38
- codeboarding-0.13.7/codeboarding_workflows/orchestration.py +0 -48
- codeboarding-0.13.7/codeboarding_workflows/rendering.py +0 -139
- codeboarding-0.13.7/diagram_analysis/cluster_delta.py +0 -940
- codeboarding-0.13.7/diagram_analysis/exceptions.py +0 -78
- codeboarding-0.13.7/diagram_analysis/run_context.py +0 -71
- codeboarding-0.13.7/diagram_analysis/scope_plan.py +0 -260
- codeboarding-0.13.7/health/checks/cohesion.py +0 -99
- codeboarding-0.13.7/health/checks/coupling.py +0 -140
- codeboarding-0.13.7/health/checks/god_class.py +0 -167
- codeboarding-0.13.7/health/checks/inheritance.py +0 -104
- codeboarding-0.13.7/health/checks/instability.py +0 -77
- codeboarding-0.13.7/output_generators/__init__.py +0 -0
- codeboarding-0.13.7/static_analyzer/cluster_helpers.py +0 -577
- codeboarding-0.13.7/static_analyzer/cluster_relations.py +0 -582
- codeboarding-0.13.7/static_analyzer/graph.py +0 -811
- codeboarding-0.13.7/static_analyzer/method_cluster_paths.py +0 -77
- codeboarding-0.13.7/tests/test_cli_parser.py +0 -70
- {codeboarding-0.13.7 → codeboarding-0.13.10}/LICENSE +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/agent.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/change_status.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/constants.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/dependency_discovery.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/llm_errors.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/model_capabilities.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/prompts/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/prompts/prompt_factory.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/retry.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/get_external_deps.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/get_method_invocations.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/list_git_changes.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/read_docs.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/read_file.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/read_file_structure.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/read_packages.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/read_source.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/read_structure.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/agents/tools/toolkit.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding.egg-info/dependency_links.txt +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_cli/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_cli/bootstrap.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_cli/commands/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_cli/view_instructions.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_workflows/sources/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_workflows/sources/local.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/codeboarding_workflows/sources/remote.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/core/plugin_loader.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/core/registry.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/diagram_analysis/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/diagram_analysis/file_coverage.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/diagram_analysis/run_mode.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/health/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/health/checks/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/health/checks/circular_deps.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/health/checks/unused_code_diagnostics.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/logging_config.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/monitoring/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/monitoring/callbacks.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/monitoring/context.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/monitoring/mixin.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/monitoring/paths.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/monitoring/stats.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/monitoring/writers.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/output_generators/html_template.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/repo_utils/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/repo_utils/change_detector.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/repo_utils/errors.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/repo_utils/fingerprint_diff.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/repo_utils/git_ops.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/repo_utils/ignore.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/repo_utils/path_utils.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/setup.cfg +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/dotnet_sdk.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/edge_build_context.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/hierarchy_builder.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/process_memory.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/progress.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/engine/utils.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/java_config_scanner.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/java_utils.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/lsp_client/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/lsp_client/diagnostics.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/static_analyzer/scanner.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/telemetry/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/telemetry/device_id.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/telemetry/events.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/telemetry/schemas.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/telemetry/service.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_install.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_logging_config.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_telemetry_events.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_telemetry_service.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_tool_registry.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_view_instructions.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_vscode_constants.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_windows_compatibility.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tests/test_windows_encoding.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tool_registry/__init__.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tool_registry/installers.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tool_registry/manifest.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/tool_registry/paths.py +0 -0
- {codeboarding-0.13.7 → codeboarding-0.13.10}/vscode_constants.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeboarding
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.10
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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(
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
110
|
+
return analysis
|
|
104
111
|
|
|
105
|
-
|
|
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=
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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 =
|
|
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
|
|
179
|
+
def _step_relation_analysis(
|
|
158
180
|
self,
|
|
159
181
|
analysis: AnalysisInsights,
|
|
160
182
|
api_surfaces: ComponentApiSurfaces,
|
|
161
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
360
|
-
merged_all_edges = Relation.
|
|
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
|
|
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[
|
|
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,
|
|
4
|
-
|
|
5
|
-
|
|
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
|