codeboarding 0.12.3__tar.gz → 0.12.5__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.12.3/codeboarding.egg-info → codeboarding-0.12.5}/PKG-INFO +1 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/agent_responses.py +59 -2
- codeboarding-0.12.5/agents/cluster_ids.py +42 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/cluster_methods_mixin.py +103 -24
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/details_agent.py +4 -2
- codeboarding-0.12.5/agents/incremental_agent.py +608 -0
- codeboarding-0.12.5/agents/incremental_planning_agent.py +291 -0
- codeboarding-0.12.5/agents/incremental_results.py +18 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/prompts/__init__.py +2 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/prompts/abstract_prompt_factory.py +4 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/prompts/claude_prompts.py +45 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/prompts/deepseek_prompts.py +41 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/prompts/gemini_flash_prompts.py +37 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/prompts/glm_prompts.py +42 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/prompts/gpt_prompts.py +37 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/prompts/kimi_prompts.py +36 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/prompts/prompt_factory.py +4 -0
- codeboarding-0.12.5/agents/scope_ids.py +1 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/base.py +2 -0
- codeboarding-0.12.5/agents/tools/read_git_diff.py +99 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/toolkit.py +15 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5/codeboarding.egg-info}/PKG-INFO +1 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding.egg-info/SOURCES.txt +9 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_cli/commands/full_analysis.py +4 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_cli/commands/incremental_analysis.py +3 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_cli/commands/partial_analysis.py +4 -0
- codeboarding-0.12.5/codeboarding_cli/view_instructions.py +41 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/analysis_json.py +1 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/cluster_delta.py +334 -68
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/diagram_generator.py +211 -28
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/io_utils.py +1 -19
- {codeboarding-0.12.3 → codeboarding-0.12.5}/pyproject.toml +1 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/repo_utils/__init__.py +2 -3
- codeboarding-0.12.5/repo_utils/path_utils.py +41 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/__init__.py +1 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/analysis_cache.py +86 -89
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/analysis_result.py +46 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/cluster_helpers.py +3 -1
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/graph.py +38 -2
- codeboarding-0.12.5/static_analyzer/incremental_orchestrator.py +338 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/language_results.py +2 -2
- codeboarding-0.12.5/static_analyzer/method_cluster_paths.py +56 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/scanner.py +83 -10
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_tool_registry.py +4 -4
- codeboarding-0.12.5/tests/test_view_instructions.py +32 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_windows_compatibility.py +30 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tool_registry/paths.py +17 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tool_registry/registry.py +3 -5
- {codeboarding-0.12.3 → codeboarding-0.12.5}/utils.py +2 -20
- codeboarding-0.12.3/agents/incremental_agent.py +0 -787
- codeboarding-0.12.3/static_analyzer/incremental_orchestrator.py +0 -125
- {codeboarding-0.12.3 → codeboarding-0.12.5}/LICENSE +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/PYPI.md +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/README.md +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/abstraction_agent.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/agent.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/change_status.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/cluster_budget.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/constants.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/dependency_discovery.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/llm_config.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/meta_agent.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/model_capabilities.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/planner_agent.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/retry.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/get_external_deps.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/get_method_invocations.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/read_cfg.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/read_docs.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/read_file.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/read_file_structure.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/read_packages.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/read_source.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/tools/read_structure.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/agents/validation.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/caching/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/caching/cache.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/caching/details_cache.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/caching/meta_cache.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding.egg-info/dependency_links.txt +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding.egg-info/entry_points.txt +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding.egg-info/requires.txt +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding.egg-info/top_level.txt +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_cli/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_cli/bootstrap.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_cli/commands/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_workflows/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_workflows/analysis.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_workflows/orchestration.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_workflows/rendering.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_workflows/sources/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_workflows/sources/local.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/codeboarding_workflows/sources/remote.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/constants.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/core/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/core/plugin_loader.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/core/protocols.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/core/registry.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/cluster_snapshot.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/exceptions.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/file_coverage.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/run_context.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/run_mode.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/diagram_analysis/version.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/github_action.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/checks/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/checks/circular_deps.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/checks/cohesion.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/checks/coupling.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/checks/function_size.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/checks/god_class.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/checks/inheritance.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/checks/instability.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/checks/unused_code_diagnostics.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/config.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/models.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/health/runner.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/install.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/logging_config.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/main.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/monitoring/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/monitoring/callbacks.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/monitoring/context.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/monitoring/mixin.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/monitoring/paths.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/monitoring/stats.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/monitoring/writers.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/output_generators/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/output_generators/html.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/output_generators/html_template.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/output_generators/markdown.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/output_generators/mdx.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/output_generators/sphinx.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/repo_utils/change_detector.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/repo_utils/diff_parser.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/repo_utils/errors.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/repo_utils/git_ops.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/repo_utils/ignore.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/setup.cfg +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/cfg_skip_planner.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/cluster_relations.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/constants.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/csharp_config_scanner.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/dotnet_sdk.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/adapters/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/adapters/csharp_adapter.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/adapters/go_adapter.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/adapters/java_adapter.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/adapters/php_adapter.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/adapters/python_adapter.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/adapters/rust_adapter.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/adapters/typescript_adapter.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/call_graph_builder.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/edge_build_context.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/edge_builder.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/hierarchy_builder.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/language_adapter.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/lsp_client.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/lsp_constants.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/models.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/progress.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/protocols.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/result_converter.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/source_inspector.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/symbol_table.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/engine/utils.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/java_config_scanner.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/java_utils.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/leiden_utils.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/lsp_client/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/lsp_client/diagnostics.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/node.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/programming_language.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/reference_resolve_mixin.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/static_analyzer/typescript_config_scanner.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/telemetry/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/telemetry/device_id.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/telemetry/events.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/telemetry/schemas.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/telemetry/service.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_cli_parser.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_github_action.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_install.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_logging_config.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_main.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_pyproject_packages.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_registry_coverage.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_telemetry_events.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_user_config.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_vscode_constants.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tests/test_windows_encoding.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tool_registry/__init__.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tool_registry/installers.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/tool_registry/manifest.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/user_config.py +0 -0
- {codeboarding-0.12.3 → codeboarding-0.12.5}/vscode_constants.py +0 -0
|
@@ -3,12 +3,16 @@ from __future__ import annotations
|
|
|
3
3
|
import abc
|
|
4
4
|
import logging
|
|
5
5
|
from abc import abstractmethod
|
|
6
|
+
from enum import StrEnum
|
|
6
7
|
from pathlib import PurePosixPath
|
|
7
8
|
from typing import get_origin, Optional
|
|
8
9
|
|
|
9
10
|
from pydantic import BaseModel, Field
|
|
10
11
|
from pydantic.fields import FieldInfo
|
|
11
12
|
|
|
13
|
+
from agents.cluster_ids import CodeBoardingClusterId, GraphClusterId
|
|
14
|
+
from agents.scope_ids import ROOT_SCOPE_ID
|
|
15
|
+
|
|
12
16
|
logger = logging.getLogger(__name__)
|
|
13
17
|
|
|
14
18
|
|
|
@@ -183,7 +187,7 @@ class ClustersComponent(LLMBaseModel):
|
|
|
183
187
|
name: str = Field(
|
|
184
188
|
description="Short, descriptive name for this cluster group (e.g., 'Authentication', 'Data Pipeline', 'Request Handling')"
|
|
185
189
|
)
|
|
186
|
-
cluster_ids: list[
|
|
190
|
+
cluster_ids: list[GraphClusterId] = Field(
|
|
187
191
|
description="List of cluster IDs from the CFG analysis that are grouped together (e.g., [1, 3, 5])"
|
|
188
192
|
)
|
|
189
193
|
description: str = Field(
|
|
@@ -305,7 +309,7 @@ class Component(LLMBaseModel):
|
|
|
305
309
|
default_factory=list,
|
|
306
310
|
)
|
|
307
311
|
|
|
308
|
-
source_cluster_ids: list[
|
|
312
|
+
source_cluster_ids: list[CodeBoardingClusterId] = Field(
|
|
309
313
|
description="List of cluster IDs from CFG analysis that this component encompasses (populated deterministically from source_group_names).",
|
|
310
314
|
default_factory=list,
|
|
311
315
|
exclude=True,
|
|
@@ -570,6 +574,59 @@ class ScopeRelations(LLMBaseModel):
|
|
|
570
574
|
return "\n".join(r.llm_str() for r in self.components_relations)
|
|
571
575
|
|
|
572
576
|
|
|
577
|
+
class ScopeOperationAction(StrEnum):
|
|
578
|
+
CREATE_COMPONENT = "create_component"
|
|
579
|
+
UPDATE_COMPONENT = "update_component"
|
|
580
|
+
DELETE_COMPONENT = "delete_component"
|
|
581
|
+
NOOP = "noop"
|
|
582
|
+
REGENERATE_SCOPE = "regenerate_scope"
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
class ScopedClusterRef(LLMBaseModel):
|
|
586
|
+
"""A cluster reference scoped by component depth and language."""
|
|
587
|
+
|
|
588
|
+
scope_id: str = Field(description="Component scope id; use 'root' for the top-level scope.")
|
|
589
|
+
language: str = Field(description="Programming language for this cluster.")
|
|
590
|
+
cluster_id: int = Field(description="Cluster id within the scope/language cluster result.")
|
|
591
|
+
|
|
592
|
+
def llm_str(self):
|
|
593
|
+
scope_id = self.scope_id or ROOT_SCOPE_ID
|
|
594
|
+
return f"{scope_id}:{self.language}:{self.cluster_id}"
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
class ScopeOperation(LLMBaseModel):
|
|
598
|
+
"""One diagram update operation for a single scope."""
|
|
599
|
+
|
|
600
|
+
action: ScopeOperationAction = Field(description="Operation to apply in this scope.")
|
|
601
|
+
cluster_refs: list[ScopedClusterRef] = Field(description="New-side clusters this operation accounts for.")
|
|
602
|
+
component_id: str | None = Field(
|
|
603
|
+
default=None,
|
|
604
|
+
description="Existing component id for update/delete/noop; null when creating a component.",
|
|
605
|
+
)
|
|
606
|
+
name: str | None = Field(default=None, description="Component name for create/update operations.")
|
|
607
|
+
description: str | None = Field(default=None, description="Component description for create/update operations.")
|
|
608
|
+
recurse: bool = Field(
|
|
609
|
+
default=False, description="Whether this component should be considered for child-scope update."
|
|
610
|
+
)
|
|
611
|
+
rationale: str = Field(description="Short reason for the operation, especially for ambiguous reshapes.")
|
|
612
|
+
|
|
613
|
+
def llm_str(self):
|
|
614
|
+
refs = ", ".join(ref.llm_str() for ref in self.cluster_refs) or "no clusters"
|
|
615
|
+
target = self.component_id or self.name or "new component"
|
|
616
|
+
return f"{self.action}: {refs} -> {target}; recurse={self.recurse}; {self.rationale}"
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
class ScopeUpdateDecision(LLMBaseModel):
|
|
620
|
+
"""LLM-selected operations for one incremental scope update."""
|
|
621
|
+
|
|
622
|
+
operations: list[ScopeOperation] = Field(description="Operations to apply to the current scope.")
|
|
623
|
+
|
|
624
|
+
def llm_str(self):
|
|
625
|
+
if not self.operations:
|
|
626
|
+
return "No scope operations."
|
|
627
|
+
return "\n".join(operation.llm_str() for operation in self.operations)
|
|
628
|
+
|
|
629
|
+
|
|
573
630
|
class FilePath(LLMBaseModel):
|
|
574
631
|
"""File path with optional line range reference."""
|
|
575
632
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
type GraphClusterId = int
|
|
2
|
+
type CodeBoardingClusterId = str
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class GraphClusterIds:
|
|
6
|
+
@classmethod
|
|
7
|
+
def sort(cls, cluster_ids: set[GraphClusterId]) -> list[GraphClusterId]:
|
|
8
|
+
return sorted(cluster_ids)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CodeBoardingClusterIds:
|
|
12
|
+
@classmethod
|
|
13
|
+
def sort(cls, cluster_ids: set[CodeBoardingClusterId]) -> list[CodeBoardingClusterId]:
|
|
14
|
+
# Sort by depth first, then naturally within a level: ["1", "2", "10", "2.1", "3.4"].
|
|
15
|
+
return sorted(cluster_ids, key=_cluster_id_sort_key)
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def from_graph_id(cls, cluster_id: GraphClusterId) -> CodeBoardingClusterId:
|
|
19
|
+
return str(cluster_id)
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def from_graph_ids(cls, cluster_ids: set[GraphClusterId]) -> list[CodeBoardingClusterId]:
|
|
23
|
+
return [cls.from_graph_id(cluster_id) for cluster_id in GraphClusterIds.sort(cluster_ids)]
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def qualify_local_id(
|
|
27
|
+
cls, local_cluster_id: CodeBoardingClusterId, source_cluster_id_prefix: str = ""
|
|
28
|
+
) -> CodeBoardingClusterId:
|
|
29
|
+
if not source_cluster_id_prefix:
|
|
30
|
+
return local_cluster_id
|
|
31
|
+
return f"{source_cluster_id_prefix}.{local_cluster_id}"
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def qualify_local_ids(
|
|
35
|
+
cls, local_cluster_ids: list[CodeBoardingClusterId], source_cluster_id_prefix: str = ""
|
|
36
|
+
) -> list[CodeBoardingClusterId]:
|
|
37
|
+
return [cls.qualify_local_id(cluster_id, source_cluster_id_prefix) for cluster_id in local_cluster_ids]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _cluster_id_sort_key(cluster_id: CodeBoardingClusterId) -> tuple[tuple[int, int | str], ...]:
|
|
41
|
+
parts = tuple((0, int(part)) if part.isdigit() else (1, part) for part in cluster_id.split("."))
|
|
42
|
+
return ((0, len(parts)), *parts)
|
|
@@ -16,9 +16,12 @@ from agents.agent_responses import (
|
|
|
16
16
|
MethodEntry,
|
|
17
17
|
)
|
|
18
18
|
from agents.cluster_budget import ClusterPromptBudget
|
|
19
|
+
from agents.cluster_ids import CodeBoardingClusterId, CodeBoardingClusterIds, GraphClusterId
|
|
19
20
|
from agents.llm_config import get_current_agent_context_window, get_current_agent_model_ref
|
|
20
21
|
from agents.model_capabilities import ContextWindow
|
|
21
22
|
from constants import MIN_CLUSTERS_THRESHOLD
|
|
23
|
+
from diagram_analysis.cluster_delta import _delta_for_language
|
|
24
|
+
from diagram_analysis.cluster_snapshot import ClusterSnapshotEntry
|
|
22
25
|
from static_analyzer.analysis_result import StaticAnalysisResults
|
|
23
26
|
from static_analyzer.cfg_skip_planner import ContextBudgetExceededError, plan_skip_set
|
|
24
27
|
from static_analyzer.cluster_helpers import (
|
|
@@ -44,7 +47,31 @@ logger = logging.getLogger(__name__)
|
|
|
44
47
|
class _RenderedClusterString:
|
|
45
48
|
text: str
|
|
46
49
|
by_language: dict[str, str]
|
|
47
|
-
cluster_ids: set[
|
|
50
|
+
cluster_ids: set[GraphClusterId]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def scoped_snapshot_from_lineage(cfg: CallGraph, scope_id: str) -> dict[int, ClusterSnapshotEntry]:
|
|
54
|
+
"""Build a scoped snapshot from each method's recorded cluster ancestry/path."""
|
|
55
|
+
if not scope_id:
|
|
56
|
+
return {}
|
|
57
|
+
prefix = f"{scope_id}."
|
|
58
|
+
entries: dict[int, ClusterSnapshotEntry] = {}
|
|
59
|
+
for qname, cluster_ids in cfg.method_cluster_paths_snapshot():
|
|
60
|
+
if qname not in cfg.nodes:
|
|
61
|
+
continue
|
|
62
|
+
for cluster_id in cluster_ids:
|
|
63
|
+
if not cluster_id.startswith(prefix):
|
|
64
|
+
continue
|
|
65
|
+
local_id = cluster_id.removeprefix(prefix)
|
|
66
|
+
if not local_id.isdigit():
|
|
67
|
+
continue
|
|
68
|
+
entry = entries.setdefault(int(local_id), ClusterSnapshotEntry())
|
|
69
|
+
entry.members.add(qname)
|
|
70
|
+
file_path = cfg.nodes[qname].file_path
|
|
71
|
+
if file_path:
|
|
72
|
+
entry.files.add(file_path)
|
|
73
|
+
entry.member_files[qname] = file_path
|
|
74
|
+
return entries
|
|
48
75
|
|
|
49
76
|
|
|
50
77
|
def _describe_window(ctx: ContextWindow) -> str:
|
|
@@ -327,7 +354,7 @@ class ClusterMethodsMixin:
|
|
|
327
354
|
|
|
328
355
|
def _resolve_cluster_ids_from_groups(self, analysis: AnalysisInsights, cluster_analysis: ClusterAnalysis) -> None:
|
|
329
356
|
"""Resolve source_cluster_ids deterministically from source_group_names via case-insensitive lookup."""
|
|
330
|
-
group_name_to_ids: dict[str, list[
|
|
357
|
+
group_name_to_ids: dict[str, list[GraphClusterId]] = {
|
|
331
358
|
cc.name.lower(): cc.cluster_ids for cc in cluster_analysis.cluster_components
|
|
332
359
|
}
|
|
333
360
|
|
|
@@ -340,7 +367,7 @@ class ClusterMethodsMixin:
|
|
|
340
367
|
logger.warning(
|
|
341
368
|
f"[{self.__class__.__name__}] Unresolved group name '{gname}' for component '{component.name}'"
|
|
342
369
|
)
|
|
343
|
-
component.source_cluster_ids =
|
|
370
|
+
component.source_cluster_ids = CodeBoardingClusterIds.from_graph_ids(set(resolved_ids))
|
|
344
371
|
|
|
345
372
|
def _expand_to_method_level_clusters(self, cfg: CallGraph, cluster_result: ClusterResult) -> ClusterResult:
|
|
346
373
|
"""
|
|
@@ -399,7 +426,9 @@ class ClusterMethodsMixin:
|
|
|
399
426
|
)
|
|
400
427
|
|
|
401
428
|
def _create_strict_component_subgraph(
|
|
402
|
-
self,
|
|
429
|
+
self,
|
|
430
|
+
component: Component,
|
|
431
|
+
source_cluster_id_prefix: str = "",
|
|
403
432
|
) -> tuple[str, dict[str, ClusterResult], dict[str, CallGraph]]:
|
|
404
433
|
"""
|
|
405
434
|
Create a strict subgraph containing ONLY nodes from the component's file_methods.
|
|
@@ -440,8 +469,17 @@ class ClusterMethodsMixin:
|
|
|
440
469
|
if sub_cfg.nodes:
|
|
441
470
|
subgraph_cfgs[lang] = sub_cfg
|
|
442
471
|
|
|
443
|
-
|
|
444
|
-
|
|
472
|
+
seeded_snapshot = scoped_snapshot_from_lineage(sub_cfg, source_cluster_id_prefix)
|
|
473
|
+
if seeded_snapshot:
|
|
474
|
+
scoped_delta = _delta_for_language(
|
|
475
|
+
str(lang),
|
|
476
|
+
sub_cfg.to_networkx(),
|
|
477
|
+
seeded_snapshot,
|
|
478
|
+
)
|
|
479
|
+
sub_cluster_result = scoped_delta.cluster_results
|
|
480
|
+
else:
|
|
481
|
+
# Calculate clusters for the subgraph
|
|
482
|
+
sub_cluster_result = sub_cfg.cluster()
|
|
445
483
|
|
|
446
484
|
# Merge into super-clusters if too many (same limit as AbstractionAgent)
|
|
447
485
|
if len(sub_cluster_result.clusters) > MAX_LLM_CLUSTERS:
|
|
@@ -461,6 +499,12 @@ class ClusterMethodsMixin:
|
|
|
461
499
|
cfg_nx = {lang: subgraph_cfgs[lang].to_networkx() for lang in cluster_results}
|
|
462
500
|
enforce_cross_language_budget(cluster_results, cfg_nx)
|
|
463
501
|
|
|
502
|
+
if source_cluster_id_prefix:
|
|
503
|
+
for lang, cluster_result in cluster_results.items():
|
|
504
|
+
self.static_analysis.get_cfg(Language(lang)).record_cluster_paths(
|
|
505
|
+
cluster_result, source_cluster_id_prefix
|
|
506
|
+
)
|
|
507
|
+
|
|
464
508
|
result_parts = []
|
|
465
509
|
for lang in self.static_analysis.get_languages():
|
|
466
510
|
if lang not in cluster_results:
|
|
@@ -609,26 +653,33 @@ class ClusterMethodsMixin:
|
|
|
609
653
|
groups.append(FileMethodGroup(file_path=file_path, methods=methods))
|
|
610
654
|
return groups
|
|
611
655
|
|
|
612
|
-
def _build_cluster_to_component_map(self, analysis: AnalysisInsights) -> dict[
|
|
656
|
+
def _build_cluster_to_component_map(self, analysis: AnalysisInsights) -> dict[CodeBoardingClusterId, Component]:
|
|
613
657
|
"""Build cluster_id -> Component mapping from source_cluster_ids."""
|
|
614
|
-
cluster_to_component: dict[
|
|
658
|
+
cluster_to_component: dict[CodeBoardingClusterId, Component] = {}
|
|
615
659
|
for comp in analysis.components:
|
|
616
660
|
for cid in comp.source_cluster_ids:
|
|
617
661
|
cluster_to_component[cid] = comp
|
|
618
662
|
return cluster_to_component
|
|
619
663
|
|
|
620
|
-
def _build_node_to_cluster_map(
|
|
664
|
+
def _build_node_to_cluster_map(
|
|
665
|
+
self, cluster_results: dict[str, ClusterResult], source_cluster_id_prefix: str = ""
|
|
666
|
+
) -> tuple[dict[str, CodeBoardingClusterId], set[CodeBoardingClusterId]]:
|
|
621
667
|
"""Build node_name (qualified name) -> cluster_id mapping and collect all cluster IDs."""
|
|
622
|
-
all_cluster_ids: set[
|
|
623
|
-
node_to_cluster: dict[str,
|
|
668
|
+
all_cluster_ids: set[CodeBoardingClusterId] = set()
|
|
669
|
+
node_to_cluster: dict[str, CodeBoardingClusterId] = {}
|
|
624
670
|
for cr in cluster_results.values():
|
|
625
671
|
for cid, members in cr.clusters.items():
|
|
626
|
-
|
|
672
|
+
cluster_id = CodeBoardingClusterIds.qualify_local_id(
|
|
673
|
+
CodeBoardingClusterIds.from_graph_id(cid), source_cluster_id_prefix
|
|
674
|
+
)
|
|
675
|
+
all_cluster_ids.add(cluster_id)
|
|
627
676
|
for name in members:
|
|
628
|
-
node_to_cluster[name] =
|
|
677
|
+
node_to_cluster[name] = cluster_id
|
|
629
678
|
return node_to_cluster, all_cluster_ids
|
|
630
679
|
|
|
631
|
-
def _validate_cluster_coverage(
|
|
680
|
+
def _validate_cluster_coverage(
|
|
681
|
+
self, cluster_to_component: dict[CodeBoardingClusterId, Component], all_cluster_ids: set[CodeBoardingClusterId]
|
|
682
|
+
) -> None:
|
|
632
683
|
"""Log an error if any cluster IDs are not mapped to a component."""
|
|
633
684
|
unmapped_cluster_ids = sorted(all_cluster_ids - set(cluster_to_component.keys()))
|
|
634
685
|
if unmapped_cluster_ids:
|
|
@@ -642,7 +693,8 @@ class ClusterMethodsMixin:
|
|
|
642
693
|
self,
|
|
643
694
|
node: Node,
|
|
644
695
|
cluster_results: dict[str, ClusterResult],
|
|
645
|
-
cluster_to_component: dict[
|
|
696
|
+
cluster_to_component: dict[str, Component],
|
|
697
|
+
source_cluster_id_prefix: str = "",
|
|
646
698
|
) -> Component | None:
|
|
647
699
|
"""Try to assign a node to a component based on its file already belonging to a cluster."""
|
|
648
700
|
file_path = node.file_path
|
|
@@ -651,7 +703,10 @@ class ClusterMethodsMixin:
|
|
|
651
703
|
for cr in cluster_results.values():
|
|
652
704
|
cluster_ids = cr.get_clusters_for_file(file_path)
|
|
653
705
|
for cid in cluster_ids:
|
|
654
|
-
|
|
706
|
+
cluster_id = CodeBoardingClusterIds.qualify_local_id(
|
|
707
|
+
CodeBoardingClusterIds.from_graph_id(cid), source_cluster_id_prefix
|
|
708
|
+
)
|
|
709
|
+
comp = cluster_to_component.get(cluster_id)
|
|
655
710
|
if comp is not None:
|
|
656
711
|
return comp
|
|
657
712
|
return None
|
|
@@ -659,11 +714,12 @@ class ClusterMethodsMixin:
|
|
|
659
714
|
def _assign_nodes_to_components(
|
|
660
715
|
self,
|
|
661
716
|
all_nodes: dict[str, Node],
|
|
662
|
-
node_to_cluster: dict[str,
|
|
663
|
-
cluster_to_component: dict[
|
|
717
|
+
node_to_cluster: dict[str, str],
|
|
718
|
+
cluster_to_component: dict[str, Component],
|
|
664
719
|
cluster_results: dict[str, ClusterResult],
|
|
665
720
|
fallback_component: Component,
|
|
666
721
|
cfg_graphs: dict[str, CallGraph] | None = None,
|
|
722
|
+
source_cluster_id_prefix: str = "",
|
|
667
723
|
) -> dict[str, list[Node]]:
|
|
668
724
|
"""Assign every node to a component via its cluster, file co-location, graph distance, or fallback."""
|
|
669
725
|
component_nodes: dict[str, list[Node]] = defaultdict(list)
|
|
@@ -691,7 +747,7 @@ class ClusterMethodsMixin:
|
|
|
691
747
|
node = all_nodes[qname]
|
|
692
748
|
|
|
693
749
|
# 1. Try file co-location: if the node's file already belongs to a cluster/component
|
|
694
|
-
comp = self._find_component_by_file(node, cluster_results, cluster_to_component)
|
|
750
|
+
comp = self._find_component_by_file(node, cluster_results, cluster_to_component, source_cluster_id_prefix)
|
|
695
751
|
if comp is not None:
|
|
696
752
|
assigned_by_file += 1
|
|
697
753
|
component_nodes[comp.component_id].append(node)
|
|
@@ -699,8 +755,15 @@ class ClusterMethodsMixin:
|
|
|
699
755
|
|
|
700
756
|
# 2. Try graph distance: find the nearest cluster in the call graph
|
|
701
757
|
nearest_cid = self._find_nearest_cluster(qname, cluster_results, undirected_graphs)
|
|
702
|
-
|
|
703
|
-
|
|
758
|
+
nearest_cluster_id = (
|
|
759
|
+
CodeBoardingClusterIds.qualify_local_id(
|
|
760
|
+
CodeBoardingClusterIds.from_graph_id(nearest_cid), source_cluster_id_prefix
|
|
761
|
+
)
|
|
762
|
+
if nearest_cid is not None
|
|
763
|
+
else ""
|
|
764
|
+
)
|
|
765
|
+
if nearest_cluster_id in cluster_to_component:
|
|
766
|
+
comp = cluster_to_component[nearest_cluster_id]
|
|
704
767
|
assigned_by_graph += 1
|
|
705
768
|
component_nodes[comp.component_id].append(node)
|
|
706
769
|
continue
|
|
@@ -716,7 +779,7 @@ class ClusterMethodsMixin:
|
|
|
716
779
|
f"{assigned_by_graph} by graph distance, {assigned_by_fallback} to fallback"
|
|
717
780
|
)
|
|
718
781
|
if assigned_by_fallback:
|
|
719
|
-
logger.
|
|
782
|
+
logger.error(
|
|
720
783
|
f"{assigned_by_fallback} node(s) fell back to '{fallback_component.name}' "
|
|
721
784
|
f"— files: {sorted(fallback_files)}"
|
|
722
785
|
)
|
|
@@ -754,6 +817,7 @@ class ClusterMethodsMixin:
|
|
|
754
817
|
analysis: AnalysisInsights,
|
|
755
818
|
cluster_results: dict[str, ClusterResult],
|
|
756
819
|
cfg_graphs: dict[str, CallGraph] | None = None,
|
|
820
|
+
source_cluster_id_prefix: str = "",
|
|
757
821
|
) -> None:
|
|
758
822
|
"""Deterministically populate ``file_methods`` on every component.
|
|
759
823
|
|
|
@@ -777,11 +841,17 @@ class ClusterMethodsMixin:
|
|
|
777
841
|
# per-component subgraph in DetailsAgent, which runs in parallel).
|
|
778
842
|
all_nodes = self._collect_all_cfg_nodes(cluster_results, cfg_graphs)
|
|
779
843
|
cluster_to_component = self._build_cluster_to_component_map(analysis)
|
|
780
|
-
node_to_cluster, all_cluster_ids = self._build_node_to_cluster_map(cluster_results)
|
|
844
|
+
node_to_cluster, all_cluster_ids = self._build_node_to_cluster_map(cluster_results, source_cluster_id_prefix)
|
|
781
845
|
self._validate_cluster_coverage(cluster_to_component, all_cluster_ids)
|
|
782
846
|
|
|
783
847
|
component_nodes = self._assign_nodes_to_components(
|
|
784
|
-
all_nodes,
|
|
848
|
+
all_nodes,
|
|
849
|
+
node_to_cluster,
|
|
850
|
+
cluster_to_component,
|
|
851
|
+
cluster_results,
|
|
852
|
+
analysis.components[0],
|
|
853
|
+
cfg_graphs,
|
|
854
|
+
source_cluster_id_prefix,
|
|
785
855
|
)
|
|
786
856
|
|
|
787
857
|
for comp in analysis.components:
|
|
@@ -795,6 +865,7 @@ class ClusterMethodsMixin:
|
|
|
795
865
|
self,
|
|
796
866
|
analysis: AnalysisInsights,
|
|
797
867
|
cfg_graphs: dict[str, CallGraph] | None = None,
|
|
868
|
+
source_cluster_id_prefix: str = "",
|
|
798
869
|
) -> None:
|
|
799
870
|
"""Build inter-component relations from CFG edges and merge with LLM relations.
|
|
800
871
|
|
|
@@ -810,6 +881,14 @@ class ClusterMethodsMixin:
|
|
|
810
881
|
node_to_component = build_node_to_component_map(analysis)
|
|
811
882
|
static_relations = build_component_relations(node_to_component, cfg_graphs)
|
|
812
883
|
analysis.components_relations = merge_relations(analysis.components_relations, static_relations, analysis)
|
|
884
|
+
self._prefix_local_cluster_ids(analysis, source_cluster_id_prefix)
|
|
885
|
+
|
|
886
|
+
def _prefix_local_cluster_ids(self, analysis: AnalysisInsights, prefix: str) -> None:
|
|
887
|
+
"""Prefix detail-subgraph cluster ids with their owning component scope."""
|
|
888
|
+
for component in analysis.components:
|
|
889
|
+
component.source_cluster_ids = CodeBoardingClusterIds.qualify_local_ids(
|
|
890
|
+
component.source_cluster_ids, prefix
|
|
891
|
+
)
|
|
813
892
|
|
|
814
893
|
def build_scope_cfg_string(self, analysis: AnalysisInsights) -> str:
|
|
815
894
|
"""Render cross-component communication edges as a human-readable string for the LLM.
|
|
@@ -217,7 +217,9 @@ class DetailsAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
217
217
|
|
|
218
218
|
# Step 1: Create subgraph from component's assigned files using strict filtering
|
|
219
219
|
# If subgraph has < MIN_CLUSTERS_THRESHOLD clusters, auto-expands to method-level
|
|
220
|
-
_subgraph_str, subgraph_cluster_results, subgraph_cfgs = self._create_strict_component_subgraph(
|
|
220
|
+
_subgraph_str, subgraph_cluster_results, subgraph_cfgs = self._create_strict_component_subgraph(
|
|
221
|
+
component, source_cluster_id_prefix=component.component_id
|
|
222
|
+
)
|
|
221
223
|
|
|
222
224
|
# Step 2: Group clusters within the subgraph
|
|
223
225
|
cluster_analysis = self.step_clusters_grouping(component, subgraph_cluster_results)
|
|
@@ -238,7 +240,7 @@ class DetailsAgent(ClusterMethodsMixin, CodeBoardingAgent):
|
|
|
238
240
|
self.populate_file_methods(analysis, subgraph_cluster_results, subgraph_cfgs)
|
|
239
241
|
|
|
240
242
|
# Step 7: Build static inter-component relations from subgraph CFG edges
|
|
241
|
-
self.build_static_relations(analysis, subgraph_cfgs)
|
|
243
|
+
self.build_static_relations(analysis, subgraph_cfgs, source_cluster_id_prefix=component.component_id)
|
|
242
244
|
|
|
243
245
|
# Step 8: Fix source code reference lines (resolves reference_file paths)
|
|
244
246
|
analysis = self.fix_source_code_reference_lines(analysis)
|