@voodocs/cli 0.1.0
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.
- package/LICENSE +37 -0
- package/README.md +153 -0
- package/USAGE.md +314 -0
- package/cli.py +1340 -0
- package/examples/.cursorrules +437 -0
- package/examples/instructions/.claude/instructions.md +372 -0
- package/examples/instructions/.cursorrules +437 -0
- package/examples/instructions/.windsurfrules +437 -0
- package/examples/instructions/VOODOCS_INSTRUCTIONS.md +437 -0
- package/examples/math_example.py +41 -0
- package/examples/phase2_test.py +24 -0
- package/examples/test_compound_conditions.py +40 -0
- package/examples/test_math_example.py +186 -0
- package/lib/darkarts/README.md +115 -0
- package/lib/darkarts/__init__.py +16 -0
- package/lib/darkarts/annotations/__init__.py +34 -0
- package/lib/darkarts/annotations/parser.py +618 -0
- package/lib/darkarts/annotations/types.py +181 -0
- package/lib/darkarts/cli.py +128 -0
- package/lib/darkarts/core/__init__.py +32 -0
- package/lib/darkarts/core/interface.py +256 -0
- package/lib/darkarts/core/loader.py +231 -0
- package/lib/darkarts/core/plugin.py +215 -0
- package/lib/darkarts/core/registry.py +146 -0
- package/lib/darkarts/exceptions.py +51 -0
- package/lib/darkarts/parsers/typescript/dist/cli.d.ts +9 -0
- package/lib/darkarts/parsers/typescript/dist/cli.d.ts.map +1 -0
- package/lib/darkarts/parsers/typescript/dist/cli.js +69 -0
- package/lib/darkarts/parsers/typescript/dist/cli.js.map +1 -0
- package/lib/darkarts/parsers/typescript/dist/parser.d.ts +111 -0
- package/lib/darkarts/parsers/typescript/dist/parser.d.ts.map +1 -0
- package/lib/darkarts/parsers/typescript/dist/parser.js +365 -0
- package/lib/darkarts/parsers/typescript/dist/parser.js.map +1 -0
- package/lib/darkarts/parsers/typescript/package-lock.json +51 -0
- package/lib/darkarts/parsers/typescript/package.json +19 -0
- package/lib/darkarts/parsers/typescript/src/cli.ts +41 -0
- package/lib/darkarts/parsers/typescript/src/parser.ts +408 -0
- package/lib/darkarts/parsers/typescript/tsconfig.json +19 -0
- package/lib/darkarts/plugins/voodocs/__init__.py +379 -0
- package/lib/darkarts/plugins/voodocs/ai_native_plugin.py +151 -0
- package/lib/darkarts/plugins/voodocs/annotation_validator.py +280 -0
- package/lib/darkarts/plugins/voodocs/api_spec_generator.py +486 -0
- package/lib/darkarts/plugins/voodocs/documentation_generator.py +610 -0
- package/lib/darkarts/plugins/voodocs/html_exporter.py +260 -0
- package/lib/darkarts/plugins/voodocs/instruction_generator.py +706 -0
- package/lib/darkarts/plugins/voodocs/pdf_exporter.py +66 -0
- package/lib/darkarts/plugins/voodocs/test_generator.py +636 -0
- package/package.json +70 -0
- package/requirements.txt +13 -0
- package/templates/ci/github-actions.yml +73 -0
- package/templates/ci/gitlab-ci.yml +35 -0
- package/templates/ci/pre-commit-hook.sh +26 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Code Documentation Plugin for DarkArts - Automated documentation generation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
import re
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from darkarts.core import (
|
|
10
|
+
DarkArtsPlugin,
|
|
11
|
+
PluginMetadata,
|
|
12
|
+
PluginInput,
|
|
13
|
+
PluginOutput,
|
|
14
|
+
ParseError,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class FunctionInfo:
|
|
20
|
+
"""Information about a function."""
|
|
21
|
+
name: str
|
|
22
|
+
args: List[str]
|
|
23
|
+
returns: Optional[str]
|
|
24
|
+
docstring: Optional[str]
|
|
25
|
+
decorators: List[str] = field(default_factory=list)
|
|
26
|
+
lineno: int = 0
|
|
27
|
+
complexity: int = 0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ClassInfo:
|
|
32
|
+
"""Information about a class."""
|
|
33
|
+
name: str
|
|
34
|
+
bases: List[str]
|
|
35
|
+
docstring: Optional[str]
|
|
36
|
+
methods: List[FunctionInfo] = field(default_factory=list)
|
|
37
|
+
attributes: List[str] = field(default_factory=list)
|
|
38
|
+
lineno: int = 0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class CodeStructure:
|
|
43
|
+
"""Parsed code structure."""
|
|
44
|
+
module_docstring: Optional[str]
|
|
45
|
+
imports: List[str]
|
|
46
|
+
functions: List[FunctionInfo]
|
|
47
|
+
classes: List[ClassInfo]
|
|
48
|
+
constants: List[str]
|
|
49
|
+
complexity_score: int = 0
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class VooDocsPlugin(DarkArtsPlugin):
|
|
53
|
+
"""Plugin for automated code documentation generation."""
|
|
54
|
+
|
|
55
|
+
def __init__(self):
|
|
56
|
+
"""Initialize the code docs plugin."""
|
|
57
|
+
super().__init__()
|
|
58
|
+
self._metadata = PluginMetadata(
|
|
59
|
+
name="voodocs",
|
|
60
|
+
version="1.0.0",
|
|
61
|
+
description="Automated code documentation generation from Python source",
|
|
62
|
+
author="DarkArts Team",
|
|
63
|
+
dependencies=[],
|
|
64
|
+
capabilities=["parse", "analyze", "execute", "explain"],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def metadata(self) -> PluginMetadata:
|
|
69
|
+
"""Return plugin metadata."""
|
|
70
|
+
return self._metadata
|
|
71
|
+
|
|
72
|
+
def parse(self, input: PluginInput) -> CodeStructure:
|
|
73
|
+
"""
|
|
74
|
+
Parse Python source code.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
input: The input containing Python source code
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Parsed code structure
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
ParseError: If parsing fails
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
tree = ast.parse(input.content)
|
|
87
|
+
return self._extract_structure(tree, input.content)
|
|
88
|
+
except SyntaxError as e:
|
|
89
|
+
raise ParseError(f"Syntax error in code: {e}")
|
|
90
|
+
except Exception as e:
|
|
91
|
+
raise ParseError(f"Failed to parse code: {e}")
|
|
92
|
+
|
|
93
|
+
def analyze(self, parsed: CodeStructure) -> Dict[str, Any]:
|
|
94
|
+
"""
|
|
95
|
+
Analyze code complexity and patterns.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
parsed: The parsed code structure
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Analysis results
|
|
102
|
+
"""
|
|
103
|
+
return {
|
|
104
|
+
"functions_count": len(parsed.functions),
|
|
105
|
+
"classes_count": len(parsed.classes),
|
|
106
|
+
"imports_count": len(parsed.imports),
|
|
107
|
+
"complexity": parsed.complexity_score,
|
|
108
|
+
"has_docstrings": self._check_docstrings(parsed),
|
|
109
|
+
"documentation_coverage": self._calculate_coverage(parsed),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
def execute(self, parsed: CodeStructure, analysis: Dict[str, Any]) -> PluginOutput:
|
|
113
|
+
"""
|
|
114
|
+
Generate documentation.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
parsed: The parsed code structure
|
|
118
|
+
analysis: Analysis results
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Plugin output with generated documentation
|
|
122
|
+
"""
|
|
123
|
+
try:
|
|
124
|
+
docs = self._generate_markdown(parsed, analysis)
|
|
125
|
+
|
|
126
|
+
return PluginOutput(
|
|
127
|
+
success=True,
|
|
128
|
+
result=docs,
|
|
129
|
+
explanation=self._generate_summary(parsed, analysis),
|
|
130
|
+
metadata=analysis,
|
|
131
|
+
)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
return PluginOutput(
|
|
134
|
+
success=False,
|
|
135
|
+
result=None,
|
|
136
|
+
error=str(e),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def explain(self, output: PluginOutput, level: str = "student") -> str:
|
|
140
|
+
"""
|
|
141
|
+
Generate explanation of the documentation.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
output: The output to explain
|
|
145
|
+
level: Explanation level
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Natural language explanation
|
|
149
|
+
"""
|
|
150
|
+
if not output.success:
|
|
151
|
+
return f"Failed to generate documentation: {output.error}"
|
|
152
|
+
|
|
153
|
+
if output.explanation:
|
|
154
|
+
return output.explanation
|
|
155
|
+
|
|
156
|
+
return "Documentation generated successfully."
|
|
157
|
+
|
|
158
|
+
def _extract_structure(self, tree: ast.AST, source: str) -> CodeStructure:
|
|
159
|
+
"""Extract structure from AST."""
|
|
160
|
+
module_docstring = ast.get_docstring(tree)
|
|
161
|
+
imports = []
|
|
162
|
+
functions = []
|
|
163
|
+
classes = []
|
|
164
|
+
constants = []
|
|
165
|
+
|
|
166
|
+
# Only iterate over top-level nodes
|
|
167
|
+
for node in tree.body:
|
|
168
|
+
if isinstance(node, (ast.Import, ast.ImportFrom)):
|
|
169
|
+
imports.append(ast.unparse(node))
|
|
170
|
+
|
|
171
|
+
elif isinstance(node, ast.FunctionDef):
|
|
172
|
+
functions.append(self._extract_function(node))
|
|
173
|
+
|
|
174
|
+
elif isinstance(node, ast.ClassDef):
|
|
175
|
+
classes.append(self._extract_class(node))
|
|
176
|
+
|
|
177
|
+
elif isinstance(node, ast.Assign):
|
|
178
|
+
# Top-level constants (uppercase names)
|
|
179
|
+
for target in node.targets:
|
|
180
|
+
if isinstance(target, ast.Name) and target.id.isupper():
|
|
181
|
+
constants.append(target.id)
|
|
182
|
+
|
|
183
|
+
# Calculate complexity
|
|
184
|
+
complexity = self._calculate_complexity(tree)
|
|
185
|
+
|
|
186
|
+
return CodeStructure(
|
|
187
|
+
module_docstring=module_docstring,
|
|
188
|
+
imports=imports,
|
|
189
|
+
functions=functions,
|
|
190
|
+
classes=classes,
|
|
191
|
+
constants=constants,
|
|
192
|
+
complexity_score=complexity,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def _extract_function(self, node: ast.FunctionDef) -> FunctionInfo:
|
|
196
|
+
"""Extract function information."""
|
|
197
|
+
args = [arg.arg for arg in node.args.args]
|
|
198
|
+
returns = ast.unparse(node.returns) if node.returns else None
|
|
199
|
+
docstring = ast.get_docstring(node)
|
|
200
|
+
decorators = [ast.unparse(d) for d in node.decorator_list]
|
|
201
|
+
|
|
202
|
+
# Calculate function complexity (simple metric)
|
|
203
|
+
complexity = sum(1 for _ in ast.walk(node) if isinstance(_, (ast.If, ast.For, ast.While, ast.Try)))
|
|
204
|
+
|
|
205
|
+
return FunctionInfo(
|
|
206
|
+
name=node.name,
|
|
207
|
+
args=args,
|
|
208
|
+
returns=returns,
|
|
209
|
+
docstring=docstring,
|
|
210
|
+
decorators=decorators,
|
|
211
|
+
lineno=node.lineno,
|
|
212
|
+
complexity=complexity,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def _extract_class(self, node: ast.ClassDef) -> ClassInfo:
|
|
216
|
+
"""Extract class information."""
|
|
217
|
+
bases = [ast.unparse(base) for base in node.bases]
|
|
218
|
+
docstring = ast.get_docstring(node)
|
|
219
|
+
methods = []
|
|
220
|
+
attributes = []
|
|
221
|
+
|
|
222
|
+
for item in node.body:
|
|
223
|
+
if isinstance(item, ast.FunctionDef):
|
|
224
|
+
methods.append(self._extract_function(item))
|
|
225
|
+
elif isinstance(item, ast.Assign):
|
|
226
|
+
for target in item.targets:
|
|
227
|
+
if isinstance(target, ast.Name):
|
|
228
|
+
attributes.append(target.id)
|
|
229
|
+
|
|
230
|
+
return ClassInfo(
|
|
231
|
+
name=node.name,
|
|
232
|
+
bases=bases,
|
|
233
|
+
docstring=docstring,
|
|
234
|
+
methods=methods,
|
|
235
|
+
attributes=attributes,
|
|
236
|
+
lineno=node.lineno,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
def _calculate_complexity(self, tree: ast.AST) -> int:
|
|
240
|
+
"""Calculate cyclomatic complexity."""
|
|
241
|
+
complexity = 1 # Base complexity
|
|
242
|
+
|
|
243
|
+
for node in ast.walk(tree):
|
|
244
|
+
if isinstance(node, (ast.If, ast.While, ast.For, ast.ExceptHandler)):
|
|
245
|
+
complexity += 1
|
|
246
|
+
elif isinstance(node, ast.BoolOp):
|
|
247
|
+
complexity += len(node.values) - 1
|
|
248
|
+
|
|
249
|
+
return complexity
|
|
250
|
+
|
|
251
|
+
def _check_docstrings(self, structure: CodeStructure) -> bool:
|
|
252
|
+
"""Check if code has docstrings."""
|
|
253
|
+
has_module_doc = structure.module_docstring is not None
|
|
254
|
+
has_function_docs = any(f.docstring for f in structure.functions)
|
|
255
|
+
has_class_docs = any(c.docstring for c in structure.classes)
|
|
256
|
+
|
|
257
|
+
return has_module_doc or has_function_docs or has_class_docs
|
|
258
|
+
|
|
259
|
+
def _calculate_coverage(self, structure: CodeStructure) -> float:
|
|
260
|
+
"""Calculate documentation coverage percentage."""
|
|
261
|
+
total_items = 1 + len(structure.functions) + len(structure.classes) # +1 for module
|
|
262
|
+
documented_items = 0
|
|
263
|
+
|
|
264
|
+
if structure.module_docstring:
|
|
265
|
+
documented_items += 1
|
|
266
|
+
|
|
267
|
+
documented_items += sum(1 for f in structure.functions if f.docstring)
|
|
268
|
+
documented_items += sum(1 for c in structure.classes if c.docstring)
|
|
269
|
+
|
|
270
|
+
return (documented_items / total_items * 100) if total_items > 0 else 0
|
|
271
|
+
|
|
272
|
+
def _generate_summary(self, structure: CodeStructure, analysis: Dict[str, Any]) -> str:
|
|
273
|
+
"""Generate a summary of the documentation."""
|
|
274
|
+
summary = f"Generated documentation for:\n"
|
|
275
|
+
summary += f"- {analysis['functions_count']} functions\n"
|
|
276
|
+
summary += f"- {analysis['classes_count']} classes\n"
|
|
277
|
+
summary += f"- {analysis['imports_count']} imports\n"
|
|
278
|
+
summary += f"- Complexity score: {analysis['complexity']}\n"
|
|
279
|
+
summary += f"- Documentation coverage: {analysis['documentation_coverage']:.1f}%\n"
|
|
280
|
+
|
|
281
|
+
return summary
|
|
282
|
+
|
|
283
|
+
def _generate_markdown(self, structure: CodeStructure, analysis: Dict[str, Any]) -> str:
|
|
284
|
+
"""Generate Markdown documentation."""
|
|
285
|
+
md = []
|
|
286
|
+
|
|
287
|
+
# Module header
|
|
288
|
+
md.append("# Module Documentation\n")
|
|
289
|
+
|
|
290
|
+
if structure.module_docstring:
|
|
291
|
+
md.append(f"{structure.module_docstring}\n")
|
|
292
|
+
|
|
293
|
+
# Metrics
|
|
294
|
+
md.append("## Metrics\n")
|
|
295
|
+
md.append(f"- **Functions**: {analysis['functions_count']}")
|
|
296
|
+
md.append(f"- **Classes**: {analysis['classes_count']}")
|
|
297
|
+
md.append(f"- **Complexity**: {analysis['complexity']}")
|
|
298
|
+
md.append(f"- **Documentation Coverage**: {analysis['documentation_coverage']:.1f}%\n")
|
|
299
|
+
|
|
300
|
+
# Imports
|
|
301
|
+
if structure.imports:
|
|
302
|
+
md.append("## Imports\n")
|
|
303
|
+
md.append("```python")
|
|
304
|
+
md.extend(structure.imports)
|
|
305
|
+
md.append("```\n")
|
|
306
|
+
|
|
307
|
+
# Constants
|
|
308
|
+
if structure.constants:
|
|
309
|
+
md.append("## Constants\n")
|
|
310
|
+
for const in structure.constants:
|
|
311
|
+
md.append(f"- `{const}`")
|
|
312
|
+
md.append("")
|
|
313
|
+
|
|
314
|
+
# Functions
|
|
315
|
+
if structure.functions:
|
|
316
|
+
md.append("## Functions\n")
|
|
317
|
+
for func in structure.functions:
|
|
318
|
+
md.append(f"### `{func.name}({', '.join(func.args)})`\n")
|
|
319
|
+
|
|
320
|
+
if func.decorators:
|
|
321
|
+
md.append(f"**Decorators**: {', '.join(f'`{d}`' for d in func.decorators)}\n")
|
|
322
|
+
|
|
323
|
+
if func.returns:
|
|
324
|
+
md.append(f"**Returns**: `{func.returns}`\n")
|
|
325
|
+
|
|
326
|
+
if func.docstring:
|
|
327
|
+
md.append(func.docstring)
|
|
328
|
+
else:
|
|
329
|
+
md.append("*No documentation available.*")
|
|
330
|
+
|
|
331
|
+
md.append(f"\n**Complexity**: {func.complexity}\n")
|
|
332
|
+
|
|
333
|
+
# Classes
|
|
334
|
+
if structure.classes:
|
|
335
|
+
md.append("## Classes\n")
|
|
336
|
+
for cls in structure.classes:
|
|
337
|
+
md.append(f"### `class {cls.name}`\n")
|
|
338
|
+
|
|
339
|
+
if cls.bases:
|
|
340
|
+
md.append(f"**Inherits from**: {', '.join(f'`{b}`' for b in cls.bases)}\n")
|
|
341
|
+
|
|
342
|
+
if cls.docstring:
|
|
343
|
+
md.append(cls.docstring)
|
|
344
|
+
else:
|
|
345
|
+
md.append("*No documentation available.*")
|
|
346
|
+
|
|
347
|
+
md.append("")
|
|
348
|
+
|
|
349
|
+
if cls.attributes:
|
|
350
|
+
md.append("**Attributes**:")
|
|
351
|
+
for attr in cls.attributes:
|
|
352
|
+
md.append(f"- `{attr}`")
|
|
353
|
+
md.append("")
|
|
354
|
+
|
|
355
|
+
if cls.methods:
|
|
356
|
+
md.append("**Methods**:\n")
|
|
357
|
+
for method in cls.methods:
|
|
358
|
+
md.append(f"#### `{method.name}({', '.join(method.args)})`\n")
|
|
359
|
+
if method.docstring:
|
|
360
|
+
md.append(method.docstring)
|
|
361
|
+
else:
|
|
362
|
+
md.append("*No documentation available.*")
|
|
363
|
+
md.append("")
|
|
364
|
+
|
|
365
|
+
return "\n".join(md)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
# Register plugin on import
|
|
369
|
+
from darkarts.core import get_global_registry
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
registry = get_global_registry()
|
|
373
|
+
if not registry.has_plugin("voodocs"):
|
|
374
|
+
registry.register(VooDocsPlugin())
|
|
375
|
+
except Exception:
|
|
376
|
+
pass # Silently fail if registration fails
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
__all__ = ["VooDocsPlugin"]
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AI-Native VooDocs Plugin for DarkArts
|
|
3
|
+
|
|
4
|
+
Enhanced version that supports both:
|
|
5
|
+
1. Traditional code documentation (AST-based)
|
|
6
|
+
2. AI-native documentation (@voodocs annotations)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from darkarts.core import (
|
|
10
|
+
DarkArtsPlugin,
|
|
11
|
+
PluginMetadata,
|
|
12
|
+
PluginInput,
|
|
13
|
+
PluginOutput,
|
|
14
|
+
)
|
|
15
|
+
from darkarts.annotations import AnnotationParser
|
|
16
|
+
from .documentation_generator import DocumentationGenerator
|
|
17
|
+
from .test_generator import TestGenerator
|
|
18
|
+
from .api_spec_generator import APISpecGenerator
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AIVooDocsPlugin(DarkArtsPlugin):
|
|
22
|
+
"""Enhanced plugin for AI-native code documentation."""
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
super().__init__()
|
|
26
|
+
self._metadata = PluginMetadata(
|
|
27
|
+
name="ai_voodocs",
|
|
28
|
+
version="2.0.0",
|
|
29
|
+
description="AI-native documentation system with automatic test and API generation from @voodocs annotations",
|
|
30
|
+
author="DarkArts Team",
|
|
31
|
+
dependencies=[],
|
|
32
|
+
capabilities=["parse", "analyze", "execute", "explain"],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Initialize generators
|
|
36
|
+
self.parser = AnnotationParser()
|
|
37
|
+
self.doc_generator = DocumentationGenerator()
|
|
38
|
+
self.test_generator = TestGenerator()
|
|
39
|
+
self.api_generator = APISpecGenerator()
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def metadata(self) -> PluginMetadata:
|
|
43
|
+
return self._metadata
|
|
44
|
+
|
|
45
|
+
def parse(self, input: PluginInput) -> dict:
|
|
46
|
+
"""Parse source code and extract @voodocs annotations."""
|
|
47
|
+
source_code = input.content
|
|
48
|
+
source_file = input.metadata.get('source_file', '<string>')
|
|
49
|
+
|
|
50
|
+
# Parse annotations
|
|
51
|
+
parsed = self.parser.parse_source(source_code, source_file)
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
'parsed_annotations': parsed,
|
|
55
|
+
'has_annotations': parsed.has_annotations(),
|
|
56
|
+
'source_code': source_code,
|
|
57
|
+
'source_file': source_file,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
def analyze(self, parsed: dict) -> dict:
|
|
61
|
+
"""Analyze the parsed annotations."""
|
|
62
|
+
annotations = parsed['parsed_annotations']
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
'module_name': annotations.module.name,
|
|
66
|
+
'has_module_annotations': bool(
|
|
67
|
+
annotations.module.module_purpose or
|
|
68
|
+
annotations.module.dependencies or
|
|
69
|
+
annotations.module.assumptions
|
|
70
|
+
),
|
|
71
|
+
'classes_count': len(annotations.module.classes),
|
|
72
|
+
'functions_count': len(annotations.get_all_functions()),
|
|
73
|
+
'has_annotations': parsed['has_annotations'],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
def execute(self, parsed: dict, analysis: dict) -> PluginOutput:
|
|
77
|
+
"""Generate documentation, tests, and API specs."""
|
|
78
|
+
try:
|
|
79
|
+
annotations = parsed['parsed_annotations']
|
|
80
|
+
|
|
81
|
+
# Generate all outputs
|
|
82
|
+
documentation = self.doc_generator.generate(annotations)
|
|
83
|
+
tests = self.test_generator.generate(annotations)
|
|
84
|
+
api_spec = self.api_generator.generate(annotations)
|
|
85
|
+
|
|
86
|
+
result = {
|
|
87
|
+
'documentation': documentation,
|
|
88
|
+
'tests': tests,
|
|
89
|
+
'api_spec': api_spec,
|
|
90
|
+
'module': analysis['module_name'],
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return PluginOutput(
|
|
94
|
+
success=True,
|
|
95
|
+
result=result,
|
|
96
|
+
explanation=self._generate_explanation(analysis, result),
|
|
97
|
+
metadata=analysis,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
except Exception as e:
|
|
101
|
+
return PluginOutput(
|
|
102
|
+
success=False,
|
|
103
|
+
result=None,
|
|
104
|
+
error=str(e),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def explain(self, output: PluginOutput, level: str = "student") -> str:
|
|
108
|
+
"""Explain the AI-native documentation generation."""
|
|
109
|
+
if not output.success:
|
|
110
|
+
return f"Failed to generate documentation: {output.error}"
|
|
111
|
+
|
|
112
|
+
if output.explanation:
|
|
113
|
+
return output.explanation
|
|
114
|
+
|
|
115
|
+
return "AI-native documentation generated successfully."
|
|
116
|
+
|
|
117
|
+
def _generate_explanation(self, analysis: dict, result: dict) -> str:
|
|
118
|
+
"""Generate explanation of the process."""
|
|
119
|
+
lines = []
|
|
120
|
+
|
|
121
|
+
lines.append("# AI-Native Documentation System")
|
|
122
|
+
lines.append("")
|
|
123
|
+
lines.append(f"**Module**: {analysis['module_name']}")
|
|
124
|
+
lines.append(f"**Has DarkArts Annotations**: {analysis['has_annotations']}")
|
|
125
|
+
lines.append("")
|
|
126
|
+
|
|
127
|
+
lines.append("## Statistics")
|
|
128
|
+
lines.append(f"- Classes: {analysis['classes_count']}")
|
|
129
|
+
lines.append(f"- Functions: {analysis['functions_count']}")
|
|
130
|
+
lines.append(f"- Module Annotations: {'Yes' if analysis['has_module_annotations'] else 'No'}")
|
|
131
|
+
lines.append("")
|
|
132
|
+
|
|
133
|
+
lines.append("## Generated Outputs")
|
|
134
|
+
lines.append("✅ **Documentation**: Human-readable Markdown from DarkArts annotations")
|
|
135
|
+
lines.append("✅ **Tests**: Automated test cases from preconditions/postconditions")
|
|
136
|
+
lines.append("✅ **API Spec**: OpenAPI specification from function contracts")
|
|
137
|
+
lines.append("")
|
|
138
|
+
|
|
139
|
+
lines.append("## How It Works")
|
|
140
|
+
lines.append("1. **AI writes** precise mathematical/logical specifications in @voodocs annotations")
|
|
141
|
+
lines.append("2. **Parser extracts** annotations from source code")
|
|
142
|
+
lines.append("3. **Generators translate** annotations into:")
|
|
143
|
+
lines.append(" - Human-readable documentation (Markdown)")
|
|
144
|
+
lines.append(" - Automated test cases (pytest/unittest/jest)")
|
|
145
|
+
lines.append(" - API specifications (OpenAPI/GraphQL)")
|
|
146
|
+
|
|
147
|
+
return "\n".join(lines)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# Export
|
|
151
|
+
__all__ = ['AIVooDocsPlugin']
|