claude-dev-cli 0.6.0__py3-none-any.whl → 0.8.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,243 @@
1
+ """Warp terminal integration for enhanced output formatting."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ import yaml
8
+
9
+
10
+ def format_as_warp_block(
11
+ content: str,
12
+ title: Optional[str] = None,
13
+ language: Optional[str] = None,
14
+ actions: Optional[List[Dict[str, str]]] = None
15
+ ) -> str:
16
+ """Format content as a Warp block with optional actions.
17
+
18
+ Warp blocks support special formatting and click-to-run actions.
19
+ """
20
+ block_parts = []
21
+
22
+ if title:
23
+ block_parts.append(f"### {title}")
24
+ block_parts.append("")
25
+
26
+ # Add content with language if code block
27
+ if language:
28
+ block_parts.append(f"```{language}")
29
+ block_parts.append(content)
30
+ block_parts.append("```")
31
+ else:
32
+ block_parts.append(content)
33
+
34
+ # Add actions if provided
35
+ if actions:
36
+ block_parts.append("")
37
+ block_parts.append("**Actions:**")
38
+ for action in actions:
39
+ label = action.get('label', 'Run')
40
+ command = action.get('command', '')
41
+ block_parts.append(f"- [{label}](`{command}`)")
42
+
43
+ return "\n".join(block_parts)
44
+
45
+
46
+ def generate_warp_workflow(
47
+ workflow_name: str,
48
+ commands: List[Dict[str, str]],
49
+ output_path: Optional[Path] = None
50
+ ) -> str:
51
+ """Generate a Warp workflow file from command list.
52
+
53
+ Warp workflows allow users to execute predefined command sequences.
54
+ """
55
+ workflow = {
56
+ "name": workflow_name,
57
+ "command": "://" + workflow_name.lower().replace(" ", "-"),
58
+ "tags": ["claude-dev-cli"],
59
+ "description": f"Generated from claude-dev-cli",
60
+ "arguments": [],
61
+ "source_specs": [
62
+ {
63
+ "type": "command",
64
+ "command": cmd.get('command', ''),
65
+ "description": cmd.get('description', '')
66
+ }
67
+ for cmd in commands
68
+ ]
69
+ }
70
+
71
+ workflow_yaml = yaml.dump(workflow, default_flow_style=False, sort_keys=False)
72
+
73
+ if output_path:
74
+ output_path.write_text(workflow_yaml)
75
+
76
+ return workflow_yaml
77
+
78
+
79
+ def export_builtin_workflows(output_dir: Path) -> List[Path]:
80
+ """Export built-in Warp workflows for common claude-dev-cli tasks."""
81
+ output_dir.mkdir(parents=True, exist_ok=True)
82
+
83
+ workflows = [
84
+ {
85
+ "name": "Code Review Workflow",
86
+ "commands": [
87
+ {
88
+ "command": "cdc review {{file}}",
89
+ "description": "Review code for issues"
90
+ },
91
+ {
92
+ "command": "cdc review {{file}} --interactive",
93
+ "description": "Review with follow-up questions"
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ "name": "Test Generation Workflow",
99
+ "commands": [
100
+ {
101
+ "command": "cdc generate tests {{file}} -o tests/test_{{file}}",
102
+ "description": "Generate tests"
103
+ },
104
+ {
105
+ "command": "pytest tests/test_{{file}}",
106
+ "description": "Run tests"
107
+ }
108
+ ]
109
+ },
110
+ {
111
+ "name": "Refactor Workflow",
112
+ "commands": [
113
+ {
114
+ "command": "cdc refactor {{file}} --interactive",
115
+ "description": "Get refactoring suggestions"
116
+ },
117
+ {
118
+ "command": "cdc review {{file}}",
119
+ "description": "Review changes"
120
+ },
121
+ {
122
+ "command": "git add {{file}} && cdc git commit",
123
+ "description": "Commit changes"
124
+ }
125
+ ]
126
+ },
127
+ {
128
+ "name": "Debug Workflow",
129
+ "commands": [
130
+ {
131
+ "command": "python {{file}} 2>&1 | cdc debug",
132
+ "description": "Run and debug errors"
133
+ },
134
+ {
135
+ "command": "cdc debug -f {{file}} -e \"{{error}}\"",
136
+ "description": "Debug specific error"
137
+ }
138
+ ]
139
+ }
140
+ ]
141
+
142
+ created_files = []
143
+ for workflow in workflows:
144
+ filename = workflow['name'].lower().replace(' ', '-') + '.yaml'
145
+ filepath = output_dir / filename
146
+ generate_warp_workflow(
147
+ workflow['name'],
148
+ workflow['commands'],
149
+ filepath
150
+ )
151
+ created_files.append(filepath)
152
+
153
+ return created_files
154
+
155
+
156
+ def format_code_review_for_warp(review_output: str, file_path: str) -> str:
157
+ """Format code review output as Warp block with actions."""
158
+ actions = [
159
+ {
160
+ "label": "Review with follow-up",
161
+ "command": f"cdc review {file_path} --interactive"
162
+ },
163
+ {
164
+ "label": "Refactor",
165
+ "command": f"cdc refactor {file_path} --interactive"
166
+ }
167
+ ]
168
+
169
+ return format_as_warp_block(
170
+ content=review_output,
171
+ title="Code Review",
172
+ actions=actions
173
+ )
174
+
175
+
176
+ def format_test_generation_for_warp(test_output: str, file_path: str) -> str:
177
+ """Format test generation output as Warp block with actions."""
178
+ test_file = f"tests/test_{Path(file_path).name}"
179
+
180
+ actions = [
181
+ {
182
+ "label": "Save tests",
183
+ "command": f"cdc generate tests {file_path} -o {test_file}"
184
+ },
185
+ {
186
+ "label": "Run tests",
187
+ "command": f"pytest {test_file}"
188
+ }
189
+ ]
190
+
191
+ return format_as_warp_block(
192
+ content=test_output,
193
+ title="Generated Tests",
194
+ language="python",
195
+ actions=actions
196
+ )
197
+
198
+
199
+ def create_warp_launch_config(
200
+ name: str,
201
+ command: str,
202
+ cwd: Optional[str] = None,
203
+ env: Optional[Dict[str, str]] = None
204
+ ) -> Dict[str, Any]:
205
+ """Create a Warp launch configuration.
206
+
207
+ Launch configs allow quick environment setup in Warp.
208
+ """
209
+ config = {
210
+ "name": name,
211
+ "command": command,
212
+ "type": "terminal"
213
+ }
214
+
215
+ if cwd:
216
+ config["cwd"] = cwd
217
+
218
+ if env:
219
+ config["env"] = env
220
+
221
+ return config
222
+
223
+
224
+ def export_launch_configs(output_path: Path) -> None:
225
+ """Export Warp launch configurations for claude-dev-cli."""
226
+ configs = [
227
+ create_warp_launch_config(
228
+ name="Claude Dev CLI - Interactive",
229
+ command="cdc interactive"
230
+ ),
231
+ create_warp_launch_config(
232
+ name="Claude Dev CLI - Review Mode",
233
+ command="cdc review"
234
+ ),
235
+ create_warp_launch_config(
236
+ name="Claude Dev CLI - Test Generation",
237
+ command="cdc generate tests"
238
+ )
239
+ ]
240
+
241
+ output_path.parent.mkdir(parents=True, exist_ok=True)
242
+ with open(output_path, 'w') as f:
243
+ json.dump({"launch_configurations": configs}, f, indent=2)
@@ -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.6.0
3
+ Version: 0.8.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
@@ -78,6 +79,13 @@ A powerful command-line tool for developers using Claude AI with multi-API routi
78
79
  - **Variable Substitution**: Use {{variable}} placeholders for dynamic content
79
80
  - **Categories**: Organize templates by category (review, testing, debugging, etc.)
80
81
 
82
+ ### 🧠 Context Intelligence (NEW in v0.8.0)
83
+ - **Auto-Context**: `--auto-context` flag for intelligent context gathering
84
+ - **Git Integration**: Automatically include branch, commits, modified files
85
+ - **Dependency Analysis**: Parse imports and include related files
86
+ - **Error Parsing**: Structured Python traceback parsing
87
+ - **Project Memory**: Remember preferences per project
88
+
81
89
  ### 🎒 TOON Format Support (Optional)
82
90
  - **Token Reduction**: 30-60% fewer tokens than JSON
83
91
  - **Cost Savings**: Reduce API costs significantly
@@ -145,23 +153,60 @@ cdc ask -a client "generate tests for this function"
145
153
  # Generate tests
146
154
  cdc generate tests mymodule.py -o tests/test_mymodule.py
147
155
 
156
+ # Generate tests with interactive refinement
157
+ cdc generate tests mymodule.py --interactive
158
+
148
159
  # Code review
149
160
  cdc review mymodule.py
150
161
 
151
- # Debug errors
152
- python script.py 2>&1 | cdc debug
162
+ # Code review with auto-context (includes git, dependencies, tests)
163
+ cdc review mymodule.py --auto-context
164
+
165
+ # Code review with interactive follow-up questions
166
+ cdc review mymodule.py --interactive
167
+
168
+ # Debug errors with intelligent error parsing
169
+ python script.py 2>&1 | cdc debug --auto-context
153
170
 
154
171
  # Generate documentation
155
172
  cdc generate docs mymodule.py
156
173
 
157
- # Refactor suggestions
158
- cdc refactor legacy_code.py
174
+ # Generate docs with interactive refinement
175
+ cdc generate docs mymodule.py --interactive
176
+
177
+ # Refactor with context (includes related files)
178
+ cdc refactor legacy_code.py --auto-context
179
+
180
+ # Refactor with interactive refinement
181
+ cdc refactor legacy_code.py --interactive
159
182
 
160
183
  # Git commit message
161
184
  git add .
162
185
  cdc git commit
163
186
  ```
164
187
 
188
+ ### 4. Context-Aware Operations (NEW in v0.8.0)
189
+
190
+ ```bash
191
+ # Auto-context includes: git info, dependencies, related files
192
+
193
+ # Review with full project context
194
+ cdc review mymodule.py --auto-context
195
+ # ✓ Context gathered (git, dependencies, tests)
196
+
197
+ # Debug with parsed error details
198
+ python broken.py 2>&1 | cdc debug -f broken.py --auto-context
199
+ # ✓ Context gathered (error details, git context)
200
+
201
+ # Ask questions with file context
202
+ cdc ask -f mycode.py --auto-context "how can I improve this?"
203
+ # ✓ Context gathered
204
+
205
+ # Refactor with related files
206
+ cdc refactor app.py --auto-context
207
+ # Automatically includes imported modules and dependencies
208
+ ```
209
+
165
210
  ### 5. Custom Templates
166
211
 
167
212
  ```bash