claude-dev-cli 0.1.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.
- claude_dev_cli/__init__.py +19 -0
- claude_dev_cli/cli.py +398 -0
- claude_dev_cli/commands.py +133 -0
- claude_dev_cli/config.py +174 -0
- claude_dev_cli/core.py +132 -0
- claude_dev_cli/templates.py +104 -0
- claude_dev_cli/usage.py +202 -0
- claude_dev_cli-0.1.0.dist-info/METADATA +392 -0
- claude_dev_cli-0.1.0.dist-info/RECORD +13 -0
- claude_dev_cli-0.1.0.dist-info/WHEEL +5 -0
- claude_dev_cli-0.1.0.dist-info/entry_points.txt +3 -0
- claude_dev_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- claude_dev_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Claude Dev CLI - A powerful CLI tool for developers using Claude AI.
|
|
3
|
+
|
|
4
|
+
Features:
|
|
5
|
+
- Multi-API key management and routing
|
|
6
|
+
- Test generation for Python projects
|
|
7
|
+
- Code review and analysis
|
|
8
|
+
- Usage tracking and cost monitoring
|
|
9
|
+
- Interactive and single-shot modes
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
__author__ = "Julio"
|
|
14
|
+
__license__ = "MIT"
|
|
15
|
+
|
|
16
|
+
from claude_dev_cli.core import ClaudeClient
|
|
17
|
+
from claude_dev_cli.config import Config
|
|
18
|
+
|
|
19
|
+
__all__ = ["ClaudeClient", "Config", "__version__"]
|
claude_dev_cli/cli.py
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"""Command-line interface for Claude Dev CLI."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.markdown import Markdown
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
|
|
12
|
+
from claude_dev_cli import __version__
|
|
13
|
+
from claude_dev_cli.config import Config
|
|
14
|
+
from claude_dev_cli.core import ClaudeClient
|
|
15
|
+
from claude_dev_cli.commands import (
|
|
16
|
+
generate_tests,
|
|
17
|
+
code_review,
|
|
18
|
+
debug_code,
|
|
19
|
+
generate_docs,
|
|
20
|
+
refactor_code,
|
|
21
|
+
git_commit_message,
|
|
22
|
+
)
|
|
23
|
+
from claude_dev_cli.usage import UsageTracker
|
|
24
|
+
|
|
25
|
+
console = Console()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@click.group()
|
|
29
|
+
@click.version_option(version=__version__)
|
|
30
|
+
@click.pass_context
|
|
31
|
+
def main(ctx: click.Context) -> None:
|
|
32
|
+
"""Claude Dev CLI - AI-powered development assistant with multi-API routing."""
|
|
33
|
+
ctx.ensure_object(dict)
|
|
34
|
+
ctx.obj['console'] = console
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@main.command()
|
|
38
|
+
@click.argument('prompt', required=False)
|
|
39
|
+
@click.option('-f', '--file', type=click.Path(exists=True), help='Include file content in prompt')
|
|
40
|
+
@click.option('-s', '--system', help='System prompt')
|
|
41
|
+
@click.option('-a', '--api', help='API config to use')
|
|
42
|
+
@click.option('-m', '--model', help='Claude model to use')
|
|
43
|
+
@click.option('--stream/--no-stream', default=True, help='Stream response')
|
|
44
|
+
@click.pass_context
|
|
45
|
+
def ask(
|
|
46
|
+
ctx: click.Context,
|
|
47
|
+
prompt: Optional[str],
|
|
48
|
+
file: Optional[str],
|
|
49
|
+
system: Optional[str],
|
|
50
|
+
api: Optional[str],
|
|
51
|
+
model: Optional[str],
|
|
52
|
+
stream: bool
|
|
53
|
+
) -> None:
|
|
54
|
+
"""Ask Claude a question (single-shot mode)."""
|
|
55
|
+
console = ctx.obj['console']
|
|
56
|
+
|
|
57
|
+
# Build prompt
|
|
58
|
+
prompt_parts = []
|
|
59
|
+
|
|
60
|
+
if file:
|
|
61
|
+
with open(file, 'r') as f:
|
|
62
|
+
file_content = f.read()
|
|
63
|
+
prompt_parts.append(f"File: {file}\n\n{file_content}\n\n")
|
|
64
|
+
|
|
65
|
+
# Read from stdin if available
|
|
66
|
+
if not sys.stdin.isatty():
|
|
67
|
+
stdin_content = sys.stdin.read().strip()
|
|
68
|
+
if stdin_content:
|
|
69
|
+
prompt_parts.append(f"{stdin_content}\n\n")
|
|
70
|
+
|
|
71
|
+
if prompt:
|
|
72
|
+
prompt_parts.append(prompt)
|
|
73
|
+
elif not prompt_parts:
|
|
74
|
+
console.print("[red]Error: No prompt provided[/red]")
|
|
75
|
+
sys.exit(1)
|
|
76
|
+
|
|
77
|
+
full_prompt = ''.join(prompt_parts)
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
client = ClaudeClient(api_config_name=api)
|
|
81
|
+
|
|
82
|
+
if stream:
|
|
83
|
+
for chunk in client.call_streaming(
|
|
84
|
+
full_prompt,
|
|
85
|
+
system_prompt=system,
|
|
86
|
+
model=model
|
|
87
|
+
):
|
|
88
|
+
console.print(chunk, end='')
|
|
89
|
+
console.print() # New line at end
|
|
90
|
+
else:
|
|
91
|
+
response = client.call(full_prompt, system_prompt=system, model=model)
|
|
92
|
+
md = Markdown(response)
|
|
93
|
+
console.print(md)
|
|
94
|
+
|
|
95
|
+
except Exception as e:
|
|
96
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@main.command()
|
|
101
|
+
@click.option('-a', '--api', help='API config to use')
|
|
102
|
+
@click.pass_context
|
|
103
|
+
def interactive(ctx: click.Context, api: Optional[str]) -> None:
|
|
104
|
+
"""Start interactive chat mode."""
|
|
105
|
+
console = ctx.obj['console']
|
|
106
|
+
|
|
107
|
+
console.print(Panel.fit(
|
|
108
|
+
"Claude Dev CLI - Interactive Mode\n"
|
|
109
|
+
"Type 'exit' or 'quit' to end\n"
|
|
110
|
+
"Type 'clear' to reset conversation",
|
|
111
|
+
title="Welcome",
|
|
112
|
+
border_style="blue"
|
|
113
|
+
))
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
client = ClaudeClient(api_config_name=api)
|
|
117
|
+
|
|
118
|
+
while True:
|
|
119
|
+
try:
|
|
120
|
+
user_input = console.input("\n[bold cyan]You:[/bold cyan] ").strip()
|
|
121
|
+
|
|
122
|
+
if user_input.lower() in ['exit', 'quit']:
|
|
123
|
+
break
|
|
124
|
+
if not user_input:
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
console.print("\n[bold green]Claude:[/bold green] ", end='')
|
|
128
|
+
for chunk in client.call_streaming(user_input):
|
|
129
|
+
console.print(chunk, end='')
|
|
130
|
+
console.print()
|
|
131
|
+
|
|
132
|
+
except KeyboardInterrupt:
|
|
133
|
+
console.print("\n\n[yellow]Interrupted. Type 'exit' to quit.[/yellow]")
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
except Exception as e:
|
|
137
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
138
|
+
sys.exit(1)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@main.group()
|
|
142
|
+
def config() -> None:
|
|
143
|
+
"""Manage configuration."""
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@config.command('add')
|
|
148
|
+
@click.argument('name')
|
|
149
|
+
@click.option('--api-key', help='API key (or set {NAME}_ANTHROPIC_API_KEY env var)')
|
|
150
|
+
@click.option('--description', help='Description of this API config')
|
|
151
|
+
@click.option('--default', is_flag=True, help='Set as default API config')
|
|
152
|
+
@click.pass_context
|
|
153
|
+
def config_add(
|
|
154
|
+
ctx: click.Context,
|
|
155
|
+
name: str,
|
|
156
|
+
api_key: Optional[str],
|
|
157
|
+
description: Optional[str],
|
|
158
|
+
default: bool
|
|
159
|
+
) -> None:
|
|
160
|
+
"""Add a new API configuration."""
|
|
161
|
+
console = ctx.obj['console']
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
config = Config()
|
|
165
|
+
config.add_api_config(
|
|
166
|
+
name=name,
|
|
167
|
+
api_key=api_key,
|
|
168
|
+
description=description,
|
|
169
|
+
make_default=default
|
|
170
|
+
)
|
|
171
|
+
console.print(f"[green]✓[/green] Added API config: {name}")
|
|
172
|
+
except Exception as e:
|
|
173
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
174
|
+
sys.exit(1)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@config.command('list')
|
|
178
|
+
@click.pass_context
|
|
179
|
+
def config_list(ctx: click.Context) -> None:
|
|
180
|
+
"""List all API configurations."""
|
|
181
|
+
console = ctx.obj['console']
|
|
182
|
+
|
|
183
|
+
config = Config()
|
|
184
|
+
api_configs = config.list_api_configs()
|
|
185
|
+
|
|
186
|
+
if not api_configs:
|
|
187
|
+
console.print("[yellow]No API configurations found.[/yellow]")
|
|
188
|
+
console.print("Run 'cdc config add' to add one.")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
for cfg in api_configs:
|
|
192
|
+
default_marker = " [bold green](default)[/bold green]" if cfg.default else ""
|
|
193
|
+
console.print(f"• {cfg.name}{default_marker}")
|
|
194
|
+
if cfg.description:
|
|
195
|
+
console.print(f" {cfg.description}")
|
|
196
|
+
console.print(f" API Key: {cfg.api_key[:15]}...")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@main.group()
|
|
200
|
+
def generate() -> None:
|
|
201
|
+
"""Generate code, tests, and documentation."""
|
|
202
|
+
pass
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@generate.command('tests')
|
|
206
|
+
@click.argument('file_path', type=click.Path(exists=True))
|
|
207
|
+
@click.option('-o', '--output', type=click.Path(), help='Output file path')
|
|
208
|
+
@click.option('-a', '--api', help='API config to use')
|
|
209
|
+
@click.pass_context
|
|
210
|
+
def gen_tests(
|
|
211
|
+
ctx: click.Context,
|
|
212
|
+
file_path: str,
|
|
213
|
+
output: Optional[str],
|
|
214
|
+
api: Optional[str]
|
|
215
|
+
) -> None:
|
|
216
|
+
"""Generate pytest tests for a Python file."""
|
|
217
|
+
console = ctx.obj['console']
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
with console.status("[bold blue]Generating tests..."):
|
|
221
|
+
result = generate_tests(file_path, api_config_name=api)
|
|
222
|
+
|
|
223
|
+
if output:
|
|
224
|
+
with open(output, 'w') as f:
|
|
225
|
+
f.write(result)
|
|
226
|
+
console.print(f"[green]✓[/green] Tests saved to: {output}")
|
|
227
|
+
else:
|
|
228
|
+
console.print(result)
|
|
229
|
+
|
|
230
|
+
except Exception as e:
|
|
231
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
232
|
+
sys.exit(1)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@generate.command('docs')
|
|
236
|
+
@click.argument('file_path', type=click.Path(exists=True))
|
|
237
|
+
@click.option('-o', '--output', type=click.Path(), help='Output file path')
|
|
238
|
+
@click.option('-a', '--api', help='API config to use')
|
|
239
|
+
@click.pass_context
|
|
240
|
+
def gen_docs(
|
|
241
|
+
ctx: click.Context,
|
|
242
|
+
file_path: str,
|
|
243
|
+
output: Optional[str],
|
|
244
|
+
api: Optional[str]
|
|
245
|
+
) -> None:
|
|
246
|
+
"""Generate documentation for a Python file."""
|
|
247
|
+
console = ctx.obj['console']
|
|
248
|
+
|
|
249
|
+
try:
|
|
250
|
+
with console.status("[bold blue]Generating documentation..."):
|
|
251
|
+
result = generate_docs(file_path, api_config_name=api)
|
|
252
|
+
|
|
253
|
+
if output:
|
|
254
|
+
with open(output, 'w') as f:
|
|
255
|
+
f.write(result)
|
|
256
|
+
console.print(f"[green]✓[/green] Documentation saved to: {output}")
|
|
257
|
+
else:
|
|
258
|
+
md = Markdown(result)
|
|
259
|
+
console.print(md)
|
|
260
|
+
|
|
261
|
+
except Exception as e:
|
|
262
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
263
|
+
sys.exit(1)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@main.command('review')
|
|
267
|
+
@click.argument('file_path', type=click.Path(exists=True))
|
|
268
|
+
@click.option('-a', '--api', help='API config to use')
|
|
269
|
+
@click.pass_context
|
|
270
|
+
def review(
|
|
271
|
+
ctx: click.Context,
|
|
272
|
+
file_path: str,
|
|
273
|
+
api: Optional[str]
|
|
274
|
+
) -> None:
|
|
275
|
+
"""Review code for bugs and improvements."""
|
|
276
|
+
console = ctx.obj['console']
|
|
277
|
+
|
|
278
|
+
try:
|
|
279
|
+
with console.status("[bold blue]Reviewing code..."):
|
|
280
|
+
result = code_review(file_path, api_config_name=api)
|
|
281
|
+
|
|
282
|
+
md = Markdown(result)
|
|
283
|
+
console.print(md)
|
|
284
|
+
|
|
285
|
+
except Exception as e:
|
|
286
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
287
|
+
sys.exit(1)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@main.command('debug')
|
|
291
|
+
@click.option('-f', '--file', type=click.Path(exists=True), help='File to debug')
|
|
292
|
+
@click.option('-e', '--error', help='Error message to analyze')
|
|
293
|
+
@click.option('-a', '--api', help='API config to use')
|
|
294
|
+
@click.pass_context
|
|
295
|
+
def debug(
|
|
296
|
+
ctx: click.Context,
|
|
297
|
+
file: Optional[str],
|
|
298
|
+
error: Optional[str],
|
|
299
|
+
api: Optional[str]
|
|
300
|
+
) -> None:
|
|
301
|
+
"""Debug code and analyze errors."""
|
|
302
|
+
console = ctx.obj['console']
|
|
303
|
+
|
|
304
|
+
# Read from stdin if available
|
|
305
|
+
stdin_content = None
|
|
306
|
+
if not sys.stdin.isatty():
|
|
307
|
+
stdin_content = sys.stdin.read().strip()
|
|
308
|
+
|
|
309
|
+
try:
|
|
310
|
+
with console.status("[bold blue]Analyzing error..."):
|
|
311
|
+
result = debug_code(
|
|
312
|
+
file_path=file,
|
|
313
|
+
error_message=error or stdin_content,
|
|
314
|
+
api_config_name=api
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
md = Markdown(result)
|
|
318
|
+
console.print(md)
|
|
319
|
+
|
|
320
|
+
except Exception as e:
|
|
321
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
322
|
+
sys.exit(1)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@main.command('refactor')
|
|
326
|
+
@click.argument('file_path', type=click.Path(exists=True))
|
|
327
|
+
@click.option('-o', '--output', type=click.Path(), help='Output file path')
|
|
328
|
+
@click.option('-a', '--api', help='API config to use')
|
|
329
|
+
@click.pass_context
|
|
330
|
+
def refactor(
|
|
331
|
+
ctx: click.Context,
|
|
332
|
+
file_path: str,
|
|
333
|
+
output: Optional[str],
|
|
334
|
+
api: Optional[str]
|
|
335
|
+
) -> None:
|
|
336
|
+
"""Suggest refactoring improvements."""
|
|
337
|
+
console = ctx.obj['console']
|
|
338
|
+
|
|
339
|
+
try:
|
|
340
|
+
with console.status("[bold blue]Analyzing code..."):
|
|
341
|
+
result = refactor_code(file_path, api_config_name=api)
|
|
342
|
+
|
|
343
|
+
if output:
|
|
344
|
+
with open(output, 'w') as f:
|
|
345
|
+
f.write(result)
|
|
346
|
+
console.print(f"[green]✓[/green] Refactored code saved to: {output}")
|
|
347
|
+
else:
|
|
348
|
+
md = Markdown(result)
|
|
349
|
+
console.print(md)
|
|
350
|
+
|
|
351
|
+
except Exception as e:
|
|
352
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
353
|
+
sys.exit(1)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
@main.group()
|
|
357
|
+
def git() -> None:
|
|
358
|
+
"""Git workflow helpers."""
|
|
359
|
+
pass
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
@git.command('commit')
|
|
363
|
+
@click.option('-a', '--api', help='API config to use')
|
|
364
|
+
@click.pass_context
|
|
365
|
+
def git_commit(ctx: click.Context, api: Optional[str]) -> None:
|
|
366
|
+
"""Generate commit message from staged changes."""
|
|
367
|
+
console = ctx.obj['console']
|
|
368
|
+
|
|
369
|
+
try:
|
|
370
|
+
with console.status("[bold blue]Analyzing changes..."):
|
|
371
|
+
result = git_commit_message(api_config_name=api)
|
|
372
|
+
|
|
373
|
+
console.print("\n[bold green]Suggested commit message:[/bold green]")
|
|
374
|
+
console.print(Panel(result, border_style="green"))
|
|
375
|
+
|
|
376
|
+
except Exception as e:
|
|
377
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
378
|
+
sys.exit(1)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
@main.command('usage')
|
|
382
|
+
@click.option('--days', type=int, help='Filter by days')
|
|
383
|
+
@click.option('--api', help='Filter by API config')
|
|
384
|
+
@click.pass_context
|
|
385
|
+
def usage(ctx: click.Context, days: Optional[int], api: Optional[str]) -> None:
|
|
386
|
+
"""Show API usage statistics."""
|
|
387
|
+
console = ctx.obj['console']
|
|
388
|
+
|
|
389
|
+
try:
|
|
390
|
+
tracker = UsageTracker()
|
|
391
|
+
tracker.display_usage(console, days=days, api_config=api)
|
|
392
|
+
except Exception as e:
|
|
393
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
394
|
+
sys.exit(1)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
if __name__ == '__main__':
|
|
398
|
+
main(obj={})
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Developer-specific commands for Claude Dev CLI."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from claude_dev_cli.core import ClaudeClient
|
|
8
|
+
from claude_dev_cli.templates import (
|
|
9
|
+
TEST_GENERATION_PROMPT,
|
|
10
|
+
CODE_REVIEW_PROMPT,
|
|
11
|
+
DEBUG_PROMPT,
|
|
12
|
+
DOCS_GENERATION_PROMPT,
|
|
13
|
+
REFACTOR_PROMPT,
|
|
14
|
+
GIT_COMMIT_PROMPT,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def generate_tests(file_path: str, api_config_name: Optional[str] = None) -> str:
|
|
19
|
+
"""Generate pytest tests for a Python file."""
|
|
20
|
+
with open(file_path, 'r') as f:
|
|
21
|
+
code = f.read()
|
|
22
|
+
|
|
23
|
+
prompt = TEST_GENERATION_PROMPT.format(
|
|
24
|
+
filename=Path(file_path).name,
|
|
25
|
+
code=code
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
client = ClaudeClient(api_config_name=api_config_name)
|
|
29
|
+
return client.call(prompt, system_prompt="You are a Python testing expert.")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def code_review(file_path: str, api_config_name: Optional[str] = None) -> str:
|
|
33
|
+
"""Review code for bugs and improvements."""
|
|
34
|
+
with open(file_path, 'r') as f:
|
|
35
|
+
code = f.read()
|
|
36
|
+
|
|
37
|
+
prompt = CODE_REVIEW_PROMPT.format(
|
|
38
|
+
filename=Path(file_path).name,
|
|
39
|
+
code=code
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
client = ClaudeClient(api_config_name=api_config_name)
|
|
43
|
+
return client.call(
|
|
44
|
+
prompt,
|
|
45
|
+
system_prompt="You are a senior code reviewer focused on security, performance, and best practices."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def debug_code(
|
|
50
|
+
file_path: Optional[str] = None,
|
|
51
|
+
error_message: Optional[str] = None,
|
|
52
|
+
api_config_name: Optional[str] = None
|
|
53
|
+
) -> str:
|
|
54
|
+
"""Debug code and analyze errors."""
|
|
55
|
+
code = ""
|
|
56
|
+
if file_path:
|
|
57
|
+
with open(file_path, 'r') as f:
|
|
58
|
+
code = f.read()
|
|
59
|
+
|
|
60
|
+
prompt = DEBUG_PROMPT.format(
|
|
61
|
+
filename=Path(file_path).name if file_path else "unknown",
|
|
62
|
+
code=code,
|
|
63
|
+
error=error_message or "No error message provided"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
client = ClaudeClient(api_config_name=api_config_name)
|
|
67
|
+
return client.call(
|
|
68
|
+
prompt,
|
|
69
|
+
system_prompt="You are a debugging expert. Analyze errors and provide fixes."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def generate_docs(file_path: str, api_config_name: Optional[str] = None) -> str:
|
|
74
|
+
"""Generate documentation for a Python file."""
|
|
75
|
+
with open(file_path, 'r') as f:
|
|
76
|
+
code = f.read()
|
|
77
|
+
|
|
78
|
+
prompt = DOCS_GENERATION_PROMPT.format(
|
|
79
|
+
filename=Path(file_path).name,
|
|
80
|
+
code=code
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
client = ClaudeClient(api_config_name=api_config_name)
|
|
84
|
+
return client.call(
|
|
85
|
+
prompt,
|
|
86
|
+
system_prompt="You are a technical documentation expert."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def refactor_code(file_path: str, api_config_name: Optional[str] = None) -> str:
|
|
91
|
+
"""Suggest refactoring improvements."""
|
|
92
|
+
with open(file_path, 'r') as f:
|
|
93
|
+
code = f.read()
|
|
94
|
+
|
|
95
|
+
prompt = REFACTOR_PROMPT.format(
|
|
96
|
+
filename=Path(file_path).name,
|
|
97
|
+
code=code
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
client = ClaudeClient(api_config_name=api_config_name)
|
|
101
|
+
return client.call(
|
|
102
|
+
prompt,
|
|
103
|
+
system_prompt="You are a refactoring expert focused on code maintainability and readability."
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def git_commit_message(api_config_name: Optional[str] = None) -> str:
|
|
108
|
+
"""Generate commit message from staged changes."""
|
|
109
|
+
try:
|
|
110
|
+
# Get staged changes
|
|
111
|
+
result = subprocess.run(
|
|
112
|
+
['git', '--no-pager', 'diff', '--cached'],
|
|
113
|
+
capture_output=True,
|
|
114
|
+
text=True,
|
|
115
|
+
check=True
|
|
116
|
+
)
|
|
117
|
+
diff = result.stdout
|
|
118
|
+
|
|
119
|
+
if not diff:
|
|
120
|
+
raise ValueError("No staged changes found. Run 'git add' first.")
|
|
121
|
+
|
|
122
|
+
prompt = GIT_COMMIT_PROMPT.format(diff=diff)
|
|
123
|
+
|
|
124
|
+
client = ClaudeClient(api_config_name=api_config_name)
|
|
125
|
+
return client.call(
|
|
126
|
+
prompt,
|
|
127
|
+
system_prompt="You are a git commit message expert. Write clear, conventional commit messages."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
except subprocess.CalledProcessError as e:
|
|
131
|
+
raise ValueError(f"Git command failed: {e}")
|
|
132
|
+
except FileNotFoundError:
|
|
133
|
+
raise ValueError("Git is not installed or not in PATH")
|