codebase-brain 0.3.1__tar.gz → 0.3.3__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.
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/PKG-INFO +1 -1
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_cli.py +1 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_parser/file_watcher.py +3 -4
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_parser/llm_router.py +10 -7
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_parser/root_cause.py +34 -8
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/codebase_brain.egg-info/PKG-INFO +1 -1
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/setup.py +1 -1
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/README.md +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_parser/__init__.py +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_parser/ast_parser.py +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_parser/bug_detector.py +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_parser/codebase_walker.py +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_parser/graph_builder.py +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_parser/query_engine.py +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/brain_parser/universal_parser.py +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/codebase_brain.egg-info/SOURCES.txt +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/codebase_brain.egg-info/dependency_links.txt +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/codebase_brain.egg-info/entry_points.txt +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/codebase_brain.egg-info/requires.txt +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/codebase_brain.egg-info/top_level.txt +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/setup.cfg +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/tests/test_bugs.py +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/tests/test_classes.py +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/tests/test_parser.py +0 -0
- {codebase_brain-0.3.1 → codebase_brain-0.3.3}/tests/test_treesitter.py +0 -0
|
@@ -206,6 +206,7 @@ def cmd_impact(filepath=None, staged=False, block=False):
|
|
|
206
206
|
return
|
|
207
207
|
|
|
208
208
|
_print_impact(G, risk, filepath, clean)
|
|
209
|
+
|
|
209
210
|
def install_hook(repo_path="."):
|
|
210
211
|
hook_dir = os.path.join(repo_path, ".git", "hooks")
|
|
211
212
|
hook_path = os.path.join(hook_dir, "pre-commit")
|
|
@@ -4,14 +4,14 @@ from watchdog.events import FileSystemEventHandler
|
|
|
4
4
|
from brain_parser.universal_parser import parse_file
|
|
5
5
|
from brain_parser.codebase_walker import walk_codebase, save_brain, load_brain
|
|
6
6
|
|
|
7
|
-
SKIP_WATCH = {'.git', '.idea', '__pycache__', 'node_modules', 'lib'}
|
|
7
|
+
SKIP_WATCH = {'.git', '.idea', '__pycache__', 'node_modules', 'lib', 'venv', 'codebase_brain.egg-info', 'temp', '.env'}
|
|
8
|
+
|
|
8
9
|
|
|
9
10
|
class BrainEventHandler(FileSystemEventHandler):
|
|
10
11
|
def __init__(self):
|
|
11
12
|
self.brain = load_brain() or {}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
def should_skip(self,path):
|
|
14
|
+
def should_skip(self, path):
|
|
15
15
|
# skip git, ide, temp files
|
|
16
16
|
if path.endswith('~'):
|
|
17
17
|
return True
|
|
@@ -25,7 +25,6 @@ class BrainEventHandler(FileSystemEventHandler):
|
|
|
25
25
|
return
|
|
26
26
|
if self.should_skip(event.src_path):
|
|
27
27
|
return
|
|
28
|
-
# skip brain output files
|
|
29
28
|
|
|
30
29
|
if event.src_path.endswith('brain.json') or event.src_path.endswith('brain_map.html'):
|
|
31
30
|
return
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from dotenv import load_dotenv
|
|
3
|
+
|
|
2
4
|
|
|
3
5
|
def get_config():
|
|
4
6
|
"""Load from local .env first, then global ~/.codebase-brain/config.env"""
|
|
5
|
-
from dotenv import load_dotenv
|
|
6
|
-
|
|
7
|
-
# try local .env first
|
|
8
7
|
local_env = os.path.join(os.getcwd(), '.env')
|
|
9
8
|
if os.path.exists(local_env):
|
|
10
|
-
load_dotenv(local_env)
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
load_dotenv(local_env, override=True)
|
|
10
|
+
|
|
11
|
+
# check if local .env actually had Brain's keys
|
|
12
|
+
has_key = os.getenv("LLM_API_KEY") or os.getenv("GROQ_API_KEY")
|
|
13
|
+
|
|
14
|
+
if not has_key:
|
|
15
|
+
# local .env didn't have Brain's config — fall back to global
|
|
13
16
|
global_config = os.path.expanduser("~/.codebase-brain/config.env")
|
|
14
17
|
if os.path.exists(global_config):
|
|
15
|
-
load_dotenv(global_config)
|
|
18
|
+
load_dotenv(global_config, override=True)
|
|
16
19
|
|
|
17
20
|
return {
|
|
18
21
|
'provider': os.getenv("LLM_PROVIDER", "groq").lower(),
|
|
@@ -37,21 +37,47 @@ def extract_frames(stacktrace):
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def match_frame_to_graph(frame_file, G):
|
|
40
|
-
"""Match
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
"""Match any path format from stacktrace to full path in graph — universal matching."""
|
|
41
|
+
|
|
42
|
+
def normalize(path):
|
|
43
|
+
p = path.replace('\\', '/').lower()
|
|
44
|
+
if len(p) > 1 and p[1] == ':':
|
|
45
|
+
p = p[2:]
|
|
46
|
+
return p.lstrip('/')
|
|
47
|
+
|
|
48
|
+
frame_norm = normalize(frame_file)
|
|
49
|
+
frame_parts = frame_norm.split('/')
|
|
43
50
|
|
|
51
|
+
candidates = []
|
|
44
52
|
for node in G.nodes():
|
|
45
|
-
node_norm = node
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
node_norm = normalize(node)
|
|
54
|
+
node_parts = node_norm.split('/')
|
|
55
|
+
|
|
56
|
+
min_len = min(len(frame_parts), len(node_parts))
|
|
57
|
+
if min_len == 0:
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
if frame_parts[-min_len:] == node_parts[-min_len:]:
|
|
61
|
+
candidates.append(node)
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
if frame_parts[-1] == node_parts[-1]:
|
|
48
65
|
candidates.append(node)
|
|
49
66
|
|
|
50
67
|
if not candidates:
|
|
51
68
|
return None
|
|
52
|
-
# prefer shortest path — most specific match
|
|
53
|
-
return min(candidates, key=len)
|
|
54
69
|
|
|
70
|
+
def match_score(node):
|
|
71
|
+
node_parts = normalize(node).split('/')
|
|
72
|
+
score = 0
|
|
73
|
+
for a, b in zip(reversed(frame_parts), reversed(node_parts)):
|
|
74
|
+
if a == b:
|
|
75
|
+
score += 1
|
|
76
|
+
else:
|
|
77
|
+
break
|
|
78
|
+
return score
|
|
79
|
+
|
|
80
|
+
return max(candidates, key=match_score)
|
|
55
81
|
|
|
56
82
|
def find_root_cause(stacktrace):
|
|
57
83
|
"""Main function — paste stacktrace, get root cause analysis."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|