@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,181 @@
1
+ """
2
+ DarkArts Annotation Types
3
+
4
+ Data structures for representing parsed DarkArts annotations.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import List, Dict, Optional, Any
9
+ from enum import Enum
10
+
11
+
12
+ class AnnotationType(Enum):
13
+ """Types of annotations supported."""
14
+ FUNCTION = "function"
15
+ METHOD = "method"
16
+ CLASS = "class"
17
+ MODULE = "module"
18
+
19
+
20
+ class Language(Enum):
21
+ """Supported programming languages."""
22
+ PYTHON = "python"
23
+ TYPESCRIPT = "typescript"
24
+ JAVASCRIPT = "javascript"
25
+ JAVA = "java"
26
+ CPP = "cpp"
27
+ CSHARP = "csharp"
28
+ GO = "go"
29
+ RUST = "rust"
30
+
31
+
32
+ @dataclass
33
+ class ComplexityAnnotation:
34
+ """Complexity annotations."""
35
+ time: str = "O(1)"
36
+ space: str = "O(1)"
37
+ best_case: Optional[str] = None
38
+ worst_case: Optional[str] = None
39
+ average_case: Optional[str] = None
40
+
41
+
42
+ @dataclass
43
+ class StateTransition:
44
+ """State transition annotation."""
45
+ from_state: str
46
+ to_state: str
47
+ condition: Optional[str] = None
48
+
49
+ def __str__(self) -> str:
50
+ if self.condition:
51
+ return f"{self.from_state} → {self.to_state}: {self.condition}"
52
+ return f"{self.from_state} → {self.to_state}"
53
+
54
+
55
+ @dataclass
56
+ class ErrorCase:
57
+ """Error case annotation."""
58
+ condition: str
59
+ error_type: str
60
+
61
+ def __str__(self) -> str:
62
+ return f"{self.condition} → {self.error_type}"
63
+
64
+
65
+ @dataclass
66
+ class FunctionAnnotation:
67
+ """Annotations for a function or method."""
68
+ name: str
69
+ solve: Optional[str] = None
70
+ preconditions: List[str] = field(default_factory=list)
71
+ postconditions: List[str] = field(default_factory=list)
72
+ invariants: List[str] = field(default_factory=list)
73
+ optimize: Optional[str] = None
74
+ complexity: Optional[ComplexityAnnotation] = None
75
+ error_cases: List[ErrorCase] = field(default_factory=list)
76
+ side_effects: List[str] = field(default_factory=list)
77
+
78
+ # Source location
79
+ line_number: int = 0
80
+ source_file: str = ""
81
+
82
+ # Raw annotation text
83
+ raw_annotation: str = ""
84
+
85
+
86
+ @dataclass
87
+ class ClassAnnotation:
88
+ """Annotations for a class."""
89
+ name: str
90
+ class_invariants: List[str] = field(default_factory=list)
91
+ state_transitions: List[StateTransition] = field(default_factory=list)
92
+
93
+ # Methods in this class
94
+ methods: List[FunctionAnnotation] = field(default_factory=list)
95
+
96
+ # Source location
97
+ line_number: int = 0
98
+ source_file: str = ""
99
+
100
+ # Raw annotation text
101
+ raw_annotation: str = ""
102
+
103
+
104
+ @dataclass
105
+ class ModuleAnnotation:
106
+ """Annotations for a module/file."""
107
+ name: str
108
+ module_purpose: Optional[str] = None
109
+ dependencies: List[str] = field(default_factory=list)
110
+ assumptions: List[str] = field(default_factory=list)
111
+
112
+ # Classes and functions in this module
113
+ classes: List[ClassAnnotation] = field(default_factory=list)
114
+ functions: List[FunctionAnnotation] = field(default_factory=list)
115
+
116
+ # Source file
117
+ source_file: str = ""
118
+ language: Language = Language.PYTHON
119
+
120
+
121
+ @dataclass
122
+ class ParsedAnnotations:
123
+ """Complete set of parsed annotations from a source file."""
124
+ module: ModuleAnnotation
125
+ language: Language
126
+ source_file: str
127
+
128
+ def get_all_functions(self) -> List[FunctionAnnotation]:
129
+ """Get all function annotations (module-level and class methods)."""
130
+ functions = list(self.module.functions)
131
+ for cls in self.module.classes:
132
+ functions.extend(cls.methods)
133
+ return functions
134
+
135
+ def get_all_classes(self) -> List[ClassAnnotation]:
136
+ """Get all class annotations."""
137
+ return self.module.classes
138
+
139
+ def has_annotations(self) -> bool:
140
+ """Check if any annotations were found."""
141
+ return (
142
+ bool(self.module.module_purpose) or
143
+ bool(self.module.dependencies) or
144
+ bool(self.module.assumptions) or
145
+ bool(self.module.classes) or
146
+ bool(self.module.functions)
147
+ )
148
+
149
+
150
+ @dataclass
151
+ class VerificationResult:
152
+ """Result of annotation verification."""
153
+ valid: bool
154
+ errors: List[str] = field(default_factory=list)
155
+ warnings: List[str] = field(default_factory=list)
156
+
157
+ def add_error(self, error: str):
158
+ """Add an error."""
159
+ self.errors.append(error)
160
+ self.valid = False
161
+
162
+ def add_warning(self, warning: str):
163
+ """Add a warning."""
164
+ self.warnings.append(warning)
165
+
166
+ def __str__(self) -> str:
167
+ if self.valid and not self.warnings:
168
+ return "✓ All annotations are valid"
169
+
170
+ result = []
171
+ if self.errors:
172
+ result.append(f"❌ {len(self.errors)} error(s):")
173
+ for error in self.errors:
174
+ result.append(f" - {error}")
175
+
176
+ if self.warnings:
177
+ result.append(f"⚠️ {len(self.warnings)} warning(s):")
178
+ for warning in self.warnings:
179
+ result.append(f" - {warning}")
180
+
181
+ return "\n".join(result)
@@ -0,0 +1,128 @@
1
+ """
2
+ Command-line interface for DarkArts.
3
+ """
4
+
5
+ import sys
6
+ import argparse
7
+ from pathlib import Path
8
+ from darkarts.core import DarkArts
9
+
10
+
11
+ def main():
12
+ """Main CLI entry point."""
13
+ parser = argparse.ArgumentParser(
14
+ description="DarkArts - General-purpose AI reasoning platform",
15
+ formatter_class=argparse.RawDescriptionHelpFormatter,
16
+ epilog="""
17
+ Examples:
18
+ # Solve a math problem
19
+ darkarts solve "Solve x^2 + 5x + 6 = 0" --plugin math
20
+
21
+ # Generate code documentation
22
+ darkarts solve myfile.py --plugin voodocs --file
23
+
24
+ # Auto-detect plugin
25
+ darkarts solve "Find the largest n where n^2 < 100"
26
+
27
+ # List available plugins
28
+ darkarts list
29
+ """
30
+ )
31
+
32
+ subparsers = parser.add_subparsers(dest="command", help="Command to execute")
33
+
34
+ # Solve command
35
+ solve_parser = subparsers.add_parser("solve", help="Solve a problem")
36
+ solve_parser.add_argument("input", help="Problem text or file path")
37
+ solve_parser.add_argument("--plugin", "-p", help="Plugin to use (auto-detect if not specified)")
38
+ solve_parser.add_argument("--file", "-f", action="store_true", help="Treat input as file path")
39
+ solve_parser.add_argument("--explain", "-e", action="store_true", help="Generate explanation")
40
+ solve_parser.add_argument("--level", "-l", default="student",
41
+ choices=["student", "teacher", "expert"],
42
+ help="Explanation level")
43
+ solve_parser.add_argument("--output", "-o", help="Output file path")
44
+
45
+ # List command
46
+ list_parser = subparsers.add_parser("list", help="List available plugins")
47
+
48
+ # Parse arguments
49
+ args = parser.parse_args()
50
+
51
+ if not args.command:
52
+ parser.print_help()
53
+ return 1
54
+
55
+ # Initialize DarkArts
56
+ da = DarkArts()
57
+
58
+ if args.command == "list":
59
+ return list_plugins(da)
60
+
61
+ elif args.command == "solve":
62
+ return solve_problem(da, args)
63
+
64
+ return 0
65
+
66
+
67
+ def list_plugins(da: DarkArts) -> int:
68
+ """List available plugins."""
69
+ print("Available Plugins:")
70
+ print("=" * 60)
71
+
72
+ for plugin in da.list_plugins():
73
+ print(f"\n{plugin.name} v{plugin.version}")
74
+ print(f" {plugin.description}")
75
+ print(f" Capabilities: {', '.join(plugin.capabilities)}")
76
+ if plugin.dependencies:
77
+ print(f" Dependencies: {', '.join(plugin.dependencies)}")
78
+
79
+ print()
80
+ return 0
81
+
82
+
83
+ def solve_problem(da: DarkArts, args) -> int:
84
+ """Solve a problem."""
85
+ # Get input
86
+ if args.file:
87
+ try:
88
+ input_text = Path(args.input).read_text()
89
+ except Exception as e:
90
+ print(f"Error reading file: {e}", file=sys.stderr)
91
+ return 1
92
+ else:
93
+ input_text = args.input
94
+
95
+ # Solve
96
+ try:
97
+ result = da.solve(input_text, plugin=args.plugin)
98
+ except Exception as e:
99
+ print(f"Error: {e}", file=sys.stderr)
100
+ return 1
101
+
102
+ if not result.success:
103
+ print(f"Failed: {result.error}", file=sys.stderr)
104
+ return 1
105
+
106
+ # Output result
107
+ output_text = str(result.result)
108
+
109
+ if args.explain:
110
+ explanation = da.explain(result, level=args.level, plugin=args.plugin)
111
+ output_text = f"{output_text}\n\n{'=' * 60}\nExplanation:\n{'=' * 60}\n\n{explanation}"
112
+
113
+ # Write output
114
+ if args.output:
115
+ try:
116
+ Path(args.output).write_text(output_text)
117
+ print(f"Output written to: {args.output}")
118
+ except Exception as e:
119
+ print(f"Error writing output: {e}", file=sys.stderr)
120
+ return 1
121
+ else:
122
+ print(output_text)
123
+
124
+ return 0
125
+
126
+
127
+ if __name__ == "__main__":
128
+ sys.exit(main())
@@ -0,0 +1,32 @@
1
+ """
2
+ DarkArts Core - Domain-agnostic reasoning engine.
3
+ """
4
+
5
+ from .plugin import (
6
+ DarkArtsPlugin,
7
+ PluginMetadata,
8
+ PluginInput,
9
+ PluginOutput,
10
+ PluginCapability,
11
+ ParseError,
12
+ ExecutionError,
13
+ PluginError,
14
+ )
15
+ from .registry import PluginRegistry, get_global_registry
16
+ from .loader import PluginLoader
17
+ from .interface import DarkArts
18
+
19
+ __all__ = [
20
+ "DarkArtsPlugin",
21
+ "PluginMetadata",
22
+ "PluginInput",
23
+ "PluginOutput",
24
+ "PluginCapability",
25
+ "ParseError",
26
+ "ExecutionError",
27
+ "PluginError",
28
+ "PluginRegistry",
29
+ "get_global_registry",
30
+ "PluginLoader",
31
+ "DarkArts",
32
+ ]
@@ -0,0 +1,256 @@
1
+ """
2
+ Unified interface to the DarkArts platform.
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+ from .plugin import DarkArtsPlugin, PluginInput, PluginOutput, PluginMetadata, PluginError
7
+ from .registry import PluginRegistry, get_global_registry
8
+ from .loader import PluginLoader
9
+
10
+
11
+ class DarkArts:
12
+ """Unified interface to DarkArts platform."""
13
+
14
+ def __init__(self, plugin: Optional[str] = None, registry: Optional[PluginRegistry] = None):
15
+ """
16
+ Initialize DarkArts interface.
17
+
18
+ Args:
19
+ plugin: Optional default plugin name
20
+ registry: Optional custom registry (uses global if not provided)
21
+ """
22
+ self.registry = registry or get_global_registry()
23
+ self.loader = PluginLoader(self.registry)
24
+ self.default_plugin = plugin
25
+
26
+ # Try to load built-in plugins if registry is empty
27
+ if len(self.registry) == 0:
28
+ try:
29
+ self.loader.load_and_register_builtin()
30
+ except Exception:
31
+ pass # Silently fail if no built-in plugins available
32
+
33
+ def solve(self, problem: str, plugin: Optional[str] = None, **options) -> PluginOutput:
34
+ """
35
+ Solve a problem using specified or default plugin.
36
+
37
+ Args:
38
+ problem: The problem to solve
39
+ plugin: Optional plugin name (uses default if not provided)
40
+ **options: Additional options to pass to the plugin
41
+
42
+ Returns:
43
+ Plugin output with results
44
+
45
+ Raises:
46
+ PluginError: If plugin not found or execution fails
47
+ """
48
+ plugin_name = plugin or self.default_plugin
49
+
50
+ if not plugin_name:
51
+ # Try to auto-detect plugin
52
+ plugin_name = self._auto_detect_plugin(problem)
53
+
54
+ if not plugin_name:
55
+ raise PluginError("No plugin specified and auto-detection failed")
56
+
57
+ plugin_obj = self.registry.get(plugin_name)
58
+
59
+ if not plugin_obj:
60
+ raise PluginError(f"Plugin '{plugin_name}' not found")
61
+
62
+ # Create input
63
+ input_obj = PluginInput(content=problem, options=options)
64
+
65
+ # Validate input
66
+ if not plugin_obj.validate_input(input_obj):
67
+ return PluginOutput(
68
+ success=False,
69
+ result=None,
70
+ error="Invalid input",
71
+ )
72
+
73
+ # Preprocess
74
+ input_obj = plugin_obj.preprocess(input_obj)
75
+
76
+ try:
77
+ # Execute pipeline
78
+ parsed = plugin_obj.parse(input_obj)
79
+ analysis = plugin_obj.analyze(parsed)
80
+ output = plugin_obj.execute(parsed, analysis)
81
+
82
+ # Postprocess
83
+ output = plugin_obj.postprocess(output)
84
+
85
+ # Learn from execution
86
+ if plugin_obj.is_learning_enabled():
87
+ plugin_obj.learn(input_obj, output)
88
+
89
+ return output
90
+
91
+ except Exception as e:
92
+ return PluginOutput(
93
+ success=False,
94
+ result=None,
95
+ error=str(e),
96
+ )
97
+
98
+ def explain(self, result: PluginOutput, level: str = "student",
99
+ plugin: Optional[str] = None) -> str:
100
+ """
101
+ Generate explanation for a result.
102
+
103
+ Args:
104
+ result: The result to explain
105
+ level: Explanation level (student, teacher, expert)
106
+ plugin: Optional plugin name (uses default if not provided)
107
+
108
+ Returns:
109
+ Natural language explanation
110
+
111
+ Raises:
112
+ PluginError: If plugin not found
113
+ """
114
+ plugin_name = plugin or self.default_plugin
115
+
116
+ if not plugin_name:
117
+ raise PluginError("No plugin specified")
118
+
119
+ plugin_obj = self.registry.get(plugin_name)
120
+
121
+ if not plugin_obj:
122
+ raise PluginError(f"Plugin '{plugin_name}' not found")
123
+
124
+ return plugin_obj.explain(result, level)
125
+
126
+ def list_plugins(self) -> List[PluginMetadata]:
127
+ """
128
+ List all available plugins.
129
+
130
+ Returns:
131
+ List of plugin metadata
132
+ """
133
+ return self.registry.list()
134
+
135
+ def get_plugin(self, name: str) -> Optional[DarkArtsPlugin]:
136
+ """
137
+ Get a plugin by name.
138
+
139
+ Args:
140
+ name: Plugin name
141
+
142
+ Returns:
143
+ The plugin, or None if not found
144
+ """
145
+ return self.registry.get(name)
146
+
147
+ def has_plugin(self, name: str) -> bool:
148
+ """
149
+ Check if a plugin is available.
150
+
151
+ Args:
152
+ name: Plugin name
153
+
154
+ Returns:
155
+ True if available, False otherwise
156
+ """
157
+ return self.registry.has_plugin(name)
158
+
159
+ def register_plugin(self, plugin: DarkArtsPlugin) -> None:
160
+ """
161
+ Register a new plugin.
162
+
163
+ Args:
164
+ plugin: The plugin to register
165
+
166
+ Raises:
167
+ PluginError: If registration fails
168
+ """
169
+ self.registry.register(plugin)
170
+
171
+ def load_plugin(self, source: str, source_type: str = "module") -> DarkArtsPlugin:
172
+ """
173
+ Load and register a plugin.
174
+
175
+ Args:
176
+ source: Module path, file path, or directory path
177
+ source_type: Type of source ('module', 'file', or 'directory')
178
+
179
+ Returns:
180
+ The loaded plugin (or list of plugins for directory)
181
+
182
+ Raises:
183
+ PluginError: If loading fails
184
+ """
185
+ return self.loader.load_and_register(source, source_type)
186
+
187
+ def set_default_plugin(self, name: str) -> None:
188
+ """
189
+ Set the default plugin.
190
+
191
+ Args:
192
+ name: Plugin name
193
+
194
+ Raises:
195
+ PluginError: If plugin not found
196
+ """
197
+ if not self.registry.has_plugin(name):
198
+ raise PluginError(f"Plugin '{name}' not found")
199
+
200
+ self.default_plugin = name
201
+
202
+ def enable_learning(self, plugin: Optional[str] = None, enabled: bool = True) -> None:
203
+ """
204
+ Enable or disable learning for a plugin.
205
+
206
+ Args:
207
+ plugin: Optional plugin name (applies to default if not provided)
208
+ enabled: Whether to enable learning
209
+
210
+ Raises:
211
+ PluginError: If plugin not found
212
+ """
213
+ plugin_name = plugin or self.default_plugin
214
+
215
+ if not plugin_name:
216
+ raise PluginError("No plugin specified")
217
+
218
+ plugin_obj = self.registry.get(plugin_name)
219
+
220
+ if not plugin_obj:
221
+ raise PluginError(f"Plugin '{plugin_name}' not found")
222
+
223
+ plugin_obj.enable_learning(enabled)
224
+
225
+ def _auto_detect_plugin(self, problem: str) -> Optional[str]:
226
+ """
227
+ Auto-detect which plugin to use based on problem content.
228
+
229
+ Args:
230
+ problem: The problem text
231
+
232
+ Returns:
233
+ Plugin name, or None if detection fails
234
+ """
235
+ # Simple heuristics for now
236
+ problem_lower = problem.lower()
237
+
238
+ # Check for math keywords
239
+ math_keywords = ["solve", "maximize", "minimize", "find", "calculate", "compute"]
240
+ if any(keyword in problem_lower for keyword in math_keywords):
241
+ if self.registry.has_plugin("math"):
242
+ return "math"
243
+
244
+ # Check for code keywords
245
+ code_keywords = ["def ", "class ", "import ", "function", "method"]
246
+ if any(keyword in problem_lower for keyword in code_keywords):
247
+ if self.registry.has_plugin("voodocs"):
248
+ return "voodocs"
249
+
250
+ # Default to math if available
251
+ if self.registry.has_plugin("math"):
252
+ return "math"
253
+
254
+ # Return first available plugin
255
+ plugins = self.registry.list_names()
256
+ return plugins[0] if plugins else None