maris 0.1.0__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.
- maris/__init__.py +20 -0
- maris/agents/__init__.py +17 -0
- maris/agents/documentation_agent.py +545 -0
- maris/agents/git_agent.py +404 -0
- maris/agents/impact_analysis_agent.py +592 -0
- maris/agents/indexing_agent.py +760 -0
- maris/agents/orchestrator_agent.py +640 -0
- maris/agents/qa_agent.py +468 -0
- maris/cli/__init__.py +7 -0
- maris/cli/main.py +870 -0
- maris/config/__init__.py +7 -0
- maris/config/settings.py +214 -0
- maris/core/__init__.py +23 -0
- maris/core/models.py +360 -0
- maris/embeddings/__init__.py +7 -0
- maris/embeddings/ollama_embeddings.py +185 -0
- maris/indexing/__init__.py +11 -0
- maris/indexing/java_parser.py +547 -0
- maris/indexing/parser.py +203 -0
- maris/indexing/parser_factory.py +260 -0
- maris/indexing/python_parser.py +438 -0
- maris/indexing/scala_parser.py +539 -0
- maris/knowledge/__init__.py +8 -0
- maris/knowledge/repository_knowledge_impl.py +260 -0
- maris/knowledge/service.py +239 -0
- maris/storage/__init__.py +8 -0
- maris/storage/metadata_store.py +774 -0
- maris/storage/vector_store.py +278 -0
- maris/utils/__init__.py +7 -0
- maris/utils/validation.py +283 -0
- maris-0.1.0.dist-info/METADATA +686 -0
- maris-0.1.0.dist-info/RECORD +36 -0
- maris-0.1.0.dist-info/WHEEL +5 -0
- maris-0.1.0.dist-info/entry_points.txt +2 -0
- maris-0.1.0.dist-info/licenses/LICENSE +21 -0
- maris-0.1.0.dist-info/top_level.txt +1 -0
maris/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MARIS - Multi-Agent Repository Intelligence System
|
|
3
|
+
|
|
4
|
+
A local-first repository intelligence platform for understanding,
|
|
5
|
+
navigating, and analyzing source code.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
|
|
10
|
+
from maris.core.models import Symbol, SymbolType, RetrievalContext, Commit
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"__version__",
|
|
14
|
+
"Symbol",
|
|
15
|
+
"SymbolType",
|
|
16
|
+
"RetrievalContext",
|
|
17
|
+
"Commit",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
# Made with Bob
|
maris/agents/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Specialized agents for repository intelligence."""
|
|
2
|
+
|
|
3
|
+
from maris.agents.documentation_agent import DocumentationAgent
|
|
4
|
+
from maris.agents.git_agent import GitAgent
|
|
5
|
+
from maris.agents.impact_analysis_agent import ImpactAnalysisAgent
|
|
6
|
+
from maris.agents.indexing_agent import IndexingAgent
|
|
7
|
+
from maris.agents.qa_agent import QAAgent
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"IndexingAgent",
|
|
11
|
+
"DocumentationAgent",
|
|
12
|
+
"QAAgent",
|
|
13
|
+
"GitAgent",
|
|
14
|
+
"ImpactAnalysisAgent",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
# Made with Bob
|
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
"""Documentation Agent - LangGraph-based implementation for generating repository documentation."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any, Dict, List, Optional, Set
|
|
6
|
+
|
|
7
|
+
from langgraph.graph import StateGraph, END
|
|
8
|
+
|
|
9
|
+
from maris.core.models import Symbol, SymbolType
|
|
10
|
+
from maris.knowledge.service import RepositoryKnowledgeService
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class ModuleDocumentation:
|
|
17
|
+
"""Documentation for a single module/file."""
|
|
18
|
+
|
|
19
|
+
file_path: str
|
|
20
|
+
language: str
|
|
21
|
+
summary: str
|
|
22
|
+
classes: List[Dict[str, str]]
|
|
23
|
+
functions: List[Dict[str, str]]
|
|
24
|
+
constants: List[Dict[str, str]]
|
|
25
|
+
dependencies: List[str]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class ArchitectureOverview:
|
|
30
|
+
"""High-level architecture documentation."""
|
|
31
|
+
|
|
32
|
+
total_files: int
|
|
33
|
+
total_symbols: int
|
|
34
|
+
languages: List[str]
|
|
35
|
+
key_modules: List[str]
|
|
36
|
+
dependency_graph_summary: str
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class DocumentationAgent:
|
|
40
|
+
"""
|
|
41
|
+
LangGraph-based Agent for generating repository documentation.
|
|
42
|
+
|
|
43
|
+
Uses a workflow with explicit state management:
|
|
44
|
+
1. retrieve_symbols: Get symbols from the file
|
|
45
|
+
2. categorize_symbols: Organize symbols by type
|
|
46
|
+
3. find_dependencies: Find file dependencies
|
|
47
|
+
4. generate_summary: Create file summary
|
|
48
|
+
5. format_output: Format as ModuleDocumentation or Markdown
|
|
49
|
+
|
|
50
|
+
Capabilities:
|
|
51
|
+
- Generate module-level documentation
|
|
52
|
+
- Create architecture overviews
|
|
53
|
+
- Document component relationships
|
|
54
|
+
- Generate dependency diagrams (text-based)
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, knowledge_service: RepositoryKnowledgeService):
|
|
58
|
+
"""
|
|
59
|
+
Initialize the documentation agent.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
knowledge_service: Repository knowledge service for data access
|
|
63
|
+
"""
|
|
64
|
+
self.knowledge_service = knowledge_service
|
|
65
|
+
|
|
66
|
+
# Build the LangGraph workflow
|
|
67
|
+
self.graph = self._build_graph()
|
|
68
|
+
|
|
69
|
+
logger.info("Initialized DocumentationAgent with LangGraph")
|
|
70
|
+
|
|
71
|
+
def _build_graph(self) -> Any:
|
|
72
|
+
"""Build the LangGraph workflow for documentation generation."""
|
|
73
|
+
|
|
74
|
+
# Define state schema
|
|
75
|
+
class State(Dict[str, Any]):
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
workflow = StateGraph(State)
|
|
79
|
+
|
|
80
|
+
# Add nodes
|
|
81
|
+
workflow.add_node("retrieve_symbols", self._retrieve_symbols)
|
|
82
|
+
workflow.add_node("categorize_symbols", self._categorize_symbols)
|
|
83
|
+
workflow.add_node("find_dependencies", self._find_dependencies)
|
|
84
|
+
workflow.add_node("generate_summary", self._generate_summary)
|
|
85
|
+
workflow.add_node("format_output", self._format_output)
|
|
86
|
+
|
|
87
|
+
# Define edges
|
|
88
|
+
workflow.set_entry_point("retrieve_symbols")
|
|
89
|
+
workflow.add_edge("retrieve_symbols", "categorize_symbols")
|
|
90
|
+
workflow.add_edge("categorize_symbols", "find_dependencies")
|
|
91
|
+
workflow.add_edge("find_dependencies", "generate_summary")
|
|
92
|
+
workflow.add_edge("generate_summary", "format_output")
|
|
93
|
+
workflow.add_edge("format_output", END)
|
|
94
|
+
|
|
95
|
+
return workflow.compile()
|
|
96
|
+
|
|
97
|
+
def _retrieve_symbols(self, state: Dict[str, Any]) -> Dict[str, Any]:
|
|
98
|
+
"""
|
|
99
|
+
Node: Retrieve symbols from the file.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
state: Current workflow state
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Updated state with symbols
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
file_path = state.get("file_path", "")
|
|
109
|
+
if not file_path:
|
|
110
|
+
raise ValueError("file_path is required")
|
|
111
|
+
logger.info(f"Retrieving symbols for: {file_path}")
|
|
112
|
+
|
|
113
|
+
symbols = self.knowledge_service.find_symbols_in_file(file_path)
|
|
114
|
+
|
|
115
|
+
state["symbols"] = symbols
|
|
116
|
+
state["language"] = symbols[0].language if symbols else "unknown"
|
|
117
|
+
|
|
118
|
+
logger.info(f"Retrieved {len(symbols)} symbols")
|
|
119
|
+
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.error(f"Error retrieving symbols: {e}")
|
|
122
|
+
state["error"] = f"Failed to retrieve symbols: {str(e)}"
|
|
123
|
+
state["symbols"] = []
|
|
124
|
+
state["language"] = "unknown"
|
|
125
|
+
|
|
126
|
+
return state
|
|
127
|
+
|
|
128
|
+
def _categorize_symbols(self, state: Dict[str, Any]) -> Dict[str, Any]:
|
|
129
|
+
"""
|
|
130
|
+
Node: Categorize symbols by type.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
state: Current workflow state
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Updated state with categorized symbols
|
|
137
|
+
"""
|
|
138
|
+
if state.get("error"):
|
|
139
|
+
return state
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
logger.info("Categorizing symbols")
|
|
143
|
+
|
|
144
|
+
symbols = state.get("symbols", [])
|
|
145
|
+
classes = []
|
|
146
|
+
functions = []
|
|
147
|
+
constants = []
|
|
148
|
+
|
|
149
|
+
for symbol in symbols:
|
|
150
|
+
doc_entry = {
|
|
151
|
+
"name": symbol.name,
|
|
152
|
+
"signature": symbol.signature or "",
|
|
153
|
+
"docstring": symbol.docstring or "No documentation available.",
|
|
154
|
+
"line": symbol.start_line,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if symbol.type == SymbolType.CLASS:
|
|
158
|
+
# Find methods of this class
|
|
159
|
+
methods = [s for s in symbols if s.parent_id == symbol.id]
|
|
160
|
+
doc_entry["methods"] = [m.name for m in methods]
|
|
161
|
+
classes.append(doc_entry)
|
|
162
|
+
elif symbol.type == SymbolType.FUNCTION and not symbol.parent_id:
|
|
163
|
+
functions.append(doc_entry)
|
|
164
|
+
elif symbol.type == SymbolType.CONSTANT:
|
|
165
|
+
constants.append(doc_entry)
|
|
166
|
+
|
|
167
|
+
state["classes"] = classes
|
|
168
|
+
state["functions"] = functions
|
|
169
|
+
state["constants"] = constants
|
|
170
|
+
|
|
171
|
+
logger.info(
|
|
172
|
+
f"Categorized: {len(classes)} classes, {len(functions)} functions, {len(constants)} constants"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
logger.error(f"Error categorizing symbols: {e}")
|
|
177
|
+
state["error"] = f"Failed to categorize symbols: {str(e)}"
|
|
178
|
+
state["classes"] = []
|
|
179
|
+
state["functions"] = []
|
|
180
|
+
state["constants"] = []
|
|
181
|
+
|
|
182
|
+
return state
|
|
183
|
+
|
|
184
|
+
def _find_dependencies(self, state: Dict[str, Any]) -> Dict[str, Any]:
|
|
185
|
+
"""
|
|
186
|
+
Node: Find file dependencies.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
state: Current workflow state
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Updated state with dependencies
|
|
193
|
+
"""
|
|
194
|
+
if state.get("error"):
|
|
195
|
+
return state
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
logger.info("Finding dependencies")
|
|
199
|
+
|
|
200
|
+
symbols = state.get("symbols", [])
|
|
201
|
+
dependencies = set()
|
|
202
|
+
|
|
203
|
+
for symbol in symbols:
|
|
204
|
+
# Find callees (symbols this symbol calls)
|
|
205
|
+
callees = self.knowledge_service.find_callees(symbol)
|
|
206
|
+
|
|
207
|
+
for callee in callees:
|
|
208
|
+
if callee.file_path != symbol.file_path:
|
|
209
|
+
dependencies.add(callee.file_path)
|
|
210
|
+
|
|
211
|
+
state["dependencies"] = sorted(list(dependencies))
|
|
212
|
+
|
|
213
|
+
logger.info(f"Found {len(dependencies)} dependencies")
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
logger.error(f"Error finding dependencies: {e}")
|
|
217
|
+
# Don't fail the workflow for dependency errors
|
|
218
|
+
state["dependencies"] = []
|
|
219
|
+
state["dependency_error"] = str(e)
|
|
220
|
+
|
|
221
|
+
return state
|
|
222
|
+
|
|
223
|
+
def _generate_summary(self, state: Dict[str, Any]) -> Dict[str, Any]:
|
|
224
|
+
"""
|
|
225
|
+
Node: Generate file summary.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
state: Current workflow state
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Updated state with summary
|
|
232
|
+
"""
|
|
233
|
+
if state.get("error"):
|
|
234
|
+
return state
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
logger.info("Generating summary")
|
|
238
|
+
|
|
239
|
+
classes = state.get("classes", [])
|
|
240
|
+
functions = state.get("functions", [])
|
|
241
|
+
constants = state.get("constants", [])
|
|
242
|
+
|
|
243
|
+
parts = []
|
|
244
|
+
if classes:
|
|
245
|
+
parts.append(f"{len(classes)} class{'es' if len(classes) != 1 else ''}")
|
|
246
|
+
if functions:
|
|
247
|
+
parts.append(f"{len(functions)} function{'s' if len(functions) != 1 else ''}")
|
|
248
|
+
if constants:
|
|
249
|
+
parts.append(f"{len(constants)} constant{'s' if len(constants) != 1 else ''}")
|
|
250
|
+
|
|
251
|
+
if not parts:
|
|
252
|
+
summary = "This file contains no documented symbols."
|
|
253
|
+
else:
|
|
254
|
+
summary = f"This module defines {', '.join(parts)}."
|
|
255
|
+
|
|
256
|
+
state["summary"] = summary
|
|
257
|
+
|
|
258
|
+
logger.info("Summary generated")
|
|
259
|
+
|
|
260
|
+
except Exception as e:
|
|
261
|
+
logger.error(f"Error generating summary: {e}")
|
|
262
|
+
state["summary"] = "Error generating summary."
|
|
263
|
+
|
|
264
|
+
return state
|
|
265
|
+
|
|
266
|
+
def _format_output(self, state: Dict[str, Any]) -> Dict[str, Any]:
|
|
267
|
+
"""
|
|
268
|
+
Node: Format output as ModuleDocumentation or Markdown.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
state: Current workflow state
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Updated state with formatted output
|
|
275
|
+
"""
|
|
276
|
+
try:
|
|
277
|
+
logger.info("Formatting output")
|
|
278
|
+
|
|
279
|
+
file_path = state.get("file_path", "unknown")
|
|
280
|
+
language = state.get("language", "unknown")
|
|
281
|
+
summary = state.get("summary", "No summary available.")
|
|
282
|
+
classes = state.get("classes", [])
|
|
283
|
+
functions = state.get("functions", [])
|
|
284
|
+
constants = state.get("constants", [])
|
|
285
|
+
dependencies = state.get("dependencies", [])
|
|
286
|
+
|
|
287
|
+
# Create ModuleDocumentation object
|
|
288
|
+
doc = ModuleDocumentation(
|
|
289
|
+
file_path=file_path,
|
|
290
|
+
language=language,
|
|
291
|
+
summary=summary,
|
|
292
|
+
classes=classes,
|
|
293
|
+
functions=functions,
|
|
294
|
+
constants=constants,
|
|
295
|
+
dependencies=dependencies,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
state["documentation"] = doc
|
|
299
|
+
|
|
300
|
+
# Generate markdown if requested
|
|
301
|
+
if state.get("format") == "markdown":
|
|
302
|
+
state["markdown"] = self._generate_markdown(doc)
|
|
303
|
+
|
|
304
|
+
logger.info("Output formatted")
|
|
305
|
+
|
|
306
|
+
except Exception as e:
|
|
307
|
+
logger.error(f"Error formatting output: {e}")
|
|
308
|
+
state["error"] = f"Failed to format output: {str(e)}"
|
|
309
|
+
|
|
310
|
+
return state
|
|
311
|
+
|
|
312
|
+
def _generate_markdown(self, doc: ModuleDocumentation) -> str:
|
|
313
|
+
"""
|
|
314
|
+
Generate Markdown documentation from ModuleDocumentation.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
doc: Module documentation object
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Markdown-formatted documentation
|
|
321
|
+
"""
|
|
322
|
+
lines = [
|
|
323
|
+
f"# {doc.file_path}",
|
|
324
|
+
"",
|
|
325
|
+
f"**Language:** {doc.language}",
|
|
326
|
+
"",
|
|
327
|
+
"## Summary",
|
|
328
|
+
"",
|
|
329
|
+
doc.summary,
|
|
330
|
+
"",
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
# Document classes
|
|
334
|
+
if doc.classes:
|
|
335
|
+
lines.extend(["## Classes", ""])
|
|
336
|
+
for cls in doc.classes:
|
|
337
|
+
lines.append(f"### `{cls['name']}`")
|
|
338
|
+
lines.append("")
|
|
339
|
+
if cls.get("signature"):
|
|
340
|
+
lines.append(f"```{doc.language}")
|
|
341
|
+
lines.append(cls["signature"])
|
|
342
|
+
lines.append("```")
|
|
343
|
+
lines.append("")
|
|
344
|
+
lines.append(cls["docstring"])
|
|
345
|
+
lines.append("")
|
|
346
|
+
if cls.get("methods"):
|
|
347
|
+
lines.append("**Methods:**")
|
|
348
|
+
for method in cls["methods"]:
|
|
349
|
+
lines.append(f"- `{method}()`")
|
|
350
|
+
lines.append("")
|
|
351
|
+
|
|
352
|
+
# Document functions
|
|
353
|
+
if doc.functions:
|
|
354
|
+
lines.extend(["## Functions", ""])
|
|
355
|
+
for func in doc.functions:
|
|
356
|
+
lines.append(f"### `{func['name']}()`")
|
|
357
|
+
lines.append("")
|
|
358
|
+
if func.get("signature"):
|
|
359
|
+
lines.append(f"```{doc.language}")
|
|
360
|
+
lines.append(func["signature"])
|
|
361
|
+
lines.append("```")
|
|
362
|
+
lines.append("")
|
|
363
|
+
lines.append(func["docstring"])
|
|
364
|
+
lines.append("")
|
|
365
|
+
|
|
366
|
+
# Document constants
|
|
367
|
+
if doc.constants:
|
|
368
|
+
lines.extend(["## Constants", ""])
|
|
369
|
+
for const in doc.constants:
|
|
370
|
+
lines.append(f"- **`{const['name']}`**: {const['docstring']}")
|
|
371
|
+
lines.append("")
|
|
372
|
+
|
|
373
|
+
# Document dependencies
|
|
374
|
+
if doc.dependencies:
|
|
375
|
+
lines.extend(
|
|
376
|
+
[
|
|
377
|
+
"## Dependencies",
|
|
378
|
+
"",
|
|
379
|
+
"This module depends on:",
|
|
380
|
+
"",
|
|
381
|
+
]
|
|
382
|
+
)
|
|
383
|
+
for dep in doc.dependencies:
|
|
384
|
+
lines.append(f"- `{dep}`")
|
|
385
|
+
lines.append("")
|
|
386
|
+
|
|
387
|
+
return "\n".join(lines)
|
|
388
|
+
|
|
389
|
+
def generate_module_documentation(self, file_path: str) -> ModuleDocumentation:
|
|
390
|
+
"""
|
|
391
|
+
Generate documentation for a specific module/file.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
file_path: Path to the file to document
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
Structured module documentation
|
|
398
|
+
"""
|
|
399
|
+
logger.info(f"Generating documentation for: {file_path}")
|
|
400
|
+
|
|
401
|
+
# Initialize state
|
|
402
|
+
initial_state: Dict[str, Any] = {
|
|
403
|
+
"file_path": file_path,
|
|
404
|
+
"format": "object",
|
|
405
|
+
"symbols": [],
|
|
406
|
+
"language": "unknown",
|
|
407
|
+
"classes": [],
|
|
408
|
+
"functions": [],
|
|
409
|
+
"constants": [],
|
|
410
|
+
"dependencies": [],
|
|
411
|
+
"summary": "",
|
|
412
|
+
"documentation": None,
|
|
413
|
+
"error": None,
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
# Run the workflow
|
|
417
|
+
final_state = self.graph.invoke(initial_state)
|
|
418
|
+
|
|
419
|
+
# Handle None return from graph
|
|
420
|
+
if final_state is None:
|
|
421
|
+
final_state = initial_state
|
|
422
|
+
|
|
423
|
+
# Return documentation or create empty one
|
|
424
|
+
if final_state.get("documentation"):
|
|
425
|
+
return final_state["documentation"]
|
|
426
|
+
else:
|
|
427
|
+
return ModuleDocumentation(
|
|
428
|
+
file_path=file_path,
|
|
429
|
+
language="unknown",
|
|
430
|
+
summary="No symbols found in this file.",
|
|
431
|
+
classes=[],
|
|
432
|
+
functions=[],
|
|
433
|
+
constants=[],
|
|
434
|
+
dependencies=[],
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
def generate_architecture_overview(self) -> ArchitectureOverview:
|
|
438
|
+
"""
|
|
439
|
+
Generate high-level architecture documentation.
|
|
440
|
+
|
|
441
|
+
Returns:
|
|
442
|
+
Architecture overview with statistics and key insights
|
|
443
|
+
"""
|
|
444
|
+
logger.info("Generating architecture overview")
|
|
445
|
+
|
|
446
|
+
# Get repository statistics
|
|
447
|
+
stats = self.knowledge_service.get_repository_stats()
|
|
448
|
+
|
|
449
|
+
total_files = stats.get("total_files", 0)
|
|
450
|
+
total_symbols = stats.get("total_symbols", 0)
|
|
451
|
+
languages = stats.get("languages", [])
|
|
452
|
+
|
|
453
|
+
# Identify key modules (files with most symbols)
|
|
454
|
+
# This is a simplified version - in production, we'd query the metadata store
|
|
455
|
+
key_modules = []
|
|
456
|
+
|
|
457
|
+
# Generate dependency graph summary
|
|
458
|
+
dep_summary = f"Repository contains {total_symbols} symbols across {total_files} files."
|
|
459
|
+
|
|
460
|
+
return ArchitectureOverview(
|
|
461
|
+
total_files=total_files,
|
|
462
|
+
total_symbols=total_symbols,
|
|
463
|
+
languages=languages,
|
|
464
|
+
key_modules=key_modules,
|
|
465
|
+
dependency_graph_summary=dep_summary,
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
def generate_markdown_documentation(self, file_path: str) -> str:
|
|
469
|
+
"""
|
|
470
|
+
Generate Markdown documentation for a file.
|
|
471
|
+
|
|
472
|
+
Args:
|
|
473
|
+
file_path: Path to the file to document
|
|
474
|
+
|
|
475
|
+
Returns:
|
|
476
|
+
Markdown-formatted documentation
|
|
477
|
+
"""
|
|
478
|
+
logger.info(f"Generating markdown documentation for: {file_path}")
|
|
479
|
+
|
|
480
|
+
# Initialize state
|
|
481
|
+
initial_state: Dict[str, Any] = {
|
|
482
|
+
"file_path": file_path,
|
|
483
|
+
"format": "markdown",
|
|
484
|
+
"symbols": [],
|
|
485
|
+
"language": "unknown",
|
|
486
|
+
"classes": [],
|
|
487
|
+
"functions": [],
|
|
488
|
+
"constants": [],
|
|
489
|
+
"dependencies": [],
|
|
490
|
+
"summary": "",
|
|
491
|
+
"documentation": None,
|
|
492
|
+
"markdown": None,
|
|
493
|
+
"error": None,
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
# Run the workflow
|
|
497
|
+
final_state = self.graph.invoke(initial_state)
|
|
498
|
+
|
|
499
|
+
# Handle None return from graph
|
|
500
|
+
if final_state is None:
|
|
501
|
+
final_state = initial_state
|
|
502
|
+
|
|
503
|
+
# Return markdown or generate from documentation
|
|
504
|
+
if final_state.get("markdown"):
|
|
505
|
+
return final_state["markdown"]
|
|
506
|
+
elif final_state.get("documentation"):
|
|
507
|
+
return self._generate_markdown(final_state["documentation"])
|
|
508
|
+
else:
|
|
509
|
+
# Return minimal markdown
|
|
510
|
+
return f"# {file_path}\n\nNo symbols found in this file.\n"
|
|
511
|
+
|
|
512
|
+
def generate_architecture_markdown(self) -> str:
|
|
513
|
+
"""
|
|
514
|
+
Generate Markdown documentation for repository architecture.
|
|
515
|
+
|
|
516
|
+
Returns:
|
|
517
|
+
Markdown-formatted architecture overview
|
|
518
|
+
"""
|
|
519
|
+
overview = self.generate_architecture_overview()
|
|
520
|
+
|
|
521
|
+
lines = [
|
|
522
|
+
"# Repository Architecture",
|
|
523
|
+
"",
|
|
524
|
+
"## Overview",
|
|
525
|
+
"",
|
|
526
|
+
f"- **Total Files:** {overview.total_files}",
|
|
527
|
+
f"- **Total Symbols:** {overview.total_symbols}",
|
|
528
|
+
f"- **Languages:** {', '.join(overview.languages)}",
|
|
529
|
+
"",
|
|
530
|
+
"## Dependency Graph",
|
|
531
|
+
"",
|
|
532
|
+
overview.dependency_graph_summary,
|
|
533
|
+
"",
|
|
534
|
+
]
|
|
535
|
+
|
|
536
|
+
if overview.key_modules:
|
|
537
|
+
lines.extend(["## Key Modules", ""])
|
|
538
|
+
for module in overview.key_modules:
|
|
539
|
+
lines.append(f"- `{module}`")
|
|
540
|
+
lines.append("")
|
|
541
|
+
|
|
542
|
+
return "\n".join(lines)
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
# Made with Bob
|