codeboarding 0.15.5__tar.gz → 0.15.6__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.15.5/codeboarding.egg-info → codeboarding-0.15.6}/PKG-INFO +1 -1
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/agent_responses.py +1 -37
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/file_index_models.py +0 -14
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/relation_edges.py +18 -54
- {codeboarding-0.15.5 → codeboarding-0.15.6/codeboarding.egg-info}/PKG-INFO +1 -1
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding.egg-info/SOURCES.txt +1 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_cli/github_action.py +7 -1
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_workflows/analysis.py +61 -46
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_workflows/fingerprint_diff.py +3 -3
- codeboarding-0.15.6/diagram_analysis/analysis_json.py +787 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/cluster_relations.py +23 -9
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/diagram_generator.py +44 -35
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/exceptions.py +38 -6
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/file_index.py +2 -3
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/io_utils.py +73 -152
- codeboarding-0.15.6/infra/analysis_format.py +11 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/telemetry/events.py +19 -2
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/telemetry/schemas.py +6 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/pyproject.toml +1 -1
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/__init__.py +8 -4
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/adapters/cpp_adapter.py +1 -1
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/adapters/csharp_adapter.py +1 -1
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/adapters/rust_adapter.py +7 -13
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/call_graph_builder.py +2 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/language_adapter.py +1 -1
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/lsp_client.py +37 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/models.py +2 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/result_converter.py +2 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/source_inspector.py +80 -13
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/incremental_orchestrator.py +9 -1
- codeboarding-0.15.5/diagram_analysis/analysis_json.py +0 -671
- {codeboarding-0.15.5 → codeboarding-0.15.6}/LICENSE +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/PYPI.md +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/README.md +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/change_status.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/component_ownership.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/constants.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/content_hash.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/incremental_results.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/llm_config.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/llm_errors.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/llm_renderers/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/llm_renderers/call_graph.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/llm_renderers/scope.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/model_capabilities.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/planner_agent.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/prompts/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/prompts/profiles.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/prompts/shared.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/retry.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/scope_analysis_agent.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/scope_ids.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/tools/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/tools/base.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/tools/get_method_calls.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/tools/read_file.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/agents/tree_planner_agent.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding.egg-info/dependency_links.txt +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding.egg-info/entry_points.txt +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding.egg-info/requires.txt +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding.egg-info/top_level.txt +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_cli/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_cli/bootstrap.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_cli/commands/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_cli/commands/full_analysis.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_cli/commands/incremental_analysis.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_cli/commands/partial_analysis.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_cli/main.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_cli/render.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_cli/view_instructions.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_workflows/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_workflows/orchestration.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_workflows/sources/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_workflows/sources/local.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/codeboarding_workflows/sources/remote.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/file_coverage.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/incremental_changes.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/incremental_update.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/reference_resolver.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/run_context.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/run_mode.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/scope_assembly.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/scope_plan.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/diagram_analysis/tree_shape.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/checks/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/checks/circular_deps.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/checks/function_size.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/checks/unused_code_diagnostics.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/config.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/main.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/models.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/plugins/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/plugins/plugin_loader.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/plugins/protocols.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/plugins/registry.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/health/runner.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/clustering_ids.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/constants.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/install.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/logging_config.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/callbacks.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/context.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/mixin.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/paths.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/stats.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/telemetry/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/telemetry/device_id.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/telemetry/service.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/monitoring/writers.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/repo_utils/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/repo_utils/change_detector.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/repo_utils/errors.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/repo_utils/git_ops.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/repo_utils/ignore.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/repo_utils/path_utils.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/tool_registry/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/tool_registry/grammars.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/tool_registry/installers.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/tool_registry/java.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/tool_registry/kotlin-server-1.json +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/tool_registry/manifest.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/tool_registry/paths.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/tool_registry/registry.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/user_config.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/utils.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/infra/vscode_constants.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/output_generators/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/output_generators/html.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/output_generators/html_template.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/output_generators/markdown.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/output_generators/mdx.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/output_generators/rendering.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/output_generators/sphinx.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/setup.cfg +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/analysis_cache.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/analysis_result.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/cfg/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/cfg/call_graph.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/cfg/edge.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/cfg/location_key.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/exceptions.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/models.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/names/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/names/draft.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/names/frontier.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/names/inventory.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/names/replay.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/names/spec.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/names/tokens.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/clustering/service.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/config.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/csharp_config_scanner.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/dotnet_sdk.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/dotnet_solution.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/adapters/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/adapters/go_adapter.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/adapters/java_adapter.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/adapters/kotlin_adapter.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/adapters/php_adapter.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/adapters/python_adapter.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/adapters/typescript_adapter.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/cpp_templates.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/edge_build_context.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/edge_builder.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/hierarchy_builder.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/lsp_constants.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/lsp_recycler.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/process_memory.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/progress.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/protocols.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/symbol_table.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/engine/utils.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/exceptions.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/external_calls.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/graph_definitions.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/internal_references.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/java_config_scanner.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/java_utils.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/language_results.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/lsp_client/__init__.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/lsp_client/diagnostics.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/node.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/programming_language.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/scanner.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/typescript_config_scanner.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/typescript_plugin/node_modules/codeboarding-workspace-sources/index.js +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/typescript_plugin/node_modules/codeboarding-workspace-sources/package.json +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/typescript_workspace.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/static_analyzer/workspace_dependencies.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/tests/test_devcontainer.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/tests/test_persisted_analysis_artifacts.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/tests/test_pyproject_packages.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/tests/test_view_instructions.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/tests/test_windows_compatibility.py +0 -0
- {codeboarding-0.15.5 → codeboarding-0.15.6}/tests/test_windows_encoding.py +0 -0
|
@@ -9,7 +9,7 @@ from enum import StrEnum
|
|
|
9
9
|
from pydantic import BaseModel, Field
|
|
10
10
|
|
|
11
11
|
from infra.clustering_ids import ComponentId
|
|
12
|
-
from agents.file_index_models import FileEntry, FileMethodGroup
|
|
12
|
+
from agents.file_index_models import FileEntry, FileMethodGroup
|
|
13
13
|
from agents.scope_ids import ROOT_SCOPE_ID
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
@@ -89,20 +89,6 @@ class RelationEdge(LLMBaseModel):
|
|
|
89
89
|
exclude=True,
|
|
90
90
|
)
|
|
91
91
|
|
|
92
|
-
@classmethod
|
|
93
|
-
def from_dict(cls, edge: dict, methods_index: dict[str, MethodIndexEntry]) -> RelationEdge:
|
|
94
|
-
source_key = edge.get("source")
|
|
95
|
-
target_key = edge.get("target")
|
|
96
|
-
if not isinstance(source_key, str) or not isinstance(target_key, str):
|
|
97
|
-
raise ValueError("Relation edge endpoints must be method-index keys")
|
|
98
|
-
call_sites = edge.get("call_sites") or []
|
|
99
|
-
return cls(
|
|
100
|
-
source=_relation_endpoint_from_key(source_key, methods_index),
|
|
101
|
-
target=_relation_endpoint_from_key(target_key, methods_index),
|
|
102
|
-
description=edge.get("description", ""),
|
|
103
|
-
call_sites=[RelationCallSite.model_validate(site) for site in call_sites],
|
|
104
|
-
)
|
|
105
|
-
|
|
106
92
|
@classmethod
|
|
107
93
|
def from_edge(cls, edge) -> RelationEdge:
|
|
108
94
|
return cls(
|
|
@@ -138,28 +124,6 @@ class RelationEdge(LLMBaseModel):
|
|
|
138
124
|
)
|
|
139
125
|
|
|
140
126
|
|
|
141
|
-
def _relation_endpoint_from_key(
|
|
142
|
-
key: str,
|
|
143
|
-
methods_index: dict[str, MethodIndexEntry],
|
|
144
|
-
) -> SourceCodeReference:
|
|
145
|
-
indexed = methods_index.get(key)
|
|
146
|
-
if indexed is not None:
|
|
147
|
-
return SourceCodeReference(
|
|
148
|
-
qualified_name=indexed.qualified_name,
|
|
149
|
-
reference_file=indexed.file_path or None,
|
|
150
|
-
reference_start_line=indexed.start_line,
|
|
151
|
-
reference_end_line=indexed.end_line,
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
file_path, separator, qualified_name = key.partition("|")
|
|
155
|
-
if not separator or not qualified_name:
|
|
156
|
-
raise ValueError(f"Malformed relation edge endpoint key: {key!r}")
|
|
157
|
-
return SourceCodeReference(
|
|
158
|
-
qualified_name=qualified_name,
|
|
159
|
-
reference_file=file_path or None,
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
|
|
163
127
|
class Relation(LLMBaseModel):
|
|
164
128
|
"""A relationship between two components."""
|
|
165
129
|
|
|
@@ -42,20 +42,6 @@ class MethodEntry(BaseModel):
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
class MethodIndexEntry(BaseModel):
|
|
46
|
-
"""Serialized method metadata referenced by relation edges."""
|
|
47
|
-
|
|
48
|
-
file_path: str = Field(description="Relative path to the source file.")
|
|
49
|
-
qualified_name: str = Field(description="Fully qualified method/function name.")
|
|
50
|
-
start_line: int = Field(description="Starting line number in the file.")
|
|
51
|
-
end_line: int = Field(description="Ending line number in the file.")
|
|
52
|
-
type: str = Field(description="Node type name (METHOD, FUNCTION, CLASS, ...).")
|
|
53
|
-
content_hash: str = Field(
|
|
54
|
-
default="",
|
|
55
|
-
description="Truncated SHA-256 of the method's source lines; '' when unknown.",
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
|
|
59
45
|
class FileMethodGroup(BaseModel):
|
|
60
46
|
"""All methods/functions belonging to a component within a single file."""
|
|
61
47
|
|
|
@@ -133,89 +133,53 @@ def edge_crosses_components(
|
|
|
133
133
|
src_id: str,
|
|
134
134
|
dst_id: str,
|
|
135
135
|
) -> bool:
|
|
136
|
-
"""Return whether
|
|
136
|
+
"""Return whether each endpoint has an owner within its side of the declared component pair.
|
|
137
|
+
|
|
138
|
+
Why an unowned endpoint fails: no component holds it, so analysis.json, which cites only
|
|
139
|
+
component members, could not record the edge.
|
|
140
|
+
"""
|
|
137
141
|
for reference, component_id in ((edge.source, src_id), (edge.target, dst_id)):
|
|
138
142
|
owner = owner_of(reference)
|
|
139
|
-
if
|
|
143
|
+
if component_id and not (owner and is_self_or_descendant(owner, component_id)):
|
|
140
144
|
return False
|
|
141
145
|
return True
|
|
142
146
|
|
|
143
147
|
|
|
144
|
-
def drop_misattributed_edges(
|
|
145
|
-
relations: list[Relation],
|
|
146
|
-
owner_of: Callable[[SourceCodeReference], str],
|
|
147
|
-
) -> list[Relation]:
|
|
148
|
-
"""Drop edges whose owning component contradicts the relation's declared pair.
|
|
149
|
-
|
|
150
|
-
Why: the per-scope ownership index sees only that scope's own components, so an
|
|
151
|
-
out-of-scope endpoint resolves to nothing and the edge survives its own gate. A
|
|
152
|
-
frontier-wide owner map resolves it and rejects the mismatch. A relation stripped of
|
|
153
|
-
every edge is kept: it still stands on its evidence prose, the ordinary shape of an
|
|
154
|
-
inferred relation.
|
|
155
|
-
"""
|
|
156
|
-
filtered: list[Relation] = []
|
|
157
|
-
for relation in relations:
|
|
158
|
-
all_edges = [
|
|
159
|
-
edge
|
|
160
|
-
for edge in relation.all_edges
|
|
161
|
-
if edge_crosses_components(edge, owner_of, relation.src_id, relation.dst_id)
|
|
162
|
-
]
|
|
163
|
-
if len(all_edges) == len(relation.all_edges):
|
|
164
|
-
filtered.append(relation)
|
|
165
|
-
continue
|
|
166
|
-
surviving = {edge.identity() for edge in all_edges}
|
|
167
|
-
filtered.append(
|
|
168
|
-
relation.model_copy(
|
|
169
|
-
update={
|
|
170
|
-
"all_edges": all_edges,
|
|
171
|
-
"key_edges": [edge for edge in relation.key_edges if edge.identity() in surviving],
|
|
172
|
-
}
|
|
173
|
-
)
|
|
174
|
-
)
|
|
175
|
-
return filtered
|
|
176
|
-
|
|
177
|
-
|
|
178
148
|
def prune_ungrounded_edges(
|
|
179
149
|
relations: list[Relation],
|
|
180
150
|
owner_of: Callable[[SourceCodeReference], str],
|
|
181
151
|
keep_edge: Callable[[RelationEdge], bool],
|
|
182
152
|
changed_members: set[str] | None = None,
|
|
183
153
|
) -> list[Relation]:
|
|
184
|
-
"""Re-apply edge filters after baseline preservation, moving misfiled
|
|
154
|
+
"""Re-apply edge filters after baseline preservation, moving each misfiled edge to the pair its owners join.
|
|
155
|
+
|
|
156
|
+
Why never left where it was: analysis.json files an edge under the pair its owners join, so a
|
|
157
|
+
misfiled one cannot be saved; one whose pair no relation holds is dropped.
|
|
158
|
+
"""
|
|
185
159
|
by_pair: dict[tuple[str, str], Relation] = {(relation.src_id, relation.dst_id): relation for relation in relations}
|
|
186
160
|
additions: dict[tuple[str, str], list[RelationEdge]] = {}
|
|
187
161
|
|
|
188
|
-
def find_relation_pair_for_edge(edge: RelationEdge) -> tuple[
|
|
162
|
+
def find_relation_pair_for_edge(edge: RelationEdge) -> tuple[str, str] | None:
|
|
189
163
|
source = owner_of(edge.source)
|
|
190
164
|
target = owner_of(edge.target)
|
|
191
|
-
if not source or not target:
|
|
192
|
-
return None, False
|
|
193
165
|
if is_self_or_descendant(source, target) or is_self_or_descendant(target, source):
|
|
194
|
-
return None
|
|
166
|
+
return None
|
|
195
167
|
for pair in by_pair:
|
|
196
168
|
if is_self_or_descendant(source, pair[0]) and is_self_or_descendant(target, pair[1]):
|
|
197
|
-
return pair
|
|
198
|
-
return None
|
|
169
|
+
return pair
|
|
170
|
+
return None
|
|
199
171
|
|
|
200
172
|
kept: list[Relation] = []
|
|
201
173
|
for relation in relations:
|
|
202
|
-
pair = (relation.src_id, relation.dst_id)
|
|
203
174
|
all_edges: list[RelationEdge] = []
|
|
204
175
|
for edge in relation.all_edges:
|
|
205
|
-
if not keep_edge(edge):
|
|
176
|
+
if not keep_edge(edge) or not (owner_of(edge.source) and owner_of(edge.target)):
|
|
206
177
|
continue
|
|
207
178
|
if edge_crosses_components(edge, owner_of, relation.src_id, relation.dst_id):
|
|
208
179
|
all_edges.append(edge)
|
|
209
180
|
continue
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
continue
|
|
213
|
-
matching_pair, is_internal = find_relation_pair_for_edge(edge)
|
|
214
|
-
if is_internal:
|
|
215
|
-
continue
|
|
216
|
-
if matching_pair is None or matching_pair == pair:
|
|
217
|
-
all_edges.append(edge)
|
|
218
|
-
else:
|
|
181
|
+
matching_pair = find_relation_pair_for_edge(edge)
|
|
182
|
+
if matching_pair is not None:
|
|
219
183
|
additions.setdefault(matching_pair, []).append(edge)
|
|
220
184
|
surviving = {edge.identity() for edge in all_edges}
|
|
221
185
|
key_edges = [edge for edge in relation.key_edges if edge.identity() in surviving]
|
|
@@ -5,6 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
|
|
6
6
|
from codeboarding_workflows.analysis import run_incremental_workflow
|
|
7
7
|
from diagram_analysis import DEFAULT_DEPTH_CAP, DiagramGenerator, RunContext
|
|
8
|
+
from diagram_analysis.exceptions import UnreadableAnalysisError
|
|
8
9
|
from diagram_analysis.io_utils import load_analysis_metadata
|
|
9
10
|
from output_generators.rendering import render_docs
|
|
10
11
|
from infra.repo_utils import checkout_repo, clone_repository
|
|
@@ -92,7 +93,12 @@ def _resolve_depth_cap(temp_repo_folder: Path) -> int:
|
|
|
92
93
|
env_depth = os.getenv("DIAGRAM_DEPTH_CAP")
|
|
93
94
|
if env_depth is not None:
|
|
94
95
|
return int(env_depth)
|
|
95
|
-
|
|
96
|
+
try:
|
|
97
|
+
metadata = load_analysis_metadata(temp_repo_folder)
|
|
98
|
+
except UnreadableAnalysisError:
|
|
99
|
+
# Why: run_incremental_workflow replaces an unreadable baseline with a full run, so it has no cap to carry.
|
|
100
|
+
logger.warning("The seeded analysis.json cannot be read; using the default depth cap.", exc_info=True)
|
|
101
|
+
return DEFAULT_DEPTH_CAP
|
|
96
102
|
if metadata is not None:
|
|
97
103
|
return int(metadata.get("depth_cap", DEFAULT_DEPTH_CAP))
|
|
98
104
|
return DEFAULT_DEPTH_CAP
|
|
@@ -14,8 +14,11 @@ import logging
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
from agents.content_hash import compute_source_tree_hash
|
|
17
|
+
from agents.agent_responses import AnalysisInsights
|
|
17
18
|
from agents.scope_ids import ROOT_SCOPE_ID
|
|
18
19
|
from diagram_analysis import DiagramGenerator
|
|
20
|
+
from diagram_analysis.analysis_json import require_format_version
|
|
21
|
+
from diagram_analysis.exceptions import UnreadableAnalysisError
|
|
19
22
|
from diagram_analysis.io_utils import (
|
|
20
23
|
load_analysis_metadata,
|
|
21
24
|
load_expandable_component_ids,
|
|
@@ -104,20 +107,14 @@ def run_partial(
|
|
|
104
107
|
"""Partial scope — regenerate a single component within an existing analysis.
|
|
105
108
|
|
|
106
109
|
Raises ``BaselineUnavailableError`` when no ``analysis.json`` baseline
|
|
107
|
-
exists — partial updates a *component within* an existing
|
|
108
|
-
has no meaningful behavior without one.
|
|
110
|
+
this Core reads exists — partial updates a *component within* an existing
|
|
111
|
+
analysis and has no meaningful behavior without one.
|
|
109
112
|
"""
|
|
110
113
|
logger.info(
|
|
111
114
|
f"Running PARTIAL analysis workflow for project '{run_paths.project_name}', component '{component_id}'."
|
|
112
115
|
)
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
metadata = load_analysis_metadata(run_paths.output_dir)
|
|
116
|
-
if metadata is None:
|
|
117
|
-
raise BaselineUnavailableError(
|
|
118
|
-
f"No baseline analysis.json found in '{run_paths.output_dir}'. Run a full analysis first."
|
|
119
|
-
)
|
|
120
|
-
|
|
117
|
+
metadata = _baseline_metadata(run_paths.output_dir)
|
|
121
118
|
baseline_source_hash = str(metadata.get("source_tree_hash", ""))
|
|
122
119
|
if not baseline_source_hash:
|
|
123
120
|
raise BaselineUnavailableError(
|
|
@@ -130,16 +127,8 @@ def run_partial(
|
|
|
130
127
|
"Partial analysis requires an up-to-date source baseline. Run incremental analysis before expanding a component."
|
|
131
128
|
)
|
|
132
129
|
|
|
133
|
-
depth_cap = int(metadata
|
|
134
|
-
|
|
135
|
-
if full_analysis is None:
|
|
136
|
-
# Metadata was present but the unified read failed — treat as a
|
|
137
|
-
# corrupt or partially-written baseline, same surfacing as cold start.
|
|
138
|
-
raise BaselineUnavailableError(
|
|
139
|
-
f"analysis.json in '{run_paths.output_dir}' could not be parsed as a unified analysis."
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
root_analysis, sub_analyses = full_analysis
|
|
130
|
+
depth_cap = int(metadata["depth_cap"])
|
|
131
|
+
root_analysis, sub_analyses = _load_baseline(run_paths.output_dir)
|
|
143
132
|
preserved_expandable_ids = load_expandable_component_ids(run_paths.output_dir)
|
|
144
133
|
|
|
145
134
|
component_to_analyze = None
|
|
@@ -180,10 +169,6 @@ def run_partial(
|
|
|
180
169
|
# its subcomponents. Rebuild reads each sub's LLM relation labels, which live
|
|
181
170
|
# only in memory (they aren't serialized), so it must run before the save.
|
|
182
171
|
sub_analyses[component_id] = sub_analysis
|
|
183
|
-
# The baseline we loaded may predate child scopes being confined to their parent.
|
|
184
|
-
# Repair it here too: this flow only regenerates one component, so nothing else
|
|
185
|
-
# would reconcile the rest of the tree before the save asserts containment.
|
|
186
|
-
generator._rescope_child_analyses(root_analysis, sub_analyses, set())
|
|
187
172
|
# Keep coverage and fingerprint artifacts unchanged; persist only the new lineage below.
|
|
188
173
|
generator.finalize_and_save(
|
|
189
174
|
root_analysis,
|
|
@@ -208,18 +193,13 @@ def run_incremental(
|
|
|
208
193
|
passes a changed-file set or git refs — the CLI and the wrapper both just say
|
|
209
194
|
"update this directory". The source-tree hash doubles as the warm-start tag.
|
|
210
195
|
|
|
211
|
-
Raises ``BaselineUnavailableError`` when no baseline analysis
|
|
212
|
-
subclass ``EngineChangedError`` when another engine version
|
|
213
|
-
should surface a "run full analysis"
|
|
214
|
-
an unscoped run.
|
|
196
|
+
Raises ``BaselineUnavailableError`` when no baseline analysis this Core reads
|
|
197
|
+
exists, and its subclass ``EngineChangedError`` when another engine version
|
|
198
|
+
wrote the static analysis — callers should surface a "run full analysis"
|
|
199
|
+
prompt rather than silently degrading to an unscoped run.
|
|
215
200
|
"""
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if metadata is None:
|
|
219
|
-
raise BaselineUnavailableError(
|
|
220
|
-
f"No baseline analysis.json found in '{run_paths.output_dir}'. Run a full analysis first."
|
|
221
|
-
)
|
|
222
|
-
depth_cap = int(metadata.get("depth_cap", metadata.get("depth_level", DEFAULT_DEPTH_CAP)))
|
|
201
|
+
metadata = _baseline_metadata(run_paths.output_dir)
|
|
202
|
+
depth_cap = int(metadata["depth_cap"])
|
|
223
203
|
|
|
224
204
|
# Why here: the analyzer refuses this pkl too, but only after servers started and a failed run was reported.
|
|
225
205
|
if StaticAnalysisCache(run_paths.output_dir, run_paths.repo_path).from_another_engine():
|
|
@@ -239,30 +219,65 @@ def run_incremental(
|
|
|
239
219
|
static_analyzer=static_analyzer,
|
|
240
220
|
changes=changes,
|
|
241
221
|
)
|
|
242
|
-
return
|
|
222
|
+
return _run_from_baseline(generator, _load_baseline(run_paths.output_dir))
|
|
243
223
|
|
|
244
224
|
|
|
245
225
|
def run_incremental_workflow(generator: DiagramGenerator) -> Path:
|
|
246
226
|
"""Run incremental analysis when a baseline exists, otherwise fall back to a full run.
|
|
247
227
|
|
|
248
|
-
Public kernel
|
|
249
|
-
``
|
|
250
|
-
|
|
251
|
-
2. Otherwise hand the loaded baseline to ``generate_analysis_incremental``,
|
|
252
|
-
which itself falls back to a full run when the cluster snapshot is
|
|
253
|
-
missing or the cluster delta produces nothing actionable.
|
|
228
|
+
Public kernel for unattended callers (``github_action.py``): with no prior ``analysis.json``,
|
|
229
|
+
or one this Core cannot read, it runs a full analysis. ``generate_analysis_incremental`` itself
|
|
230
|
+
falls back to a full run when the cluster snapshot is missing or the delta has nothing actionable.
|
|
254
231
|
"""
|
|
232
|
+
try:
|
|
233
|
+
baseline = load_full_analysis(Path(generator.output_dir))
|
|
234
|
+
except UnreadableAnalysisError:
|
|
235
|
+
# Why: nobody is there to answer a "run a full analysis" prompt.
|
|
236
|
+
logger.warning("The existing analysis.json cannot be updated; running full analysis.", exc_info=True)
|
|
237
|
+
baseline = None
|
|
238
|
+
return _run_from_baseline(generator, baseline)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _baseline_metadata(output_dir: Path) -> dict:
|
|
242
|
+
"""The baseline's metadata; raises ``BaselineUnavailableError`` unless this Core can update the baseline."""
|
|
243
|
+
try:
|
|
244
|
+
metadata = load_analysis_metadata(output_dir)
|
|
245
|
+
if metadata is not None:
|
|
246
|
+
require_format_version(metadata)
|
|
247
|
+
except UnreadableAnalysisError as error:
|
|
248
|
+
raise BaselineUnavailableError(str(error)) from error
|
|
249
|
+
if metadata is None:
|
|
250
|
+
raise BaselineUnavailableError(f"No baseline analysis.json found in '{output_dir}'. Run a full analysis first.")
|
|
251
|
+
return metadata
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _load_baseline(output_dir: Path) -> tuple[AnalysisInsights, dict[str, AnalysisInsights]]:
|
|
255
|
+
"""The root analysis and sub-analyses of a baseline ``_baseline_metadata`` accepted.
|
|
256
|
+
|
|
257
|
+
Raises ``BaselineUnavailableError`` when its body is not one this Core can read.
|
|
258
|
+
"""
|
|
259
|
+
try:
|
|
260
|
+
baseline = load_full_analysis(output_dir)
|
|
261
|
+
except UnreadableAnalysisError as error:
|
|
262
|
+
raise BaselineUnavailableError(str(error)) from error
|
|
263
|
+
if baseline is None:
|
|
264
|
+
raise BaselineUnavailableError(f"analysis.json in '{output_dir}' was removed during the run.")
|
|
265
|
+
return baseline
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _run_from_baseline(
|
|
269
|
+
generator: DiagramGenerator, baseline: tuple[AnalysisInsights, dict[str, AnalysisInsights]] | None
|
|
270
|
+
) -> Path:
|
|
271
|
+
"""Update *baseline* incrementally, or run in full without one or with an empty one."""
|
|
255
272
|
output_dir = Path(generator.output_dir)
|
|
256
273
|
# Progress saves land mid-run, so a failed run must not leave its partial analysis.json behind.
|
|
257
274
|
before = snapshot_analysis(output_dir)
|
|
258
275
|
try:
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if existing is None or metadata is None:
|
|
262
|
-
logger.info("No existing analysis baseline; running full analysis.")
|
|
276
|
+
if baseline is None:
|
|
277
|
+
logger.info("No usable analysis baseline; running full analysis.")
|
|
263
278
|
return generator.generate_analysis()
|
|
264
279
|
|
|
265
|
-
root_analysis, sub_analyses =
|
|
280
|
+
root_analysis, sub_analyses = baseline
|
|
266
281
|
|
|
267
282
|
if not root_analysis.components:
|
|
268
283
|
logger.info("Baseline analysis has no components; running full analysis.")
|
|
@@ -23,9 +23,9 @@ class BaselineUnavailableError(RuntimeError):
|
|
|
23
23
|
"""Raised when a workflow needs an existing analysis.json baseline but none is usable.
|
|
24
24
|
|
|
25
25
|
Covers the cases where incremental/partial cannot trust its starting state: no
|
|
26
|
-
``analysis.json`` on disk,
|
|
27
|
-
whole-tree fingerprint to diff
|
|
28
|
-
indistinguishable from "no changes").
|
|
26
|
+
``analysis.json`` on disk, one this Core cannot read (an older format), or a
|
|
27
|
+
baseline that predates content versioning (no whole-tree fingerprint to diff
|
|
28
|
+
against, so an "empty" change set is indistinguishable from "no changes").
|
|
29
29
|
|
|
30
30
|
Callers must surface a "run full analysis" prompt rather than silently degrading
|
|
31
31
|
to an unscoped run or an empty-but-successful update.
|