codeboarding 0.9.3__tar.gz → 0.9.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.9.3/codeboarding.egg-info → codeboarding-0.9.5}/PKG-INFO +1 -1
- codeboarding-0.9.5/README.md +149 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/agent_responses.py +4 -4
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/cluster_methods_mixin.py +5 -2
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/dependency_discovery.py +5 -5
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/planner_agent.py +9 -9
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/prompts/gpt_prompts.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/read_file_structure.py +6 -6
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/read_source.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5/codeboarding.egg-info}/PKG-INFO +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5}/codeboarding.egg-info/SOURCES.txt +4 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/diagram_generator.py +4 -4
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/file_coverage.py +2 -2
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/io_utils.py +2 -2
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/models.py +3 -3
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/manifest.py +2 -2
- {codeboarding-0.9.3 → codeboarding-0.9.5}/github_action.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5}/install.py +86 -45
- {codeboarding-0.9.3 → codeboarding-0.9.5}/logging_config.py +14 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/main.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5}/monitoring/context.py +4 -4
- {codeboarding-0.9.3 → codeboarding-0.9.5}/monitoring/writers.py +2 -2
- {codeboarding-0.9.3 → codeboarding-0.9.5}/output_generators/markdown.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5}/output_generators/mdx.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5}/output_generators/sphinx.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5}/pyproject.toml +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/cluster_change_analyzer.py +1 -1
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/graph.py +3 -3
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/lsp_client/client.py +29 -11
- codeboarding-0.9.5/tests/test_install.py +120 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/tests/test_logging_config.py +73 -0
- codeboarding-0.9.5/tests/test_tool_registry.py +94 -0
- codeboarding-0.9.5/tests/test_user_config.py +65 -0
- codeboarding-0.9.5/tests/test_windows_encoding.py +49 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/tool_registry.py +133 -9
- {codeboarding-0.9.3 → codeboarding-0.9.5}/user_config.py +1 -1
- codeboarding-0.9.3/README.md +0 -281
- codeboarding-0.9.3/tests/test_install.py +0 -51
- {codeboarding-0.9.3 → codeboarding-0.9.5}/LICENSE +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/PYPI.md +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/abstraction_agent.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/agent.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/constants.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/details_agent.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/llm_config.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/meta_agent.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/prompts/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/prompts/abstract_prompt_factory.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/prompts/claude_prompts.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/prompts/deepseek_prompts.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/prompts/gemini_flash_prompts.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/prompts/glm_prompts.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/prompts/kimi_prompts.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/prompts/prompt_factory.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/base.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/get_external_deps.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/get_method_invocations.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/read_cfg.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/read_docs.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/read_file.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/read_git_diff.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/read_packages.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/read_structure.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/tools/toolkit.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/agents/validation.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/caching/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/caching/cache.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/caching/meta_cache.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/codeboarding.egg-info/dependency_links.txt +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/codeboarding.egg-info/entry_points.txt +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/codeboarding.egg-info/requires.txt +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/codeboarding.egg-info/top_level.txt +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/core/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/core/plugin_loader.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/core/protocols.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/core/registry.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/analysis_json.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/component_checker.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/file_manager.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/impact_analyzer.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/path_patching.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/reexpansion.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/scoped_analysis.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/updater.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/incremental/validation.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/diagram_analysis/version.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/duckdb_crud.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/checks/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/checks/circular_deps.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/checks/cohesion.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/checks/coupling.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/checks/function_size.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/checks/god_class.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/checks/inheritance.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/checks/instability.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/checks/unused_code_diagnostics.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/config.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/constants.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/models.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health/runner.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/health_main.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/monitoring/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/monitoring/callbacks.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/monitoring/mixin.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/monitoring/paths.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/monitoring/stats.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/output_generators/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/output_generators/html.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/output_generators/html_template.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/repo_utils/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/repo_utils/change_detector.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/repo_utils/errors.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/repo_utils/git_diff.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/repo_utils/ignore.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/setup.cfg +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/analysis_cache.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/analysis_result.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/cluster_helpers.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/constants.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/git_diff_analyzer.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/incremental_orchestrator.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/java_config_scanner.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/java_utils.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/lsp_client/__init__.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/lsp_client/diagnostics.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/lsp_client/java_client.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/lsp_client/language_settings.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/lsp_client/typescript_client.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/programming_language.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/reference_resolve_mixin.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/scanner.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/static_analyzer/typescript_config_scanner.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/tests/test_github_action.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/tests/test_incremental_analyzer.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/tests/test_main.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/tests/test_vscode_constants.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/tests/test_windows_compatibility.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/utils.py +0 -0
- {codeboarding-0.9.3 → codeboarding-0.9.5}/vscode_constants.py +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# CodeBoarding
|
|
2
|
+
|
|
3
|
+
See what your AI is building before it breaks.
|
|
4
|
+
|
|
5
|
+
CodeBoarding gives developers and coding agents a visual map of a codebase. It combines static analysis with LLM reasoning to generate architecture diagrams, component-level documentation, and navigable outputs you can use in your IDE, CI, and docs.
|
|
6
|
+
|
|
7
|
+
[Website](https://codeboarding.org) · [Open VSX extension](https://open-vsx.org/extension/CodeBoarding/codeboarding) · [Explore examples](https://codeboarding.org/diagrams) · [VS Code extension](https://marketplace.visualstudio.com/items?itemName=Codeboarding.codeboarding) · [GitHub Action](https://github.com/marketplace/actions/codeboarding-diagram-first-documentation) ·[Discord](https://discord.gg/T5zHTJYFuy)
|
|
8
|
+
|
|
9
|
+
[](https://open-vsx.org/extension/CodeBoarding/codeboarding)
|
|
10
|
+
|
|
11
|
+
Install the extension from Open VSX.
|
|
12
|
+
|
|
13
|
+
[](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
|
14
|
+
[](https://www.typescriptlang.org/)
|
|
15
|
+
[](https://www.java.com/)
|
|
16
|
+
[](https://www.python.org/)
|
|
17
|
+
[](https://go.dev/)
|
|
18
|
+
[](https://www.php.net/)
|
|
19
|
+
|
|
20
|
+
## Few use cases:
|
|
21
|
+
|
|
22
|
+
- Keep architecture visible while agents code.
|
|
23
|
+
- Review AI-generated changes with system context before they turn into hidden debt.
|
|
24
|
+
- Understand large repositories faster with layered diagrams and component breakdowns.
|
|
25
|
+
- Share the same visual model across local workflows, IDEs, pull requests, and docs.
|
|
26
|
+
|
|
27
|
+
## What CodeBoarding generates
|
|
28
|
+
|
|
29
|
+
- High-level system architecture diagrams.
|
|
30
|
+
- Deeper component diagrams for important subsystems.
|
|
31
|
+
- Markdown documentation in `.codeboarding/`.
|
|
32
|
+
- Mermaid output that is easy to embed in docs and PRs.
|
|
33
|
+
- Incremental updates when only part of the codebase changes.
|
|
34
|
+
|
|
35
|
+
## How it works
|
|
36
|
+
|
|
37
|
+
```mermaid
|
|
38
|
+
graph LR
|
|
39
|
+
Application_Orchestrator_Repository_Manager["Application Orchestrator & Repository Manager"]
|
|
40
|
+
LLM_Agent_Core["LLM Agent Core"]
|
|
41
|
+
Static_Code_Analyzer["Static Code Analyzer"]
|
|
42
|
+
Agent_Tooling_Interface["Agent Tooling Interface"]
|
|
43
|
+
Incremental_Analysis_Engine["Incremental Analysis Engine"]
|
|
44
|
+
Documentation_Diagram_Generator["Documentation & Diagram Generator"]
|
|
45
|
+
Application_Orchestrator_Repository_Manager -- "Orchestrator initiates analysis workflow, leveraging incremental updates based on detected code changes." --> Incremental_Analysis_Engine
|
|
46
|
+
Application_Orchestrator_Repository_Manager -- "Orchestrator passes project context and triggers the main analysis workflow for the LLM Agent." --> LLM_Agent_Core
|
|
47
|
+
Incremental_Analysis_Engine -- "Incremental engine requests static analysis for specific code segments (new or changed)." --> Static_Code_Analyzer
|
|
48
|
+
Static_Code_Analyzer -- "Static analyzer provides analysis results to the incremental engine for caching." --> Incremental_Analysis_Engine
|
|
49
|
+
LLM_Agent_Core -- "LLM Agent invokes specialized tools to interact with the codebase and analysis data." --> Agent_Tooling_Interface
|
|
50
|
+
Agent_Tooling_Interface -- "Agent tools query the static analysis engine for detailed code insights." --> Static_Code_Analyzer
|
|
51
|
+
Static_Code_Analyzer -- "Static analysis engine provides requested data to the agent tools." --> Agent_Tooling_Interface
|
|
52
|
+
LLM_Agent_Core -- "LLM Agent delivers structured analysis insights for documentation and diagram generation." --> Documentation_Diagram_Generator
|
|
53
|
+
click Application_Orchestrator_Repository_Manager href "https://github.com/CodeBoarding/CodeBoarding/blob/main/.codeboarding/Application_Orchestrator_Repository_Manager.md" "Details"
|
|
54
|
+
click LLM_Agent_Core href "https://github.com/CodeBoarding/CodeBoarding/blob/main/.codeboarding/LLM_Agent_Core.md" "Details"
|
|
55
|
+
click Static_Code_Analyzer href "https://github.com/CodeBoarding/CodeBoarding/blob/main/.codeboarding/Static_Code_Analyzer.md" "Details"
|
|
56
|
+
click Agent_Tooling_Interface href "https://github.com/CodeBoarding/CodeBoarding/blob/main/.codeboarding/Agent_Tooling_Interface.md" "Details"
|
|
57
|
+
click Incremental_Analysis_Engine href "https://github.com/CodeBoarding/CodeBoarding/blob/main/.codeboarding/Incremental_Analysis_Engine.md" "Details"
|
|
58
|
+
click Documentation_Diagram_Generator href "https://github.com/CodeBoarding/CodeBoarding/blob/main/.codeboarding/Documentation_Diagram_Generator.md" "Details"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
For a deeper architecture walkthrough, see [`.codeboarding/overview.md`](.codeboarding/overview.md).
|
|
62
|
+
|
|
63
|
+
## Quick start
|
|
64
|
+
|
|
65
|
+
### Run from source
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uv sync --frozen
|
|
69
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
70
|
+
python install.py
|
|
71
|
+
python main.py --local /path/to/repo
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Use the packaged CLI
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install codeboarding
|
|
78
|
+
codeboarding-setup
|
|
79
|
+
codeboarding --local /path/to/repo
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Output is written to `/path/to/repo/.codeboarding/`.
|
|
83
|
+
|
|
84
|
+
`python install.py` and `codeboarding-setup` download language server binaries to `~/.codeboarding/servers/`, shared across projects. `npm` is required for Python, TypeScript, JavaScript, and PHP language servers; if it is missing, setup can install it via `nodeenv`.
|
|
85
|
+
|
|
86
|
+
## Configuration
|
|
87
|
+
|
|
88
|
+
On first run, CodeBoarding creates `~/.codeboarding/config.toml`. Set one provider there or use environment variables.
|
|
89
|
+
|
|
90
|
+
```toml
|
|
91
|
+
[provider]
|
|
92
|
+
# openai_api_key = "sk-..."
|
|
93
|
+
# anthropic_api_key = "sk-ant-..."
|
|
94
|
+
# google_api_key = "AIza..."
|
|
95
|
+
# vercel_api_key = "vck_..."
|
|
96
|
+
# aws_bearer_token_bedrock = "..."
|
|
97
|
+
# ollama_base_url = "http://localhost:11434"
|
|
98
|
+
# openrouter_api_key = "sk-..."
|
|
99
|
+
|
|
100
|
+
[llm]
|
|
101
|
+
# agent_model = "gemini-3-flash"
|
|
102
|
+
# parsing_model = "gemini-3-flash"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Shell environment variables such as `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, and `OLLAMA_BASE_URL` take precedence over the config file. For private repositories, set `GITHUB_TOKEN` in your environment.
|
|
106
|
+
|
|
107
|
+
## Common commands
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Analyze a local repository
|
|
111
|
+
python main.py --local ./my-project
|
|
112
|
+
|
|
113
|
+
# Increase diagram depth
|
|
114
|
+
python main.py --local ./my-project --depth-level 2
|
|
115
|
+
|
|
116
|
+
# Re-analyze only changed parts when possible
|
|
117
|
+
python main.py --local ./my-project --incremental
|
|
118
|
+
|
|
119
|
+
# Update a single component by ID
|
|
120
|
+
python main.py --local ./my-project --partial-component-id "a3f2b1c4d5e6f789"
|
|
121
|
+
|
|
122
|
+
# Analyze a remote GitHub repository
|
|
123
|
+
python main.py https://github.com/pytorch/pytorch
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Where to use it
|
|
127
|
+
|
|
128
|
+
- [CLI](https://github.com/CodeBoarding/CodeBoarding) for local analysis, automation, and CI workflows.
|
|
129
|
+
- [VS Code extension](https://marketplace.visualstudio.com/items?itemName=Codeboarding.codeboarding) for in-editor visual architecture.
|
|
130
|
+
- [GitHub Action](https://github.com/marketplace/actions/codeboarding-diagram-first-documentation) to keep diagrams updated in CI.
|
|
131
|
+
|
|
132
|
+
## Supported stack
|
|
133
|
+
|
|
134
|
+
- Languages: Python, TypeScript, JavaScript, Java, Go, PHP.
|
|
135
|
+
- LLM providers: OpenAI, Anthropic, Google, Vercel AI Gateway, AWS Bedrock, Ollama, OpenRouter, and more.
|
|
136
|
+
|
|
137
|
+
## Examples
|
|
138
|
+
|
|
139
|
+
- Visualized 800+ open-source repositories.
|
|
140
|
+
- Browse generated examples in [GeneratedOnBoardings](https://github.com/CodeBoarding/GeneratedOnBoardings).
|
|
141
|
+
- Try the hosted explorer at [codeboarding.org/diagrams](https://codeboarding.org/diagrams).
|
|
142
|
+
|
|
143
|
+
## Contributing
|
|
144
|
+
|
|
145
|
+
If you want to improve CodeBoarding, open an [issue](https://github.com/CodeBoarding/CodeBoarding/issues) or send a pull request. We welcome improvements to analysis quality, output generators, integrations, and developer experience.
|
|
146
|
+
|
|
147
|
+
## Vision
|
|
148
|
+
|
|
149
|
+
CodeBoarding is building an open standard for code understanding: a visual, accurate, high-level representation of a codebase that both humans and agents can use.
|
|
@@ -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):
|
|
@@ -134,5 +134,8 @@ tests/test_incremental_analyzer.py
|
|
|
134
134
|
tests/test_install.py
|
|
135
135
|
tests/test_logging_config.py
|
|
136
136
|
tests/test_main.py
|
|
137
|
+
tests/test_tool_registry.py
|
|
138
|
+
tests/test_user_config.py
|
|
137
139
|
tests/test_vscode_constants.py
|
|
138
|
-
tests/test_windows_compatibility.py
|
|
140
|
+
tests/test_windows_compatibility.py
|
|
141
|
+
tests/test_windows_encoding.py
|
|
@@ -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)
|