codebrain 0.1.1__tar.gz → 0.3.0__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.
- {codebrain-0.1.1 → codebrain-0.3.0}/LICENSE +1 -1
- {codebrain-0.1.1 → codebrain-0.3.0}/PKG-INFO +49 -8
- {codebrain-0.1.1 → codebrain-0.3.0}/README.md +25 -6
- codebrain-0.3.0/codebrain/__init__.py +10 -0
- codebrain-0.3.0/codebrain/actions/__init__.py +16 -0
- codebrain-0.3.0/codebrain/actions/base.py +139 -0
- codebrain-0.3.0/codebrain/actions/refactor.py +365 -0
- codebrain-0.3.0/codebrain/actions/reviewer.py +313 -0
- codebrain-0.3.0/codebrain/actions/test_gen.py +283 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/analyzer.py +953 -943
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/api.py +330 -0
- codebrain-0.3.0/codebrain/architecture.py +824 -0
- codebrain-0.3.0/codebrain/cli.py +4282 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/comprehension.py +98 -31
- codebrain-0.3.0/codebrain/config.py +67 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/context.py +1 -0
- codebrain-0.3.0/codebrain/cross_query.py +450 -0
- codebrain-0.3.0/codebrain/cross_registry.py +328 -0
- codebrain-0.3.0/codebrain/diff_impact.py +400 -0
- codebrain-0.3.0/codebrain/env_migration.py +560 -0
- codebrain-0.3.0/codebrain/equivalence.py +584 -0
- codebrain-0.3.0/codebrain/frontend.py +443 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/graph/query.py +308 -18
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/graph/schema.py +66 -1
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/graph/store.py +81 -3
- codebrain-0.3.0/codebrain/hook_runner.py +176 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/hooks.py +2 -2
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/indexer.py +85 -1
- codebrain-0.3.0/codebrain/kt.py +401 -0
- codebrain-0.3.0/codebrain/kt_video.py +948 -0
- codebrain-0.3.0/codebrain/mcp_lifecycle.py +327 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/mcp_server.py +2992 -1635
- codebrain-0.3.0/codebrain/migration.py +729 -0
- codebrain-0.3.0/codebrain/modernize.py +962 -0
- codebrain-0.3.0/codebrain/onboard.py +352 -0
- codebrain-0.3.0/codebrain/parser/cobol_parser.py +294 -0
- codebrain-0.3.0/codebrain/parser/config_parser.py +657 -0
- codebrain-0.3.0/codebrain/parser/csharp_parser.py +797 -0
- codebrain-0.3.0/codebrain/parser/dart_parser.py +865 -0
- codebrain-0.3.0/codebrain/parser/fortran_parser.py +281 -0
- codebrain-0.3.0/codebrain/parser/frontend_parser.py +1047 -0
- codebrain-0.3.0/codebrain/parser/go_parser.py +560 -0
- codebrain-0.3.0/codebrain/parser/java_parser.py +813 -0
- codebrain-0.3.0/codebrain/parser/kotlin_parser.py +823 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/parser/models.py +1 -0
- codebrain-0.3.0/codebrain/parser/mumps_parser.py +250 -0
- codebrain-0.3.0/codebrain/parser/plsql_parser.py +242 -0
- codebrain-0.3.0/codebrain/parser/python_parser.py +1255 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/parser/registry.py +97 -1
- codebrain-0.3.0/codebrain/parser/rust_parser.py +709 -0
- codebrain-0.3.0/codebrain/parser/schema_parser.py +491 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/parser/typescript_treesitter.py +70 -10
- codebrain-0.3.0/codebrain/parser/vue_parser.py +353 -0
- codebrain-0.3.0/codebrain/rewriter.py +2527 -0
- codebrain-0.3.0/codebrain/schema_migration.py +548 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/settings.py +3 -0
- codebrain-0.3.0/codebrain/susa_auth.py +157 -0
- codebrain-0.3.0/codebrain/test_gaps.py +325 -0
- codebrain-0.3.0/codebrain/test_runner.py +878 -0
- codebrain-0.3.0/codebrain/tour.py +475 -0
- codebrain-0.3.0/codebrain/ui_migration.py +394 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/utils.py +27 -7
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/watcher/file_watcher.py +23 -13
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain.egg-info/PKG-INFO +49 -8
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain.egg-info/SOURCES.txt +75 -0
- codebrain-0.3.0/codebrain.egg-info/entry_points.txt +2 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain.egg-info/requires.txt +1 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/pyproject.toml +16 -5
- codebrain-0.3.0/tests/test_actions.py +614 -0
- codebrain-0.3.0/tests/test_architecture.py +569 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_cli.py +2 -1
- codebrain-0.3.0/tests/test_coverage_gaps.py +517 -0
- codebrain-0.3.0/tests/test_cross_repo.py +479 -0
- codebrain-0.3.0/tests/test_csharp_parser.py +456 -0
- codebrain-0.3.0/tests/test_dart_parser.py +487 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_dead_code_confidence.py +17 -12
- codebrain-0.3.0/tests/test_diff_impact.py +122 -0
- codebrain-0.3.0/tests/test_env_migration.py +243 -0
- codebrain-0.3.0/tests/test_equivalence.py +385 -0
- codebrain-0.3.0/tests/test_fingerprints.py +327 -0
- codebrain-0.3.0/tests/test_frontend.py +1964 -0
- codebrain-0.3.0/tests/test_gate_battle.py +321 -0
- codebrain-0.3.0/tests/test_go_parser.py +437 -0
- codebrain-0.3.0/tests/test_hooks.py +316 -0
- codebrain-0.3.0/tests/test_infra_parser.py +239 -0
- codebrain-0.3.0/tests/test_java_parser.py +589 -0
- codebrain-0.3.0/tests/test_kotlin_parser.py +426 -0
- codebrain-0.3.0/tests/test_kt.py +143 -0
- codebrain-0.3.0/tests/test_legacy_parsers.py +463 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_llm.py +4 -2
- codebrain-0.3.0/tests/test_mcp_lifecycle.py +319 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_mcp_server.py +623 -615
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_memory.py +470 -462
- codebrain-0.3.0/tests/test_migration.py +365 -0
- codebrain-0.3.0/tests/test_modernize.py +367 -0
- codebrain-0.3.0/tests/test_onboard.py +90 -0
- codebrain-0.3.0/tests/test_orm_detection.py +293 -0
- codebrain-0.3.0/tests/test_output_quality.py +384 -0
- codebrain-0.3.0/tests/test_real_codebase.py +223 -0
- codebrain-0.3.0/tests/test_real_features.py +448 -0
- codebrain-0.3.0/tests/test_real_frontend.py +434 -0
- codebrain-0.3.0/tests/test_real_repos.py +878 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_real_world.py +3 -2
- codebrain-0.3.0/tests/test_rewriter.py +1331 -0
- codebrain-0.3.0/tests/test_rust_parser.py +595 -0
- codebrain-0.3.0/tests/test_schema_migration.py +533 -0
- codebrain-0.3.0/tests/test_schema_parser.py +328 -0
- codebrain-0.3.0/tests/test_test_runner.py +546 -0
- codebrain-0.3.0/tests/test_tour.py +101 -0
- codebrain-0.3.0/tests/test_translate.py +524 -0
- codebrain-0.3.0/tests/test_ui_migration.py +686 -0
- codebrain-0.3.0/tests/test_vscode_extension.py +278 -0
- codebrain-0.1.1/codebrain/__init__.py +0 -3
- codebrain-0.1.1/codebrain/cli.py +0 -1927
- codebrain-0.1.1/codebrain/config.py +0 -46
- codebrain-0.1.1/codebrain/hook_runner.py +0 -71
- codebrain-0.1.1/codebrain/parser/config_parser.py +0 -228
- codebrain-0.1.1/codebrain/parser/python_parser.py +0 -658
- codebrain-0.1.1/codebrain.egg-info/entry_points.txt +0 -6
- codebrain-0.1.1/tests/test_hooks.py +0 -142
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/__main__.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/agent_bridge.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/api_models.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/export.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/graph/__init__.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/llm.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/logging.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/memory/__init__.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/memory/store.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/parser/__init__.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/parser/base.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/parser/typescript_parser.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/py.typed +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/resolver.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/validator.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain/watcher/__init__.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain.egg-info/dependency_links.txt +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/codebrain.egg-info/top_level.txt +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/setup.cfg +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_agent_bridge.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_analyzer.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_api.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_ci.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_comprehension.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_context.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_contracts_real.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_dataflow.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_error_recovery.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_export.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_indexer.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_install.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_integration.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_jyotishyamitra.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_multi_project_cli.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_narratives.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_parser.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_plugin_system.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_production_hardening.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_query.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_resolver.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_scale.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_scale_optimizations.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_scale_real.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_schema.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_settings.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_store.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_ts_ast_parser.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_ts_parser_enhanced.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_typescript_parser.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_utils.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_validation_narratives.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_validator.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_validator_scenarios.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_watch_validate.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_watcher.py +0 -0
- {codebrain-0.1.1 → codebrain-0.3.0}/tests/test_zoom.py +0 -0
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codebrain
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Know what breaks before you break it. Structural knowledge graph for codebases — impact analysis, dead code detection, health scores. No LLM required.
|
|
5
5
|
Author: CodeBrain Contributors
|
|
6
|
-
License: MIT
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 CodeBrain Contributors
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
7
28
|
Project-URL: Homepage, https://github.com/monk0062006/CodeBrain
|
|
8
29
|
Project-URL: Repository, https://github.com/monk0062006/CodeBrain
|
|
9
30
|
Project-URL: Issues, https://github.com/monk0062006/CodeBrain/issues
|
|
@@ -28,6 +49,7 @@ Requires-Dist: click>=8.1
|
|
|
28
49
|
Requires-Dist: watchdog>=3.0
|
|
29
50
|
Requires-Dist: mcp<1.20,>=1.0
|
|
30
51
|
Requires-Dist: jinja2>=3.1
|
|
52
|
+
Requires-Dist: psutil>=5.9
|
|
31
53
|
Provides-Extra: api
|
|
32
54
|
Requires-Dist: fastapi>=0.110; extra == "api"
|
|
33
55
|
Requires-Dist: uvicorn>=0.27; extra == "api"
|
|
@@ -59,14 +81,26 @@ cd your-project
|
|
|
59
81
|
brain init
|
|
60
82
|
```
|
|
61
83
|
|
|
62
|
-
|
|
84
|
+
You'll immediately see:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
Indexing my-project (93 files) ...
|
|
88
|
+
Done in 1.2s — 1861 nodes, 8837 edges
|
|
89
|
+
|
|
90
|
+
=== Instant Findings ===
|
|
91
|
+
Health: 60/100 (B)
|
|
92
|
+
Riskiest symbols: Flask (34 files), App (35 files), Scaffold (28 files)
|
|
93
|
+
Dead code: 11 candidates (none high-confidence)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Then explore:
|
|
63
97
|
|
|
64
98
|
```bash
|
|
65
99
|
brain impact my_function # What breaks if I change this?
|
|
100
|
+
brain callers my_function # Who calls this?
|
|
66
101
|
brain health # Codebase health score (0-100)
|
|
67
102
|
brain hotspots # Riskiest symbols in the codebase
|
|
68
103
|
brain deadcode # Unused functions with confidence levels
|
|
69
|
-
brain coupling # Which files are too tightly coupled?
|
|
70
104
|
brain zoom # Google Maps-style navigation of your architecture
|
|
71
105
|
```
|
|
72
106
|
|
|
@@ -173,7 +207,7 @@ Module: main.py — 429 symbols, 15853 lines. Isolated (no dependents).
|
|
|
173
207
|
### Claude Code (MCP)
|
|
174
208
|
|
|
175
209
|
```bash
|
|
176
|
-
brain
|
|
210
|
+
brain setup # auto-generates CLAUDE.md + MCP config
|
|
177
211
|
```
|
|
178
212
|
|
|
179
213
|
Or add manually to your MCP config:
|
|
@@ -214,6 +248,7 @@ The `propose_change` MCP tool validates changes **before** the AI writes them. W
|
|
|
214
248
|
| `brain status` | Show index statistics |
|
|
215
249
|
| `brain search <query>` | Find symbols by name |
|
|
216
250
|
| `brain impact <name>` | What breaks if this changes? |
|
|
251
|
+
| `brain callers <name>` | Who calls this function? (reverse trace) |
|
|
217
252
|
| `brain trace <name>` | Forward call chain from a symbol |
|
|
218
253
|
| `brain deps <name>` | Dependencies of a symbol or file |
|
|
219
254
|
| `brain deadcode` | Find unused functions/classes |
|
|
@@ -265,7 +300,13 @@ Or use environment variables:
|
|
|
265
300
|
| Python | AST (stdlib) | Full — functions, classes, imports, calls, dataflow |
|
|
266
301
|
| TypeScript/JSX | tree-sitter | Full — requires `pip install codebrain[ts]` |
|
|
267
302
|
| JavaScript/JSX | tree-sitter | Full — requires `pip install codebrain[ts]` |
|
|
268
|
-
|
|
|
303
|
+
| Java | tree-sitter | Full — Spring Boot aware (DI, endpoints, JPA) |
|
|
304
|
+
| Go | tree-sitter | Full — interfaces, goroutines, struct methods |
|
|
305
|
+
| Rust | tree-sitter | Full — traits, impls, modules, derive macros |
|
|
306
|
+
| C# | tree-sitter | Full — async/await, LINQ, attributes |
|
|
307
|
+
| Kotlin | tree-sitter | Full — coroutines, extensions, data classes |
|
|
308
|
+
| Dart/Flutter | regex | Good — classes, mixins, extensions |
|
|
309
|
+
| Vue SFC | regex | Good — components, props, events |
|
|
269
310
|
|
|
270
311
|
### Adding Languages
|
|
271
312
|
|
|
@@ -275,9 +316,9 @@ Implement `BaseParser` and register via entry points:
|
|
|
275
316
|
from codebrain.parser.base import BaseParser
|
|
276
317
|
from codebrain.parser.models import ParsedFile
|
|
277
318
|
|
|
278
|
-
class
|
|
319
|
+
class MyParser(BaseParser):
|
|
279
320
|
def extensions(self) -> frozenset[str]:
|
|
280
|
-
return frozenset({".
|
|
321
|
+
return frozenset({".xyz"})
|
|
281
322
|
|
|
282
323
|
def parse(self, path, repo_root) -> ParsedFile:
|
|
283
324
|
... # parse and return nodes + edges
|
|
@@ -10,14 +10,26 @@ cd your-project
|
|
|
10
10
|
brain init
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
You'll immediately see:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Indexing my-project (93 files) ...
|
|
17
|
+
Done in 1.2s — 1861 nodes, 8837 edges
|
|
18
|
+
|
|
19
|
+
=== Instant Findings ===
|
|
20
|
+
Health: 60/100 (B)
|
|
21
|
+
Riskiest symbols: Flask (34 files), App (35 files), Scaffold (28 files)
|
|
22
|
+
Dead code: 11 candidates (none high-confidence)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then explore:
|
|
14
26
|
|
|
15
27
|
```bash
|
|
16
28
|
brain impact my_function # What breaks if I change this?
|
|
29
|
+
brain callers my_function # Who calls this?
|
|
17
30
|
brain health # Codebase health score (0-100)
|
|
18
31
|
brain hotspots # Riskiest symbols in the codebase
|
|
19
32
|
brain deadcode # Unused functions with confidence levels
|
|
20
|
-
brain coupling # Which files are too tightly coupled?
|
|
21
33
|
brain zoom # Google Maps-style navigation of your architecture
|
|
22
34
|
```
|
|
23
35
|
|
|
@@ -124,7 +136,7 @@ Module: main.py — 429 symbols, 15853 lines. Isolated (no dependents).
|
|
|
124
136
|
### Claude Code (MCP)
|
|
125
137
|
|
|
126
138
|
```bash
|
|
127
|
-
brain
|
|
139
|
+
brain setup # auto-generates CLAUDE.md + MCP config
|
|
128
140
|
```
|
|
129
141
|
|
|
130
142
|
Or add manually to your MCP config:
|
|
@@ -165,6 +177,7 @@ The `propose_change` MCP tool validates changes **before** the AI writes them. W
|
|
|
165
177
|
| `brain status` | Show index statistics |
|
|
166
178
|
| `brain search <query>` | Find symbols by name |
|
|
167
179
|
| `brain impact <name>` | What breaks if this changes? |
|
|
180
|
+
| `brain callers <name>` | Who calls this function? (reverse trace) |
|
|
168
181
|
| `brain trace <name>` | Forward call chain from a symbol |
|
|
169
182
|
| `brain deps <name>` | Dependencies of a symbol or file |
|
|
170
183
|
| `brain deadcode` | Find unused functions/classes |
|
|
@@ -216,7 +229,13 @@ Or use environment variables:
|
|
|
216
229
|
| Python | AST (stdlib) | Full — functions, classes, imports, calls, dataflow |
|
|
217
230
|
| TypeScript/JSX | tree-sitter | Full — requires `pip install codebrain[ts]` |
|
|
218
231
|
| JavaScript/JSX | tree-sitter | Full — requires `pip install codebrain[ts]` |
|
|
219
|
-
|
|
|
232
|
+
| Java | tree-sitter | Full — Spring Boot aware (DI, endpoints, JPA) |
|
|
233
|
+
| Go | tree-sitter | Full — interfaces, goroutines, struct methods |
|
|
234
|
+
| Rust | tree-sitter | Full — traits, impls, modules, derive macros |
|
|
235
|
+
| C# | tree-sitter | Full — async/await, LINQ, attributes |
|
|
236
|
+
| Kotlin | tree-sitter | Full — coroutines, extensions, data classes |
|
|
237
|
+
| Dart/Flutter | regex | Good — classes, mixins, extensions |
|
|
238
|
+
| Vue SFC | regex | Good — components, props, events |
|
|
220
239
|
|
|
221
240
|
### Adding Languages
|
|
222
241
|
|
|
@@ -226,9 +245,9 @@ Implement `BaseParser` and register via entry points:
|
|
|
226
245
|
from codebrain.parser.base import BaseParser
|
|
227
246
|
from codebrain.parser.models import ParsedFile
|
|
228
247
|
|
|
229
|
-
class
|
|
248
|
+
class MyParser(BaseParser):
|
|
230
249
|
def extensions(self) -> frozenset[str]:
|
|
231
|
-
return frozenset({".
|
|
250
|
+
return frozenset({".xyz"})
|
|
232
251
|
|
|
233
252
|
def parse(self, path, repo_root) -> ParsedFile:
|
|
234
253
|
... # parse and return nodes + edges
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""CodeBrain — persistent structural knowledge graph for codebases."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version as _pkg_version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = _pkg_version("codebrain")
|
|
7
|
+
except PackageNotFoundError:
|
|
8
|
+
# Source checkout without installation (e.g. running from a worktree).
|
|
9
|
+
# Keep in sync with [project.version] in pyproject.toml.
|
|
10
|
+
__version__ = "0.3.0"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Autonomous actions engine — CodeBrain acts, not just analyzes."""
|
|
2
|
+
|
|
3
|
+
from codebrain.actions.test_gen import TestGenerator, GeneratedTest
|
|
4
|
+
from codebrain.actions.reviewer import CodeReviewer, CodeReview, ReviewFinding
|
|
5
|
+
from codebrain.actions.refactor import Refactorer, RefactorSuggestion, RefactorResult
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"TestGenerator",
|
|
9
|
+
"GeneratedTest",
|
|
10
|
+
"CodeReviewer",
|
|
11
|
+
"CodeReview",
|
|
12
|
+
"ReviewFinding",
|
|
13
|
+
"Refactorer",
|
|
14
|
+
"RefactorSuggestion",
|
|
15
|
+
"RefactorResult",
|
|
16
|
+
]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Shared infrastructure for autonomous actions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from codebrain.context import ContextGenerator
|
|
10
|
+
from codebrain.graph.query import QueryEngine
|
|
11
|
+
from codebrain.graph.store import GraphStore
|
|
12
|
+
from codebrain.rewriter import LLMClient
|
|
13
|
+
from codebrain.utils import is_test_file
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ActionEngine:
|
|
17
|
+
"""Base class for all autonomous actions."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, store: GraphStore, repo_root: Path, llm: LLMClient):
|
|
20
|
+
self.store = store
|
|
21
|
+
self.repo_root = repo_root
|
|
22
|
+
self.llm = llm
|
|
23
|
+
self.context = ContextGenerator(store)
|
|
24
|
+
self.engine = QueryEngine(store)
|
|
25
|
+
|
|
26
|
+
def _get_symbol_context(self, name: str) -> list[dict]:
|
|
27
|
+
"""Full structural context for a symbol."""
|
|
28
|
+
return self.context.for_symbol(name)
|
|
29
|
+
|
|
30
|
+
def _get_file_source(self, file_path: str) -> str:
|
|
31
|
+
"""Read actual source code from disk."""
|
|
32
|
+
full = self.repo_root / file_path
|
|
33
|
+
if not full.exists():
|
|
34
|
+
return ""
|
|
35
|
+
try:
|
|
36
|
+
return full.read_text(encoding="utf-8", errors="replace")
|
|
37
|
+
except Exception:
|
|
38
|
+
return ""
|
|
39
|
+
|
|
40
|
+
def _get_function_source(self, file_path: str, name: str, line_start: int) -> str:
|
|
41
|
+
"""Extract a single function/class source from a file."""
|
|
42
|
+
source = self._get_file_source(file_path)
|
|
43
|
+
if not source:
|
|
44
|
+
return ""
|
|
45
|
+
try:
|
|
46
|
+
tree = ast.parse(source)
|
|
47
|
+
except SyntaxError:
|
|
48
|
+
return ""
|
|
49
|
+
for node in ast.walk(tree):
|
|
50
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
|
|
51
|
+
if node.name == name:
|
|
52
|
+
lines = source.splitlines()
|
|
53
|
+
end = getattr(node, "end_lineno", None) or (node.lineno + 20)
|
|
54
|
+
return "\n".join(lines[node.lineno - 1 : end])
|
|
55
|
+
# Fallback: return from line_start
|
|
56
|
+
if line_start > 0:
|
|
57
|
+
lines = source.splitlines()
|
|
58
|
+
return "\n".join(lines[line_start - 1 : line_start + 30])
|
|
59
|
+
return ""
|
|
60
|
+
|
|
61
|
+
def _get_existing_tests(self, file_path: str) -> str:
|
|
62
|
+
"""Find existing test files and return a sample of test patterns."""
|
|
63
|
+
# Look for test files in tests/ directory
|
|
64
|
+
tests_dir = self.repo_root / "tests"
|
|
65
|
+
if not tests_dir.exists():
|
|
66
|
+
tests_dir = self.repo_root / "test"
|
|
67
|
+
if not tests_dir.exists():
|
|
68
|
+
return "# No existing test directory found"
|
|
69
|
+
|
|
70
|
+
samples = []
|
|
71
|
+
for tf in sorted(tests_dir.iterdir()):
|
|
72
|
+
if tf.name.startswith("test_") and tf.suffix == ".py":
|
|
73
|
+
try:
|
|
74
|
+
content = tf.read_text(encoding="utf-8", errors="replace")
|
|
75
|
+
# Extract first test function as pattern example
|
|
76
|
+
for match in re.finditer(
|
|
77
|
+
r"((?:@\w+.*\n)*def test_\w+\(.*?\):.*?)(?=\ndef |\nclass |\Z)",
|
|
78
|
+
content,
|
|
79
|
+
re.DOTALL,
|
|
80
|
+
):
|
|
81
|
+
sample = match.group(1).strip()
|
|
82
|
+
if len(sample) < 500:
|
|
83
|
+
samples.append(f"# From {tf.name}:\n{sample}")
|
|
84
|
+
break
|
|
85
|
+
except Exception:
|
|
86
|
+
continue
|
|
87
|
+
if len(samples) >= 3:
|
|
88
|
+
break
|
|
89
|
+
|
|
90
|
+
return "\n\n".join(samples) if samples else "# No test examples found"
|
|
91
|
+
|
|
92
|
+
def _validate_python(self, code: str) -> list[str]:
|
|
93
|
+
"""Check Python syntax and basic correctness."""
|
|
94
|
+
errors = []
|
|
95
|
+
try:
|
|
96
|
+
ast.parse(code)
|
|
97
|
+
except SyntaxError as e:
|
|
98
|
+
errors.append(f"Syntax error: {e.msg} at line {e.lineno}")
|
|
99
|
+
return errors
|
|
100
|
+
|
|
101
|
+
def _extract_code(self, response: str) -> str:
|
|
102
|
+
"""Strip markdown fences from LLM output."""
|
|
103
|
+
pattern = r"```(?:python|java|typescript|javascript)?\s*\n(.*?)```"
|
|
104
|
+
matches = re.findall(pattern, response, re.DOTALL)
|
|
105
|
+
if matches:
|
|
106
|
+
return max(matches, key=len).strip()
|
|
107
|
+
# If no fences, return as-is but trim non-code lines at start
|
|
108
|
+
lines = response.strip().split("\n")
|
|
109
|
+
for i, line in enumerate(lines):
|
|
110
|
+
s = line.strip()
|
|
111
|
+
if s.startswith(("import ", "from ", "def ", "class ", "#", '"""', "@", "async ")):
|
|
112
|
+
return "\n".join(lines[i:]).strip()
|
|
113
|
+
return response.strip()
|
|
114
|
+
|
|
115
|
+
def _interpret_fingerprint(self, fingerprint: str) -> str:
|
|
116
|
+
"""Convert structural fingerprint to actionable guidance."""
|
|
117
|
+
if not fingerprint:
|
|
118
|
+
return "- No structural fingerprint available."
|
|
119
|
+
guidance = []
|
|
120
|
+
if "returns:" in fingerprint:
|
|
121
|
+
returns = fingerprint.split("returns:")[1].split("|")[0].strip()
|
|
122
|
+
guidance.append(f"- Returns: {returns}. Test each return path.")
|
|
123
|
+
if "raises:" in fingerprint:
|
|
124
|
+
exc = fingerprint.split("raises:")[1].split("|")[0].strip()
|
|
125
|
+
guidance.append(f"- Raises: {exc}. Test exception paths.")
|
|
126
|
+
if "mutates:" in fingerprint:
|
|
127
|
+
mut = fingerprint.split("mutates:")[1].split("|")[0].strip()
|
|
128
|
+
guidance.append(f"- Mutates: {mut}. Verify state changes.")
|
|
129
|
+
if "io:" in fingerprint:
|
|
130
|
+
io = fingerprint.split("io:")[1].split("|")[0].strip()
|
|
131
|
+
guidance.append(f"- I/O: {io}. Mock these in tests.")
|
|
132
|
+
if "yields" in fingerprint:
|
|
133
|
+
guidance.append("- Generator function. Test iteration and empty case.")
|
|
134
|
+
if "async" in fingerprint:
|
|
135
|
+
guidance.append("- Async function. Use @pytest.mark.asyncio.")
|
|
136
|
+
if "control:" in fingerprint:
|
|
137
|
+
ctrl = fingerprint.split("control:")[1].split("|")[0].strip()
|
|
138
|
+
guidance.append(f"- Control flow: {ctrl}. Test each branch.")
|
|
139
|
+
return "\n".join(guidance) if guidance else "- No special patterns detected."
|