codebase-digest-ai 0.1.1__py3-none-any.whl
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_digest/__init__.py +8 -0
- codebase_digest/analyzer/__init__.py +7 -0
- codebase_digest/analyzer/codebase_analyzer.py +183 -0
- codebase_digest/analyzer/flow_analyzer.py +164 -0
- codebase_digest/analyzer/metrics_analyzer.py +130 -0
- codebase_digest/cli/__init__.py +1 -0
- codebase_digest/cli/main.py +284 -0
- codebase_digest/exporters/__init__.py +9 -0
- codebase_digest/exporters/graph_exporter.py +1038 -0
- codebase_digest/exporters/html_exporter.py +1052 -0
- codebase_digest/exporters/json_exporter.py +105 -0
- codebase_digest/exporters/markdown_exporter.py +273 -0
- codebase_digest/exporters/readme_exporter.py +306 -0
- codebase_digest/models.py +81 -0
- codebase_digest/parser/__init__.py +7 -0
- codebase_digest/parser/base.py +41 -0
- codebase_digest/parser/javascript_parser.py +36 -0
- codebase_digest/parser/python_parser.py +270 -0
- codebase_digest_ai-0.1.1.dist-info/METADATA +233 -0
- codebase_digest_ai-0.1.1.dist-info/RECORD +24 -0
- codebase_digest_ai-0.1.1.dist-info/WHEEL +5 -0
- codebase_digest_ai-0.1.1.dist-info/entry_points.txt +2 -0
- codebase_digest_ai-0.1.1.dist-info/licenses/LICENSE +21 -0
- codebase_digest_ai-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Core data models for codebase analysis."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Dict, List, Optional, Set
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Symbol:
|
|
10
|
+
"""Represents a code symbol (function, class, method, etc.)."""
|
|
11
|
+
name: str
|
|
12
|
+
type: str # 'function', 'class', 'method', 'variable'
|
|
13
|
+
file_path: Path
|
|
14
|
+
line_number: int
|
|
15
|
+
docstring: Optional[str] = None
|
|
16
|
+
parameters: List[str] = field(default_factory=list)
|
|
17
|
+
return_type: Optional[str] = None
|
|
18
|
+
decorators: List[str] = field(default_factory=list)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Import:
|
|
23
|
+
"""Represents an import statement."""
|
|
24
|
+
module: str
|
|
25
|
+
names: List[str]
|
|
26
|
+
alias: Optional[str] = None
|
|
27
|
+
file_path: Optional[Path] = None
|
|
28
|
+
line_number: Optional[int] = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class CallRelation:
|
|
33
|
+
"""Represents a function/method call relationship."""
|
|
34
|
+
caller_symbol: Symbol
|
|
35
|
+
callee_name: str
|
|
36
|
+
line_number: Optional[int] = None
|
|
37
|
+
callee_file: Optional[Path] = None # For cross-file calls
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class DomainEntity:
|
|
42
|
+
"""Represents a domain entity (business object)."""
|
|
43
|
+
name: str
|
|
44
|
+
type: str # 'class', 'dataclass', 'pydantic_model', etc.
|
|
45
|
+
file_path: Path
|
|
46
|
+
fields: List[str] = field(default_factory=list)
|
|
47
|
+
methods: List[str] = field(default_factory=list)
|
|
48
|
+
creation_points: List[str] = field(default_factory=list)
|
|
49
|
+
modification_points: List[str] = field(default_factory=list)
|
|
50
|
+
validation_points: List[str] = field(default_factory=list)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class ExecutionFlow:
|
|
55
|
+
"""Represents an execution flow through the system."""
|
|
56
|
+
name: str
|
|
57
|
+
entry_point: str
|
|
58
|
+
steps: List[str] = field(default_factory=list)
|
|
59
|
+
files_involved: Set[Path] = field(default_factory=set)
|
|
60
|
+
description: Optional[str] = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class CodebaseAnalysis:
|
|
65
|
+
"""Complete analysis results for a codebase."""
|
|
66
|
+
root_path: Path
|
|
67
|
+
symbols: List[Symbol] = field(default_factory=list)
|
|
68
|
+
imports: List[Import] = field(default_factory=list)
|
|
69
|
+
call_relations: List[CallRelation] = field(default_factory=list)
|
|
70
|
+
domain_entities: List[DomainEntity] = field(default_factory=list)
|
|
71
|
+
execution_flows: List[ExecutionFlow] = field(default_factory=list)
|
|
72
|
+
entry_points: List[Path] = field(default_factory=list)
|
|
73
|
+
|
|
74
|
+
# Metrics
|
|
75
|
+
total_files: int = 0
|
|
76
|
+
total_lines: int = 0
|
|
77
|
+
languages: Set[str] = field(default_factory=set)
|
|
78
|
+
complexity_score: float = 0.0
|
|
79
|
+
|
|
80
|
+
# Directory structure
|
|
81
|
+
directory_tree: Dict = field(default_factory=dict)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Base parser interface."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
from ..models import Symbol, Import, CallRelation, DomainEntity
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseParser(ABC):
|
|
11
|
+
"""Abstract base class for language-specific parsers."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, file_path: Path):
|
|
14
|
+
self.file_path = file_path
|
|
15
|
+
self.content = file_path.read_text(encoding='utf-8')
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def parse_symbols(self) -> List[Symbol]:
|
|
19
|
+
"""Extract symbols (functions, classes, methods) from the file."""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def parse_imports(self) -> List[Import]:
|
|
24
|
+
"""Extract import statements from the file."""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def parse_calls(self) -> List[CallRelation]:
|
|
29
|
+
"""Extract function/method calls from the file."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def parse_domain_entities(self) -> List[DomainEntity]:
|
|
34
|
+
"""Extract domain entities (business objects) from the file."""
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def supported_extensions(self) -> List[str]:
|
|
40
|
+
"""Return list of file extensions this parser supports."""
|
|
41
|
+
pass
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""JavaScript/TypeScript parser using tree-sitter."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from .base import BaseParser
|
|
7
|
+
from ..models import Symbol, Import, CallRelation, DomainEntity
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class JavaScriptParser(BaseParser):
|
|
11
|
+
"""Parser for JavaScript/TypeScript files using tree-sitter."""
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def supported_extensions(self) -> List[str]:
|
|
15
|
+
return ['.js', '.jsx', '.ts', '.tsx']
|
|
16
|
+
|
|
17
|
+
def parse_symbols(self) -> List[Symbol]:
|
|
18
|
+
"""Extract JavaScript/TypeScript symbols."""
|
|
19
|
+
# TODO: Implement tree-sitter parsing
|
|
20
|
+
# For now, return empty list as placeholder
|
|
21
|
+
return []
|
|
22
|
+
|
|
23
|
+
def parse_imports(self) -> List[Import]:
|
|
24
|
+
"""Extract import statements."""
|
|
25
|
+
# TODO: Implement tree-sitter parsing
|
|
26
|
+
return []
|
|
27
|
+
|
|
28
|
+
def parse_calls(self) -> List[CallRelation]:
|
|
29
|
+
"""Extract function calls."""
|
|
30
|
+
# TODO: Implement tree-sitter parsing
|
|
31
|
+
return []
|
|
32
|
+
|
|
33
|
+
def parse_domain_entities(self) -> List[DomainEntity]:
|
|
34
|
+
"""Extract domain entities."""
|
|
35
|
+
# TODO: Implement tree-sitter parsing
|
|
36
|
+
return []
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""Python AST-based parser."""
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
|
|
7
|
+
from .base import BaseParser
|
|
8
|
+
from ..models import Symbol, Import, CallRelation, DomainEntity
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PythonParser(BaseParser):
|
|
12
|
+
"""Parser for Python files using AST."""
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def supported_extensions(self) -> List[str]:
|
|
16
|
+
return ['.py']
|
|
17
|
+
|
|
18
|
+
def parse_symbols(self) -> List[Symbol]:
|
|
19
|
+
"""Extract Python symbols using AST."""
|
|
20
|
+
symbols = []
|
|
21
|
+
try:
|
|
22
|
+
tree = ast.parse(self.content)
|
|
23
|
+
|
|
24
|
+
for node in ast.walk(tree):
|
|
25
|
+
if isinstance(node, ast.FunctionDef):
|
|
26
|
+
symbols.append(self._create_function_symbol(node))
|
|
27
|
+
elif isinstance(node, ast.ClassDef):
|
|
28
|
+
symbols.append(self._create_class_symbol(node))
|
|
29
|
+
# Add methods
|
|
30
|
+
for item in node.body:
|
|
31
|
+
if isinstance(item, ast.FunctionDef):
|
|
32
|
+
symbols.append(self._create_method_symbol(item, node.name))
|
|
33
|
+
|
|
34
|
+
except SyntaxError:
|
|
35
|
+
# Skip files with syntax errors
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
return symbols
|
|
39
|
+
|
|
40
|
+
def parse_imports(self) -> List[Import]:
|
|
41
|
+
"""Extract import statements."""
|
|
42
|
+
imports = []
|
|
43
|
+
try:
|
|
44
|
+
tree = ast.parse(self.content)
|
|
45
|
+
|
|
46
|
+
for node in ast.walk(tree):
|
|
47
|
+
if isinstance(node, ast.Import):
|
|
48
|
+
for alias in node.names:
|
|
49
|
+
imports.append(Import(
|
|
50
|
+
module=alias.name,
|
|
51
|
+
names=[alias.name],
|
|
52
|
+
alias=alias.asname,
|
|
53
|
+
file_path=self.file_path,
|
|
54
|
+
line_number=node.lineno
|
|
55
|
+
))
|
|
56
|
+
elif isinstance(node, ast.ImportFrom):
|
|
57
|
+
if node.module:
|
|
58
|
+
names = [alias.name for alias in node.names]
|
|
59
|
+
imports.append(Import(
|
|
60
|
+
module=node.module,
|
|
61
|
+
names=names,
|
|
62
|
+
file_path=self.file_path,
|
|
63
|
+
line_number=node.lineno
|
|
64
|
+
))
|
|
65
|
+
|
|
66
|
+
except SyntaxError:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
return imports
|
|
70
|
+
|
|
71
|
+
def parse_calls(self) -> List[CallRelation]:
|
|
72
|
+
"""Extract function calls with symbol context."""
|
|
73
|
+
calls = []
|
|
74
|
+
try:
|
|
75
|
+
tree = ast.parse(self.content)
|
|
76
|
+
|
|
77
|
+
# First pass: collect all symbols for context
|
|
78
|
+
symbols_by_name = {}
|
|
79
|
+
for node in ast.walk(tree):
|
|
80
|
+
if isinstance(node, ast.FunctionDef):
|
|
81
|
+
symbol = self._create_function_symbol(node)
|
|
82
|
+
symbols_by_name[node.name] = symbol
|
|
83
|
+
elif isinstance(node, ast.ClassDef):
|
|
84
|
+
class_symbol = self._create_class_symbol(node)
|
|
85
|
+
symbols_by_name[node.name] = class_symbol
|
|
86
|
+
# Add methods
|
|
87
|
+
for item in node.body:
|
|
88
|
+
if isinstance(item, ast.FunctionDef):
|
|
89
|
+
method_symbol = self._create_method_symbol(item, node.name)
|
|
90
|
+
symbols_by_name[f"{node.name}.{item.name}"] = method_symbol
|
|
91
|
+
|
|
92
|
+
# Second pass: extract calls with symbol context
|
|
93
|
+
for node in ast.walk(tree):
|
|
94
|
+
if isinstance(node, ast.FunctionDef):
|
|
95
|
+
caller_symbol = symbols_by_name.get(node.name)
|
|
96
|
+
if caller_symbol:
|
|
97
|
+
# Find calls within this function
|
|
98
|
+
for child in ast.walk(node):
|
|
99
|
+
if isinstance(child, ast.Call):
|
|
100
|
+
callee_name = self._extract_call_name(child)
|
|
101
|
+
if callee_name:
|
|
102
|
+
calls.append(CallRelation(
|
|
103
|
+
caller_symbol=caller_symbol,
|
|
104
|
+
callee_name=callee_name,
|
|
105
|
+
line_number=child.lineno
|
|
106
|
+
))
|
|
107
|
+
|
|
108
|
+
elif isinstance(node, ast.ClassDef):
|
|
109
|
+
# Handle method calls
|
|
110
|
+
for item in node.body:
|
|
111
|
+
if isinstance(item, ast.FunctionDef):
|
|
112
|
+
method_key = f"{node.name}.{item.name}"
|
|
113
|
+
caller_symbol = symbols_by_name.get(method_key)
|
|
114
|
+
if caller_symbol:
|
|
115
|
+
# Find calls within this method
|
|
116
|
+
for child in ast.walk(item):
|
|
117
|
+
if isinstance(child, ast.Call):
|
|
118
|
+
callee_name = self._extract_call_name(child)
|
|
119
|
+
if callee_name:
|
|
120
|
+
calls.append(CallRelation(
|
|
121
|
+
caller_symbol=caller_symbol,
|
|
122
|
+
callee_name=callee_name,
|
|
123
|
+
line_number=child.lineno
|
|
124
|
+
))
|
|
125
|
+
|
|
126
|
+
except SyntaxError:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
return calls
|
|
130
|
+
|
|
131
|
+
def _extract_call_name(self, call_node: ast.Call) -> Optional[str]:
|
|
132
|
+
"""Extract the called function/method name from a Call node."""
|
|
133
|
+
if isinstance(call_node.func, ast.Name):
|
|
134
|
+
call_name = call_node.func.id
|
|
135
|
+
# Filter out builtin functions
|
|
136
|
+
if call_name in {'print', 'len', 'str', 'int', 'float', 'bool', 'list', 'dict', 'set', 'tuple', 'range', 'enumerate', 'zip', 'map', 'filter', 'sorted', 'reversed', 'sum', 'min', 'max', 'abs', 'round', 'type', 'isinstance', 'hasattr', 'getattr', 'setattr', 'delattr'}:
|
|
137
|
+
return None
|
|
138
|
+
return call_name
|
|
139
|
+
elif isinstance(call_node.func, ast.Attribute):
|
|
140
|
+
attr_name = self._get_attribute_name_from_node(call_node.func)
|
|
141
|
+
# Filter out common builtin method patterns
|
|
142
|
+
if attr_name and any(pattern in attr_name.lower() for pattern in ['append', 'extend', 'pop', 'remove', 'insert', 'sort', 'reverse', 'datetime.now', 'time.time']):
|
|
143
|
+
return None
|
|
144
|
+
return attr_name
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
def parse_domain_entities(self) -> List[DomainEntity]:
|
|
148
|
+
"""Extract domain entities (classes that represent business objects)."""
|
|
149
|
+
entities = []
|
|
150
|
+
try:
|
|
151
|
+
tree = ast.parse(self.content)
|
|
152
|
+
|
|
153
|
+
for node in ast.walk(tree):
|
|
154
|
+
if isinstance(node, ast.ClassDef):
|
|
155
|
+
# Look for common domain entity patterns
|
|
156
|
+
if self._is_domain_entity(node):
|
|
157
|
+
entity = DomainEntity(
|
|
158
|
+
name=node.name,
|
|
159
|
+
type='class',
|
|
160
|
+
file_path=self.file_path,
|
|
161
|
+
fields=self._extract_class_fields(node),
|
|
162
|
+
methods=self._extract_class_methods(node)
|
|
163
|
+
)
|
|
164
|
+
entities.append(entity)
|
|
165
|
+
|
|
166
|
+
except SyntaxError:
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
return entities
|
|
170
|
+
|
|
171
|
+
def _create_function_symbol(self, node: ast.FunctionDef) -> Symbol:
|
|
172
|
+
"""Create a Symbol from a function AST node."""
|
|
173
|
+
return Symbol(
|
|
174
|
+
name=node.name,
|
|
175
|
+
type='function',
|
|
176
|
+
file_path=self.file_path,
|
|
177
|
+
line_number=node.lineno,
|
|
178
|
+
docstring=ast.get_docstring(node),
|
|
179
|
+
parameters=[arg.arg for arg in node.args.args],
|
|
180
|
+
decorators=[self._get_decorator_name(dec) for dec in node.decorator_list]
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def _create_class_symbol(self, node: ast.ClassDef) -> Symbol:
|
|
184
|
+
"""Create a Symbol from a class AST node."""
|
|
185
|
+
return Symbol(
|
|
186
|
+
name=node.name,
|
|
187
|
+
type='class',
|
|
188
|
+
file_path=self.file_path,
|
|
189
|
+
line_number=node.lineno,
|
|
190
|
+
docstring=ast.get_docstring(node),
|
|
191
|
+
decorators=[self._get_decorator_name(dec) for dec in node.decorator_list]
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
def _create_method_symbol(self, node: ast.FunctionDef, class_name: str) -> Symbol:
|
|
195
|
+
"""Create a Symbol from a method AST node."""
|
|
196
|
+
return Symbol(
|
|
197
|
+
name=f"{class_name}.{node.name}",
|
|
198
|
+
type='method',
|
|
199
|
+
file_path=self.file_path,
|
|
200
|
+
line_number=node.lineno,
|
|
201
|
+
docstring=ast.get_docstring(node),
|
|
202
|
+
parameters=[arg.arg for arg in node.args.args],
|
|
203
|
+
decorators=[self._get_decorator_name(dec) for dec in node.decorator_list]
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def _get_decorator_name(self, decorator) -> str:
|
|
207
|
+
"""Extract decorator name from AST node."""
|
|
208
|
+
if isinstance(decorator, ast.Name):
|
|
209
|
+
return decorator.id
|
|
210
|
+
elif isinstance(decorator, ast.Attribute):
|
|
211
|
+
return self._get_attribute_name_from_node(decorator) or ""
|
|
212
|
+
return ""
|
|
213
|
+
|
|
214
|
+
def _get_attribute_name_from_node(self, node: ast.Attribute) -> Optional[str]:
|
|
215
|
+
"""Get full attribute name (e.g., 'obj.method') from AST node."""
|
|
216
|
+
if isinstance(node.value, ast.Name):
|
|
217
|
+
return f"{node.value.id}.{node.attr}"
|
|
218
|
+
elif isinstance(node.value, ast.Attribute):
|
|
219
|
+
base = self._get_attribute_name_from_node(node.value)
|
|
220
|
+
return f"{base}.{node.attr}" if base else None
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
def _is_domain_entity(self, node: ast.ClassDef) -> bool:
|
|
224
|
+
"""Determine if a class represents a domain entity."""
|
|
225
|
+
# Simple heuristics for domain entities
|
|
226
|
+
class_name = node.name.lower()
|
|
227
|
+
|
|
228
|
+
# Common domain entity names
|
|
229
|
+
domain_keywords = [
|
|
230
|
+
'user', 'account', 'profile', 'customer', 'client',
|
|
231
|
+
'order', 'payment', 'transaction', 'invoice', 'billing',
|
|
232
|
+
'product', 'item', 'catalog', 'inventory',
|
|
233
|
+
'wallet', 'balance', 'credit', 'debit',
|
|
234
|
+
'session', 'token', 'auth', 'permission',
|
|
235
|
+
'notification', 'message', 'email', 'sms',
|
|
236
|
+
'address', 'location', 'contact', 'phone'
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
# Check if class name contains domain keywords
|
|
240
|
+
for keyword in domain_keywords:
|
|
241
|
+
if keyword in class_name:
|
|
242
|
+
return True
|
|
243
|
+
|
|
244
|
+
# Check for dataclass or pydantic model decorators
|
|
245
|
+
for decorator in node.decorator_list:
|
|
246
|
+
if isinstance(decorator, ast.Name):
|
|
247
|
+
if decorator.id in ['dataclass', 'BaseModel']:
|
|
248
|
+
return True
|
|
249
|
+
|
|
250
|
+
return False
|
|
251
|
+
|
|
252
|
+
def _extract_class_fields(self, node: ast.ClassDef) -> List[str]:
|
|
253
|
+
"""Extract field names from a class."""
|
|
254
|
+
fields = []
|
|
255
|
+
for item in node.body:
|
|
256
|
+
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
|
|
257
|
+
fields.append(item.target.id)
|
|
258
|
+
elif isinstance(item, ast.Assign):
|
|
259
|
+
for target in item.targets:
|
|
260
|
+
if isinstance(target, ast.Name):
|
|
261
|
+
fields.append(target.id)
|
|
262
|
+
return fields
|
|
263
|
+
|
|
264
|
+
def _extract_class_methods(self, node: ast.ClassDef) -> List[str]:
|
|
265
|
+
"""Extract method names from a class."""
|
|
266
|
+
methods = []
|
|
267
|
+
for item in node.body:
|
|
268
|
+
if isinstance(item, ast.FunctionDef):
|
|
269
|
+
methods.append(item.name)
|
|
270
|
+
return methods
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codebase-digest-ai
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: AI-native code intelligence engine for semantic codebase analysis
|
|
5
|
+
Author: Harsh Bothara
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/codebase-digest/codebase-digest
|
|
8
|
+
Project-URL: Documentation, https://github.com/codebase-digest/codebase-digest#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/codebase-digest/codebase-digest
|
|
10
|
+
Project-URL: Issues, https://github.com/codebase-digest/codebase-digest/issues
|
|
11
|
+
Keywords: code-analysis,ast,static-analysis,documentation,ai,developer-tools
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: typer>=0.9.0
|
|
26
|
+
Requires-Dist: networkx>=3.0
|
|
27
|
+
Requires-Dist: rich>=13.0.0
|
|
28
|
+
Requires-Dist: jinja2>=3.1.0
|
|
29
|
+
Requires-Dist: pathspec>=0.11.0
|
|
30
|
+
Requires-Dist: pyvis>=0.3.2
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: build>=0.10.0; extra == "dev"
|
|
37
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# codebase-digest
|
|
41
|
+
|
|
42
|
+
π **AI-Native Code Intelligence Engine**
|
|
43
|
+
|
|
44
|
+
Transform any codebase into semantic architectural understanding, execution flows, and human-readable engineering reports.
|
|
45
|
+
|
|
46
|
+
## π§± What It Does
|
|
47
|
+
|
|
48
|
+
This is NOT a repo summarizer. This is a code intelligence engine that explains:
|
|
49
|
+
- **What this system does** - Infers project purpose from domain entities
|
|
50
|
+
- **How data flows** - Maps execution paths and call relationships
|
|
51
|
+
- **Where logic lives** - Identifies core components and their responsibilities
|
|
52
|
+
- **What domains exist** - Detects business entities (User, Payment, Wallet, etc.)
|
|
53
|
+
- **What files matter** - Highlights entry points and key modules
|
|
54
|
+
|
|
55
|
+
## β¨ Features
|
|
56
|
+
|
|
57
|
+
- **π Semantic Analysis**: Extract functions, classes, methods, and imports with full context
|
|
58
|
+
- **π Interactive Call Graphs**: Visualize function relationships and execution flows
|
|
59
|
+
- **ποΈ Domain Entity Detection**: Automatically identify core business objects
|
|
60
|
+
- **π Execution Flow Mapping**: Trace request paths through the system
|
|
61
|
+
- **π Project README Generation**: Auto-generate documentation for new developers
|
|
62
|
+
- **π Multi-format Output**: HTML dashboards + Markdown reports + JSON data + Interactive graphs
|
|
63
|
+
|
|
64
|
+
## π Quick Start
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Install
|
|
68
|
+
pip install codebase-digest
|
|
69
|
+
|
|
70
|
+
# Analyze current directory
|
|
71
|
+
codebase-digest build
|
|
72
|
+
|
|
73
|
+
# Analyze specific directory
|
|
74
|
+
codebase-digest build /path/to/project
|
|
75
|
+
|
|
76
|
+
# Generate with interactive call graph
|
|
77
|
+
codebase-digest build --graph
|
|
78
|
+
|
|
79
|
+
# Quick stats
|
|
80
|
+
codebase-digest stats
|
|
81
|
+
|
|
82
|
+
# Search for patterns
|
|
83
|
+
codebase-digest query "wallet"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## π Output Structure
|
|
87
|
+
|
|
88
|
+
Generates `.digest/` directory with comprehensive analysis:
|
|
89
|
+
```
|
|
90
|
+
.digest/
|
|
91
|
+
βββ README.md # Project documentation for developers
|
|
92
|
+
βββ callgraph.html # Interactive call graph visualization
|
|
93
|
+
βββ report.html # Comprehensive HTML dashboard
|
|
94
|
+
βββ architecture.md # Technical architecture breakdown
|
|
95
|
+
βββ flows.md # Execution flow documentation
|
|
96
|
+
βββ ai-context.md # AI-optimized context file
|
|
97
|
+
βββ entities.json # Structured analysis data
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## π Example Output
|
|
101
|
+
|
|
102
|
+
For a Python financial services project:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
π Codebase Statistics
|
|
106
|
+
ββββββββββββββββββββ³βββββββββ
|
|
107
|
+
β Total Files β 4 β
|
|
108
|
+
β Lines of Code β 189 β
|
|
109
|
+
β Languages β Python β
|
|
110
|
+
β Functions β 24 β
|
|
111
|
+
β Classes β 8 β
|
|
112
|
+
β Domain Entities β 7 β
|
|
113
|
+
β Execution Flows β 4 β
|
|
114
|
+
β Complexity Score β 1.8 β
|
|
115
|
+
ββββββββββββββββββββ»βββββββββ
|
|
116
|
+
|
|
117
|
+
Graph Stats: 29 nodes, 27 edges, 7 components
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Generated README.md excerpt:**
|
|
121
|
+
```markdown
|
|
122
|
+
# Project Overview
|
|
123
|
+
|
|
124
|
+
This is a financial services application that provides user management,
|
|
125
|
+
payment processing, and digital wallet functionality. The system is built
|
|
126
|
+
with a service-oriented architecture using Python dataclasses for domain
|
|
127
|
+
modeling and separate service layers for business logic.
|
|
128
|
+
|
|
129
|
+
## Architecture
|
|
130
|
+
|
|
131
|
+
The application follows a layered architecture with clear separation of concerns:
|
|
132
|
+
- **Domain Layer**: Contains core business entities (User, Payment, Wallet)
|
|
133
|
+
- **Service Layer**: Implements business logic (UserService, PaymentService)
|
|
134
|
+
- **Application Layer**: Handles bootstrapping and orchestration
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## π‘ Commands
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# Full analysis with all outputs
|
|
141
|
+
codebase-digest build [PATH]
|
|
142
|
+
|
|
143
|
+
# Specific formats
|
|
144
|
+
codebase-digest build --format html # HTML dashboard only
|
|
145
|
+
codebase-digest build --format markdown # Markdown reports only
|
|
146
|
+
codebase-digest build --format json # JSON data only
|
|
147
|
+
|
|
148
|
+
# Interactive call graph with depth filtering
|
|
149
|
+
codebase-digest build --graph --graph-depth 3
|
|
150
|
+
|
|
151
|
+
# Quick metrics and search
|
|
152
|
+
codebase-digest stats [PATH] # Project statistics
|
|
153
|
+
codebase-digest query "search term" [PATH] # Search patterns
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## π― Key Features
|
|
157
|
+
|
|
158
|
+
### πΈοΈ Interactive Call Graph
|
|
159
|
+
- **Probabilistic entrypoint detection** - Finds real execution starting points
|
|
160
|
+
- **Noise filtering** - Removes builtin calls and isolated nodes
|
|
161
|
+
- **Depth filtering** - Focus on core execution spine
|
|
162
|
+
- **Professional UI** - GitHub/Linear/Notion inspired design
|
|
163
|
+
|
|
164
|
+
### π Smart README Generation
|
|
165
|
+
- **Project type inference** - Detects financial, e-commerce, CMS patterns
|
|
166
|
+
- **Architecture analysis** - Service-oriented vs modular detection
|
|
167
|
+
- **Run instructions** - Inferred from entry points
|
|
168
|
+
- **Future improvements** - Realistic enhancement suggestions
|
|
169
|
+
|
|
170
|
+
### π Semantic Understanding
|
|
171
|
+
- **Symbol-aware analysis** - True function-level relationships
|
|
172
|
+
- **Domain entity detection** - Business object identification
|
|
173
|
+
- **Execution flow mapping** - Startup and runtime sequences
|
|
174
|
+
- **Cross-file analysis** - Import and dependency tracking
|
|
175
|
+
|
|
176
|
+
## π οΈ Tech Stack
|
|
177
|
+
|
|
178
|
+
- **Python 3.10+** - Core language
|
|
179
|
+
- **AST parsing** - Deep Python code analysis
|
|
180
|
+
- **NetworkX** - Call graph analysis and visualization
|
|
181
|
+
- **vis.js** - Interactive graph rendering
|
|
182
|
+
- **Typer** - CLI interface
|
|
183
|
+
- **Rich** - Beautiful terminal output
|
|
184
|
+
|
|
185
|
+
## π Supported Languages
|
|
186
|
+
|
|
187
|
+
- β
**Python** - Full AST analysis with call graphs
|
|
188
|
+
- π§ **JavaScript/TypeScript** - Parser implemented, integration in progress
|
|
189
|
+
- π§ **Java** - Planned
|
|
190
|
+
- π§ **Go** - Planned
|
|
191
|
+
|
|
192
|
+
## π― Use Cases
|
|
193
|
+
|
|
194
|
+
- **New Developer Onboarding** - Understand unfamiliar codebases quickly
|
|
195
|
+
- **Code Reviews** - Architectural overview and impact analysis
|
|
196
|
+
- **Documentation Generation** - Auto-generate project documentation
|
|
197
|
+
- **Refactoring Planning** - Identify core components and dependencies
|
|
198
|
+
- **AI-Assisted Development** - Provide context for LLM code assistance
|
|
199
|
+
|
|
200
|
+
## π§ Development
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Install development dependencies
|
|
204
|
+
pip install -e ".[dev]"
|
|
205
|
+
|
|
206
|
+
# Run tests
|
|
207
|
+
pytest
|
|
208
|
+
|
|
209
|
+
# Format code
|
|
210
|
+
black .
|
|
211
|
+
isort .
|
|
212
|
+
|
|
213
|
+
# Type checking
|
|
214
|
+
mypy codebase_digest/
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## π€ Contributing
|
|
218
|
+
|
|
219
|
+
1. Fork the repository
|
|
220
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
221
|
+
3. Make your changes
|
|
222
|
+
4. Add tests if applicable
|
|
223
|
+
5. Submit a pull request
|
|
224
|
+
|
|
225
|
+
## π License
|
|
226
|
+
|
|
227
|
+
MIT License - see LICENSE file for details.
|
|
228
|
+
|
|
229
|
+
## π Acknowledgments
|
|
230
|
+
|
|
231
|
+
- Built with modern Python tooling and best practices
|
|
232
|
+
- Inspired by professional developer tools (JetBrains, Sourcegraph)
|
|
233
|
+
- Designed for AI-native development workflows
|