codeboarding 0.9.3__tar.gz → 0.9.4__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.9.3/codeboarding.egg-info → codeboarding-0.9.4}/PKG-INFO +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/agent_responses.py +4 -4
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/cluster_methods_mixin.py +5 -2
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/dependency_discovery.py +5 -5
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/planner_agent.py +9 -9
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/prompts/gpt_prompts.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/read_file_structure.py +6 -6
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/read_source.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4/codeboarding.egg-info}/PKG-INFO +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/codeboarding.egg-info/SOURCES.txt +2 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/diagram_generator.py +4 -4
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/file_coverage.py +2 -2
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/io_utils.py +2 -2
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/models.py +3 -3
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/manifest.py +2 -2
- {codeboarding-0.9.3 → codeboarding-0.9.4}/github_action.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/logging_config.py +14 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/main.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/monitoring/context.py +4 -4
- {codeboarding-0.9.3 → codeboarding-0.9.4}/monitoring/writers.py +2 -2
- {codeboarding-0.9.3 → codeboarding-0.9.4}/output_generators/markdown.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/output_generators/mdx.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/output_generators/sphinx.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/pyproject.toml +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/cluster_change_analyzer.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/graph.py +3 -3
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/lsp_client/client.py +29 -11
- {codeboarding-0.9.3 → codeboarding-0.9.4}/tests/test_logging_config.py +73 -0
- codeboarding-0.9.4/tests/test_windows_encoding.py +49 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/tool_registry.py +46 -5
- {codeboarding-0.9.3 → codeboarding-0.9.4}/user_config.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.4}/LICENSE +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/PYPI.md +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/README.md +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/abstraction_agent.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/agent.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/constants.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/details_agent.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/llm_config.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/meta_agent.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/prompts/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/prompts/abstract_prompt_factory.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/prompts/claude_prompts.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/prompts/deepseek_prompts.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/prompts/gemini_flash_prompts.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/prompts/glm_prompts.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/prompts/kimi_prompts.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/prompts/prompt_factory.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/base.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/get_external_deps.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/get_method_invocations.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/read_cfg.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/read_docs.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/read_file.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/read_git_diff.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/read_packages.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/read_structure.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/tools/toolkit.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/agents/validation.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/caching/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/caching/cache.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/caching/meta_cache.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/codeboarding.egg-info/dependency_links.txt +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/codeboarding.egg-info/entry_points.txt +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/codeboarding.egg-info/requires.txt +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/codeboarding.egg-info/top_level.txt +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/core/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/core/plugin_loader.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/core/protocols.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/core/registry.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/analysis_json.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/component_checker.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/file_manager.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/impact_analyzer.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/path_patching.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/reexpansion.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/scoped_analysis.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/updater.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/incremental/validation.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/diagram_analysis/version.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/duckdb_crud.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/checks/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/checks/circular_deps.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/checks/cohesion.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/checks/coupling.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/checks/function_size.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/checks/god_class.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/checks/inheritance.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/checks/instability.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/checks/unused_code_diagnostics.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/config.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/constants.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/models.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health/runner.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/health_main.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/install.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/monitoring/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/monitoring/callbacks.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/monitoring/mixin.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/monitoring/paths.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/monitoring/stats.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/output_generators/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/output_generators/html.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/output_generators/html_template.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/repo_utils/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/repo_utils/change_detector.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/repo_utils/errors.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/repo_utils/git_diff.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/repo_utils/ignore.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/setup.cfg +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/analysis_cache.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/analysis_result.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/cluster_helpers.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/constants.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/git_diff_analyzer.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/incremental_orchestrator.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/java_config_scanner.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/java_utils.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/lsp_client/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/lsp_client/diagnostics.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/lsp_client/java_client.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/lsp_client/language_settings.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/lsp_client/typescript_client.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/programming_language.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/reference_resolve_mixin.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/scanner.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/static_analyzer/typescript_config_scanner.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/tests/test_github_action.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/tests/test_incremental_analyzer.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/tests/test_install.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/tests/test_main.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/tests/test_vscode_constants.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/tests/test_windows_compatibility.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/utils.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.4}/vscode_constants.py +0 -0
|
@@ -180,7 +180,7 @@ class AnalysisInsights(LLMBaseModel):
|
|
|
180
180
|
def llm_str(self):
|
|
181
181
|
if not self.components:
|
|
182
182
|
return "No abstract components found."
|
|
183
|
-
title = "#
|
|
183
|
+
title = "# Abstract Components Overview\n"
|
|
184
184
|
body = "\n".join(ac.llm_str() for ac in self.components)
|
|
185
185
|
relations = "\n".join(cr.llm_str() for cr in self.components_relations)
|
|
186
186
|
return title + body + relations
|
|
@@ -251,7 +251,7 @@ class CFGAnalysisInsights(LLMBaseModel):
|
|
|
251
251
|
def llm_str(self):
|
|
252
252
|
if not self.components:
|
|
253
253
|
return "No abstract components found in the CFG."
|
|
254
|
-
title = "#
|
|
254
|
+
title = "# Abstract Components Overview from CFG\n"
|
|
255
255
|
body = "\n".join(ac.llm_str() for ac in self.components)
|
|
256
256
|
relations = "\n".join(cr.llm_str() for cr in self.components_relations)
|
|
257
257
|
return title + body + relations
|
|
@@ -309,7 +309,7 @@ class MetaAnalysisInsights(LLMBaseModel):
|
|
|
309
309
|
)
|
|
310
310
|
|
|
311
311
|
def llm_str(self):
|
|
312
|
-
title = "#
|
|
312
|
+
title = "# Project Metadata Analysis\n"
|
|
313
313
|
content = f"""
|
|
314
314
|
**Project Type:** {self.project_type}
|
|
315
315
|
**Domain:** {self.domain}
|
|
@@ -341,7 +341,7 @@ class ComponentFiles(LLMBaseModel):
|
|
|
341
341
|
def llm_str(self):
|
|
342
342
|
if not self.file_paths:
|
|
343
343
|
return "No files classified."
|
|
344
|
-
title = "#
|
|
344
|
+
title = "# Component File Classifications\n"
|
|
345
345
|
body = "\n".join(f"- `{fc.file_path}` -> Component: `{fc.component_name}`" for fc in self.file_paths)
|
|
346
346
|
return title + body
|
|
347
347
|
|
|
@@ -2,10 +2,13 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from agents.agent_responses import
|
|
5
|
+
from agents.agent_responses import AnalysisInsights, Component
|
|
6
6
|
from static_analyzer.analysis_result import StaticAnalysisResults
|
|
7
|
+
from static_analyzer.cluster_helpers import (
|
|
8
|
+
get_all_cluster_ids,
|
|
9
|
+
get_files_for_cluster_ids,
|
|
10
|
+
)
|
|
7
11
|
from static_analyzer.graph import ClusterResult
|
|
8
|
-
from static_analyzer.cluster_helpers import get_files_for_cluster_ids, get_all_cluster_ids
|
|
9
12
|
|
|
10
13
|
logger = logging.getLogger(__name__)
|
|
11
14
|
|
|
@@ -31,7 +31,7 @@ class DependencyFileSpec:
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
DEPENDENCY_REGISTRY: tuple[DependencyFileSpec, ...] = (
|
|
34
|
-
#
|
|
34
|
+
# -- Python --
|
|
35
35
|
DependencyFileSpec("requirements.txt", Ecosystem.PYTHON, FileRole.MANIFEST),
|
|
36
36
|
DependencyFileSpec("requirements-dev.txt", Ecosystem.PYTHON, FileRole.MANIFEST),
|
|
37
37
|
DependencyFileSpec("requirements-test.txt", Ecosystem.PYTHON, FileRole.MANIFEST),
|
|
@@ -52,7 +52,7 @@ DEPENDENCY_REGISTRY: tuple[DependencyFileSpec, ...] = (
|
|
|
52
52
|
DependencyFileSpec("pixi.toml", Ecosystem.PYTHON, FileRole.MANIFEST),
|
|
53
53
|
DependencyFileSpec("requirements.in", Ecosystem.PYTHON, FileRole.MANIFEST),
|
|
54
54
|
DependencyFileSpec("pixi.lock", Ecosystem.PYTHON, FileRole.LOCK),
|
|
55
|
-
#
|
|
55
|
+
# -- Node / TypeScript / JavaScript --
|
|
56
56
|
DependencyFileSpec("package.json", Ecosystem.NODE, FileRole.MANIFEST),
|
|
57
57
|
DependencyFileSpec("package-lock.json", Ecosystem.NODE, FileRole.LOCK),
|
|
58
58
|
DependencyFileSpec("yarn.lock", Ecosystem.NODE, FileRole.LOCK),
|
|
@@ -65,12 +65,12 @@ DEPENDENCY_REGISTRY: tuple[DependencyFileSpec, ...] = (
|
|
|
65
65
|
DependencyFileSpec("deno.jsonc", Ecosystem.NODE, FileRole.MANIFEST),
|
|
66
66
|
DependencyFileSpec("deno.lock", Ecosystem.NODE, FileRole.LOCK),
|
|
67
67
|
DependencyFileSpec("lerna.json", Ecosystem.NODE, FileRole.CONFIG),
|
|
68
|
-
#
|
|
68
|
+
# -- Go --
|
|
69
69
|
DependencyFileSpec("go.mod", Ecosystem.GO, FileRole.MANIFEST),
|
|
70
70
|
DependencyFileSpec("go.sum", Ecosystem.GO, FileRole.LOCK),
|
|
71
71
|
DependencyFileSpec("go.work", Ecosystem.GO, FileRole.CONFIG),
|
|
72
72
|
DependencyFileSpec("go.work.sum", Ecosystem.GO, FileRole.LOCK),
|
|
73
|
-
#
|
|
73
|
+
# -- Java / JVM --
|
|
74
74
|
DependencyFileSpec("pom.xml", Ecosystem.JAVA, FileRole.MANIFEST),
|
|
75
75
|
DependencyFileSpec("pom.properties", Ecosystem.JAVA, FileRole.CONFIG),
|
|
76
76
|
DependencyFileSpec("build.gradle", Ecosystem.JAVA, FileRole.MANIFEST),
|
|
@@ -81,7 +81,7 @@ DEPENDENCY_REGISTRY: tuple[DependencyFileSpec, ...] = (
|
|
|
81
81
|
DependencyFileSpec("build.sbt", Ecosystem.JAVA, FileRole.MANIFEST),
|
|
82
82
|
DependencyFileSpec("gradle.lockfile", Ecosystem.JAVA, FileRole.LOCK),
|
|
83
83
|
DependencyFileSpec("verification-metadata.xml", Ecosystem.JAVA, FileRole.LOCK),
|
|
84
|
-
#
|
|
84
|
+
# -- PHP --
|
|
85
85
|
DependencyFileSpec("composer.json", Ecosystem.PHP, FileRole.MANIFEST),
|
|
86
86
|
DependencyFileSpec("composer.lock", Ecosystem.PHP, FileRole.LOCK),
|
|
87
87
|
DependencyFileSpec("symfony.lock", Ecosystem.PHP, FileRole.LOCK),
|
|
@@ -6,14 +6,14 @@ should be expanded into sub-components. Unlike the previous LLM-based approach,
|
|
|
6
6
|
this uses CFG clustering structure as the source of truth.
|
|
7
7
|
|
|
8
8
|
Expansion Rules:
|
|
9
|
-
1. If component has source_cluster_ids
|
|
10
|
-
2. If component has no clusters but has files
|
|
11
|
-
3. If neither component nor its parent has clusters
|
|
9
|
+
1. If component has source_cluster_ids -> expandable (CFG structure exists)
|
|
10
|
+
2. If component has no clusters but has files -> expandable ONE level (to explain files)
|
|
11
|
+
3. If neither component nor its parent has clusters -> leaf (stop expanding)
|
|
12
12
|
|
|
13
13
|
Example:
|
|
14
|
-
- Component: "Agents" (clusters: [1,2,3])
|
|
15
|
-
- Sub-component: "DetailsAgent" (clusters: [], files: [details_agent.py])
|
|
16
|
-
- Sub-sub-component: "run_method" (clusters: [], files: [])
|
|
14
|
+
- Component: "Agents" (clusters: [1,2,3]) -> expand (yes)
|
|
15
|
+
- Sub-component: "DetailsAgent" (clusters: [], files: [details_agent.py]) -> expand (yes, parent had clusters)
|
|
16
|
+
- Sub-sub-component: "run_method" (clusters: [], files: []) -> DON'T expand (no, parent had no clusters)
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
import logging
|
|
@@ -35,10 +35,10 @@ def should_expand_component(
|
|
|
35
35
|
Determine if a component should be expanded into sub-components.
|
|
36
36
|
|
|
37
37
|
Expansion logic:
|
|
38
|
-
- If component has clusters
|
|
39
|
-
- If component has no clusters but has files
|
|
38
|
+
- If component has clusters -> expand (there's CFG structure to decompose)
|
|
39
|
+
- If component has no clusters but has files -> expand if parent had clusters
|
|
40
40
|
(allows one more level to explain file internals)
|
|
41
|
-
- If neither component nor parent has clusters
|
|
41
|
+
- If neither component nor parent has clusters -> stop (leaf node)
|
|
42
42
|
|
|
43
43
|
Args:
|
|
44
44
|
component: The component to evaluate
|
|
@@ -213,7 +213,7 @@ RELATIONSHIPS_VALIDATION = """Validate component relationships for accuracy and
|
|
|
213
213
|
|
|
214
214
|
1. **Accuracy:**
|
|
215
215
|
- [ ] Relationship type is correct (dependency, composition, inheritance, etc.)
|
|
216
|
-
- [ ] Direction is accurate (source
|
|
216
|
+
- [ ] Direction is accurate (source -> target)
|
|
217
217
|
- [ ] Both components exist in the analysis
|
|
218
218
|
|
|
219
219
|
2. **Completeness:**
|
|
@@ -124,19 +124,19 @@ def get_tree_string(
|
|
|
124
124
|
entries = sorted([p for p in startpath.iterdir() if not (ignore_manager and ignore_manager.should_ignore(p))])
|
|
125
125
|
except (PermissionError, FileNotFoundError):
|
|
126
126
|
# Handle permission errors or non-existent directories
|
|
127
|
-
return [indent + "
|
|
127
|
+
return [indent + "`--[Error reading directory]"]
|
|
128
128
|
|
|
129
129
|
for i, entry_path in enumerate(entries):
|
|
130
130
|
# Check if we've exceeded the maximum number of lines
|
|
131
131
|
if len(tree_lines) >= max_lines:
|
|
132
|
-
tree_lines.append(indent + "
|
|
132
|
+
tree_lines.append(indent + "`-- [Output truncated due to size limits]")
|
|
133
133
|
return tree_lines
|
|
134
134
|
|
|
135
|
-
connector = "
|
|
135
|
+
connector = "`-- " if i == len(entries) - 1 else "|-- "
|
|
136
136
|
tree_lines.append(indent + connector + entry_path.name)
|
|
137
137
|
|
|
138
138
|
if entry_path.is_dir():
|
|
139
|
-
extension = " " if i == len(entries) - 1 else "
|
|
139
|
+
extension = " " if i == len(entries) - 1 else "| "
|
|
140
140
|
subtree = get_tree_string(
|
|
141
141
|
entry_path,
|
|
142
142
|
indent + extension,
|
|
@@ -149,8 +149,8 @@ def get_tree_string(
|
|
|
149
149
|
|
|
150
150
|
# Check again after adding subtree
|
|
151
151
|
if len(tree_lines) >= max_lines:
|
|
152
|
-
if tree_lines[-1] != indent + "
|
|
153
|
-
tree_lines.append(indent + "
|
|
152
|
+
if tree_lines[-1] != indent + "`-- [Output truncated due to size limits]":
|
|
153
|
+
tree_lines.append(indent + "`-- [Output truncated due to size limits]")
|
|
154
154
|
return tree_lines
|
|
155
155
|
|
|
156
156
|
return tree_lines
|
|
@@ -95,7 +95,7 @@ class CodeReferenceReader(BaseRepoTool):
|
|
|
95
95
|
logger.error(f"[Source Reference Tool] File {file_path} does not exist.")
|
|
96
96
|
return f"Error: File {file_path} does not exist."
|
|
97
97
|
|
|
98
|
-
with open(file_path, "r") as f:
|
|
98
|
+
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
|
99
99
|
lines = f.readlines()
|
|
100
100
|
|
|
101
101
|
if start_line < 0 or end_line > len(lines):
|
|
@@ -114,7 +114,7 @@ class DiagramGenerator:
|
|
|
114
114
|
)
|
|
115
115
|
if health_report is not None:
|
|
116
116
|
health_path = Path(self.output_dir) / "health" / "health_report.json"
|
|
117
|
-
with open(health_path, "w") as f:
|
|
117
|
+
with open(health_path, "w", encoding="utf-8") as f:
|
|
118
118
|
f.write(health_report.model_dump_json(indent=2, exclude_none=True))
|
|
119
119
|
logger.info(f"Health report written to {health_path} (score: {health_report.overall_score:.3f})")
|
|
120
120
|
else:
|
|
@@ -145,7 +145,7 @@ class DiagramGenerator:
|
|
|
145
145
|
)
|
|
146
146
|
|
|
147
147
|
coverage_path = Path(self.output_dir) / "file_coverage.json"
|
|
148
|
-
with open(coverage_path, "w") as f:
|
|
148
|
+
with open(coverage_path, "w", encoding="utf-8") as f:
|
|
149
149
|
f.write(report.model_dump_json(indent=2, exclude_none=True))
|
|
150
150
|
logger.info(f"File coverage report written to {coverage_path}")
|
|
151
151
|
|
|
@@ -232,7 +232,7 @@ class DiagramGenerator:
|
|
|
232
232
|
self._monitoring_agents["AbstractionAgent"] = self.abstraction_agent
|
|
233
233
|
|
|
234
234
|
version_file = Path(self.output_dir) / "codeboarding_version.json"
|
|
235
|
-
with open(version_file, "w") as f:
|
|
235
|
+
with open(version_file, "w", encoding="utf-8") as f:
|
|
236
236
|
f.write(
|
|
237
237
|
Version(
|
|
238
238
|
commit_hash=get_git_commit_hash(self.repo_location),
|
|
@@ -253,7 +253,7 @@ class DiagramGenerator:
|
|
|
253
253
|
|
|
254
254
|
# Save code_stats.json
|
|
255
255
|
code_stats_file = monitoring_dir / "code_stats.json"
|
|
256
|
-
with open(code_stats_file, "w") as f:
|
|
256
|
+
with open(code_stats_file, "w", encoding="utf-8") as f:
|
|
257
257
|
json.dump(static_stats, f, indent=2)
|
|
258
258
|
logger.debug(f"Written code_stats.json to {code_stats_file}")
|
|
259
259
|
|
|
@@ -187,7 +187,7 @@ class FileCoverage:
|
|
|
187
187
|
return None
|
|
188
188
|
|
|
189
189
|
try:
|
|
190
|
-
with open(coverage_path, "r") as f:
|
|
190
|
+
with open(coverage_path, "r", encoding="utf-8") as f:
|
|
191
191
|
data = json.load(f)
|
|
192
192
|
# Validate basic structure
|
|
193
193
|
if "analyzed_files" in data and "not_analyzed_files" in data:
|
|
@@ -207,6 +207,6 @@ class FileCoverage:
|
|
|
207
207
|
coverage: File coverage dictionary to save
|
|
208
208
|
"""
|
|
209
209
|
coverage_path = output_dir / "file_coverage.json"
|
|
210
|
-
with open(coverage_path, "w") as f:
|
|
210
|
+
with open(coverage_path, "w", encoding="utf-8") as f:
|
|
211
211
|
json.dump(coverage, f, indent=2)
|
|
212
212
|
logger.info(f"File coverage saved to {coverage_path}")
|
|
@@ -79,7 +79,7 @@ class _AnalysisFileStore:
|
|
|
79
79
|
return None
|
|
80
80
|
|
|
81
81
|
try:
|
|
82
|
-
with open(self._analysis_path, "r") as f:
|
|
82
|
+
with open(self._analysis_path, "r", encoding="utf-8") as f:
|
|
83
83
|
data = json.load(f)
|
|
84
84
|
|
|
85
85
|
root_analysis, sub_analyses = parse_unified_analysis(data)
|
|
@@ -200,7 +200,7 @@ class _AnalysisFileStore:
|
|
|
200
200
|
sub_expandable = self._compute_expandable_components(sub, parent_had_clusters=parent_had_clusters)
|
|
201
201
|
sub_analyses_tuples[cid] = (sub, sub_expandable)
|
|
202
202
|
|
|
203
|
-
with open(self._analysis_path, "w") as f:
|
|
203
|
+
with open(self._analysis_path, "w", encoding="utf-8") as f:
|
|
204
204
|
f.write(
|
|
205
205
|
build_unified_analysis_json(
|
|
206
206
|
analysis=analysis,
|
|
@@ -58,11 +58,11 @@ class ChangeImpact:
|
|
|
58
58
|
f"Dirty components: {self.dirty_components}",
|
|
59
59
|
]
|
|
60
60
|
if self.components_needing_reexpansion:
|
|
61
|
-
lines.append(f"
|
|
61
|
+
lines.append(f"Components needing re-expansion: {self.components_needing_reexpansion}")
|
|
62
62
|
if self.architecture_dirty:
|
|
63
|
-
lines.append("
|
|
63
|
+
lines.append("WARNING: Architecture refresh needed")
|
|
64
64
|
if self.unassigned_files:
|
|
65
|
-
lines.append(f"
|
|
65
|
+
lines.append(f"WARNING: Unassigned files: {self.unassigned_files}")
|
|
66
66
|
return "\n".join(lines)
|
|
67
67
|
|
|
68
68
|
|
|
@@ -109,7 +109,7 @@ def save_manifest(manifest: AnalysisManifest, output_dir: Path) -> Path:
|
|
|
109
109
|
"""
|
|
110
110
|
manifest_path = output_dir / MANIFEST_FILENAME
|
|
111
111
|
|
|
112
|
-
with open(manifest_path, "w") as f:
|
|
112
|
+
with open(manifest_path, "w", encoding="utf-8") as f:
|
|
113
113
|
f.write(manifest.model_dump_json(indent=2))
|
|
114
114
|
|
|
115
115
|
logger.info(f"Saved analysis manifest to {manifest_path}")
|
|
@@ -129,7 +129,7 @@ def load_manifest(output_dir: Path) -> AnalysisManifest | None:
|
|
|
129
129
|
return None
|
|
130
130
|
|
|
131
131
|
try:
|
|
132
|
-
with open(manifest_path, "r") as f:
|
|
132
|
+
with open(manifest_path, "r", encoding="utf-8") as f:
|
|
133
133
|
data = json.load(f)
|
|
134
134
|
|
|
135
135
|
manifest = AnalysisManifest.model_validate(data)
|
|
@@ -22,7 +22,7 @@ def _load_all_analyses(analysis_path: Path) -> list[tuple[str, AnalysisInsights,
|
|
|
22
22
|
|
|
23
23
|
Returns the root analysis as 'overview' plus one entry per expanded component.
|
|
24
24
|
"""
|
|
25
|
-
with open(analysis_path, "r") as f:
|
|
25
|
+
with open(analysis_path, "r", encoding="utf-8") as f:
|
|
26
26
|
data = json.load(f)
|
|
27
27
|
|
|
28
28
|
root_analysis, sub_analyses = parse_unified_analysis(data)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import io
|
|
1
2
|
import logging
|
|
2
3
|
import logging.config
|
|
3
4
|
import os
|
|
@@ -87,6 +88,19 @@ def setup_logging(
|
|
|
87
88
|
|
|
88
89
|
logging.config.dictConfig(config)
|
|
89
90
|
|
|
91
|
+
# Reconfigure the console handler's stream to use 'replace' error handling
|
|
92
|
+
# so that non-encodable Unicode characters (e.g. \u2011 on Windows cp1251)
|
|
93
|
+
# don't crash the logging system.
|
|
94
|
+
for handler in logging.root.handlers:
|
|
95
|
+
if isinstance(handler, logging.StreamHandler) and not isinstance(handler, logging.FileHandler):
|
|
96
|
+
stream = handler.stream
|
|
97
|
+
if hasattr(stream, "reconfigure"):
|
|
98
|
+
stream.reconfigure(errors="replace")
|
|
99
|
+
elif hasattr(stream, "encoding") and stream.encoding and stream.encoding.lower() != "utf-8":
|
|
100
|
+
handler.stream = io.TextIOWrapper(
|
|
101
|
+
stream.buffer, encoding=stream.encoding, errors="replace", line_buffering=stream.line_buffering
|
|
102
|
+
)
|
|
103
|
+
|
|
90
104
|
# Handle _latest.log symlink
|
|
91
105
|
latest_log_path = logs_dir / "_latest.log"
|
|
92
106
|
try:
|
|
@@ -82,7 +82,7 @@ def generate_markdown_docs(
|
|
|
82
82
|
|
|
83
83
|
# Load the single unified analysis.json
|
|
84
84
|
analysis_path = analysis_files[0]
|
|
85
|
-
with open(analysis_path, "r") as f:
|
|
85
|
+
with open(analysis_path, "r", encoding="utf-8") as f:
|
|
86
86
|
data = json.load(f)
|
|
87
87
|
|
|
88
88
|
root_analysis, sub_analyses = parse_unified_analysis(data)
|
|
@@ -111,18 +111,18 @@ def monitor_execution(
|
|
|
111
111
|
# Cleanup & Save Summary (Happens automatically on exit/crash)
|
|
112
112
|
summary_file = out_path / f"summary.json"
|
|
113
113
|
try:
|
|
114
|
-
with open(summary_file, "w") as f:
|
|
114
|
+
with open(summary_file, "w", encoding="utf-8") as f:
|
|
115
115
|
json.dump(run_stats.to_dict(), f, indent=2)
|
|
116
|
-
logger.debug(f"
|
|
116
|
+
logger.debug(f"Monitoring summary saved to {summary_file}")
|
|
117
117
|
except Exception as e:
|
|
118
118
|
logger.error(f"Failed to save monitoring summary: {e}")
|
|
119
119
|
|
|
120
120
|
# Cleanup handler
|
|
121
121
|
trace_logger.removeHandler(trace_handler)
|
|
122
122
|
trace_handler.close()
|
|
123
|
-
logger.debug(f"
|
|
123
|
+
logger.debug(f"Execution traces saved to {trace_file}")
|
|
124
124
|
|
|
125
|
-
logger.info(f"
|
|
125
|
+
logger.info(f"Run results saved to {out_path}")
|
|
126
126
|
|
|
127
127
|
# Reset context var
|
|
128
128
|
current_stats.reset(stats_token)
|
|
@@ -129,7 +129,7 @@ class StreamingStatsWriter:
|
|
|
129
129
|
|
|
130
130
|
# Atomic write
|
|
131
131
|
temp_file = self.llm_usage_file.with_suffix(".tmp")
|
|
132
|
-
with open(temp_file, "w") as f:
|
|
132
|
+
with open(temp_file, "w", encoding="utf-8") as f:
|
|
133
133
|
json.dump(data, f, indent=2)
|
|
134
134
|
os.replace(temp_file, self.llm_usage_file)
|
|
135
135
|
|
|
@@ -165,7 +165,7 @@ class StreamingStatsWriter:
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
metadata_file = self.monitoring_dir / "run_metadata.json"
|
|
168
|
-
with open(metadata_file, "w") as f:
|
|
168
|
+
with open(metadata_file, "w", encoding="utf-8") as f:
|
|
169
169
|
json.dump(metadata, f, indent=2)
|
|
170
170
|
|
|
171
171
|
except Exception as e:
|
|
@@ -124,7 +124,7 @@ def generate_markdown_file(
|
|
|
124
124
|
repo_path=repo_path,
|
|
125
125
|
)
|
|
126
126
|
markdown_file = temp_dir / f"{file_name}.md"
|
|
127
|
-
with open(markdown_file, "w") as f:
|
|
127
|
+
with open(markdown_file, "w", encoding="utf-8") as f:
|
|
128
128
|
f.write(content)
|
|
129
129
|
return markdown_file
|
|
130
130
|
|
|
@@ -175,7 +175,7 @@ class ClusterChangeAnalyzer:
|
|
|
175
175
|
for new_id in new_clusters.get_cluster_ids():
|
|
176
176
|
new_nodes = new_clusters.get_nodes_for_cluster(new_id)
|
|
177
177
|
|
|
178
|
-
# Calculate Jaccard similarity: |A
|
|
178
|
+
# Calculate Jaccard similarity: |A & B| / |A | B|
|
|
179
179
|
intersection = old_nodes & new_nodes
|
|
180
180
|
union = old_nodes | new_nodes
|
|
181
181
|
|
|
@@ -5,7 +5,7 @@ from dataclasses import dataclass, field
|
|
|
5
5
|
import networkx as nx
|
|
6
6
|
import networkx.algorithms.community as nx_comm
|
|
7
7
|
|
|
8
|
-
from static_analyzer.constants import ClusteringConfig,
|
|
8
|
+
from static_analyzer.constants import ClusteringConfig, Language, Node, NodeType
|
|
9
9
|
|
|
10
10
|
logger = logging.getLogger(__name__)
|
|
11
11
|
|
|
@@ -639,7 +639,7 @@ class CallGraph:
|
|
|
639
639
|
if src_cluster is None or dst_cluster is None:
|
|
640
640
|
continue
|
|
641
641
|
if src_cluster != dst_cluster:
|
|
642
|
-
cluster_to_cluster_calls[src_cluster][dst_cluster].append(f"{src}
|
|
642
|
+
cluster_to_cluster_calls[src_cluster][dst_cluster].append(f"{src} -> {dst}")
|
|
643
643
|
|
|
644
644
|
inter_cluster_str = "Inter-Cluster Connections:\n\n"
|
|
645
645
|
if cluster_to_cluster_calls:
|
|
@@ -649,7 +649,7 @@ class CallGraph:
|
|
|
649
649
|
src_display = src_cluster_id + 1
|
|
650
650
|
dst_display = dst_cluster_id + 1
|
|
651
651
|
|
|
652
|
-
inter_cluster_str += f"Cluster {src_display}
|
|
652
|
+
inter_cluster_str += f"Cluster {src_display} -> Cluster {dst_display} via method calls:\n"
|
|
653
653
|
for call in calls:
|
|
654
654
|
inter_cluster_str += f" - {call}\n"
|
|
655
655
|
inter_cluster_str += "\n"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
+
import platform
|
|
4
5
|
import subprocess
|
|
5
6
|
import threading
|
|
6
7
|
import time
|
|
@@ -105,6 +106,9 @@ class LSPClient(ABC):
|
|
|
105
106
|
self._responses: dict[int, dict] = {}
|
|
106
107
|
self._notifications: list[dict] = []
|
|
107
108
|
self._lock = threading.RLock()
|
|
109
|
+
# Separate lock for stdin writes to avoid holding _lock during
|
|
110
|
+
# potentially blocking I/O (pipe buffer full on Windows).
|
|
111
|
+
self._stdin_lock = threading.Lock()
|
|
108
112
|
self._response_condition = threading.Condition(self._lock)
|
|
109
113
|
|
|
110
114
|
# Initialize CallGraph
|
|
@@ -152,16 +156,17 @@ class LSPClient(ABC):
|
|
|
152
156
|
message_id = self._message_id
|
|
153
157
|
self._message_id += 1
|
|
154
158
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
request = {
|
|
160
|
+
"jsonrpc": "2.0",
|
|
161
|
+
"id": message_id,
|
|
162
|
+
"method": method,
|
|
163
|
+
"params": params,
|
|
164
|
+
}
|
|
161
165
|
|
|
162
|
-
|
|
163
|
-
|
|
166
|
+
body = json.dumps(request)
|
|
167
|
+
message = f"Content-Length: {len(body)}\r\n\r\n{body}"
|
|
164
168
|
|
|
169
|
+
with self._stdin_lock:
|
|
165
170
|
if self._process and self._process.stdin:
|
|
166
171
|
self._process.stdin.write(message.encode("utf-8"))
|
|
167
172
|
self._process.stdin.flush()
|
|
@@ -178,7 +183,7 @@ class LSPClient(ABC):
|
|
|
178
183
|
body = json.dumps(notification)
|
|
179
184
|
message = f"Content-Length: {len(body)}\r\n\r\n{body}"
|
|
180
185
|
|
|
181
|
-
with self.
|
|
186
|
+
with self._stdin_lock:
|
|
182
187
|
if self._process and self._process.stdin:
|
|
183
188
|
self._process.stdin.write(message.encode("utf-8"))
|
|
184
189
|
self._process.stdin.flush()
|
|
@@ -396,7 +401,7 @@ class LSPClient(ABC):
|
|
|
396
401
|
body_bytes = body.encode("utf-8")
|
|
397
402
|
header = f"Content-Length: {len(body_bytes)}\r\n\r\n"
|
|
398
403
|
|
|
399
|
-
with self.
|
|
404
|
+
with self._stdin_lock:
|
|
400
405
|
if self._process and self._process.stdin:
|
|
401
406
|
self._process.stdin.write(header.encode("utf-8") + body_bytes)
|
|
402
407
|
self._process.stdin.flush()
|
|
@@ -754,7 +759,16 @@ class LSPClient(ABC):
|
|
|
754
759
|
logger.info(f"Found {len(all_classes)} classes in workspace")
|
|
755
760
|
|
|
756
761
|
cpu_count = os.cpu_count()
|
|
757
|
-
|
|
762
|
+
if platform.system() == "Windows":
|
|
763
|
+
# On Windows, limit to 1 worker to prevent pipe buffer deadlock.
|
|
764
|
+
# Multiple concurrent stdin writes can fill the 4KB pipe buffer;
|
|
765
|
+
# if the LSP server then sends a request (e.g. workspace/configuration)
|
|
766
|
+
# and blocks waiting for our response, but we can't write it because
|
|
767
|
+
# the pipe is full, we deadlock. Sequential writes avoid this.
|
|
768
|
+
max_workers = 1
|
|
769
|
+
else:
|
|
770
|
+
max_workers = max(1, cpu_count - 1) if cpu_count else 1
|
|
771
|
+
logger.info(f"Starting file analysis with {max_workers} workers for {total_files} files...")
|
|
758
772
|
successful_results = []
|
|
759
773
|
|
|
760
774
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
@@ -764,6 +778,7 @@ class LSPClient(ABC):
|
|
|
764
778
|
}
|
|
765
779
|
|
|
766
780
|
# Collect results as they complete
|
|
781
|
+
files_done = 0
|
|
767
782
|
with tqdm(total=total_files, desc="[Unified Analysis] Processing files") as pbar:
|
|
768
783
|
for future in as_completed(future_to_file):
|
|
769
784
|
file_path = future_to_file[future]
|
|
@@ -782,7 +797,10 @@ class LSPClient(ABC):
|
|
|
782
797
|
except Exception as e:
|
|
783
798
|
logger.error(f"Exception processing {file_path}: {e}")
|
|
784
799
|
finally:
|
|
800
|
+
files_done += 1
|
|
785
801
|
pbar.update(1)
|
|
802
|
+
if files_done % 10 == 0 or files_done == total_files:
|
|
803
|
+
logger.info(f"Static analysis progress: {files_done}/{total_files} files done")
|
|
786
804
|
|
|
787
805
|
logger.info(f"Successfully processed {len(successful_results)} files")
|
|
788
806
|
|