@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,231 @@
1
+ """
2
+ Plugin loader for discovering and loading plugins.
3
+ """
4
+
5
+ import importlib
6
+ import importlib.util
7
+ import os
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import List, Optional
11
+ from .plugin import DarkArtsPlugin, PluginError
12
+ from .registry import PluginRegistry
13
+
14
+
15
+ class PluginLoader:
16
+ """Load plugins from various sources."""
17
+
18
+ def __init__(self, registry: PluginRegistry):
19
+ """
20
+ Initialize the loader.
21
+
22
+ Args:
23
+ registry: The plugin registry to register loaded plugins
24
+ """
25
+ self.registry = registry
26
+
27
+ def load_from_module(self, module_path: str, class_name: Optional[str] = None) -> DarkArtsPlugin:
28
+ """
29
+ Load plugin from Python module.
30
+
31
+ Args:
32
+ module_path: Python module path (e.g., 'darkarts.plugins.math')
33
+ class_name: Optional class name (if not provided, looks for DarkArtsPlugin subclass)
34
+
35
+ Returns:
36
+ The loaded plugin instance
37
+
38
+ Raises:
39
+ PluginError: If loading fails
40
+ """
41
+ try:
42
+ module = importlib.import_module(module_path)
43
+ except ImportError as e:
44
+ raise PluginError(f"Failed to import module '{module_path}': {e}")
45
+
46
+ if class_name:
47
+ if not hasattr(module, class_name):
48
+ raise PluginError(f"Module '{module_path}' has no class '{class_name}'")
49
+ plugin_class = getattr(module, class_name)
50
+ else:
51
+ # Find DarkArtsPlugin subclass
52
+ plugin_class = None
53
+ for attr_name in dir(module):
54
+ attr = getattr(module, attr_name)
55
+ if (isinstance(attr, type) and
56
+ issubclass(attr, DarkArtsPlugin) and
57
+ attr is not DarkArtsPlugin):
58
+ plugin_class = attr
59
+ break
60
+
61
+ if not plugin_class:
62
+ raise PluginError(f"No DarkArtsPlugin subclass found in '{module_path}'")
63
+
64
+ try:
65
+ plugin = plugin_class()
66
+ except Exception as e:
67
+ raise PluginError(f"Failed to instantiate plugin: {e}")
68
+
69
+ return plugin
70
+
71
+ def load_from_file(self, file_path: str, class_name: Optional[str] = None) -> DarkArtsPlugin:
72
+ """
73
+ Load plugin from Python file.
74
+
75
+ Args:
76
+ file_path: Path to Python file
77
+ class_name: Optional class name
78
+
79
+ Returns:
80
+ The loaded plugin instance
81
+
82
+ Raises:
83
+ PluginError: If loading fails
84
+ """
85
+ file_path = Path(file_path).resolve()
86
+
87
+ if not file_path.exists():
88
+ raise PluginError(f"File not found: {file_path}")
89
+
90
+ if not file_path.suffix == '.py':
91
+ raise PluginError(f"File must be a Python file: {file_path}")
92
+
93
+ # Load module from file
94
+ module_name = file_path.stem
95
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
96
+
97
+ if not spec or not spec.loader:
98
+ raise PluginError(f"Failed to load spec from file: {file_path}")
99
+
100
+ module = importlib.util.module_from_spec(spec)
101
+ sys.modules[module_name] = module
102
+
103
+ try:
104
+ spec.loader.exec_module(module)
105
+ except Exception as e:
106
+ raise PluginError(f"Failed to execute module: {e}")
107
+
108
+ # Find and instantiate plugin class
109
+ if class_name:
110
+ if not hasattr(module, class_name):
111
+ raise PluginError(f"Module has no class '{class_name}'")
112
+ plugin_class = getattr(module, class_name)
113
+ else:
114
+ plugin_class = None
115
+ for attr_name in dir(module):
116
+ attr = getattr(module, attr_name)
117
+ if (isinstance(attr, type) and
118
+ issubclass(attr, DarkArtsPlugin) and
119
+ attr is not DarkArtsPlugin):
120
+ plugin_class = attr
121
+ break
122
+
123
+ if not plugin_class:
124
+ raise PluginError(f"No DarkArtsPlugin subclass found in file")
125
+
126
+ try:
127
+ plugin = plugin_class()
128
+ except Exception as e:
129
+ raise PluginError(f"Failed to instantiate plugin: {e}")
130
+
131
+ return plugin
132
+
133
+ def load_from_directory(self, directory: str) -> List[DarkArtsPlugin]:
134
+ """
135
+ Load all plugins from a directory.
136
+
137
+ Args:
138
+ directory: Path to directory containing plugin files
139
+
140
+ Returns:
141
+ List of loaded plugins
142
+ """
143
+ directory = Path(directory).resolve()
144
+
145
+ if not directory.exists():
146
+ raise PluginError(f"Directory not found: {directory}")
147
+
148
+ if not directory.is_dir():
149
+ raise PluginError(f"Not a directory: {directory}")
150
+
151
+ plugins = []
152
+
153
+ for file_path in directory.glob("*.py"):
154
+ if file_path.name.startswith("_"):
155
+ continue # Skip __init__.py and private files
156
+
157
+ try:
158
+ plugin = self.load_from_file(str(file_path))
159
+ plugins.append(plugin)
160
+ except PluginError as e:
161
+ print(f"Warning: Failed to load plugin from {file_path}: {e}")
162
+ continue
163
+
164
+ return plugins
165
+
166
+ def load_builtin_plugins(self) -> List[DarkArtsPlugin]:
167
+ """
168
+ Load built-in plugins.
169
+
170
+ Returns:
171
+ List of loaded built-in plugins
172
+ """
173
+ plugins = []
174
+
175
+ # Try to load math plugin
176
+ try:
177
+ plugin = self.load_from_module("darkarts.plugins.math", "MathPlugin")
178
+ plugins.append(plugin)
179
+ except PluginError:
180
+ pass # Math plugin not yet implemented
181
+
182
+ # Try to load voodocs plugin
183
+ try:
184
+ plugin = self.load_from_module("darkarts.plugins.voodocs", "VooDocsPlugin")
185
+ plugins.append(plugin)
186
+ except PluginError:
187
+ pass # VooDocs plugin not yet implemented
188
+
189
+ return plugins
190
+
191
+ def load_and_register(self, source: str, source_type: str = "module") -> DarkArtsPlugin:
192
+ """
193
+ Load a plugin and register it.
194
+
195
+ Args:
196
+ source: Module path, file path, or directory path
197
+ source_type: Type of source ('module', 'file', or 'directory')
198
+
199
+ Returns:
200
+ The loaded plugin (or list of plugins for directory)
201
+
202
+ Raises:
203
+ PluginError: If loading or registration fails
204
+ """
205
+ if source_type == "module":
206
+ plugin = self.load_from_module(source)
207
+ self.registry.register(plugin)
208
+ return plugin
209
+ elif source_type == "file":
210
+ plugin = self.load_from_file(source)
211
+ self.registry.register(plugin)
212
+ return plugin
213
+ elif source_type == "directory":
214
+ plugins = self.load_from_directory(source)
215
+ for plugin in plugins:
216
+ self.registry.register(plugin)
217
+ return plugins
218
+ else:
219
+ raise PluginError(f"Unknown source type: {source_type}")
220
+
221
+ def load_and_register_builtin(self) -> List[DarkArtsPlugin]:
222
+ """
223
+ Load and register all built-in plugins.
224
+
225
+ Returns:
226
+ List of loaded plugins
227
+ """
228
+ plugins = self.load_builtin_plugins()
229
+ for plugin in plugins:
230
+ self.registry.register(plugin)
231
+ return plugins
@@ -0,0 +1,215 @@
1
+ """
2
+ Core plugin system for DarkArts.
3
+
4
+ This module defines the base classes and interfaces for the DarkArts plugin system.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from dataclasses import dataclass, field
9
+ from typing import Any, Dict, List, Optional
10
+ from enum import Enum
11
+
12
+
13
+ class PluginCapability(Enum):
14
+ """Standard plugin capabilities."""
15
+ PARSE = "parse"
16
+ ANALYZE = "analyze"
17
+ EXECUTE = "execute"
18
+ EXPLAIN = "explain"
19
+ LEARN = "learn"
20
+ MULTI_STEP = "multi_step"
21
+
22
+
23
+ @dataclass
24
+ class PluginMetadata:
25
+ """Metadata about a plugin."""
26
+ name: str
27
+ version: str
28
+ description: str
29
+ author: str
30
+ dependencies: List[str] = field(default_factory=list)
31
+ capabilities: List[str] = field(default_factory=list)
32
+
33
+ def has_capability(self, capability: str) -> bool:
34
+ """Check if plugin has a specific capability."""
35
+ return capability in self.capabilities
36
+
37
+
38
+ @dataclass
39
+ class PluginInput:
40
+ """Input to a plugin."""
41
+ content: str
42
+ context: Dict[str, Any] = field(default_factory=dict)
43
+ options: Dict[str, Any] = field(default_factory=dict)
44
+
45
+ def get_option(self, key: str, default: Any = None) -> Any:
46
+ """Get an option value with default."""
47
+ return self.options.get(key, default)
48
+
49
+
50
+ @dataclass
51
+ class PluginOutput:
52
+ """Output from a plugin."""
53
+ success: bool
54
+ result: Any
55
+ explanation: Optional[str] = None
56
+ metadata: Dict[str, Any] = field(default_factory=dict)
57
+ error: Optional[str] = None
58
+ steps: List[Dict[str, Any]] = field(default_factory=list)
59
+
60
+ def add_step(self, name: str, description: str, result: Any = None):
61
+ """Add an execution step."""
62
+ self.steps.append({
63
+ "name": name,
64
+ "description": description,
65
+ "result": result,
66
+ })
67
+
68
+ def add_metadata(self, key: str, value: Any):
69
+ """Add metadata."""
70
+ self.metadata[key] = value
71
+
72
+
73
+ class DarkArtsPlugin(ABC):
74
+ """Base class for all DarkArts plugins."""
75
+
76
+ def __init__(self):
77
+ """Initialize the plugin."""
78
+ self._learning_enabled = True
79
+
80
+ @property
81
+ @abstractmethod
82
+ def metadata(self) -> PluginMetadata:
83
+ """Return plugin metadata."""
84
+ pass
85
+
86
+ @abstractmethod
87
+ def parse(self, input: PluginInput) -> Any:
88
+ """
89
+ Parse domain-specific input into internal representation.
90
+
91
+ Args:
92
+ input: The input to parse
93
+
94
+ Returns:
95
+ Parsed representation (domain-specific)
96
+
97
+ Raises:
98
+ ParseError: If parsing fails
99
+ """
100
+ pass
101
+
102
+ @abstractmethod
103
+ def analyze(self, parsed: Any) -> Dict[str, Any]:
104
+ """
105
+ Analyze the parsed input for complexity, requirements, etc.
106
+
107
+ Args:
108
+ parsed: The parsed representation
109
+
110
+ Returns:
111
+ Analysis results as a dictionary
112
+ """
113
+ pass
114
+
115
+ @abstractmethod
116
+ def execute(self, parsed: Any, analysis: Dict[str, Any]) -> PluginOutput:
117
+ """
118
+ Execute the main processing logic.
119
+
120
+ Args:
121
+ parsed: The parsed representation
122
+ analysis: Analysis results from analyze()
123
+
124
+ Returns:
125
+ Plugin output with results
126
+ """
127
+ pass
128
+
129
+ def explain(self, output: PluginOutput, level: str = "student") -> str:
130
+ """
131
+ Generate natural language explanation (optional override).
132
+
133
+ Args:
134
+ output: The plugin output to explain
135
+ level: Explanation level (student, teacher, expert)
136
+
137
+ Returns:
138
+ Natural language explanation
139
+ """
140
+ if output.explanation:
141
+ return output.explanation
142
+
143
+ if not output.success:
144
+ return f"Execution failed: {output.error}"
145
+
146
+ return f"Result: {output.result}"
147
+
148
+ def learn(self, input: PluginInput, output: PluginOutput) -> None:
149
+ """
150
+ Learn from this execution (optional override).
151
+
152
+ Args:
153
+ input: The input that was processed
154
+ output: The output that was generated
155
+ """
156
+ pass
157
+
158
+ def enable_learning(self, enabled: bool = True):
159
+ """Enable or disable learning for this plugin."""
160
+ self._learning_enabled = enabled
161
+
162
+ def is_learning_enabled(self) -> bool:
163
+ """Check if learning is enabled."""
164
+ return self._learning_enabled
165
+
166
+ def validate_input(self, input: PluginInput) -> bool:
167
+ """
168
+ Validate input before processing (optional override).
169
+
170
+ Args:
171
+ input: The input to validate
172
+
173
+ Returns:
174
+ True if valid, False otherwise
175
+ """
176
+ return bool(input.content and input.content.strip())
177
+
178
+ def preprocess(self, input: PluginInput) -> PluginInput:
179
+ """
180
+ Preprocess input before parsing (optional override).
181
+
182
+ Args:
183
+ input: The input to preprocess
184
+
185
+ Returns:
186
+ Preprocessed input
187
+ """
188
+ return input
189
+
190
+ def postprocess(self, output: PluginOutput) -> PluginOutput:
191
+ """
192
+ Postprocess output before returning (optional override).
193
+
194
+ Args:
195
+ output: The output to postprocess
196
+
197
+ Returns:
198
+ Postprocessed output
199
+ """
200
+ return output
201
+
202
+
203
+ class ParseError(Exception):
204
+ """Exception raised when parsing fails."""
205
+ pass
206
+
207
+
208
+ class ExecutionError(Exception):
209
+ """Exception raised when execution fails."""
210
+ pass
211
+
212
+
213
+ class PluginError(Exception):
214
+ """Base exception for plugin-related errors."""
215
+ pass
@@ -0,0 +1,146 @@
1
+ """
2
+ Plugin registry for managing and discovering plugins.
3
+ """
4
+
5
+ from typing import Dict, List, Optional
6
+ from .plugin import DarkArtsPlugin, PluginMetadata, PluginError
7
+
8
+
9
+ class PluginRegistry:
10
+ """Registry for managing plugins."""
11
+
12
+ def __init__(self):
13
+ """Initialize the registry."""
14
+ self._plugins: Dict[str, DarkArtsPlugin] = {}
15
+ self._metadata: Dict[str, PluginMetadata] = {}
16
+
17
+ def register(self, plugin: DarkArtsPlugin) -> None:
18
+ """
19
+ Register a plugin.
20
+
21
+ Args:
22
+ plugin: The plugin to register
23
+
24
+ Raises:
25
+ PluginError: If plugin with same name already registered
26
+ """
27
+ metadata = plugin.metadata
28
+
29
+ if metadata.name in self._plugins:
30
+ raise PluginError(f"Plugin '{metadata.name}' is already registered")
31
+
32
+ # Check dependencies
33
+ for dep in metadata.dependencies:
34
+ if dep not in self._plugins:
35
+ raise PluginError(
36
+ f"Plugin '{metadata.name}' depends on '{dep}' which is not registered"
37
+ )
38
+
39
+ self._plugins[metadata.name] = plugin
40
+ self._metadata[metadata.name] = metadata
41
+
42
+ def unregister(self, name: str) -> None:
43
+ """
44
+ Unregister a plugin.
45
+
46
+ Args:
47
+ name: Name of the plugin to unregister
48
+ """
49
+ if name in self._plugins:
50
+ del self._plugins[name]
51
+ del self._metadata[name]
52
+
53
+ def get(self, name: str) -> Optional[DarkArtsPlugin]:
54
+ """
55
+ Get a plugin by name.
56
+
57
+ Args:
58
+ name: Name of the plugin
59
+
60
+ Returns:
61
+ The plugin, or None if not found
62
+ """
63
+ return self._plugins.get(name)
64
+
65
+ def get_metadata(self, name: str) -> Optional[PluginMetadata]:
66
+ """
67
+ Get plugin metadata by name.
68
+
69
+ Args:
70
+ name: Name of the plugin
71
+
72
+ Returns:
73
+ The plugin metadata, or None if not found
74
+ """
75
+ return self._metadata.get(name)
76
+
77
+ def list(self) -> List[PluginMetadata]:
78
+ """
79
+ List all registered plugins.
80
+
81
+ Returns:
82
+ List of plugin metadata
83
+ """
84
+ return list(self._metadata.values())
85
+
86
+ def list_names(self) -> List[str]:
87
+ """
88
+ List all registered plugin names.
89
+
90
+ Returns:
91
+ List of plugin names
92
+ """
93
+ return list(self._plugins.keys())
94
+
95
+ def has_plugin(self, name: str) -> bool:
96
+ """
97
+ Check if a plugin is registered.
98
+
99
+ Args:
100
+ name: Name of the plugin
101
+
102
+ Returns:
103
+ True if registered, False otherwise
104
+ """
105
+ return name in self._plugins
106
+
107
+ def has_capability(self, capability: str) -> List[str]:
108
+ """
109
+ Find plugins with a specific capability.
110
+
111
+ Args:
112
+ capability: The capability to search for
113
+
114
+ Returns:
115
+ List of plugin names with that capability
116
+ """
117
+ return [
118
+ name for name, meta in self._metadata.items()
119
+ if capability in meta.capabilities
120
+ ]
121
+
122
+ def clear(self) -> None:
123
+ """Clear all registered plugins."""
124
+ self._plugins.clear()
125
+ self._metadata.clear()
126
+
127
+ def __len__(self) -> int:
128
+ """Return number of registered plugins."""
129
+ return len(self._plugins)
130
+
131
+ def __contains__(self, name: str) -> bool:
132
+ """Check if plugin is registered."""
133
+ return name in self._plugins
134
+
135
+ def __iter__(self):
136
+ """Iterate over plugin names."""
137
+ return iter(self._plugins.keys())
138
+
139
+
140
+ # Global registry instance
141
+ _global_registry = PluginRegistry()
142
+
143
+
144
+ def get_global_registry() -> PluginRegistry:
145
+ """Get the global plugin registry."""
146
+ return _global_registry
@@ -0,0 +1,51 @@
1
+ """
2
+ VooDocs Custom Exceptions
3
+
4
+ Provides specific exception types for better error handling and user feedback.
5
+ """
6
+
7
+
8
+ class VooDocsError(Exception):
9
+ """Base exception for all VooDocs errors."""
10
+ pass
11
+
12
+
13
+ class ParserError(VooDocsError):
14
+ """Raised when a parser encounters an error."""
15
+ pass
16
+
17
+
18
+ class ParserNotBuiltError(ParserError):
19
+ """Raised when the TypeScript parser hasn't been built."""
20
+
21
+ def __init__(self, parser_name="TypeScript"):
22
+ self.parser_name = parser_name
23
+ super().__init__(
24
+ f"{parser_name} parser not built. "
25
+ f"Run: cd lib/darkarts/parsers/typescript && npm install && npm run build"
26
+ )
27
+
28
+
29
+ class AnnotationError(VooDocsError):
30
+ """Raised when annotation parsing fails."""
31
+ pass
32
+
33
+
34
+ class InvalidAnnotationError(AnnotationError):
35
+ """Raised when an annotation has invalid syntax or structure."""
36
+ pass
37
+
38
+
39
+ class GeneratorError(VooDocsError):
40
+ """Raised when a generator encounters an error."""
41
+ pass
42
+
43
+
44
+ class ConfigurationError(VooDocsError):
45
+ """Raised when configuration is invalid or missing."""
46
+ pass
47
+
48
+
49
+ class FileNotFoundError(VooDocsError):
50
+ """Raised when a required file is not found."""
51
+ pass
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI wrapper for the TypeScript annotation parser
4
+ *
5
+ * Usage: darkarts-ts-parser <file_path>
6
+ * Output: JSON to stdout
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;GAKG"}