@voria/cli 0.0.2

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 (67) hide show
  1. package/README.md +439 -0
  2. package/bin/voria +730 -0
  3. package/docs/ARCHITECTURE.md +419 -0
  4. package/docs/CHANGELOG.md +189 -0
  5. package/docs/CONTRIBUTING.md +447 -0
  6. package/docs/DESIGN_DECISIONS.md +380 -0
  7. package/docs/DEVELOPMENT.md +535 -0
  8. package/docs/EXAMPLES.md +434 -0
  9. package/docs/INSTALL.md +335 -0
  10. package/docs/IPC_PROTOCOL.md +310 -0
  11. package/docs/LLM_INTEGRATION.md +416 -0
  12. package/docs/MODULES.md +470 -0
  13. package/docs/PERFORMANCE.md +346 -0
  14. package/docs/PLUGINS.md +432 -0
  15. package/docs/QUICKSTART.md +184 -0
  16. package/docs/README.md +133 -0
  17. package/docs/ROADMAP.md +346 -0
  18. package/docs/SECURITY.md +334 -0
  19. package/docs/TROUBLESHOOTING.md +565 -0
  20. package/docs/USER_GUIDE.md +700 -0
  21. package/package.json +63 -0
  22. package/python/voria/__init__.py +8 -0
  23. package/python/voria/__pycache__/__init__.cpython-312.pyc +0 -0
  24. package/python/voria/__pycache__/engine.cpython-312.pyc +0 -0
  25. package/python/voria/core/__init__.py +1 -0
  26. package/python/voria/core/__pycache__/__init__.cpython-312.pyc +0 -0
  27. package/python/voria/core/__pycache__/setup.cpython-312.pyc +0 -0
  28. package/python/voria/core/agent/__init__.py +9 -0
  29. package/python/voria/core/agent/__pycache__/__init__.cpython-312.pyc +0 -0
  30. package/python/voria/core/agent/__pycache__/loop.cpython-312.pyc +0 -0
  31. package/python/voria/core/agent/loop.py +343 -0
  32. package/python/voria/core/executor/__init__.py +19 -0
  33. package/python/voria/core/executor/__pycache__/__init__.cpython-312.pyc +0 -0
  34. package/python/voria/core/executor/__pycache__/executor.cpython-312.pyc +0 -0
  35. package/python/voria/core/executor/executor.py +431 -0
  36. package/python/voria/core/github/__init__.py +33 -0
  37. package/python/voria/core/github/__pycache__/__init__.cpython-312.pyc +0 -0
  38. package/python/voria/core/github/__pycache__/client.cpython-312.pyc +0 -0
  39. package/python/voria/core/github/client.py +438 -0
  40. package/python/voria/core/llm/__init__.py +55 -0
  41. package/python/voria/core/llm/__pycache__/__init__.cpython-312.pyc +0 -0
  42. package/python/voria/core/llm/__pycache__/base.cpython-312.pyc +0 -0
  43. package/python/voria/core/llm/__pycache__/claude_provider.cpython-312.pyc +0 -0
  44. package/python/voria/core/llm/__pycache__/gemini_provider.cpython-312.pyc +0 -0
  45. package/python/voria/core/llm/__pycache__/modal_provider.cpython-312.pyc +0 -0
  46. package/python/voria/core/llm/__pycache__/model_discovery.cpython-312.pyc +0 -0
  47. package/python/voria/core/llm/__pycache__/openai_provider.cpython-312.pyc +0 -0
  48. package/python/voria/core/llm/base.py +152 -0
  49. package/python/voria/core/llm/claude_provider.py +188 -0
  50. package/python/voria/core/llm/gemini_provider.py +148 -0
  51. package/python/voria/core/llm/modal_provider.py +228 -0
  52. package/python/voria/core/llm/model_discovery.py +289 -0
  53. package/python/voria/core/llm/openai_provider.py +146 -0
  54. package/python/voria/core/patcher/__init__.py +9 -0
  55. package/python/voria/core/patcher/__pycache__/__init__.cpython-312.pyc +0 -0
  56. package/python/voria/core/patcher/__pycache__/patcher.cpython-312.pyc +0 -0
  57. package/python/voria/core/patcher/patcher.py +375 -0
  58. package/python/voria/core/planner/__init__.py +1 -0
  59. package/python/voria/core/setup.py +201 -0
  60. package/python/voria/core/token_manager/__init__.py +29 -0
  61. package/python/voria/core/token_manager/__pycache__/__init__.cpython-312.pyc +0 -0
  62. package/python/voria/core/token_manager/__pycache__/manager.cpython-312.pyc +0 -0
  63. package/python/voria/core/token_manager/manager.py +241 -0
  64. package/python/voria/engine.py +1185 -0
  65. package/python/voria/plugins/__init__.py +1 -0
  66. package/python/voria/plugins/python/__init__.py +1 -0
  67. package/python/voria/plugins/typescript/__init__.py +1 -0
@@ -0,0 +1,470 @@
1
+ # Module Documentation
2
+
3
+ Detailed documentation of voria's core modules and their APIs.
4
+
5
+ ## Python Core Modules
6
+
7
+ ### `llm/` - LLM Provider Integration
8
+
9
+ **Purpose**: Abstract interface to multiple LLM providers.
10
+
11
+ **Key Classes**:
12
+ - `BaseLLMProvider` - Abstract base class
13
+ - `ModelDiscovery` - Runtime model discovery
14
+ - `ProviderSetup` - Interactive configuration
15
+ - `ModelInfo` - Model metadata
16
+ - Provider implementations:
17
+ - `ModalProvider` - Modal Z.ai backend
18
+ - `OpenAIProvider` - OpenAI API
19
+ - `GeminiProvider` - Google Gemini
20
+ - `ClaudeProvider` - Anthropic Claude
21
+
22
+ **Main Methods**:
23
+ ```python
24
+ # Discover available models
25
+ models = await LLMProviderFactory.discover_models("openai", api_key)
26
+
27
+ # Create provider instance
28
+ provider = LLMProviderFactory.create("openai", api_key, "gpt-5.4")
29
+
30
+ # Use provider
31
+ await provider.plan("Issue description")
32
+ await provider.generate_patch(issue_context, plan)
33
+ await provider.analyze_test_failure(test_output, code)
34
+ ```
35
+
36
+ **Configuration**:
37
+ - Stored in `~/.voria/providers.json`
38
+ - Supports environment variable fallback
39
+ - Interactive setup via `python3 -m voria.core.setup`
40
+
41
+ **Token Tracking**:
42
+ ```python
43
+ response = await provider.call_llm(prompt)
44
+ print(response.token_usage) # {"used": 1000, "max": 4000}
45
+ ```
46
+
47
+ ### `patcher/` - Code Patching
48
+
49
+ **Purpose**: Parse and apply unified diffs.
50
+
51
+ **Key Classes**:
52
+ - `UnifiedDiffParser` - Parse diff format
53
+ - `PatchHunk` - Individual hunk data
54
+ - `CodePatcher` - Apply patches with rollback
55
+
56
+ **Main Methods**:
57
+ ```python
58
+ # Parse diff
59
+ hunks = UnifiedDiffParser.parse(unified_diff_string)
60
+
61
+ # Create patcher
62
+ patcher = CodePatcher(repo_path)
63
+
64
+ # Apply patch
65
+ result = await patcher.apply_patch(diff_content, strategy="fuzzy")
66
+
67
+ # Rollback if needed
68
+ await patcher.rollback_patch(file_path, backup_path)
69
+
70
+ # Cleanup old backups
71
+ await patcher.cleanup_backups(keep_count=10)
72
+ ```
73
+
74
+ **Features**:
75
+ - Auto-backup before applying
76
+ - Strict/fuzzy matching strategies
77
+ - Automatic rollback on failure
78
+ - Backup retention management
79
+ - Located in `~/.voria/backups/`
80
+
81
+ ### `executor/` - Test Execution
82
+
83
+ **Purpose**: Detect and run test suites.
84
+
85
+ **Key Classes**:
86
+ - `TestExecutor` - Main coordinator
87
+ - `TestStatus` - Result enum
88
+ - `TestResult` - Individual test result
89
+ - `TestSuiteResult` - Full suite results
90
+ - Framework parsers:
91
+ - `PytestParser` - Python pytest
92
+ - `JestParser` - JavaScript Jest
93
+ - (extensible for others)
94
+
95
+ **Main Methods**:
96
+ ```python
97
+ # Create executor
98
+ executor = TestExecutor(repo_path)
99
+
100
+ # Detect framework
101
+ framework = await executor.detect_framework() # "pytest"|"jest"|None
102
+
103
+ # Run tests
104
+ result = await executor.run_tests()
105
+
106
+ # Format results
107
+ output = executor.format_results(result)
108
+ ```
109
+
110
+ **Results Structure**:
111
+ ```python
112
+ TestSuiteResult(
113
+ framework="pytest",
114
+ total=25,
115
+ passed=24,
116
+ failed=1,
117
+ skipped=0,
118
+ duration=2.5,
119
+ results=[
120
+ TestResult(name="test_api", status=TestStatus.PASSED, duration=0.1),
121
+ TestResult(name="test_db", status=TestStatus.FAILED, message="timeout")
122
+ ]
123
+ )
124
+ ```
125
+
126
+ ### `agent/` - Orchestration
127
+
128
+ **Purpose**: Main agent loop for issue fixing.
129
+
130
+ **Key Classes**:
131
+ - `AgentLoop` - Core orchestrator
132
+ - `LoopState` - State tracking
133
+ - `LoopAction` - Action enum
134
+
135
+ **Main Methods**:
136
+ ```python
137
+ # Create agent
138
+ loop = AgentLoop(
139
+ provider_name="openai",
140
+ api_key="sk-...",
141
+ repo_path="/repo"
142
+ )
143
+
144
+ # Initialize (setup provider)
145
+ await loop.initialize("gpt-5.4")
146
+
147
+ # Run full loop
148
+ result = await loop.run(
149
+ issue_id=42,
150
+ issue_description="Fix bug in parser"
151
+ )
152
+ ```
153
+
154
+ **Loop Stages**:
155
+ 1. `_step_plan()` - Generate fix strategy
156
+ 2. `_step_patch()` - Generate diff
157
+ 3. `_step_apply()` - Apply changes
158
+ 4. `_step_test()` - Run tests
159
+ 5. `_analyze_failure()` - Analyze if failed
160
+ 6. Loop back or succeed
161
+
162
+ **Result Structure**:
163
+ ```python
164
+ {
165
+ "status": "success"|"failure"|"timeout",
166
+ "iterations": 3,
167
+ "plan": "Generated plan...",
168
+ "patch": "Generated diff...",
169
+ "test_results": {...},
170
+ "errors": []
171
+ }
172
+ ```
173
+
174
+ ### `github/` - GitHub Integration
175
+
176
+ **Purpose**: Fetch issues and create PRs.
177
+
178
+ **Key Classes**:
179
+ - `GitHubClient` - Main client
180
+ - GitHub operations:
181
+ - `fetch_issue(id)` - Get issue details
182
+ - `create_pr(head, base, title, body)` - Create PR
183
+ - `add_comment(issue_id, text)` - Add issue comment
184
+ - `list_issues()` - Get issues
185
+
186
+ **Usage**:
187
+ ```python
188
+ github = GitHubClient(token="ghp_...")
189
+ issue = await github.fetch_issue(42)
190
+ pr = await github.create_pr(
191
+ head="voria-fix-42",
192
+ base="main",
193
+ title="Fix issue #42",
194
+ body="Automatic fix by voria"
195
+ )
196
+ ```
197
+
198
+ ### `token_manager/` - Cost Tracking
199
+
200
+ **Purpose**: Track LLM spending and enforce budgets.
201
+
202
+ **Key Classes**:
203
+ - `TokenManager` - Main tracker
204
+ - `TokenBudget` - Per-provider budgets
205
+
206
+ **Usage**:
207
+ ```python
208
+ manager = TokenManager()
209
+
210
+ # Log usage
211
+ manager.log_usage(
212
+ provider="openai",
213
+ tokens_used=1000,
214
+ cost=0.05
215
+ )
216
+
217
+ # Check budget
218
+ if not manager.within_budget("openai"):
219
+ raise BudgetExceededError()
220
+
221
+ # Get stats
222
+ stats = manager.get_stats() # Total cost today, etc.
223
+ ```
224
+
225
+ **Budget Defaults**:
226
+ ```
227
+ modal: $0.00/hr (free until Apr 30)
228
+ openai: $5.00/hr
229
+ gemini: $1.00/hr
230
+ claude: $3.00/hr
231
+ ```
232
+
233
+ ### `setup/` - Configuration
234
+
235
+ **Purpose**: Interactive provider setup.
236
+
237
+ **Key Classes**:
238
+ - `ProviderSetup` - Configuration manager
239
+
240
+ **Usage**:
241
+ ```python
242
+ setup = ProviderSetup()
243
+
244
+ # Interactive flow
245
+ config = await setup.setup_provider()
246
+ # → Choose provider
247
+ # → Enter API key
248
+ # → Select model
249
+ # → Save to ~/.voria/providers.json
250
+
251
+ # Get saved config
252
+ cfg = setup.get_provider_config("openai")
253
+
254
+ # List configured providers
255
+ providers = setup.list_configured()
256
+ ```
257
+
258
+ ## Rust Core Modules
259
+
260
+ ### `main.rs` - Entry Point
261
+
262
+ **Responsibilities**:
263
+ - Parse CLI arguments
264
+ - Initialize logging
265
+ - Dispatch to subcommands
266
+ - Exit code handling
267
+
268
+ **Key Functions**:
269
+ ```rust
270
+ async fn main() -> Result<()>
271
+ async fn handle_plan(issue_id: u32) -> Result<()>
272
+ async fn handle_issue(issue_id: u32) -> Result<()>
273
+ async fn handle_apply(plan_id: &str) -> Result<()>
274
+ ```
275
+
276
+ ### `cli/mod.rs` - Command Dispatch
277
+
278
+ **Responsibilities**:
279
+ - Parse subcommands (plan, issue, apply)
280
+ - Validate arguments
281
+ - Route to handlers
282
+
283
+ **Subcommands**:
284
+ - `plan <issue_id>` - Plan a fix
285
+ - `issue <issue_id>` - Full automation
286
+ - `apply <plan_id>` - Apply saved plan
287
+
288
+ ### `ipc/mod.rs` - NDJSON Protocol
289
+
290
+ **Responsibilities**:
291
+ - Spawn Python subprocess
292
+ - Send NDJSON requests
293
+ - Receive NDJSON responses
294
+ - Timeout detection
295
+
296
+ **Key Structs**:
297
+ ```rust
298
+ pub struct ProcessManager {
299
+ child: Child,
300
+ stdin: ChildStdin,
301
+ stdout: BufReader<ChildStdout>,
302
+ }
303
+
304
+ impl ProcessManager {
305
+ async fn send_request(&mut self, req: &Value) -> Result<()>
306
+ async fn read_response(&mut self) -> Result<Value>
307
+ async fn with_timeout(&mut self, req: &Value, secs: u64) -> Result<Value>
308
+ }
309
+ ```
310
+
311
+ ### `orchestrator/mod.rs` - Coordination
312
+
313
+ **Responsibilities**:
314
+ - Coordinate Rust-Python workflow
315
+ - Handle multi-step commands
316
+ - Error recovery
317
+
318
+ ### `config/mod.rs` - Configuration
319
+
320
+ **Responsibilities**:
321
+ - Load config files
322
+ - Override with CLI flags
323
+ - Merge environment variables
324
+
325
+ ### `ui/mod.rs` - Terminal UI
326
+
327
+ **Responsibilities**:
328
+ - Colored output (Blue/Green/Red)
329
+ - Progress display
330
+ - Error formatting
331
+
332
+ **Key Functions**:
333
+ ```rust
334
+ fn print_info(msg: &str) // Blue [i]
335
+ fn print_success(msg: &str) // Green [✓]
336
+ fn print_error(msg: &str) // Red [✗]
337
+ fn print_warning(msg: &str) // Yellow [!]
338
+ ```
339
+
340
+ ## Plugin Architecture
341
+
342
+ ### Language Plugins
343
+
344
+ Location: `python/voria/plugins/`
345
+
346
+ **Plugin Structure**:
347
+ ```python
348
+ class PythonPlugin:
349
+ async def parse_code(self, source: str) -> AST
350
+ async def run_tests(self, path: str) -> TestResult
351
+ async def format_code(self, source: str) -> str
352
+ ```
353
+
354
+ **Supported Languages**:
355
+ - Python (pytest)
356
+ - JavaScript/TypeScript (Jest)
357
+ - (Extensible)
358
+
359
+ ## Data Structures
360
+
361
+ ### LLM Response
362
+
363
+ ```python
364
+ class LLMResponse:
365
+ content: str # Response text
366
+ tokens_used: int # Tokens consumed
367
+ finish_reason: str # "stop"|"length"|"error"
368
+ metadata: Dict[str, Any] # Provider-specific
369
+ ```
370
+
371
+ ### Patch Hunk
372
+
373
+ ```python
374
+ @dataclass
375
+ class PatchHunk:
376
+ old_file: str # File path
377
+ new_file: str
378
+ old_start: int # Starting line (1-indexed)
379
+ old_count: int # Num lines before
380
+ new_start: int # Starting line after
381
+ new_count: int # Num lines after
382
+ lines: List[str] # Diff lines
383
+ ```
384
+
385
+ ### Test Result
386
+
387
+ ```python
388
+ @dataclass
389
+ class TestResult:
390
+ name: str # Test identifier
391
+ status: TestStatus # PASSED|FAILED|SKIPPED|ERROR
392
+ duration: float # Seconds
393
+ message: str # Fail message (if any)
394
+ error_type: Optional[str] # Exception type
395
+ stacktrace: Optional[str] # Full trace
396
+ ```
397
+
398
+ ## Usage Patterns
399
+
400
+ ### Using an LLM Provider
401
+
402
+ ```python
403
+ # 1. Create provider
404
+ provider = LLMProviderFactory.create("openai", api_key, "gpt-5.4")
405
+
406
+ # 2. Call methods
407
+ plan = await provider.plan(issue_description)
408
+ patch = await provider.generate_patch(context, plan)
409
+
410
+ # 3. Check tokens
411
+ print(f"Used: {plan.tokens_used} tokens")
412
+ ```
413
+
414
+ ### Using the Agent Loop
415
+
416
+ ```python
417
+ # 1. Create loop
418
+ loop = AgentLoop("openai", api_key, repo_path="/repo")
419
+
420
+ # 2. Initialize
421
+ await loop.initialize("gpt-5.4")
422
+
423
+ # 3. Run
424
+ result = await loop.run(issue_id=42, issue_description="...")
425
+
426
+ # 4. Check result
427
+ if result["status"] == "success":
428
+ print("Issue fixed!")
429
+ else:
430
+ print(f"Failed: {result['errors']}")
431
+ ```
432
+
433
+ ### Using Patching & Testing
434
+
435
+ ```python
436
+ # 1. Create patcher
437
+ patcher = CodePatcher("/repo")
438
+
439
+ # 2. Apply patch
440
+ result = await patcher.apply_patch(diff_text)
441
+
442
+ # 3. Create executor
443
+ executor = TestExecutor("/repo")
444
+
445
+ # 4. Run tests
446
+ test_result = await executor.run_tests()
447
+
448
+ # 5. Check results
449
+ if test_result.passed == test_result.total:
450
+ print("All tests pass!")
451
+ ```
452
+
453
+ ## Dependencies
454
+
455
+ ### Python
456
+ - httpx 0.24.0 - Async HTTP
457
+ - aiofiles 23.0 - Async file I/O
458
+ - pytest - Testing
459
+
460
+ ### Rust
461
+ - tokio 1.51 - Async runtime
462
+ - serde_json - JSON
463
+ - colored - Terminal colors
464
+ - clap - CLI args
465
+
466
+ ---
467
+
468
+ **See Also:**
469
+ - [ARCHITECTURE.md](ARCHITECTURE.md) - System design
470
+ - [DESIGN_DECISIONS.md](DESIGN_DECISIONS.md) - Why these choices