claude-dev-cli 0.5.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.

Files changed (37) hide show
  1. {claude_dev_cli-0.5.0/src/claude_dev_cli.egg-info → claude_dev_cli-0.6.0}/PKG-INFO +56 -3
  2. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/README.md +55 -2
  3. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/pyproject.toml +1 -1
  4. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/__init__.py +1 -1
  5. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/cli.py +204 -0
  6. claude_dev_cli-0.6.0/src/claude_dev_cli/template_manager.py +288 -0
  7. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0/src/claude_dev_cli.egg-info}/PKG-INFO +56 -3
  8. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli.egg-info/SOURCES.txt +2 -0
  9. claude_dev_cli-0.6.0/tests/test_template_manager.py +327 -0
  10. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/LICENSE +0 -0
  11. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/MANIFEST.in +0 -0
  12. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/setup.cfg +0 -0
  13. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/commands.py +0 -0
  14. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/config.py +0 -0
  15. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/core.py +0 -0
  16. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/history.py +0 -0
  17. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/plugins/__init__.py +0 -0
  18. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/plugins/base.py +0 -0
  19. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/plugins/diff_editor/__init__.py +0 -0
  20. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/plugins/diff_editor/plugin.py +0 -0
  21. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/plugins/diff_editor/viewer.py +0 -0
  22. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/secure_storage.py +0 -0
  23. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/templates.py +0 -0
  24. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/toon_utils.py +0 -0
  25. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli/usage.py +0 -0
  26. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli.egg-info/dependency_links.txt +0 -0
  27. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli.egg-info/entry_points.txt +0 -0
  28. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli.egg-info/requires.txt +0 -0
  29. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/src/claude_dev_cli.egg-info/top_level.txt +0 -0
  30. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/tests/test_cli.py +0 -0
  31. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/tests/test_commands.py +0 -0
  32. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/tests/test_config.py +0 -0
  33. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/tests/test_core.py +0 -0
  34. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/tests/test_diff_editor.py +0 -0
  35. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/tests/test_secure_storage.py +0 -0
  36. {claude_dev_cli-0.5.0 → claude_dev_cli-0.6.0}/tests/test_toon_utils.py +0 -0
  37. {claude_dev_cli-0.5.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.5.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
- ### 4. Usage Tracking
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
- ### 5. TOON Format (Optional - Reduces Tokens by 30-60%)
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
- ### 4. Usage Tracking
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
- ### 5. TOON Format (Optional - Reduces Tokens by 30-60%)
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.5.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"
@@ -9,7 +9,7 @@ Features:
9
9
  - Interactive and single-shot modes
10
10
  """
11
11
 
12
- __version__ = "0.2.0"
12
+ __version__ = "0.6.0"
13
13
  __author__ = "Julio"
14
14
  __license__ = "MIT"
15
15
 
@@ -25,6 +25,7 @@ from claude_dev_cli.usage import UsageTracker
25
25
  from claude_dev_cli import toon_utils
26
26
  from claude_dev_cli.plugins import load_plugins
27
27
  from claude_dev_cli.history import ConversationHistory, Conversation
28
+ from claude_dev_cli.template_manager import TemplateManager, Template
28
29
 
29
30
  console = Console()
30
31
 
@@ -753,5 +754,208 @@ def toon_info(ctx: click.Context) -> None:
753
754
  console.print("• Same data, fewer tokens")
754
755
 
755
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
+
756
960
  if __name__ == '__main__':
757
961
  main(obj={})
@@ -0,0 +1,288 @@
1
+ """Template management for reusable prompts."""
2
+
3
+ import json
4
+ import re
5
+ from pathlib import Path
6
+ from typing import Dict, List, Optional, Any
7
+
8
+
9
+ class Template:
10
+ """Represents a reusable prompt template."""
11
+
12
+ def __init__(
13
+ self,
14
+ name: str,
15
+ content: str,
16
+ description: Optional[str] = None,
17
+ variables: Optional[List[str]] = None,
18
+ category: Optional[str] = None,
19
+ builtin: bool = False
20
+ ):
21
+ self.name = name
22
+ self.content = content
23
+ self.description = description or ""
24
+ self.variables = variables or self._extract_variables(content)
25
+ self.category = category or "general"
26
+ self.builtin = builtin
27
+
28
+ @staticmethod
29
+ def _extract_variables(content: str) -> List[str]:
30
+ """Extract {{variable}} placeholders from content."""
31
+ return list(set(re.findall(r'\{\{(\w+)\}\}', content)))
32
+
33
+ def render(self, **kwargs: str) -> str:
34
+ """Render template with provided variables."""
35
+ result = self.content
36
+ for var, value in kwargs.items():
37
+ result = result.replace(f'{{{{{var}}}}}', value)
38
+ return result
39
+
40
+ def get_missing_variables(self, **kwargs: str) -> List[str]:
41
+ """Get list of required variables not provided."""
42
+ return [var for var in self.variables if var not in kwargs]
43
+
44
+ def to_dict(self) -> Dict[str, Any]:
45
+ """Convert to dictionary for storage."""
46
+ return {
47
+ "name": self.name,
48
+ "content": self.content,
49
+ "description": self.description,
50
+ "variables": self.variables,
51
+ "category": self.category,
52
+ "builtin": self.builtin
53
+ }
54
+
55
+ @classmethod
56
+ def from_dict(cls, data: Dict[str, Any]) -> "Template":
57
+ """Create from dictionary."""
58
+ return cls(
59
+ name=data["name"],
60
+ content=data["content"],
61
+ description=data.get("description", ""),
62
+ variables=data.get("variables", []),
63
+ category=data.get("category", "general"),
64
+ builtin=data.get("builtin", False)
65
+ )
66
+
67
+
68
+ class TemplateManager:
69
+ """Manages template storage and retrieval."""
70
+
71
+ # Built-in templates
72
+ BUILTIN_TEMPLATES = [
73
+ Template(
74
+ name="code-review",
75
+ content="""Review this code for:
76
+ - Security vulnerabilities
77
+ - Performance issues
78
+ - Best practices
79
+ - Potential bugs
80
+ - Code clarity
81
+
82
+ {{code}}
83
+
84
+ Focus on: {{focus}}""",
85
+ description="Comprehensive code review with customizable focus",
86
+ category="review",
87
+ builtin=True
88
+ ),
89
+ Template(
90
+ name="code-review-security",
91
+ content="""Perform a security-focused code review of this code:
92
+
93
+ {{code}}
94
+
95
+ Check for:
96
+ - SQL injection vulnerabilities
97
+ - XSS vulnerabilities
98
+ - Authentication/authorization issues
99
+ - Data validation problems
100
+ - Sensitive data exposure
101
+ - CSRF vulnerabilities""",
102
+ description="Security-focused code review",
103
+ category="review",
104
+ builtin=True
105
+ ),
106
+ Template(
107
+ name="test-strategy",
108
+ content="""Generate a comprehensive test strategy for this {{language}} code:
109
+
110
+ {{code}}
111
+
112
+ Include:
113
+ - Unit tests for core functionality
114
+ - Edge cases and error handling
115
+ - Integration test scenarios
116
+ - Mock/stub suggestions
117
+ - Test data examples""",
118
+ description="Generate testing strategy and test cases",
119
+ category="testing",
120
+ builtin=True
121
+ ),
122
+ Template(
123
+ name="debug-error",
124
+ content="""Help me debug this error:
125
+
126
+ Error: {{error}}
127
+
128
+ Code context:
129
+ {{code}}
130
+
131
+ Please:
132
+ 1. Explain what's causing the error
133
+ 2. Suggest fixes with code examples
134
+ 3. Explain how to prevent similar errors""",
135
+ description="Debug error with context",
136
+ category="debugging",
137
+ builtin=True
138
+ ),
139
+ Template(
140
+ name="optimize-performance",
141
+ content="""Analyze this code for performance optimization:
142
+
143
+ {{code}}
144
+
145
+ Consider:
146
+ - Time complexity improvements
147
+ - Memory usage optimization
148
+ - Algorithm efficiency
149
+ - Database query optimization (if applicable)
150
+ - Caching opportunities
151
+
152
+ Provide specific code improvements.""",
153
+ description="Performance optimization analysis",
154
+ category="optimization",
155
+ builtin=True
156
+ ),
157
+ Template(
158
+ name="refactor-clean",
159
+ content="""Refactor this code to improve:
160
+ - Readability
161
+ - Maintainability
162
+ - Code organization
163
+ - Naming conventions
164
+ - {{language}} idioms
165
+
166
+ {{code}}
167
+
168
+ Provide the refactored version with explanations.""",
169
+ description="Clean code refactoring",
170
+ category="refactoring",
171
+ builtin=True
172
+ ),
173
+ Template(
174
+ name="explain-code",
175
+ content="""Explain this code in detail:
176
+
177
+ {{code}}
178
+
179
+ Include:
180
+ - What it does (high-level)
181
+ - How it works (step-by-step)
182
+ - Why certain approaches were used
183
+ - Potential improvements
184
+
185
+ Audience level: {{level}}""",
186
+ description="Detailed code explanation",
187
+ category="documentation",
188
+ builtin=True
189
+ ),
190
+ Template(
191
+ name="api-design",
192
+ content="""Design a {{style}} API for:
193
+
194
+ {{description}}
195
+
196
+ Include:
197
+ - Endpoint definitions
198
+ - Request/response formats
199
+ - Error handling
200
+ - Authentication approach
201
+ - Rate limiting considerations""",
202
+ description="API design assistance",
203
+ category="design",
204
+ builtin=True
205
+ ),
206
+ ]
207
+
208
+ def __init__(self, templates_dir: Path):
209
+ self.templates_dir = templates_dir
210
+ self.templates_file = templates_dir / "templates.json"
211
+ self.templates_dir.mkdir(parents=True, exist_ok=True)
212
+ self._load_templates()
213
+
214
+ def _load_templates(self) -> None:
215
+ """Load templates from disk."""
216
+ self.templates: Dict[str, Template] = {}
217
+
218
+ # Load built-in templates
219
+ for template in self.BUILTIN_TEMPLATES:
220
+ self.templates[template.name] = template
221
+
222
+ # Load user templates
223
+ if self.templates_file.exists():
224
+ try:
225
+ with open(self.templates_file, 'r') as f:
226
+ data = json.load(f)
227
+ for template_data in data.get("templates", []):
228
+ template = Template.from_dict(template_data)
229
+ self.templates[template.name] = template
230
+ except Exception:
231
+ pass
232
+
233
+ def _save_templates(self) -> None:
234
+ """Save user templates to disk."""
235
+ # Only save non-builtin templates
236
+ user_templates = [
237
+ t.to_dict() for t in self.templates.values() if not t.builtin
238
+ ]
239
+
240
+ with open(self.templates_file, 'w') as f:
241
+ json.dump({"templates": user_templates}, f, indent=2)
242
+
243
+ def add_template(self, template: Template) -> None:
244
+ """Add or update a template."""
245
+ if template.name in self.templates and self.templates[template.name].builtin:
246
+ raise ValueError(f"Cannot override builtin template: {template.name}")
247
+
248
+ self.templates[template.name] = template
249
+ self._save_templates()
250
+
251
+ def get_template(self, name: str) -> Optional[Template]:
252
+ """Get a template by name."""
253
+ return self.templates.get(name)
254
+
255
+ def list_templates(
256
+ self,
257
+ category: Optional[str] = None,
258
+ builtin_only: bool = False,
259
+ user_only: bool = False
260
+ ) -> List[Template]:
261
+ """List templates with optional filters."""
262
+ templates = list(self.templates.values())
263
+
264
+ if category:
265
+ templates = [t for t in templates if t.category == category]
266
+
267
+ if builtin_only:
268
+ templates = [t for t in templates if t.builtin]
269
+ elif user_only:
270
+ templates = [t for t in templates if not t.builtin]
271
+
272
+ return sorted(templates, key=lambda t: (t.category, t.name))
273
+
274
+ def delete_template(self, name: str) -> bool:
275
+ """Delete a template (cannot delete builtins)."""
276
+ if name not in self.templates:
277
+ return False
278
+
279
+ if self.templates[name].builtin:
280
+ raise ValueError(f"Cannot delete builtin template: {name}")
281
+
282
+ del self.templates[name]
283
+ self._save_templates()
284
+ return True
285
+
286
+ def get_categories(self) -> List[str]:
287
+ """Get list of all template categories."""
288
+ return sorted(set(t.category for t in self.templates.values()))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-dev-cli
3
- Version: 0.5.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
- ### 4. Usage Tracking
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
- ### 5. TOON Format (Optional - Reduces Tokens by 30-60%)
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 |
@@ -9,6 +9,7 @@ src/claude_dev_cli/config.py
9
9
  src/claude_dev_cli/core.py
10
10
  src/claude_dev_cli/history.py
11
11
  src/claude_dev_cli/secure_storage.py
12
+ src/claude_dev_cli/template_manager.py
12
13
  src/claude_dev_cli/templates.py
13
14
  src/claude_dev_cli/toon_utils.py
14
15
  src/claude_dev_cli/usage.py
@@ -29,5 +30,6 @@ tests/test_config.py
29
30
  tests/test_core.py
30
31
  tests/test_diff_editor.py
31
32
  tests/test_secure_storage.py
33
+ tests/test_template_manager.py
32
34
  tests/test_toon_utils.py
33
35
  tests/test_usage.py
@@ -0,0 +1,327 @@
1
+ """Tests for template manager."""
2
+
3
+ import json
4
+ import pytest
5
+ from pathlib import Path
6
+ from claude_dev_cli.template_manager import Template, TemplateManager
7
+
8
+
9
+ class TestTemplate:
10
+ """Test Template class."""
11
+
12
+ def test_template_creation(self):
13
+ """Test creating a basic template."""
14
+ tmpl = Template(
15
+ name="test",
16
+ content="Hello {{name}}",
17
+ description="Test template",
18
+ category="test"
19
+ )
20
+
21
+ assert tmpl.name == "test"
22
+ assert tmpl.content == "Hello {{name}}"
23
+ assert tmpl.description == "Test template"
24
+ assert tmpl.category == "test"
25
+ assert tmpl.builtin is False
26
+
27
+ def test_extract_variables(self):
28
+ """Test variable extraction from content."""
29
+ tmpl = Template(
30
+ name="test",
31
+ content="Hello {{name}}, you are {{age}} years old. {{name}} is cool."
32
+ )
33
+
34
+ # Should extract unique variables
35
+ assert set(tmpl.variables) == {"name", "age"}
36
+
37
+ def test_render_template(self):
38
+ """Test rendering template with variables."""
39
+ tmpl = Template(
40
+ name="test",
41
+ content="Hello {{name}}, you are {{age}} years old."
42
+ )
43
+
44
+ result = tmpl.render(name="Alice", age="30")
45
+ assert result == "Hello Alice, you are 30 years old."
46
+
47
+ def test_render_partial(self):
48
+ """Test rendering with missing variables."""
49
+ tmpl = Template(
50
+ name="test",
51
+ content="Hello {{name}}, you are {{age}} years old."
52
+ )
53
+
54
+ result = tmpl.render(name="Bob")
55
+ # Missing variables remain as placeholders
56
+ assert result == "Hello Bob, you are {{age}} years old."
57
+
58
+ def test_get_missing_variables(self):
59
+ """Test checking for missing variables."""
60
+ tmpl = Template(
61
+ name="test",
62
+ content="Hello {{name}}, you are {{age}} years old."
63
+ )
64
+
65
+ assert tmpl.get_missing_variables(name="Alice") == ["age"]
66
+ assert tmpl.get_missing_variables(name="Alice", age="30") == []
67
+
68
+ def test_to_dict(self):
69
+ """Test serialization to dict."""
70
+ tmpl = Template(
71
+ name="test",
72
+ content="Hello {{name}}",
73
+ description="Test template",
74
+ category="test",
75
+ builtin=True
76
+ )
77
+
78
+ data = tmpl.to_dict()
79
+ assert data["name"] == "test"
80
+ assert data["content"] == "Hello {{name}}"
81
+ assert data["description"] == "Test template"
82
+ assert data["category"] == "test"
83
+ assert data["builtin"] is True
84
+ assert "name" in data["variables"]
85
+
86
+ def test_from_dict(self):
87
+ """Test deserialization from dict."""
88
+ data = {
89
+ "name": "test",
90
+ "content": "Hello {{name}}",
91
+ "description": "Test template",
92
+ "category": "test",
93
+ "builtin": True,
94
+ "variables": ["name"]
95
+ }
96
+
97
+ tmpl = Template.from_dict(data)
98
+ assert tmpl.name == "test"
99
+ assert tmpl.content == "Hello {{name}}"
100
+ assert tmpl.description == "Test template"
101
+ assert tmpl.category == "test"
102
+ assert tmpl.builtin is True
103
+ assert tmpl.variables == ["name"]
104
+
105
+
106
+ class TestTemplateManager:
107
+ """Test TemplateManager class."""
108
+
109
+ @pytest.fixture
110
+ def temp_dir(self, tmp_path: Path) -> Path:
111
+ """Create temporary directory for tests."""
112
+ return tmp_path / "templates"
113
+
114
+ @pytest.fixture
115
+ def manager(self, temp_dir: Path) -> TemplateManager:
116
+ """Create template manager with temp directory."""
117
+ return TemplateManager(temp_dir)
118
+
119
+ def test_manager_creation(self, temp_dir: Path, manager: TemplateManager):
120
+ """Test manager creates directory structure."""
121
+ assert temp_dir.exists()
122
+ assert manager.templates_file == temp_dir / "templates.json"
123
+
124
+ def test_builtin_templates_loaded(self, manager: TemplateManager):
125
+ """Test built-in templates are loaded."""
126
+ # Should have 8 built-in templates
127
+ builtins = [t for t in manager.templates.values() if t.builtin]
128
+ assert len(builtins) == 8
129
+
130
+ # Check specific templates exist
131
+ assert "code-review" in manager.templates
132
+ assert "test-strategy" in manager.templates
133
+ assert "debug-error" in manager.templates
134
+
135
+ def test_add_user_template(self, manager: TemplateManager):
136
+ """Test adding a user template."""
137
+ tmpl = Template(
138
+ name="my-template",
139
+ content="Custom content {{var}}",
140
+ description="My template"
141
+ )
142
+
143
+ manager.add_template(tmpl)
144
+
145
+ # Should be in memory
146
+ assert "my-template" in manager.templates
147
+
148
+ # Should be saved to disk
149
+ assert manager.templates_file.exists()
150
+ with open(manager.templates_file) as f:
151
+ data = json.load(f)
152
+
153
+ assert len(data["templates"]) == 1
154
+ assert data["templates"][0]["name"] == "my-template"
155
+
156
+ def test_cannot_override_builtin(self, manager: TemplateManager):
157
+ """Test cannot override built-in templates."""
158
+ tmpl = Template(
159
+ name="code-review",
160
+ content="Override attempt"
161
+ )
162
+
163
+ with pytest.raises(ValueError, match="Cannot override builtin"):
164
+ manager.add_template(tmpl)
165
+
166
+ def test_get_template(self, manager: TemplateManager):
167
+ """Test getting template by name."""
168
+ # Get built-in
169
+ tmpl = manager.get_template("code-review")
170
+ assert tmpl is not None
171
+ assert tmpl.name == "code-review"
172
+
173
+ # Get non-existent
174
+ assert manager.get_template("nonexistent") is None
175
+
176
+ def test_list_templates_all(self, manager: TemplateManager):
177
+ """Test listing all templates."""
178
+ templates = manager.list_templates()
179
+
180
+ # Should have 8 built-ins
181
+ assert len(templates) >= 8
182
+
183
+ # Should be sorted by category then name
184
+ categories = [t.category for t in templates]
185
+ assert categories == sorted(categories)
186
+
187
+ def test_list_templates_by_category(self, manager: TemplateManager):
188
+ """Test filtering templates by category."""
189
+ review_templates = manager.list_templates(category="review")
190
+
191
+ assert len(review_templates) >= 2 # code-review and code-review-security
192
+ assert all(t.category == "review" for t in review_templates)
193
+
194
+ def test_list_templates_builtin_only(self, manager: TemplateManager):
195
+ """Test listing only built-in templates."""
196
+ # Add a user template
197
+ manager.add_template(Template(name="user-tmpl", content="test"))
198
+
199
+ builtins = manager.list_templates(builtin_only=True)
200
+ assert len(builtins) == 8
201
+ assert all(t.builtin for t in builtins)
202
+
203
+ def test_list_templates_user_only(self, manager: TemplateManager):
204
+ """Test listing only user templates."""
205
+ # Add user templates
206
+ manager.add_template(Template(name="user-1", content="test1"))
207
+ manager.add_template(Template(name="user-2", content="test2"))
208
+
209
+ user_templates = manager.list_templates(user_only=True)
210
+ assert len(user_templates) == 2
211
+ assert all(not t.builtin for t in user_templates)
212
+
213
+ def test_delete_user_template(self, manager: TemplateManager):
214
+ """Test deleting user template."""
215
+ # Add template
216
+ manager.add_template(Template(name="to-delete", content="test"))
217
+ assert "to-delete" in manager.templates
218
+
219
+ # Delete it
220
+ result = manager.delete_template("to-delete")
221
+ assert result is True
222
+ assert "to-delete" not in manager.templates
223
+
224
+ # Should be removed from disk
225
+ with open(manager.templates_file) as f:
226
+ data = json.load(f)
227
+
228
+ assert not any(t["name"] == "to-delete" for t in data["templates"])
229
+
230
+ def test_cannot_delete_builtin(self, manager: TemplateManager):
231
+ """Test cannot delete built-in templates."""
232
+ with pytest.raises(ValueError, match="Cannot delete builtin"):
233
+ manager.delete_template("code-review")
234
+
235
+ def test_delete_nonexistent(self, manager: TemplateManager):
236
+ """Test deleting non-existent template."""
237
+ result = manager.delete_template("nonexistent")
238
+ assert result is False
239
+
240
+ def test_get_categories(self, manager: TemplateManager):
241
+ """Test getting list of categories."""
242
+ categories = manager.get_categories()
243
+
244
+ # Should include all built-in categories
245
+ assert "review" in categories
246
+ assert "testing" in categories
247
+ assert "debugging" in categories
248
+ assert "optimization" in categories
249
+ assert "refactoring" in categories
250
+ assert "documentation" in categories
251
+ assert "design" in categories
252
+
253
+ # Should be sorted
254
+ assert categories == sorted(categories)
255
+
256
+ def test_persistence(self, temp_dir: Path):
257
+ """Test templates persist across manager instances."""
258
+ # Create manager and add template
259
+ manager1 = TemplateManager(temp_dir)
260
+ manager1.add_template(Template(
261
+ name="persistent",
262
+ content="test {{var}}",
263
+ description="Test persistence"
264
+ ))
265
+
266
+ # Create new manager instance
267
+ manager2 = TemplateManager(temp_dir)
268
+
269
+ # Should load the saved template
270
+ tmpl = manager2.get_template("persistent")
271
+ assert tmpl is not None
272
+ assert tmpl.name == "persistent"
273
+ assert tmpl.content == "test {{var}}"
274
+
275
+ def test_update_template(self, manager: TemplateManager):
276
+ """Test updating an existing user template."""
277
+ # Add initial template
278
+ manager.add_template(Template(
279
+ name="update-test",
280
+ content="Original {{var}}",
281
+ description="Original"
282
+ ))
283
+
284
+ # Update it
285
+ manager.add_template(Template(
286
+ name="update-test",
287
+ content="Updated {{var}}",
288
+ description="Updated"
289
+ ))
290
+
291
+ # Should be updated
292
+ tmpl = manager.get_template("update-test")
293
+ assert tmpl.content == "Updated {{var}}"
294
+ assert tmpl.description == "Updated"
295
+
296
+ def test_multiple_categories(self, manager: TemplateManager):
297
+ """Test templates with different categories."""
298
+ manager.add_template(Template(
299
+ name="cat1",
300
+ content="test",
301
+ category="category1"
302
+ ))
303
+ manager.add_template(Template(
304
+ name="cat2",
305
+ content="test",
306
+ category="category2"
307
+ ))
308
+
309
+ # Filter by each category
310
+ cat1_templates = manager.list_templates(category="category1")
311
+ cat2_templates = manager.list_templates(category="category2")
312
+
313
+ assert len(cat1_templates) == 1
314
+ assert len(cat2_templates) == 1
315
+ assert cat1_templates[0].name == "cat1"
316
+ assert cat2_templates[0].name == "cat2"
317
+
318
+ def test_template_with_no_variables(self, manager: TemplateManager):
319
+ """Test template with no variables."""
320
+ tmpl = Template(
321
+ name="no-vars",
322
+ content="Static content with no variables"
323
+ )
324
+
325
+ assert tmpl.variables == []
326
+ result = tmpl.render()
327
+ assert result == "Static content with no variables"
File without changes
File without changes