@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.
Files changed (52) hide show
  1. package/LICENSE +37 -0
  2. package/README.md +153 -0
  3. package/USAGE.md +314 -0
  4. package/cli.py +1340 -0
  5. package/examples/.cursorrules +437 -0
  6. package/examples/instructions/.claude/instructions.md +372 -0
  7. package/examples/instructions/.cursorrules +437 -0
  8. package/examples/instructions/.windsurfrules +437 -0
  9. package/examples/instructions/VOODOCS_INSTRUCTIONS.md +437 -0
  10. package/examples/math_example.py +41 -0
  11. package/examples/phase2_test.py +24 -0
  12. package/examples/test_compound_conditions.py +40 -0
  13. package/examples/test_math_example.py +186 -0
  14. package/lib/darkarts/README.md +115 -0
  15. package/lib/darkarts/__init__.py +16 -0
  16. package/lib/darkarts/annotations/__init__.py +34 -0
  17. package/lib/darkarts/annotations/parser.py +618 -0
  18. package/lib/darkarts/annotations/types.py +181 -0
  19. package/lib/darkarts/cli.py +128 -0
  20. package/lib/darkarts/core/__init__.py +32 -0
  21. package/lib/darkarts/core/interface.py +256 -0
  22. package/lib/darkarts/core/loader.py +231 -0
  23. package/lib/darkarts/core/plugin.py +215 -0
  24. package/lib/darkarts/core/registry.py +146 -0
  25. package/lib/darkarts/exceptions.py +51 -0
  26. package/lib/darkarts/parsers/typescript/dist/cli.d.ts +9 -0
  27. package/lib/darkarts/parsers/typescript/dist/cli.d.ts.map +1 -0
  28. package/lib/darkarts/parsers/typescript/dist/cli.js +69 -0
  29. package/lib/darkarts/parsers/typescript/dist/cli.js.map +1 -0
  30. package/lib/darkarts/parsers/typescript/dist/parser.d.ts +111 -0
  31. package/lib/darkarts/parsers/typescript/dist/parser.d.ts.map +1 -0
  32. package/lib/darkarts/parsers/typescript/dist/parser.js +365 -0
  33. package/lib/darkarts/parsers/typescript/dist/parser.js.map +1 -0
  34. package/lib/darkarts/parsers/typescript/package-lock.json +51 -0
  35. package/lib/darkarts/parsers/typescript/package.json +19 -0
  36. package/lib/darkarts/parsers/typescript/src/cli.ts +41 -0
  37. package/lib/darkarts/parsers/typescript/src/parser.ts +408 -0
  38. package/lib/darkarts/parsers/typescript/tsconfig.json +19 -0
  39. package/lib/darkarts/plugins/voodocs/__init__.py +379 -0
  40. package/lib/darkarts/plugins/voodocs/ai_native_plugin.py +151 -0
  41. package/lib/darkarts/plugins/voodocs/annotation_validator.py +280 -0
  42. package/lib/darkarts/plugins/voodocs/api_spec_generator.py +486 -0
  43. package/lib/darkarts/plugins/voodocs/documentation_generator.py +610 -0
  44. package/lib/darkarts/plugins/voodocs/html_exporter.py +260 -0
  45. package/lib/darkarts/plugins/voodocs/instruction_generator.py +706 -0
  46. package/lib/darkarts/plugins/voodocs/pdf_exporter.py +66 -0
  47. package/lib/darkarts/plugins/voodocs/test_generator.py +636 -0
  48. package/package.json +70 -0
  49. package/requirements.txt +13 -0
  50. package/templates/ci/github-actions.yml +73 -0
  51. package/templates/ci/gitlab-ci.yml +35 -0
  52. package/templates/ci/pre-commit-hook.sh +26 -0
@@ -0,0 +1,280 @@
1
+ """
2
+ VooDocs Annotation Validator
3
+
4
+ Validates @voodocs annotations for correctness and quality.
5
+ """
6
+
7
+ from typing import List, Dict, Any, Optional
8
+ from darkarts.annotations.types import ParsedAnnotations, FunctionAnnotation, ClassAnnotation
9
+ import re
10
+
11
+
12
+ class ValidationIssue:
13
+ """Represents a validation issue."""
14
+
15
+ def __init__(self, severity: str, message: str, location: str, suggestion: Optional[str] = None):
16
+ self.severity = severity # "error", "warning", "info"
17
+ self.message = message
18
+ self.location = location
19
+ self.suggestion = suggestion
20
+
21
+ def __str__(self):
22
+ icon = "❌" if self.severity == "error" else "⚠️" if self.severity == "warning" else "ℹ️"
23
+ result = f"{icon} [{self.severity.upper()}] {self.location}: {self.message}"
24
+ if self.suggestion:
25
+ result += f"\n 💡 Suggestion: {self.suggestion}"
26
+ return result
27
+
28
+
29
+ class AnnotationValidator:
30
+ """Validates VooDocs annotations."""
31
+
32
+ VALID_FIELDS = {
33
+ "module_purpose", "dependencies", "assumptions", "security_model",
34
+ "preconditions", "postconditions", "invariants", "complexity",
35
+ "error_cases", "security_implications", "solve", "optimize",
36
+ "class_invariants", "state_transitions", "thread_safety"
37
+ }
38
+
39
+ REQUIRED_FUNCTION_FIELDS = {"preconditions", "postconditions"}
40
+ REQUIRED_CLASS_FIELDS = {"class_invariants"}
41
+
42
+ def __init__(self):
43
+ self.issues = []
44
+
45
+ def validate(self, parsed: ParsedAnnotations) -> List[ValidationIssue]:
46
+ """Validate all annotations in a parsed file."""
47
+ self.issues = []
48
+
49
+ # Validate module annotations
50
+ self._validate_module(parsed.module)
51
+
52
+ # Validate function annotations
53
+ for func in parsed.get_all_functions():
54
+ self._validate_function(func)
55
+
56
+ # Validate class annotations
57
+ for cls in parsed.module.classes:
58
+ self._validate_class(cls)
59
+
60
+ return self.issues
61
+
62
+ def _validate_module(self, module):
63
+ """Validate module-level annotations."""
64
+ location = f"Module '{module.name}'"
65
+
66
+ # Check for module_purpose
67
+ if not module.module_purpose:
68
+ self.issues.append(ValidationIssue(
69
+ "info",
70
+ "No module_purpose defined",
71
+ location,
72
+ "Add a module_purpose to describe what this module does"
73
+ ))
74
+
75
+ # Check dependencies format
76
+ if module.dependencies:
77
+ for dep in module.dependencies:
78
+ if not isinstance(dep, str):
79
+ self.issues.append(ValidationIssue(
80
+ "error",
81
+ f"Invalid dependency format: {dep}",
82
+ location,
83
+ "Dependencies should be strings"
84
+ ))
85
+
86
+ def _validate_function(self, func: FunctionAnnotation):
87
+ """Validate function annotations."""
88
+ location = f"Function '{func.name}'"
89
+
90
+ # Check for required fields
91
+ if not func.preconditions:
92
+ self.issues.append(ValidationIssue(
93
+ "warning",
94
+ "No preconditions defined",
95
+ location,
96
+ "Add preconditions to specify input requirements"
97
+ ))
98
+
99
+ if not func.postconditions:
100
+ self.issues.append(ValidationIssue(
101
+ "warning",
102
+ "No postconditions defined",
103
+ location,
104
+ "Add postconditions to specify output guarantees"
105
+ ))
106
+
107
+ # Validate preconditions
108
+ if func.preconditions:
109
+ for pre in func.preconditions:
110
+ self._validate_condition(pre, location, "precondition")
111
+
112
+ # Validate postconditions
113
+ if func.postconditions:
114
+ for post in func.postconditions:
115
+ self._validate_condition(post, location, "postcondition")
116
+
117
+ # Check for complexity annotation
118
+ if not func.complexity:
119
+ self.issues.append(ValidationIssue(
120
+ "info",
121
+ "No complexity annotation",
122
+ location,
123
+ "Add complexity annotation for performance-critical functions"
124
+ ))
125
+ else:
126
+ # Validate complexity format
127
+ if func.complexity.time:
128
+ if not self._is_valid_complexity(func.complexity.time):
129
+ self.issues.append(ValidationIssue(
130
+ "warning",
131
+ f"Invalid time complexity format: {func.complexity.time}",
132
+ location,
133
+ "Use Big-O notation like O(n), O(log n), O(1)"
134
+ ))
135
+
136
+ if func.complexity.space:
137
+ if not self._is_valid_complexity(func.complexity.space):
138
+ self.issues.append(ValidationIssue(
139
+ "warning",
140
+ f"Invalid space complexity format: {func.complexity.space}",
141
+ location,
142
+ "Use Big-O notation like O(n), O(log n), O(1)"
143
+ ))
144
+
145
+ # Check for error_cases
146
+ if not func.error_cases:
147
+ self.issues.append(ValidationIssue(
148
+ "info",
149
+ "No error_cases defined",
150
+ location,
151
+ "Add error_cases to document failure modes"
152
+ ))
153
+
154
+ # Validate consistency between pre and post conditions
155
+ self._validate_consistency(func, location)
156
+
157
+ def _validate_class(self, cls: ClassAnnotation):
158
+ """Validate class annotations."""
159
+ location = f"Class '{cls.name}'"
160
+
161
+ # Check for class invariants
162
+ if not cls.class_invariants:
163
+ self.issues.append(ValidationIssue(
164
+ "warning",
165
+ "No class_invariants defined",
166
+ location,
167
+ "Add class_invariants to specify object state constraints"
168
+ ))
169
+
170
+ # Check for state transitions
171
+ if not cls.state_transitions:
172
+ self.issues.append(ValidationIssue(
173
+ "info",
174
+ "No state_transitions defined",
175
+ location,
176
+ "Add state_transitions if this class has state changes"
177
+ ))
178
+
179
+ # Validate methods
180
+ if cls.methods:
181
+ for method in cls.methods:
182
+ self._validate_function(method)
183
+
184
+ def _validate_condition(self, condition: str, location: str, cond_type: str):
185
+ """Validate a single condition (precondition or postcondition)."""
186
+ # Check for empty conditions
187
+ if not condition or not condition.strip():
188
+ self.issues.append(ValidationIssue(
189
+ "error",
190
+ f"Empty {cond_type}",
191
+ location,
192
+ f"Remove empty {cond_type} or add meaningful constraint"
193
+ ))
194
+ return
195
+
196
+ # Check for common typos in mathematical notation
197
+ if '∈' in condition:
198
+ # Valid: x ∈ ℝ, x ∈ ℤ, x ∈ ℕ
199
+ if not re.search(r'\w+\s*∈\s*[ℝℤℕ]', condition):
200
+ self.issues.append(ValidationIssue(
201
+ "warning",
202
+ f"Possibly malformed set membership: {condition}",
203
+ location,
204
+ "Use ∈ with standard sets: ℝ (reals), ℤ (integers), ℕ (naturals)"
205
+ ))
206
+
207
+ # Check for inconsistent operators
208
+ if '⇒' in condition or '⇔' in condition:
209
+ # These should be used correctly
210
+ if condition.count('⇒') > 1 and '∧' not in condition and '∨' not in condition:
211
+ self.issues.append(ValidationIssue(
212
+ "info",
213
+ f"Complex implication: {condition}",
214
+ location,
215
+ "Consider breaking into multiple conditions for clarity"
216
+ ))
217
+
218
+ # Check for undefined variables
219
+ # This is a simple heuristic - in practice, you'd need proper parsing
220
+ if cond_type == "postcondition" and "result" not in condition.lower():
221
+ # Postconditions should usually reference the result
222
+ self.issues.append(ValidationIssue(
223
+ "info",
224
+ f"Postcondition doesn't reference 'result': {condition}",
225
+ location,
226
+ "Postconditions typically specify properties of the result"
227
+ ))
228
+
229
+ def _is_valid_complexity(self, complexity: str) -> bool:
230
+ """Check if complexity notation is valid."""
231
+ # Valid patterns: O(1), O(n), O(log n), O(n log n), O(n^2), etc.
232
+ pattern = r'^O\([^)]+\)$'
233
+ return bool(re.match(pattern, complexity))
234
+
235
+ def _validate_consistency(self, func: FunctionAnnotation, location: str):
236
+ """Validate consistency between preconditions and postconditions."""
237
+ if not func.preconditions or not func.postconditions:
238
+ return
239
+
240
+ # Extract variable names from preconditions
241
+ pre_vars = set()
242
+ for pre in func.preconditions:
243
+ # Simple heuristic: extract words that look like variable names
244
+ matches = re.findall(r'\b([a-z_][a-z0-9_]*)\b', pre.lower())
245
+ pre_vars.update(matches)
246
+
247
+ # Extract variable names from postconditions
248
+ post_vars = set()
249
+ for post in func.postconditions:
250
+ matches = re.findall(r'\b([a-z_][a-z0-9_]*)\b', post.lower())
251
+ post_vars.update(matches)
252
+
253
+ # Remove common keywords
254
+ keywords = {'and', 'or', 'not', 'in', 'is', 'must', 'be', 'the', 'a', 'an', 'for', 'all', 'exists'}
255
+ pre_vars -= keywords
256
+ post_vars -= keywords
257
+
258
+ # Check if postconditions reference precondition variables
259
+ # (This is a weak check but can catch some issues)
260
+ if pre_vars and not (pre_vars & post_vars or 'result' in post_vars):
261
+ self.issues.append(ValidationIssue(
262
+ "info",
263
+ "Postconditions don't reference precondition variables",
264
+ location,
265
+ "Consider relating output to input in postconditions"
266
+ ))
267
+
268
+ def get_summary(self) -> Dict[str, int]:
269
+ """Get summary of validation issues."""
270
+ summary = {
271
+ "errors": sum(1 for issue in self.issues if issue.severity == "error"),
272
+ "warnings": sum(1 for issue in self.issues if issue.severity == "warning"),
273
+ "info": sum(1 for issue in self.issues if issue.severity == "info"),
274
+ "total": len(self.issues)
275
+ }
276
+ return summary
277
+
278
+ def has_errors(self) -> bool:
279
+ """Check if there are any errors."""
280
+ return any(issue.severity == "error" for issue in self.issues)