claude-dev-cli 0.4.0__tar.gz → 0.6.0__tar.gz
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-0.4.0/src/claude_dev_cli.egg-info → claude_dev_cli-0.6.0}/PKG-INFO +56 -3
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/README.md +55 -2
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/pyproject.toml +1 -1
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/__init__.py +1 -1
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/cli.py +381 -1
- claude_dev_cli-0.6.0/src/claude_dev_cli/history.py +189 -0
- claude_dev_cli-0.6.0/src/claude_dev_cli/template_manager.py +288 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0/src/claude_dev_cli.egg-info}/PKG-INFO +56 -3
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli.egg-info/SOURCES.txt +3 -0
- claude_dev_cli-0.6.0/tests/test_template_manager.py +327 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/LICENSE +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/MANIFEST.in +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/setup.cfg +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/commands.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/config.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/core.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/plugins/__init__.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/plugins/base.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/plugins/diff_editor/__init__.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/plugins/diff_editor/plugin.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/plugins/diff_editor/viewer.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/secure_storage.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/templates.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/toon_utils.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/usage.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli.egg-info/dependency_links.txt +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli.egg-info/entry_points.txt +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli.egg-info/requires.txt +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli.egg-info/top_level.txt +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/tests/test_cli.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/tests/test_commands.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/tests/test_config.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/tests/test_core.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/tests/test_diff_editor.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/tests/test_secure_storage.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/tests/test_toon_utils.py +0 -0
- {claude_dev_cli-0.4.0 → claude_dev_cli-0.6.0}/tests/test_usage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-dev-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -72,6 +72,12 @@ A powerful command-line tool for developers using Claude AI with multi-API routi
|
|
|
72
72
|
- **Interactive**: Chat mode with conversation history
|
|
73
73
|
- **Streaming**: Real-time responses
|
|
74
74
|
|
|
75
|
+
### 📝 Custom Templates
|
|
76
|
+
- **Built-in Templates**: 8 pre-built templates for common tasks (code review, testing, debugging, etc.)
|
|
77
|
+
- **User Templates**: Create and manage your own reusable prompt templates
|
|
78
|
+
- **Variable Substitution**: Use {{variable}} placeholders for dynamic content
|
|
79
|
+
- **Categories**: Organize templates by category (review, testing, debugging, etc.)
|
|
80
|
+
|
|
75
81
|
### 🎒 TOON Format Support (Optional)
|
|
76
82
|
- **Token Reduction**: 30-60% fewer tokens than JSON
|
|
77
83
|
- **Cost Savings**: Reduce API costs significantly
|
|
@@ -156,7 +162,44 @@ git add .
|
|
|
156
162
|
cdc git commit
|
|
157
163
|
```
|
|
158
164
|
|
|
159
|
-
###
|
|
165
|
+
### 5. Custom Templates
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# List all templates (built-in and user)
|
|
169
|
+
cdc template list
|
|
170
|
+
|
|
171
|
+
# Show template details
|
|
172
|
+
cdc template show code-review
|
|
173
|
+
|
|
174
|
+
# Add a custom template
|
|
175
|
+
cdc template add my-review -c "Review this code for {{focus}}: {{code}}" \
|
|
176
|
+
-d "Custom review template" --category review
|
|
177
|
+
|
|
178
|
+
# Use a template (interactive variable input)
|
|
179
|
+
cdc template use debug-error
|
|
180
|
+
|
|
181
|
+
# Delete a user template
|
|
182
|
+
cdc template delete my-review
|
|
183
|
+
|
|
184
|
+
# Filter by category
|
|
185
|
+
cdc template list --category review
|
|
186
|
+
|
|
187
|
+
# Show only user templates
|
|
188
|
+
cdc template list --user
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### Built-in Templates
|
|
192
|
+
|
|
193
|
+
- **code-review**: Comprehensive code review with customizable focus
|
|
194
|
+
- **code-review-security**: Security-focused code review
|
|
195
|
+
- **test-strategy**: Generate testing strategy and test cases
|
|
196
|
+
- **debug-error**: Debug error with context
|
|
197
|
+
- **optimize-performance**: Performance optimization analysis
|
|
198
|
+
- **refactor-clean**: Clean code refactoring
|
|
199
|
+
- **explain-code**: Detailed code explanation
|
|
200
|
+
- **api-design**: API design assistance
|
|
201
|
+
|
|
202
|
+
### 6. Usage Tracking
|
|
160
203
|
|
|
161
204
|
```bash
|
|
162
205
|
# View all usage
|
|
@@ -169,7 +212,7 @@ cdc usage --days 7
|
|
|
169
212
|
cdc usage --api client
|
|
170
213
|
```
|
|
171
214
|
|
|
172
|
-
###
|
|
215
|
+
### 7. TOON Format (Optional - Reduces Tokens by 30-60%)
|
|
173
216
|
|
|
174
217
|
```bash
|
|
175
218
|
# Check if TOON is installed
|
|
@@ -322,6 +365,16 @@ When using a client's Enterprise API:
|
|
|
322
365
|
| `cdc debug -f <file> -e <error>` | Debug code |
|
|
323
366
|
| `cdc refactor <file>` | Refactoring suggestions |
|
|
324
367
|
|
|
368
|
+
### Templates
|
|
369
|
+
|
|
370
|
+
| Command | Description |
|
|
371
|
+
|---------|-------------|
|
|
372
|
+
| `cdc template list` | List all templates |
|
|
373
|
+
| `cdc template show <name>` | Show template details |
|
|
374
|
+
| `cdc template add <name>` | Add new template |
|
|
375
|
+
| `cdc template delete <name>` | Delete user template |
|
|
376
|
+
| `cdc template use <name>` | Use template interactively |
|
|
377
|
+
|
|
325
378
|
### Git Helpers
|
|
326
379
|
|
|
327
380
|
| Command | Description |
|
|
@@ -29,6 +29,12 @@ A powerful command-line tool for developers using Claude AI with multi-API routi
|
|
|
29
29
|
- **Interactive**: Chat mode with conversation history
|
|
30
30
|
- **Streaming**: Real-time responses
|
|
31
31
|
|
|
32
|
+
### 📝 Custom Templates
|
|
33
|
+
- **Built-in Templates**: 8 pre-built templates for common tasks (code review, testing, debugging, etc.)
|
|
34
|
+
- **User Templates**: Create and manage your own reusable prompt templates
|
|
35
|
+
- **Variable Substitution**: Use {{variable}} placeholders for dynamic content
|
|
36
|
+
- **Categories**: Organize templates by category (review, testing, debugging, etc.)
|
|
37
|
+
|
|
32
38
|
### 🎒 TOON Format Support (Optional)
|
|
33
39
|
- **Token Reduction**: 30-60% fewer tokens than JSON
|
|
34
40
|
- **Cost Savings**: Reduce API costs significantly
|
|
@@ -113,7 +119,44 @@ git add .
|
|
|
113
119
|
cdc git commit
|
|
114
120
|
```
|
|
115
121
|
|
|
116
|
-
###
|
|
122
|
+
### 5. Custom Templates
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# List all templates (built-in and user)
|
|
126
|
+
cdc template list
|
|
127
|
+
|
|
128
|
+
# Show template details
|
|
129
|
+
cdc template show code-review
|
|
130
|
+
|
|
131
|
+
# Add a custom template
|
|
132
|
+
cdc template add my-review -c "Review this code for {{focus}}: {{code}}" \
|
|
133
|
+
-d "Custom review template" --category review
|
|
134
|
+
|
|
135
|
+
# Use a template (interactive variable input)
|
|
136
|
+
cdc template use debug-error
|
|
137
|
+
|
|
138
|
+
# Delete a user template
|
|
139
|
+
cdc template delete my-review
|
|
140
|
+
|
|
141
|
+
# Filter by category
|
|
142
|
+
cdc template list --category review
|
|
143
|
+
|
|
144
|
+
# Show only user templates
|
|
145
|
+
cdc template list --user
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### Built-in Templates
|
|
149
|
+
|
|
150
|
+
- **code-review**: Comprehensive code review with customizable focus
|
|
151
|
+
- **code-review-security**: Security-focused code review
|
|
152
|
+
- **test-strategy**: Generate testing strategy and test cases
|
|
153
|
+
- **debug-error**: Debug error with context
|
|
154
|
+
- **optimize-performance**: Performance optimization analysis
|
|
155
|
+
- **refactor-clean**: Clean code refactoring
|
|
156
|
+
- **explain-code**: Detailed code explanation
|
|
157
|
+
- **api-design**: API design assistance
|
|
158
|
+
|
|
159
|
+
### 6. Usage Tracking
|
|
117
160
|
|
|
118
161
|
```bash
|
|
119
162
|
# View all usage
|
|
@@ -126,7 +169,7 @@ cdc usage --days 7
|
|
|
126
169
|
cdc usage --api client
|
|
127
170
|
```
|
|
128
171
|
|
|
129
|
-
###
|
|
172
|
+
### 7. TOON Format (Optional - Reduces Tokens by 30-60%)
|
|
130
173
|
|
|
131
174
|
```bash
|
|
132
175
|
# Check if TOON is installed
|
|
@@ -279,6 +322,16 @@ When using a client's Enterprise API:
|
|
|
279
322
|
| `cdc debug -f <file> -e <error>` | Debug code |
|
|
280
323
|
| `cdc refactor <file>` | Refactoring suggestions |
|
|
281
324
|
|
|
325
|
+
### Templates
|
|
326
|
+
|
|
327
|
+
| Command | Description |
|
|
328
|
+
|---------|-------------|
|
|
329
|
+
| `cdc template list` | List all templates |
|
|
330
|
+
| `cdc template show <name>` | Show template details |
|
|
331
|
+
| `cdc template add <name>` | Add new template |
|
|
332
|
+
| `cdc template delete <name>` | Delete user template |
|
|
333
|
+
| `cdc template use <name>` | Use template interactively |
|
|
334
|
+
|
|
282
335
|
### Git Helpers
|
|
283
336
|
|
|
284
337
|
| Command | Description |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "claude-dev-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.6.0"
|
|
8
8
|
description = "A powerful CLI tool for developers using Claude AI with multi-API routing, test generation, code review, and usage tracking"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Command-line interface for Claude Dev CLI."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
import sys
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Optional
|
|
@@ -23,6 +24,8 @@ from claude_dev_cli.commands import (
|
|
|
23
24
|
from claude_dev_cli.usage import UsageTracker
|
|
24
25
|
from claude_dev_cli import toon_utils
|
|
25
26
|
from claude_dev_cli.plugins import load_plugins
|
|
27
|
+
from claude_dev_cli.history import ConversationHistory, Conversation
|
|
28
|
+
from claude_dev_cli.template_manager import TemplateManager, Template
|
|
26
29
|
|
|
27
30
|
console = Console()
|
|
28
31
|
|
|
@@ -112,11 +115,36 @@ def ask(
|
|
|
112
115
|
|
|
113
116
|
@main.command()
|
|
114
117
|
@click.option('-a', '--api', help='API config to use')
|
|
118
|
+
@click.option('--continue', 'continue_conversation', is_flag=True,
|
|
119
|
+
help='Continue the last conversation')
|
|
120
|
+
@click.option('--save/--no-save', default=True, help='Save conversation history')
|
|
115
121
|
@click.pass_context
|
|
116
|
-
def interactive(
|
|
122
|
+
def interactive(
|
|
123
|
+
ctx: click.Context,
|
|
124
|
+
api: Optional[str],
|
|
125
|
+
continue_conversation: bool,
|
|
126
|
+
save: bool
|
|
127
|
+
) -> None:
|
|
117
128
|
"""Start interactive chat mode."""
|
|
118
129
|
console = ctx.obj['console']
|
|
119
130
|
|
|
131
|
+
# Setup conversation history
|
|
132
|
+
config = Config()
|
|
133
|
+
history_dir = config.config_dir / "history"
|
|
134
|
+
conv_history = ConversationHistory(history_dir)
|
|
135
|
+
|
|
136
|
+
# Load or create conversation
|
|
137
|
+
if continue_conversation:
|
|
138
|
+
conversation = conv_history.get_latest_conversation()
|
|
139
|
+
if conversation:
|
|
140
|
+
console.print(f"[green]↶ Continuing conversation from {conversation.updated_at.strftime('%Y-%m-%d %H:%M')}[/green]")
|
|
141
|
+
console.print(f"[dim]Messages: {len(conversation.messages)}[/dim]\n")
|
|
142
|
+
else:
|
|
143
|
+
console.print("[yellow]No previous conversation found, starting new one[/yellow]\n")
|
|
144
|
+
conversation = Conversation()
|
|
145
|
+
else:
|
|
146
|
+
conversation = Conversation()
|
|
147
|
+
|
|
120
148
|
console.print(Panel.fit(
|
|
121
149
|
"Claude Dev CLI - Interactive Mode\n"
|
|
122
150
|
"Type 'exit' or 'quit' to end\n"
|
|
@@ -127,28 +155,177 @@ def interactive(ctx: click.Context, api: Optional[str]) -> None:
|
|
|
127
155
|
|
|
128
156
|
try:
|
|
129
157
|
client = ClaudeClient(api_config_name=api)
|
|
158
|
+
response_buffer = []
|
|
130
159
|
|
|
131
160
|
while True:
|
|
132
161
|
try:
|
|
133
162
|
user_input = console.input("\n[bold cyan]You:[/bold cyan] ").strip()
|
|
134
163
|
|
|
135
164
|
if user_input.lower() in ['exit', 'quit']:
|
|
165
|
+
if save and conversation.messages:
|
|
166
|
+
conv_history.save_conversation(conversation)
|
|
167
|
+
console.print(f"\n[dim]💾 Saved conversation: {conversation.conversation_id}[/dim]")
|
|
136
168
|
break
|
|
169
|
+
|
|
170
|
+
if user_input.lower() == 'clear':
|
|
171
|
+
conversation = Conversation()
|
|
172
|
+
console.print("[yellow]Conversation cleared[/yellow]")
|
|
173
|
+
continue
|
|
174
|
+
|
|
137
175
|
if not user_input:
|
|
138
176
|
continue
|
|
139
177
|
|
|
178
|
+
# Add user message to history
|
|
179
|
+
conversation.add_message("user", user_input)
|
|
180
|
+
|
|
181
|
+
# Get response
|
|
140
182
|
console.print("\n[bold green]Claude:[/bold green] ", end='')
|
|
183
|
+
response_buffer = []
|
|
141
184
|
for chunk in client.call_streaming(user_input):
|
|
142
185
|
console.print(chunk, end='')
|
|
186
|
+
response_buffer.append(chunk)
|
|
143
187
|
console.print()
|
|
144
188
|
|
|
189
|
+
# Add assistant response to history
|
|
190
|
+
full_response = ''.join(response_buffer)
|
|
191
|
+
conversation.add_message("assistant", full_response)
|
|
192
|
+
|
|
193
|
+
# Auto-save periodically
|
|
194
|
+
if save and len(conversation.messages) % 10 == 0:
|
|
195
|
+
conv_history.save_conversation(conversation)
|
|
196
|
+
|
|
145
197
|
except KeyboardInterrupt:
|
|
146
198
|
console.print("\n\n[yellow]Interrupted. Type 'exit' to quit.[/yellow]")
|
|
147
199
|
continue
|
|
148
200
|
|
|
149
201
|
except Exception as e:
|
|
150
202
|
console.print(f"[red]Error: {e}[/red]")
|
|
203
|
+
if save and conversation.messages:
|
|
204
|
+
conv_history.save_conversation(conversation)
|
|
205
|
+
sys.exit(1)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@main.group()
|
|
209
|
+
def completion() -> None:
|
|
210
|
+
"""Shell completion installation."""
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@completion.command('install')
|
|
215
|
+
@click.option('--shell', type=click.Choice(['bash', 'zsh', 'fish', 'auto']), default='auto',
|
|
216
|
+
help='Shell type (auto-detects if not specified)')
|
|
217
|
+
@click.pass_context
|
|
218
|
+
def completion_install(ctx: click.Context, shell: str) -> None:
|
|
219
|
+
"""Install shell completion for cdc command."""
|
|
220
|
+
console = ctx.obj['console']
|
|
221
|
+
|
|
222
|
+
# Auto-detect shell if needed
|
|
223
|
+
if shell == 'auto':
|
|
224
|
+
shell_path = os.environ.get('SHELL', '')
|
|
225
|
+
if 'zsh' in shell_path:
|
|
226
|
+
shell = 'zsh'
|
|
227
|
+
elif 'bash' in shell_path:
|
|
228
|
+
shell = 'bash'
|
|
229
|
+
elif 'fish' in shell_path:
|
|
230
|
+
shell = 'fish'
|
|
231
|
+
else:
|
|
232
|
+
console.print("[red]Could not auto-detect shell[/red]")
|
|
233
|
+
console.print("Please specify: --shell bash|zsh|fish")
|
|
234
|
+
sys.exit(1)
|
|
235
|
+
|
|
236
|
+
console.print(f"[cyan]Installing completion for {shell}...[/cyan]\n")
|
|
237
|
+
|
|
238
|
+
if shell == 'zsh':
|
|
239
|
+
console.print("Add this to your ~/.zshrc:\n")
|
|
240
|
+
console.print("[yellow]eval \"$(_CDC_COMPLETE=zsh_source cdc)\"[/yellow]\n")
|
|
241
|
+
console.print("Then run: [cyan]source ~/.zshrc[/cyan]")
|
|
242
|
+
elif shell == 'bash':
|
|
243
|
+
console.print("Add this to your ~/.bashrc:\n")
|
|
244
|
+
console.print("[yellow]eval \"$(_CDC_COMPLETE=bash_source cdc)\"[/yellow]\n")
|
|
245
|
+
console.print("Then run: [cyan]source ~/.bashrc[/cyan]")
|
|
246
|
+
elif shell == 'fish':
|
|
247
|
+
console.print("Add this to ~/.config/fish/completions/cdc.fish:\n")
|
|
248
|
+
console.print("[yellow]_CDC_COMPLETE=fish_source cdc | source[/yellow]\n")
|
|
249
|
+
console.print("Then reload: [cyan]exec fish[/cyan]")
|
|
250
|
+
|
|
251
|
+
console.print("\n[green]✓[/green] Instructions displayed above")
|
|
252
|
+
console.print("[dim]Completion will provide command and option suggestions[/dim]")
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@completion.command('generate')
|
|
256
|
+
@click.option('--shell', type=click.Choice(['bash', 'zsh', 'fish']), required=True,
|
|
257
|
+
help='Shell type')
|
|
258
|
+
@click.pass_context
|
|
259
|
+
def completion_generate(ctx: click.Context, shell: str) -> None:
|
|
260
|
+
"""Generate completion script for shell."""
|
|
261
|
+
import subprocess
|
|
262
|
+
import sys
|
|
263
|
+
|
|
264
|
+
env_var = f"_CDC_COMPLETE={shell}_source"
|
|
265
|
+
result = subprocess.run(
|
|
266
|
+
[sys.executable, '-m', 'claude_dev_cli.cli'],
|
|
267
|
+
env={**os.environ, '_CDC_COMPLETE': f'{shell}_source'},
|
|
268
|
+
capture_output=True,
|
|
269
|
+
text=True
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
if result.returncode == 0:
|
|
273
|
+
click.echo(result.stdout)
|
|
274
|
+
else:
|
|
275
|
+
ctx.obj['console'].print(f"[red]Error generating completion: {result.stderr}[/red]")
|
|
276
|
+
sys.exit(1)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@main.group()
|
|
280
|
+
def history() -> None:
|
|
281
|
+
"""Manage conversation history."""
|
|
282
|
+
pass
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@history.command('list')
|
|
286
|
+
@click.option('-n', '--limit', type=int, default=10, help='Number of conversations to show')
|
|
287
|
+
@click.option('-s', '--search', help='Search conversations')
|
|
288
|
+
@click.pass_context
|
|
289
|
+
def history_list(ctx: click.Context, limit: int, search: Optional[str]) -> None:
|
|
290
|
+
"""List conversation history."""
|
|
291
|
+
console = ctx.obj['console']
|
|
292
|
+
config = Config()
|
|
293
|
+
conv_history = ConversationHistory(config.config_dir / "history")
|
|
294
|
+
|
|
295
|
+
conversations = conv_history.list_conversations(limit=limit, search_query=search)
|
|
296
|
+
|
|
297
|
+
if not conversations:
|
|
298
|
+
console.print("[yellow]No conversations found[/yellow]")
|
|
299
|
+
return
|
|
300
|
+
|
|
301
|
+
for conv in conversations:
|
|
302
|
+
summary = conv.get_summary(80)
|
|
303
|
+
console.print(f"\n[cyan]{conv.conversation_id}[/cyan]")
|
|
304
|
+
console.print(f"[dim]{conv.updated_at.strftime('%Y-%m-%d %H:%M')} | {len(conv.messages)} messages[/dim]")
|
|
305
|
+
console.print(f" {summary}")
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@history.command('export')
|
|
309
|
+
@click.argument('conversation_id')
|
|
310
|
+
@click.option('--format', type=click.Choice(['markdown', 'json']), default='markdown')
|
|
311
|
+
@click.option('-o', '--output', type=click.Path(), help='Output file')
|
|
312
|
+
@click.pass_context
|
|
313
|
+
def history_export(ctx: click.Context, conversation_id: str, format: str, output: Optional[str]) -> None:
|
|
314
|
+
"""Export a conversation."""
|
|
315
|
+
console = ctx.obj['console']
|
|
316
|
+
config = Config()
|
|
317
|
+
conv_history = ConversationHistory(config.config_dir / "history")
|
|
318
|
+
|
|
319
|
+
content = conv_history.export_conversation(conversation_id, format)
|
|
320
|
+
if not content:
|
|
321
|
+
console.print(f"[red]Conversation {conversation_id} not found[/red]")
|
|
151
322
|
sys.exit(1)
|
|
323
|
+
|
|
324
|
+
if output:
|
|
325
|
+
Path(output).write_text(content)
|
|
326
|
+
console.print(f"[green]✓[/green] Exported to {output}")
|
|
327
|
+
else:
|
|
328
|
+
click.echo(content)
|
|
152
329
|
|
|
153
330
|
|
|
154
331
|
@main.group()
|
|
@@ -577,5 +754,208 @@ def toon_info(ctx: click.Context) -> None:
|
|
|
577
754
|
console.print("• Same data, fewer tokens")
|
|
578
755
|
|
|
579
756
|
|
|
757
|
+
@main.group()
|
|
758
|
+
def template() -> None:
|
|
759
|
+
"""Manage custom prompt templates."""
|
|
760
|
+
pass
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
@template.command('list')
|
|
764
|
+
@click.option('-c', '--category', help='Filter by category')
|
|
765
|
+
@click.option('--builtin', is_flag=True, help='Show only built-in templates')
|
|
766
|
+
@click.option('--user', is_flag=True, help='Show only user templates')
|
|
767
|
+
@click.pass_context
|
|
768
|
+
def template_list(
|
|
769
|
+
ctx: click.Context,
|
|
770
|
+
category: Optional[str],
|
|
771
|
+
builtin: bool,
|
|
772
|
+
user: bool
|
|
773
|
+
) -> None:
|
|
774
|
+
"""List available templates."""
|
|
775
|
+
console = ctx.obj['console']
|
|
776
|
+
config = Config()
|
|
777
|
+
manager = TemplateManager(config.config_dir)
|
|
778
|
+
|
|
779
|
+
templates = manager.list_templates(
|
|
780
|
+
category=category,
|
|
781
|
+
builtin_only=builtin,
|
|
782
|
+
user_only=user
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
if not templates:
|
|
786
|
+
console.print("[yellow]No templates found.[/yellow]")
|
|
787
|
+
return
|
|
788
|
+
|
|
789
|
+
from rich.table import Table
|
|
790
|
+
|
|
791
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
792
|
+
table.add_column("Name", style="cyan")
|
|
793
|
+
table.add_column("Category", style="green")
|
|
794
|
+
table.add_column("Variables", style="yellow")
|
|
795
|
+
table.add_column("Type", style="blue")
|
|
796
|
+
table.add_column("Description")
|
|
797
|
+
|
|
798
|
+
for tmpl in templates:
|
|
799
|
+
vars_display = ", ".join(tmpl.variables) if tmpl.variables else "-"
|
|
800
|
+
type_display = "🔒 Built-in" if tmpl.builtin else "📝 User"
|
|
801
|
+
table.add_row(
|
|
802
|
+
tmpl.name,
|
|
803
|
+
tmpl.category,
|
|
804
|
+
vars_display,
|
|
805
|
+
type_display,
|
|
806
|
+
tmpl.description
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
console.print(table)
|
|
810
|
+
|
|
811
|
+
# Show categories
|
|
812
|
+
categories = manager.get_categories()
|
|
813
|
+
console.print(f"\n[dim]Categories: {', '.join(categories)}[/dim]")
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
@template.command('show')
|
|
817
|
+
@click.argument('name')
|
|
818
|
+
@click.pass_context
|
|
819
|
+
def template_show(ctx: click.Context, name: str) -> None:
|
|
820
|
+
"""Show template details."""
|
|
821
|
+
console = ctx.obj['console']
|
|
822
|
+
config = Config()
|
|
823
|
+
manager = TemplateManager(config.config_dir)
|
|
824
|
+
|
|
825
|
+
tmpl = manager.get_template(name)
|
|
826
|
+
if not tmpl:
|
|
827
|
+
console.print(f"[red]Template not found: {name}[/red]")
|
|
828
|
+
sys.exit(1)
|
|
829
|
+
|
|
830
|
+
console.print(Panel(
|
|
831
|
+
f"[bold]{tmpl.name}[/bold]\n\n"
|
|
832
|
+
f"[dim]{tmpl.description}[/dim]\n\n"
|
|
833
|
+
f"Category: [green]{tmpl.category}[/green]\n"
|
|
834
|
+
f"Type: {'🔒 Built-in' if tmpl.builtin else '📝 User'}\n"
|
|
835
|
+
f"Variables: [yellow]{', '.join(tmpl.variables) if tmpl.variables else 'None'}[/yellow]",
|
|
836
|
+
title="Template Info",
|
|
837
|
+
border_style="blue"
|
|
838
|
+
))
|
|
839
|
+
|
|
840
|
+
console.print("\n[bold]Content:[/bold]\n")
|
|
841
|
+
console.print(Panel(tmpl.content, border_style="dim"))
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
@template.command('add')
|
|
845
|
+
@click.argument('name')
|
|
846
|
+
@click.option('-c', '--content', help='Template content (or use stdin)')
|
|
847
|
+
@click.option('-d', '--description', help='Template description')
|
|
848
|
+
@click.option('--category', default='general', help='Template category')
|
|
849
|
+
@click.pass_context
|
|
850
|
+
def template_add(
|
|
851
|
+
ctx: click.Context,
|
|
852
|
+
name: str,
|
|
853
|
+
content: Optional[str],
|
|
854
|
+
description: Optional[str],
|
|
855
|
+
category: str
|
|
856
|
+
) -> None:
|
|
857
|
+
"""Add a new template."""
|
|
858
|
+
console = ctx.obj['console']
|
|
859
|
+
config = Config()
|
|
860
|
+
manager = TemplateManager(config.config_dir)
|
|
861
|
+
|
|
862
|
+
# Get content from stdin if not provided
|
|
863
|
+
if not content:
|
|
864
|
+
if sys.stdin.isatty():
|
|
865
|
+
console.print("[yellow]Enter template content (Ctrl+D to finish):[/yellow]")
|
|
866
|
+
content = sys.stdin.read().strip()
|
|
867
|
+
|
|
868
|
+
if not content:
|
|
869
|
+
console.print("[red]Error: No content provided[/red]")
|
|
870
|
+
sys.exit(1)
|
|
871
|
+
|
|
872
|
+
try:
|
|
873
|
+
tmpl = Template(
|
|
874
|
+
name=name,
|
|
875
|
+
content=content,
|
|
876
|
+
description=description,
|
|
877
|
+
category=category
|
|
878
|
+
)
|
|
879
|
+
manager.add_template(tmpl)
|
|
880
|
+
|
|
881
|
+
console.print(f"[green]✓[/green] Template added: {name}")
|
|
882
|
+
if tmpl.variables:
|
|
883
|
+
console.print(f"[dim]Variables: {', '.join(tmpl.variables)}[/dim]")
|
|
884
|
+
|
|
885
|
+
except ValueError as e:
|
|
886
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
887
|
+
sys.exit(1)
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
@template.command('delete')
|
|
891
|
+
@click.argument('name')
|
|
892
|
+
@click.pass_context
|
|
893
|
+
def template_delete(ctx: click.Context, name: str) -> None:
|
|
894
|
+
"""Delete a user template."""
|
|
895
|
+
console = ctx.obj['console']
|
|
896
|
+
config = Config()
|
|
897
|
+
manager = TemplateManager(config.config_dir)
|
|
898
|
+
|
|
899
|
+
try:
|
|
900
|
+
if manager.delete_template(name):
|
|
901
|
+
console.print(f"[green]✓[/green] Template deleted: {name}")
|
|
902
|
+
else:
|
|
903
|
+
console.print(f"[red]Template not found: {name}[/red]")
|
|
904
|
+
sys.exit(1)
|
|
905
|
+
|
|
906
|
+
except ValueError as e:
|
|
907
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
908
|
+
sys.exit(1)
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
@template.command('use')
|
|
912
|
+
@click.argument('name')
|
|
913
|
+
@click.option('-a', '--api', help='API config to use')
|
|
914
|
+
@click.option('-m', '--model', help='Claude model to use')
|
|
915
|
+
@click.pass_context
|
|
916
|
+
def template_use(ctx: click.Context, name: str, api: Optional[str], model: Optional[str]) -> None:
|
|
917
|
+
"""Use a template with interactive variable input."""
|
|
918
|
+
console = ctx.obj['console']
|
|
919
|
+
config = Config()
|
|
920
|
+
manager = TemplateManager(config.config_dir)
|
|
921
|
+
|
|
922
|
+
tmpl = manager.get_template(name)
|
|
923
|
+
if not tmpl:
|
|
924
|
+
console.print(f"[red]Template not found: {name}[/red]")
|
|
925
|
+
sys.exit(1)
|
|
926
|
+
|
|
927
|
+
# Get variable values
|
|
928
|
+
variables = {}
|
|
929
|
+
if tmpl.variables:
|
|
930
|
+
console.print(f"\n[bold]Template: {name}[/bold]")
|
|
931
|
+
console.print(f"[dim]{tmpl.description}[/dim]\n")
|
|
932
|
+
|
|
933
|
+
for var in tmpl.variables:
|
|
934
|
+
value = console.input(f"[cyan]{var}:[/cyan] ").strip()
|
|
935
|
+
variables[var] = value
|
|
936
|
+
|
|
937
|
+
# Check for missing variables
|
|
938
|
+
missing = tmpl.get_missing_variables(**variables)
|
|
939
|
+
if missing:
|
|
940
|
+
console.print(f"[red]Missing required variables: {', '.join(missing)}[/red]")
|
|
941
|
+
sys.exit(1)
|
|
942
|
+
|
|
943
|
+
# Render template
|
|
944
|
+
prompt = tmpl.render(**variables)
|
|
945
|
+
|
|
946
|
+
# Call Claude
|
|
947
|
+
try:
|
|
948
|
+
client = ClaudeClient(api_config_name=api)
|
|
949
|
+
|
|
950
|
+
console.print("\n[bold green]Claude:[/bold green] ", end='')
|
|
951
|
+
for chunk in client.call_streaming(prompt, model=model):
|
|
952
|
+
console.print(chunk, end='')
|
|
953
|
+
console.print()
|
|
954
|
+
|
|
955
|
+
except Exception as e:
|
|
956
|
+
console.print(f"\n[red]Error: {e}[/red]")
|
|
957
|
+
sys.exit(1)
|
|
958
|
+
|
|
959
|
+
|
|
580
960
|
if __name__ == '__main__':
|
|
581
961
|
main(obj={})
|