claude-dev-cli 0.5.0__py3-none-any.whl → 0.7.0__py3-none-any.whl

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.

Potentially problematic release.


This version of claude-dev-cli might be problematic. Click here for more details.

@@ -0,0 +1,340 @@
1
+ """Workflow execution engine for chaining AI operations."""
2
+
3
+ import re
4
+ import subprocess
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Optional, Union
7
+ from dataclasses import dataclass, field
8
+
9
+ import yaml
10
+ from rich.console import Console
11
+
12
+
13
+ @dataclass
14
+ class StepResult:
15
+ """Result from executing a workflow step."""
16
+ success: bool
17
+ output: str
18
+ error: Optional[str] = None
19
+ metadata: Dict[str, Any] = field(default_factory=dict)
20
+
21
+
22
+ @dataclass
23
+ class WorkflowContext:
24
+ """Context passed between workflow steps."""
25
+ variables: Dict[str, Any] = field(default_factory=dict)
26
+ step_results: Dict[str, StepResult] = field(default_factory=dict)
27
+
28
+
29
+ class WorkflowEngine:
30
+ """Execute workflow definitions with step chaining."""
31
+
32
+ def __init__(self, console: Optional[Console] = None):
33
+ self.console = console or Console()
34
+
35
+ def load_workflow(self, path: Path) -> Dict[str, Any]:
36
+ """Load workflow from YAML file."""
37
+ with open(path, 'r') as f:
38
+ return yaml.safe_load(f)
39
+
40
+ def execute(
41
+ self,
42
+ workflow: Union[Dict[str, Any], Path],
43
+ initial_vars: Optional[Dict[str, Any]] = None
44
+ ) -> WorkflowContext:
45
+ """Execute a workflow definition."""
46
+ # Load from file if path provided
47
+ if isinstance(workflow, Path):
48
+ workflow = self.load_workflow(workflow)
49
+
50
+ # Initialize context
51
+ context = WorkflowContext(variables=initial_vars or {})
52
+
53
+ # Extract workflow metadata
54
+ name = workflow.get('name', 'Unnamed Workflow')
55
+ description = workflow.get('description', '')
56
+ steps = workflow.get('steps', [])
57
+
58
+ self.console.print(f"\n[bold cyan]Running workflow:[/bold cyan] {name}")
59
+ if description:
60
+ self.console.print(f"[dim]{description}[/dim]")
61
+
62
+ # Execute steps
63
+ for i, step in enumerate(steps, 1):
64
+ step_name = step.get('name', f'step-{i}')
65
+
66
+ # Check conditional
67
+ if 'if' in step:
68
+ condition = self._evaluate_condition(step['if'], context)
69
+ if not condition:
70
+ self.console.print(f"[yellow]↷[/yellow] Skipping step {i}: {step_name} (condition false)")
71
+ continue
72
+
73
+ self.console.print(f"\n[bold]Step {i}:[/bold] {step_name}")
74
+
75
+ # Check for approval gate
76
+ if step.get('approval_required', False):
77
+ if not self._request_approval(step_name):
78
+ self.console.print("[yellow]Workflow stopped by user[/yellow]")
79
+ break
80
+
81
+ # Execute step
82
+ try:
83
+ result = self._execute_step(step, context)
84
+ context.step_results[step_name] = result
85
+
86
+ if result.success:
87
+ self.console.print(f"[green]✓[/green] {step_name} completed")
88
+ else:
89
+ self.console.print(f"[red]✗[/red] {step_name} failed: {result.error}")
90
+
91
+ # Check if workflow should continue on error
92
+ if not step.get('continue_on_error', False):
93
+ self.console.print("[red]Workflow stopped due to error[/red]")
94
+ break
95
+
96
+ except Exception as e:
97
+ self.console.print(f"[red]Error in step {step_name}: {e}[/red]")
98
+ if not step.get('continue_on_error', False):
99
+ break
100
+
101
+ self.console.print("\n[bold green]Workflow completed[/bold green]")
102
+ return context
103
+
104
+ def _execute_step(self, step: Dict[str, Any], context: WorkflowContext) -> StepResult:
105
+ """Execute a single workflow step."""
106
+ # Determine step type
107
+ if 'command' in step:
108
+ return self._execute_command_step(step, context)
109
+ elif 'shell' in step:
110
+ return self._execute_shell_step(step, context)
111
+ elif 'set' in step:
112
+ return self._execute_set_step(step, context)
113
+ else:
114
+ return StepResult(
115
+ success=False,
116
+ output="",
117
+ error="Unknown step type"
118
+ )
119
+
120
+ def _execute_command_step(self, step: Dict[str, Any], context: WorkflowContext) -> StepResult:
121
+ """Execute a cdc command step."""
122
+ command = step['command']
123
+ args = step.get('args', {})
124
+
125
+ # Interpolate variables in args
126
+ interpolated_args = self._interpolate_variables(args, context)
127
+
128
+ # Import here to avoid circular dependency
129
+ from claude_dev_cli.commands import (
130
+ generate_tests, code_review, debug_code,
131
+ generate_docs, refactor_code, git_commit_message
132
+ )
133
+
134
+ # Map commands to functions
135
+ command_map = {
136
+ 'generate tests': generate_tests,
137
+ 'review': code_review,
138
+ 'debug': debug_code,
139
+ 'generate docs': generate_docs,
140
+ 'refactor': refactor_code,
141
+ 'git commit': git_commit_message,
142
+ }
143
+
144
+ if command not in command_map:
145
+ return StepResult(
146
+ success=False,
147
+ output="",
148
+ error=f"Unknown command: {command}"
149
+ )
150
+
151
+ try:
152
+ func = command_map[command]
153
+
154
+ # Build function arguments
155
+ func_args = {}
156
+ if 'file' in interpolated_args:
157
+ func_args['file_path'] = interpolated_args['file']
158
+ if 'error' in interpolated_args:
159
+ func_args['error_message'] = interpolated_args['error']
160
+ if 'api' in interpolated_args:
161
+ func_args['api_config_name'] = interpolated_args['api']
162
+
163
+ # Execute
164
+ result = func(**func_args)
165
+
166
+ # Store output in context for next steps
167
+ if 'output_var' in step:
168
+ context.variables[step['output_var']] = result
169
+
170
+ return StepResult(
171
+ success=True,
172
+ output=result,
173
+ metadata={'command': command}
174
+ )
175
+
176
+ except Exception as e:
177
+ return StepResult(
178
+ success=False,
179
+ output="",
180
+ error=str(e)
181
+ )
182
+
183
+ def _execute_shell_step(self, step: Dict[str, Any], context: WorkflowContext) -> StepResult:
184
+ """Execute a shell command step."""
185
+ command = step['shell']
186
+
187
+ # Interpolate variables
188
+ command = self._interpolate_string(command, context)
189
+
190
+ try:
191
+ result = subprocess.run(
192
+ command,
193
+ shell=True,
194
+ capture_output=True,
195
+ text=True,
196
+ check=False
197
+ )
198
+
199
+ success = result.returncode == 0
200
+ output = result.stdout or result.stderr
201
+
202
+ # Store output in context
203
+ if 'output_var' in step:
204
+ context.variables[step['output_var']] = output.strip()
205
+
206
+ return StepResult(
207
+ success=success,
208
+ output=output,
209
+ error=result.stderr if not success else None,
210
+ metadata={'returncode': result.returncode}
211
+ )
212
+
213
+ except Exception as e:
214
+ return StepResult(
215
+ success=False,
216
+ output="",
217
+ error=str(e)
218
+ )
219
+
220
+ def _execute_set_step(self, step: Dict[str, Any], context: WorkflowContext) -> StepResult:
221
+ """Execute a variable assignment step."""
222
+ var_name = step['set']
223
+ value = step.get('value', '')
224
+
225
+ # Interpolate value
226
+ value = self._interpolate_value(value, context)
227
+
228
+ context.variables[var_name] = value
229
+
230
+ return StepResult(
231
+ success=True,
232
+ output=str(value),
233
+ metadata={'variable': var_name}
234
+ )
235
+
236
+ def _interpolate_variables(
237
+ self,
238
+ data: Union[Dict, List, str, Any],
239
+ context: WorkflowContext
240
+ ) -> Any:
241
+ """Recursively interpolate variables in data structures."""
242
+ if isinstance(data, dict):
243
+ return {k: self._interpolate_variables(v, context) for k, v in data.items()}
244
+ elif isinstance(data, list):
245
+ return [self._interpolate_variables(item, context) for item in data]
246
+ elif isinstance(data, str):
247
+ return self._interpolate_string(data, context)
248
+ else:
249
+ return data
250
+
251
+ def _interpolate_string(self, text: str, context: WorkflowContext) -> str:
252
+ """Interpolate {{variable}} placeholders in string."""
253
+ def replace(match: re.Match) -> str:
254
+ var_path = match.group(1)
255
+ return str(self._resolve_variable(var_path, context))
256
+
257
+ return re.sub(r'\{\{([^}]+)\}\}', replace, text)
258
+
259
+ def _interpolate_value(self, value: Any, context: WorkflowContext) -> Any:
260
+ """Interpolate a single value."""
261
+ if isinstance(value, str):
262
+ return self._interpolate_string(value, context)
263
+ return value
264
+
265
+ def _resolve_variable(self, var_path: str, context: WorkflowContext) -> Any:
266
+ """Resolve a variable path like 'step1.output' or 'vars.filename'."""
267
+ parts = var_path.strip().split('.')
268
+
269
+ # Check step results first
270
+ if len(parts) >= 2 and parts[0] in context.step_results:
271
+ step_result = context.step_results[parts[0]]
272
+ if parts[1] == 'output':
273
+ return step_result.output
274
+ elif parts[1] == 'success':
275
+ return step_result.success
276
+ elif parts[1] == 'error':
277
+ return step_result.error or ''
278
+
279
+ # Check variables
280
+ if parts[0] in context.variables:
281
+ value = context.variables[parts[0]]
282
+ # Support nested access for dicts
283
+ for part in parts[1:]:
284
+ if isinstance(value, dict):
285
+ value = value.get(part, '')
286
+ else:
287
+ return ''
288
+ return value
289
+
290
+ return f'{{{{{var_path}}}}}' # Return placeholder if not found
291
+
292
+ def _evaluate_condition(self, condition: str, context: WorkflowContext) -> bool:
293
+ """Evaluate a simple condition expression."""
294
+ # Interpolate variables
295
+ evaluated = self._interpolate_string(condition, context)
296
+
297
+ # Simple boolean evaluation
298
+ if evaluated.lower() in ('true', '1', 'yes'):
299
+ return True
300
+ if evaluated.lower() in ('false', '0', 'no', ''):
301
+ return False
302
+
303
+ # Try to evaluate as Python expression (limited to safe operations)
304
+ try:
305
+ # Only allow simple comparisons
306
+ if any(op in evaluated for op in ['==', '!=', '>', '<', '>=', '<=']):
307
+ return eval(evaluated, {"__builtins__": {}}, {})
308
+ except Exception:
309
+ pass
310
+
311
+ return bool(evaluated)
312
+
313
+ def _request_approval(self, step_name: str) -> bool:
314
+ """Request user approval to proceed."""
315
+ self.console.print(f"\n[yellow]⚠ Approval required for:[/yellow] {step_name}")
316
+ response = self.console.input("[cyan]Proceed? (y/n):[/cyan] ").strip().lower()
317
+ return response in ('y', 'yes')
318
+
319
+
320
+ def list_workflows(workflow_dir: Path) -> List[Dict[str, str]]:
321
+ """List available workflows in a directory."""
322
+ if not workflow_dir.exists():
323
+ return []
324
+
325
+ workflows = []
326
+ for file in workflow_dir.glob('*.yaml'):
327
+ try:
328
+ with open(file, 'r') as f:
329
+ data = yaml.safe_load(f)
330
+
331
+ workflows.append({
332
+ 'name': data.get('name', file.stem),
333
+ 'description': data.get('description', ''),
334
+ 'file': str(file),
335
+ 'steps': len(data.get('steps', []))
336
+ })
337
+ except Exception:
338
+ continue
339
+
340
+ return workflows
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-dev-cli
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: A powerful CLI tool for developers using Claude AI with multi-API routing, test generation, code review, and usage tracking
5
5
  Author-email: Julio <thinmanj@users.noreply.github.com>
6
6
  License: MIT
@@ -28,6 +28,7 @@ Requires-Dist: rich>=13.0.0
28
28
  Requires-Dist: pydantic>=2.0.0
29
29
  Requires-Dist: keyring>=24.0.0
30
30
  Requires-Dist: cryptography>=41.0.0
31
+ Requires-Dist: pyyaml>=6.0.0
31
32
  Provides-Extra: toon
32
33
  Requires-Dist: toon-format>=0.9.0; extra == "toon"
33
34
  Provides-Extra: plugins
@@ -72,6 +73,12 @@ A powerful command-line tool for developers using Claude AI with multi-API routi
72
73
  - **Interactive**: Chat mode with conversation history
73
74
  - **Streaming**: Real-time responses
74
75
 
76
+ ### 📝 Custom Templates
77
+ - **Built-in Templates**: 8 pre-built templates for common tasks (code review, testing, debugging, etc.)
78
+ - **User Templates**: Create and manage your own reusable prompt templates
79
+ - **Variable Substitution**: Use {{variable}} placeholders for dynamic content
80
+ - **Categories**: Organize templates by category (review, testing, debugging, etc.)
81
+
75
82
  ### 🎒 TOON Format Support (Optional)
76
83
  - **Token Reduction**: 30-60% fewer tokens than JSON
77
84
  - **Cost Savings**: Reduce API costs significantly
@@ -139,24 +146,73 @@ cdc ask -a client "generate tests for this function"
139
146
  # Generate tests
140
147
  cdc generate tests mymodule.py -o tests/test_mymodule.py
141
148
 
149
+ # Generate tests with interactive refinement
150
+ cdc generate tests mymodule.py --interactive
151
+
142
152
  # Code review
143
153
  cdc review mymodule.py
144
154
 
155
+ # Code review with interactive follow-up questions
156
+ cdc review mymodule.py --interactive
157
+
145
158
  # Debug errors
146
159
  python script.py 2>&1 | cdc debug
147
160
 
148
161
  # Generate documentation
149
162
  cdc generate docs mymodule.py
150
163
 
164
+ # Generate docs with interactive refinement
165
+ cdc generate docs mymodule.py --interactive
166
+
151
167
  # Refactor suggestions
152
168
  cdc refactor legacy_code.py
153
169
 
170
+ # Refactor with interactive refinement
171
+ cdc refactor legacy_code.py --interactive
172
+
154
173
  # Git commit message
155
174
  git add .
156
175
  cdc git commit
157
176
  ```
158
177
 
159
- ### 4. Usage Tracking
178
+ ### 5. Custom Templates
179
+
180
+ ```bash
181
+ # List all templates (built-in and user)
182
+ cdc template list
183
+
184
+ # Show template details
185
+ cdc template show code-review
186
+
187
+ # Add a custom template
188
+ cdc template add my-review -c "Review this code for {{focus}}: {{code}}" \
189
+ -d "Custom review template" --category review
190
+
191
+ # Use a template (interactive variable input)
192
+ cdc template use debug-error
193
+
194
+ # Delete a user template
195
+ cdc template delete my-review
196
+
197
+ # Filter by category
198
+ cdc template list --category review
199
+
200
+ # Show only user templates
201
+ cdc template list --user
202
+ ```
203
+
204
+ #### Built-in Templates
205
+
206
+ - **code-review**: Comprehensive code review with customizable focus
207
+ - **code-review-security**: Security-focused code review
208
+ - **test-strategy**: Generate testing strategy and test cases
209
+ - **debug-error**: Debug error with context
210
+ - **optimize-performance**: Performance optimization analysis
211
+ - **refactor-clean**: Clean code refactoring
212
+ - **explain-code**: Detailed code explanation
213
+ - **api-design**: API design assistance
214
+
215
+ ### 6. Usage Tracking
160
216
 
161
217
  ```bash
162
218
  # View all usage
@@ -169,7 +225,7 @@ cdc usage --days 7
169
225
  cdc usage --api client
170
226
  ```
171
227
 
172
- ### 5. TOON Format (Optional - Reduces Tokens by 30-60%)
228
+ ### 7. TOON Format (Optional - Reduces Tokens by 30-60%)
173
229
 
174
230
  ```bash
175
231
  # Check if TOON is installed
@@ -322,6 +378,16 @@ When using a client's Enterprise API:
322
378
  | `cdc debug -f <file> -e <error>` | Debug code |
323
379
  | `cdc refactor <file>` | Refactoring suggestions |
324
380
 
381
+ ### Templates
382
+
383
+ | Command | Description |
384
+ |---------|-------------|
385
+ | `cdc template list` | List all templates |
386
+ | `cdc template show <name>` | Show template details |
387
+ | `cdc template add <name>` | Add new template |
388
+ | `cdc template delete <name>` | Delete user template |
389
+ | `cdc template use <name>` | Use template interactively |
390
+
325
391
  ### Git Helpers
326
392
 
327
393
  | Command | Description |
@@ -1,21 +1,24 @@
1
- claude_dev_cli/__init__.py,sha256=2ulyIQ3E-s6wBTKyeXAlqHMVA73zUGdaaNUsFiJ-nqs,469
2
- claude_dev_cli/cli.py,sha256=WBJdA1KKA0scT-gOE5Sd39HhtJr7uyc3qfn_-rYXTGc,25019
1
+ claude_dev_cli/__init__.py,sha256=sRGmW8jYKXWyBKqr2_-3AornNC32eNj4sESrXfoTUoQ,469
2
+ claude_dev_cli/cli.py,sha256=feTRI1fQoDPJiB7JM45ZU2I1jEjXX643191H_U0rMio,44612
3
3
  claude_dev_cli/commands.py,sha256=RKGx2rv56PM6eErvA2uoQ20hY8babuI5jav8nCUyUOk,3964
4
4
  claude_dev_cli/config.py,sha256=RGX0sKplHUsrJJmU-4FuWWjoTbQVgWaMT8DgRUofrR4,8134
5
5
  claude_dev_cli/core.py,sha256=yaLjEixDvPzvUy4fJ2UB7nMpPPLyKACjR-RuM-1OQBY,4780
6
6
  claude_dev_cli/history.py,sha256=iQlqgTnXCsyCq5q-XaDl7V5MyPKQ3bx7o_k76-xWSAA,6863
7
7
  claude_dev_cli/secure_storage.py,sha256=TK3WOaU7a0yTOtzdP_t_28fDRp2lovANNAC6MBdm4nQ,7096
8
+ claude_dev_cli/template_manager.py,sha256=ZFXOtRIoB6hpf8kLSF9TWJfvUPJt9b-PyEv3qTBK7Zs,8600
8
9
  claude_dev_cli/templates.py,sha256=lKxH943ySfUKgyHaWa4W3LVv91SgznKgajRtSRp_4UY,2260
9
10
  claude_dev_cli/toon_utils.py,sha256=S3px2UvmNEaltmTa5K-h21n2c0CPvYjZc9mc7kHGqNQ,2828
10
11
  claude_dev_cli/usage.py,sha256=32rs0_dUn6ihha3vCfT3rwnvel_-sED7jvLpO7gu-KQ,7446
12
+ claude_dev_cli/warp_integration.py,sha256=PDufAJecl7uN0DGz6XW4uDSEeu7Ssg53DOIFKm-AXVg,6909
13
+ claude_dev_cli/workflows.py,sha256=WpLq9I_0MmDsJIbCi9-f662JVyn8iKTs1KZ50w-GlZU,12202
11
14
  claude_dev_cli/plugins/__init__.py,sha256=BdiZlylBzEgnwK2tuEdn8cITxhAZRVbTnDbWhdDhgqs,1340
12
15
  claude_dev_cli/plugins/base.py,sha256=H4HQet1I-a3WLCfE9F06Lp8NuFvVoIlou7sIgyJFK-c,1417
13
16
  claude_dev_cli/plugins/diff_editor/__init__.py,sha256=gqR5S2TyIVuq-sK107fegsutQ7Z-sgAIEbtc71FhXIM,101
14
17
  claude_dev_cli/plugins/diff_editor/plugin.py,sha256=M1bUoqpasD3ZNQo36Fu_8g92uySPZyG_ujMbj5UplsU,3073
15
18
  claude_dev_cli/plugins/diff_editor/viewer.py,sha256=1IOXIKw_01ppJx5C1dQt9Kr6U1TdAHT8_iUT5r_q0NM,17169
16
- claude_dev_cli-0.5.0.dist-info/licenses/LICENSE,sha256=DGueuJwMJtMwgLO5mWlS0TaeBrFwQuNpNZ22PU9J2bw,1062
17
- claude_dev_cli-0.5.0.dist-info/METADATA,sha256=GCboIRGV9N6gFFiwotWuCFMqMbhJZs4Gk6pyztZX984,11288
18
- claude_dev_cli-0.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- claude_dev_cli-0.5.0.dist-info/entry_points.txt,sha256=zymgUIIVpFTARkFmxAuW2A4BQsNITh_L0uU-XunytHg,85
20
- claude_dev_cli-0.5.0.dist-info/top_level.txt,sha256=m7MF6LOIuTe41IT5Fgt0lc-DK1EgM4gUU_IZwWxK0pg,15
21
- claude_dev_cli-0.5.0.dist-info/RECORD,,
19
+ claude_dev_cli-0.7.0.dist-info/licenses/LICENSE,sha256=DGueuJwMJtMwgLO5mWlS0TaeBrFwQuNpNZ22PU9J2bw,1062
20
+ claude_dev_cli-0.7.0.dist-info/METADATA,sha256=YIN5U4urelKIEFjhd-s2EyHsPb-qEvhhqDwOek2hjPw,13364
21
+ claude_dev_cli-0.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ claude_dev_cli-0.7.0.dist-info/entry_points.txt,sha256=zymgUIIVpFTARkFmxAuW2A4BQsNITh_L0uU-XunytHg,85
23
+ claude_dev_cli-0.7.0.dist-info/top_level.txt,sha256=m7MF6LOIuTe41IT5Fgt0lc-DK1EgM4gUU_IZwWxK0pg,15
24
+ claude_dev_cli-0.7.0.dist-info/RECORD,,