claude-dev-cli 0.8.1__tar.gz → 0.8.2__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.8.1/src/claude_dev_cli.egg-info → claude_dev_cli-0.8.2}/PKG-INFO +12 -4
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/README.md +11 -3
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/pyproject.toml +1 -1
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/__init__.py +1 -1
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/cli.py +101 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/context.py +179 -5
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2/src/claude_dev_cli.egg-info}/PKG-INFO +12 -4
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/tests/test_context.py +136 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/LICENSE +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/MANIFEST.in +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/setup.cfg +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/commands.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/config.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/core.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/history.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/plugins/__init__.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/plugins/base.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/plugins/diff_editor/__init__.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/plugins/diff_editor/plugin.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/plugins/diff_editor/viewer.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/secure_storage.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/template_manager.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/templates.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/toon_utils.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/usage.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/warp_integration.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/workflows.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli.egg-info/SOURCES.txt +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli.egg-info/dependency_links.txt +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli.egg-info/entry_points.txt +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli.egg-info/requires.txt +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli.egg-info/top_level.txt +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/tests/test_cli.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/tests/test_commands.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/tests/test_config.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/tests/test_core.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/tests/test_diff_editor.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/tests/test_secure_storage.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/tests/test_template_manager.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/tests/test_toon_utils.py +0 -0
- {claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/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.8.
|
|
3
|
+
Version: 0.8.2
|
|
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
|
|
@@ -85,7 +85,8 @@ A powerful command-line tool for developers using Claude AI with multi-API routi
|
|
|
85
85
|
- `git commit`, `generate tests`, `generate docs` (v0.8.1)
|
|
86
86
|
- **Git Integration**: Automatically include branch, commits, modified files
|
|
87
87
|
- **Dependency Analysis**: Parse imports and include related files
|
|
88
|
-
- **Error Parsing
|
|
88
|
+
- **Multi-Language Error Parsing** (v0.8.2): Python, JavaScript/TypeScript, Go, Rust, Java
|
|
89
|
+
- **Context Summary** (v0.8.2): Preview context before API calls with `cdc context summary`
|
|
89
90
|
- **Smart Truncation**: Prevent token limits with configurable file size limits
|
|
90
91
|
- **Project Memory**: Remember preferences per project
|
|
91
92
|
- **Global Config**: Set context defaults in `~/.claude-dev-cli/config.json`
|
|
@@ -199,7 +200,7 @@ git add .
|
|
|
199
200
|
cdc git commit --auto-context
|
|
200
201
|
```
|
|
201
202
|
|
|
202
|
-
### 4. Context-Aware Operations (
|
|
203
|
+
### 4. Context-Aware Operations (v0.8.0+)
|
|
203
204
|
|
|
204
205
|
```bash
|
|
205
206
|
# Auto-context includes: git info, dependencies, related files
|
|
@@ -208,9 +209,16 @@ cdc git commit --auto-context
|
|
|
208
209
|
cdc review mymodule.py --auto-context
|
|
209
210
|
# ✓ Context gathered (git, dependencies, tests)
|
|
210
211
|
|
|
211
|
-
# Debug with parsed error details
|
|
212
|
+
# Debug with parsed error details (multi-language support)
|
|
212
213
|
python broken.py 2>&1 | cdc debug -f broken.py --auto-context
|
|
214
|
+
node app.js 2>&1 | cdc debug --auto-context # JavaScript/TypeScript
|
|
215
|
+
go run main.go 2>&1 | cdc debug --auto-context # Go
|
|
213
216
|
# ✓ Context gathered (error details, git context)
|
|
217
|
+
# Supports: Python, JavaScript, TypeScript, Go, Rust, Java
|
|
218
|
+
|
|
219
|
+
# Preview context before making API calls - NEW in v0.8.2
|
|
220
|
+
cdc context summary mymodule.py
|
|
221
|
+
# Shows: files, sizes, lines, estimated tokens, truncation warnings
|
|
214
222
|
|
|
215
223
|
# Ask questions with file context
|
|
216
224
|
cdc ask -f mycode.py --auto-context "how can I improve this?"
|
|
@@ -41,7 +41,8 @@ A powerful command-line tool for developers using Claude AI with multi-API routi
|
|
|
41
41
|
- `git commit`, `generate tests`, `generate docs` (v0.8.1)
|
|
42
42
|
- **Git Integration**: Automatically include branch, commits, modified files
|
|
43
43
|
- **Dependency Analysis**: Parse imports and include related files
|
|
44
|
-
- **Error Parsing
|
|
44
|
+
- **Multi-Language Error Parsing** (v0.8.2): Python, JavaScript/TypeScript, Go, Rust, Java
|
|
45
|
+
- **Context Summary** (v0.8.2): Preview context before API calls with `cdc context summary`
|
|
45
46
|
- **Smart Truncation**: Prevent token limits with configurable file size limits
|
|
46
47
|
- **Project Memory**: Remember preferences per project
|
|
47
48
|
- **Global Config**: Set context defaults in `~/.claude-dev-cli/config.json`
|
|
@@ -155,7 +156,7 @@ git add .
|
|
|
155
156
|
cdc git commit --auto-context
|
|
156
157
|
```
|
|
157
158
|
|
|
158
|
-
### 4. Context-Aware Operations (
|
|
159
|
+
### 4. Context-Aware Operations (v0.8.0+)
|
|
159
160
|
|
|
160
161
|
```bash
|
|
161
162
|
# Auto-context includes: git info, dependencies, related files
|
|
@@ -164,9 +165,16 @@ cdc git commit --auto-context
|
|
|
164
165
|
cdc review mymodule.py --auto-context
|
|
165
166
|
# ✓ Context gathered (git, dependencies, tests)
|
|
166
167
|
|
|
167
|
-
# Debug with parsed error details
|
|
168
|
+
# Debug with parsed error details (multi-language support)
|
|
168
169
|
python broken.py 2>&1 | cdc debug -f broken.py --auto-context
|
|
170
|
+
node app.js 2>&1 | cdc debug --auto-context # JavaScript/TypeScript
|
|
171
|
+
go run main.go 2>&1 | cdc debug --auto-context # Go
|
|
169
172
|
# ✓ Context gathered (error details, git context)
|
|
173
|
+
# Supports: Python, JavaScript, TypeScript, Go, Rust, Java
|
|
174
|
+
|
|
175
|
+
# Preview context before making API calls - NEW in v0.8.2
|
|
176
|
+
cdc context summary mymodule.py
|
|
177
|
+
# Shows: files, sizes, lines, estimated tokens, truncation warnings
|
|
170
178
|
|
|
171
179
|
# Ask questions with file context
|
|
172
180
|
cdc ask -f mycode.py --auto-context "how can I improve this?"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "claude-dev-cli"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.2"
|
|
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"
|
|
@@ -884,6 +884,107 @@ def git_commit(ctx: click.Context, api: Optional[str], auto_context: bool) -> No
|
|
|
884
884
|
sys.exit(1)
|
|
885
885
|
|
|
886
886
|
|
|
887
|
+
@main.group()
|
|
888
|
+
def context() -> None:
|
|
889
|
+
"""Context gathering tools and information."""
|
|
890
|
+
pass
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
@context.command('summary')
|
|
894
|
+
@click.argument('file_path', type=click.Path(exists=True))
|
|
895
|
+
@click.option('--include-git/--no-git', default=True, help='Include git context')
|
|
896
|
+
@click.option('--include-deps/--no-deps', default=True, help='Include dependencies')
|
|
897
|
+
@click.option('--include-tests/--no-tests', default=True, help='Include test files')
|
|
898
|
+
@click.pass_context
|
|
899
|
+
def context_summary(
|
|
900
|
+
ctx: click.Context,
|
|
901
|
+
file_path: str,
|
|
902
|
+
include_git: bool,
|
|
903
|
+
include_deps: bool,
|
|
904
|
+
include_tests: bool
|
|
905
|
+
) -> None:
|
|
906
|
+
"""Show what context would be gathered for a file."""
|
|
907
|
+
from claude_dev_cli.context import ContextGatherer
|
|
908
|
+
from rich.table import Table
|
|
909
|
+
|
|
910
|
+
console = ctx.obj['console']
|
|
911
|
+
|
|
912
|
+
try:
|
|
913
|
+
file_path_obj = Path(file_path)
|
|
914
|
+
gatherer = ContextGatherer()
|
|
915
|
+
|
|
916
|
+
# Gather context
|
|
917
|
+
with console.status("[bold blue]Analyzing context..."):
|
|
918
|
+
context = gatherer.gather_for_review(
|
|
919
|
+
file_path_obj,
|
|
920
|
+
include_git=include_git,
|
|
921
|
+
include_tests=include_tests
|
|
922
|
+
)
|
|
923
|
+
|
|
924
|
+
# Display summary
|
|
925
|
+
console.print(f"\n[bold cyan]Context Summary for:[/bold cyan] {file_path}\n")
|
|
926
|
+
|
|
927
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
928
|
+
table.add_column("Type", style="cyan")
|
|
929
|
+
table.add_column("Content", style="white")
|
|
930
|
+
table.add_column("Size", justify="right", style="yellow")
|
|
931
|
+
table.add_column("Lines", justify="right", style="green")
|
|
932
|
+
table.add_column("Truncated", justify="center", style="red")
|
|
933
|
+
|
|
934
|
+
total_chars = 0
|
|
935
|
+
total_lines = 0
|
|
936
|
+
|
|
937
|
+
for item in context.items:
|
|
938
|
+
lines = len(item.content.split('\n'))
|
|
939
|
+
chars = len(item.content)
|
|
940
|
+
truncated = "✓" if item.metadata.get('truncated') else ""
|
|
941
|
+
|
|
942
|
+
# Format content preview
|
|
943
|
+
if item.type == 'file':
|
|
944
|
+
content = item.metadata.get('path', 'unknown')
|
|
945
|
+
if item.metadata.get('is_test'):
|
|
946
|
+
content += " [TEST]"
|
|
947
|
+
elif item.type == 'git':
|
|
948
|
+
branch = item.metadata.get('branch', 'unknown')
|
|
949
|
+
modified = item.metadata.get('modified_count', 0)
|
|
950
|
+
content = f"Branch: {branch}, {modified} modified files"
|
|
951
|
+
elif item.type == 'dependency':
|
|
952
|
+
dep_files = item.metadata.get('dependency_files', [])
|
|
953
|
+
content = f"{len(dep_files)} dependency files"
|
|
954
|
+
else:
|
|
955
|
+
content = item.type
|
|
956
|
+
|
|
957
|
+
table.add_row(
|
|
958
|
+
item.type.title(),
|
|
959
|
+
content[:60] + "..." if len(content) > 60 else content,
|
|
960
|
+
f"{chars:,}",
|
|
961
|
+
f"{lines:,}",
|
|
962
|
+
truncated
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
total_chars += chars
|
|
966
|
+
total_lines += lines
|
|
967
|
+
|
|
968
|
+
console.print(table)
|
|
969
|
+
|
|
970
|
+
# Show totals
|
|
971
|
+
console.print(f"\n[bold]Total:[/bold]")
|
|
972
|
+
console.print(f" Characters: [yellow]{total_chars:,}[/yellow]")
|
|
973
|
+
console.print(f" Lines: [green]{total_lines:,}[/green]")
|
|
974
|
+
console.print(f" Estimated tokens: [cyan]~{total_chars // 4:,}[/cyan] (rough estimate)")
|
|
975
|
+
|
|
976
|
+
# Show any truncation warnings
|
|
977
|
+
truncated_items = [item for item in context.items if item.metadata.get('truncated')]
|
|
978
|
+
if truncated_items:
|
|
979
|
+
console.print(f"\n[yellow]⚠ {len(truncated_items)} item(s) truncated to fit size limits[/yellow]")
|
|
980
|
+
|
|
981
|
+
console.print(f"\n[dim]Use --auto-context with commands to include this context[/dim]")
|
|
982
|
+
|
|
983
|
+
except Exception as e:
|
|
984
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
985
|
+
sys.exit(1)
|
|
986
|
+
|
|
987
|
+
|
|
887
988
|
@main.command('usage')
|
|
888
989
|
@click.option('--days', type=int, help='Filter by days')
|
|
889
990
|
@click.option('--api', help='Filter by API config')
|
|
@@ -339,10 +339,25 @@ class DependencyAnalyzer:
|
|
|
339
339
|
|
|
340
340
|
|
|
341
341
|
class ErrorContext:
|
|
342
|
-
"""Parse and format error context."""
|
|
342
|
+
"""Parse and format error context for multiple languages."""
|
|
343
343
|
|
|
344
344
|
@staticmethod
|
|
345
|
-
def
|
|
345
|
+
def detect_language(error_text: str) -> str:
|
|
346
|
+
"""Detect programming language from error format."""
|
|
347
|
+
if 'Traceback (most recent call last):' in error_text or 'File "' in error_text:
|
|
348
|
+
return 'python'
|
|
349
|
+
elif 'at ' in error_text and ('.js:' in error_text or '.ts:' in error_text):
|
|
350
|
+
return 'javascript'
|
|
351
|
+
elif 'panic:' in error_text and '.go:' in error_text:
|
|
352
|
+
return 'go'
|
|
353
|
+
elif 'thread' in error_text and 'panicked at' in error_text and '.rs:' in error_text:
|
|
354
|
+
return 'rust'
|
|
355
|
+
elif 'at ' in error_text and '.java:' in error_text:
|
|
356
|
+
return 'java'
|
|
357
|
+
return 'unknown'
|
|
358
|
+
|
|
359
|
+
@staticmethod
|
|
360
|
+
def parse_python_traceback(error_text: str) -> Dict[str, Any]:
|
|
346
361
|
"""Parse Python traceback into structured data."""
|
|
347
362
|
lines = error_text.split('\n')
|
|
348
363
|
|
|
@@ -391,24 +406,183 @@ class ErrorContext:
|
|
|
391
406
|
'raw': error_text
|
|
392
407
|
}
|
|
393
408
|
|
|
394
|
-
return {'frames': frames, 'raw': error_text}
|
|
409
|
+
return {'frames': frames, 'raw': error_text, 'language': 'python'}
|
|
410
|
+
|
|
411
|
+
@staticmethod
|
|
412
|
+
def parse_javascript_stack(error_text: str) -> Dict[str, Any]:
|
|
413
|
+
"""Parse JavaScript/TypeScript stack trace."""
|
|
414
|
+
lines = error_text.split('\n')
|
|
415
|
+
frames = []
|
|
416
|
+
error_type = None
|
|
417
|
+
error_message = None
|
|
418
|
+
|
|
419
|
+
for line in lines:
|
|
420
|
+
# Error message usually first: "Error: message" or "TypeError: message"
|
|
421
|
+
if not error_type and (':' in line and not line.strip().startswith('at')):
|
|
422
|
+
parts = line.split(':', 1)
|
|
423
|
+
error_type = parts[0].strip()
|
|
424
|
+
error_message = parts[1].strip() if len(parts) > 1 else ''
|
|
425
|
+
# Stack frame: "at functionName (file.js:line:col)" or "at file.js:line:col"
|
|
426
|
+
elif line.strip().startswith('at '):
|
|
427
|
+
match = re.search(r'at\s+(?:(.+?)\s+)?\(([^)]+)\)|(\S+)$', line)
|
|
428
|
+
if match:
|
|
429
|
+
function = match.group(1) or 'anonymous'
|
|
430
|
+
location = match.group(2) or match.group(3)
|
|
431
|
+
if location and ':' in location:
|
|
432
|
+
parts = location.rsplit(':', 2)
|
|
433
|
+
frames.append({
|
|
434
|
+
'file': parts[0],
|
|
435
|
+
'line': int(parts[1]) if len(parts) > 1 and parts[1].isdigit() else None,
|
|
436
|
+
'column': int(parts[2]) if len(parts) > 2 and parts[2].isdigit() else None,
|
|
437
|
+
'function': function
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
'frames': frames,
|
|
442
|
+
'error_type': error_type,
|
|
443
|
+
'error_message': error_message,
|
|
444
|
+
'raw': error_text,
|
|
445
|
+
'language': 'javascript'
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
@staticmethod
|
|
449
|
+
def parse_go_panic(error_text: str) -> Dict[str, Any]:
|
|
450
|
+
"""Parse Go panic trace."""
|
|
451
|
+
lines = error_text.split('\n')
|
|
452
|
+
frames = []
|
|
453
|
+
error_message = None
|
|
454
|
+
|
|
455
|
+
for line in lines:
|
|
456
|
+
# Panic message: "panic: message"
|
|
457
|
+
if line.startswith('panic:'):
|
|
458
|
+
error_message = line.replace('panic:', '').strip()
|
|
459
|
+
# Stack frame: "function(args)" followed by "\tfile.go:line +0xhex"
|
|
460
|
+
elif '\t' in line and '.go:' in line:
|
|
461
|
+
match = re.search(r'([^/\s]+\.go):(\d+)', line)
|
|
462
|
+
if match:
|
|
463
|
+
frames.append({
|
|
464
|
+
'file': match.group(1),
|
|
465
|
+
'line': int(match.group(2)),
|
|
466
|
+
'function': 'goroutine'
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
return {
|
|
470
|
+
'frames': frames,
|
|
471
|
+
'error_type': 'panic',
|
|
472
|
+
'error_message': error_message,
|
|
473
|
+
'raw': error_text,
|
|
474
|
+
'language': 'go'
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
@staticmethod
|
|
478
|
+
def parse_rust_panic(error_text: str) -> Dict[str, Any]:
|
|
479
|
+
"""Parse Rust panic message."""
|
|
480
|
+
lines = error_text.split('\n')
|
|
481
|
+
frames = []
|
|
482
|
+
error_message = None
|
|
483
|
+
|
|
484
|
+
for line in lines:
|
|
485
|
+
# Panic message: "thread 'main' panicked at 'message', file.rs:line:col"
|
|
486
|
+
if 'panicked at' in line:
|
|
487
|
+
match = re.search(r"panicked at '([^']+)', ([^:]+):(\d+):(\d+)", line)
|
|
488
|
+
if match:
|
|
489
|
+
error_message = match.group(1)
|
|
490
|
+
frames.append({
|
|
491
|
+
'file': match.group(2),
|
|
492
|
+
'line': int(match.group(3)),
|
|
493
|
+
'column': int(match.group(4)),
|
|
494
|
+
'function': 'panic'
|
|
495
|
+
})
|
|
496
|
+
# Stack backtrace frames
|
|
497
|
+
elif '.rs:' in line:
|
|
498
|
+
match = re.search(r'([^/\s]+\.rs):(\d+):(\d+)', line)
|
|
499
|
+
if match:
|
|
500
|
+
frames.append({
|
|
501
|
+
'file': match.group(1),
|
|
502
|
+
'line': int(match.group(2)),
|
|
503
|
+
'column': int(match.group(3)),
|
|
504
|
+
'function': 'unknown'
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
return {
|
|
508
|
+
'frames': frames,
|
|
509
|
+
'error_type': 'panic',
|
|
510
|
+
'error_message': error_message,
|
|
511
|
+
'raw': error_text,
|
|
512
|
+
'language': 'rust'
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
@staticmethod
|
|
516
|
+
def parse_java_stack(error_text: str) -> Dict[str, Any]:
|
|
517
|
+
"""Parse Java stack trace."""
|
|
518
|
+
lines = error_text.split('\n')
|
|
519
|
+
frames = []
|
|
520
|
+
error_type = None
|
|
521
|
+
error_message = None
|
|
522
|
+
|
|
523
|
+
for line in lines:
|
|
524
|
+
# Exception message: "java.lang.NullPointerException: message"
|
|
525
|
+
if not error_type and 'Exception' in line or 'Error' in line:
|
|
526
|
+
parts = line.split(':', 1)
|
|
527
|
+
error_type = parts[0].strip().split('.')[-1] # Get last part of package
|
|
528
|
+
error_message = parts[1].strip() if len(parts) > 1 else ''
|
|
529
|
+
# Stack frame: "at package.Class.method(File.java:line)"
|
|
530
|
+
elif line.strip().startswith('at '):
|
|
531
|
+
match = re.search(r'at\s+([^(]+)\(([^:]+\.java):(\d+)\)', line)
|
|
532
|
+
if match:
|
|
533
|
+
frames.append({
|
|
534
|
+
'function': match.group(1),
|
|
535
|
+
'file': match.group(2),
|
|
536
|
+
'line': int(match.group(3))
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
return {
|
|
540
|
+
'frames': frames,
|
|
541
|
+
'error_type': error_type,
|
|
542
|
+
'error_message': error_message,
|
|
543
|
+
'raw': error_text,
|
|
544
|
+
'language': 'java'
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
@staticmethod
|
|
548
|
+
def parse_traceback(error_text: str) -> Dict[str, Any]:
|
|
549
|
+
"""Parse error/traceback with language auto-detection."""
|
|
550
|
+
language = ErrorContext.detect_language(error_text)
|
|
551
|
+
|
|
552
|
+
if language == 'python':
|
|
553
|
+
return ErrorContext.parse_python_traceback(error_text)
|
|
554
|
+
elif language == 'javascript':
|
|
555
|
+
return ErrorContext.parse_javascript_stack(error_text)
|
|
556
|
+
elif language == 'go':
|
|
557
|
+
return ErrorContext.parse_go_panic(error_text)
|
|
558
|
+
elif language == 'rust':
|
|
559
|
+
return ErrorContext.parse_rust_panic(error_text)
|
|
560
|
+
elif language == 'java':
|
|
561
|
+
return ErrorContext.parse_java_stack(error_text)
|
|
562
|
+
else:
|
|
563
|
+
return {'raw': error_text, 'language': 'unknown'}
|
|
395
564
|
|
|
396
565
|
@staticmethod
|
|
397
566
|
def format_for_ai(error_text: str) -> str:
|
|
398
|
-
"""Format error for AI consumption."""
|
|
567
|
+
"""Format error for AI consumption with language detection."""
|
|
399
568
|
parsed = ErrorContext.parse_traceback(error_text)
|
|
400
569
|
|
|
401
570
|
if 'error_type' not in parsed:
|
|
402
571
|
return error_text
|
|
403
572
|
|
|
573
|
+
language = parsed.get('language', 'unknown')
|
|
404
574
|
parts = [
|
|
575
|
+
f"Language: {language.title()}",
|
|
405
576
|
f"Error Type: {parsed['error_type']}",
|
|
406
577
|
f"Error Message: {parsed.get('error_message', 'N/A')}",
|
|
407
578
|
"\nStack Trace:"
|
|
408
579
|
]
|
|
409
580
|
|
|
410
581
|
for i, frame in enumerate(parsed.get('frames', []), 1):
|
|
411
|
-
|
|
582
|
+
file_loc = f"{frame.get('file', 'unknown')}:{frame.get('line', '?')}"
|
|
583
|
+
if 'column' in frame:
|
|
584
|
+
file_loc += f":{frame.get('column', '?')}"
|
|
585
|
+
parts.append(f" {i}. {file_loc} in {frame.get('function', 'unknown')}")
|
|
412
586
|
if 'code' in frame:
|
|
413
587
|
parts.append(f" > {frame['code']}")
|
|
414
588
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-dev-cli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.2
|
|
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
|
|
@@ -85,7 +85,8 @@ A powerful command-line tool for developers using Claude AI with multi-API routi
|
|
|
85
85
|
- `git commit`, `generate tests`, `generate docs` (v0.8.1)
|
|
86
86
|
- **Git Integration**: Automatically include branch, commits, modified files
|
|
87
87
|
- **Dependency Analysis**: Parse imports and include related files
|
|
88
|
-
- **Error Parsing
|
|
88
|
+
- **Multi-Language Error Parsing** (v0.8.2): Python, JavaScript/TypeScript, Go, Rust, Java
|
|
89
|
+
- **Context Summary** (v0.8.2): Preview context before API calls with `cdc context summary`
|
|
89
90
|
- **Smart Truncation**: Prevent token limits with configurable file size limits
|
|
90
91
|
- **Project Memory**: Remember preferences per project
|
|
91
92
|
- **Global Config**: Set context defaults in `~/.claude-dev-cli/config.json`
|
|
@@ -199,7 +200,7 @@ git add .
|
|
|
199
200
|
cdc git commit --auto-context
|
|
200
201
|
```
|
|
201
202
|
|
|
202
|
-
### 4. Context-Aware Operations (
|
|
203
|
+
### 4. Context-Aware Operations (v0.8.0+)
|
|
203
204
|
|
|
204
205
|
```bash
|
|
205
206
|
# Auto-context includes: git info, dependencies, related files
|
|
@@ -208,9 +209,16 @@ cdc git commit --auto-context
|
|
|
208
209
|
cdc review mymodule.py --auto-context
|
|
209
210
|
# ✓ Context gathered (git, dependencies, tests)
|
|
210
211
|
|
|
211
|
-
# Debug with parsed error details
|
|
212
|
+
# Debug with parsed error details (multi-language support)
|
|
212
213
|
python broken.py 2>&1 | cdc debug -f broken.py --auto-context
|
|
214
|
+
node app.js 2>&1 | cdc debug --auto-context # JavaScript/TypeScript
|
|
215
|
+
go run main.go 2>&1 | cdc debug --auto-context # Go
|
|
213
216
|
# ✓ Context gathered (error details, git context)
|
|
217
|
+
# Supports: Python, JavaScript, TypeScript, Go, Rust, Java
|
|
218
|
+
|
|
219
|
+
# Preview context before making API calls - NEW in v0.8.2
|
|
220
|
+
cdc context summary mymodule.py
|
|
221
|
+
# Shows: files, sizes, lines, estimated tokens, truncation warnings
|
|
214
222
|
|
|
215
223
|
# Ask questions with file context
|
|
216
224
|
cdc ask -f mycode.py --auto-context "how can I improve this?"
|
|
@@ -410,6 +410,31 @@ import json.decoder
|
|
|
410
410
|
class TestErrorContext:
|
|
411
411
|
"""Tests for ErrorContext class."""
|
|
412
412
|
|
|
413
|
+
def test_detect_language_python(self) -> None:
|
|
414
|
+
"""Test Python language detection."""
|
|
415
|
+
error_text = "Traceback (most recent call last):\n File \"test.py\", line 10"
|
|
416
|
+
assert ErrorContext.detect_language(error_text) == 'python'
|
|
417
|
+
|
|
418
|
+
def test_detect_language_javascript(self) -> None:
|
|
419
|
+
"""Test JavaScript language detection."""
|
|
420
|
+
error_text = "Error: test\n at func (app.js:10:5)"
|
|
421
|
+
assert ErrorContext.detect_language(error_text) == 'javascript'
|
|
422
|
+
|
|
423
|
+
def test_detect_language_go(self) -> None:
|
|
424
|
+
"""Test Go language detection."""
|
|
425
|
+
error_text = "panic: test\n\tmain.go:15"
|
|
426
|
+
assert ErrorContext.detect_language(error_text) == 'go'
|
|
427
|
+
|
|
428
|
+
def test_detect_language_rust(self) -> None:
|
|
429
|
+
"""Test Rust language detection."""
|
|
430
|
+
error_text = "thread 'main' panicked at 'test', main.rs:10:5"
|
|
431
|
+
assert ErrorContext.detect_language(error_text) == 'rust'
|
|
432
|
+
|
|
433
|
+
def test_detect_language_java(self) -> None:
|
|
434
|
+
"""Test Java language detection."""
|
|
435
|
+
error_text = "Exception in thread\nat Test.java:10"
|
|
436
|
+
assert ErrorContext.detect_language(error_text) == 'java'
|
|
437
|
+
|
|
413
438
|
def test_parse_simple_traceback(self) -> None:
|
|
414
439
|
"""Test parsing a simple Python traceback."""
|
|
415
440
|
error_text = """
|
|
@@ -505,6 +530,117 @@ TypeError: execute() missing 1 required positional argument
|
|
|
505
530
|
assert "TypeError" in context_item.content
|
|
506
531
|
assert "main.py" in context_item.content
|
|
507
532
|
assert "error_type" in context_item.metadata
|
|
533
|
+
|
|
534
|
+
def test_parse_javascript_stack(self) -> None:
|
|
535
|
+
"""Test JavaScript stack trace parsing."""
|
|
536
|
+
error_text = """
|
|
537
|
+
TypeError: Cannot read property 'foo' of undefined
|
|
538
|
+
at myFunction (app.js:42:15)
|
|
539
|
+
at main (index.js:10:5)
|
|
540
|
+
at startup.js:100:1
|
|
541
|
+
"""
|
|
542
|
+
|
|
543
|
+
parsed = ErrorContext.parse_javascript_stack(error_text)
|
|
544
|
+
|
|
545
|
+
assert parsed['language'] == 'javascript'
|
|
546
|
+
assert parsed['error_type'] == 'TypeError'
|
|
547
|
+
assert "Cannot read property" in parsed['error_message']
|
|
548
|
+
assert len(parsed['frames']) == 3
|
|
549
|
+
|
|
550
|
+
frame1 = parsed['frames'][0]
|
|
551
|
+
assert frame1['file'] == 'app.js'
|
|
552
|
+
assert frame1['line'] == 42
|
|
553
|
+
assert frame1['column'] == 15
|
|
554
|
+
assert frame1['function'] == 'myFunction'
|
|
555
|
+
|
|
556
|
+
def test_parse_go_panic(self) -> None:
|
|
557
|
+
"""Test Go panic trace parsing."""
|
|
558
|
+
error_text = """
|
|
559
|
+
panic: runtime error: index out of range
|
|
560
|
+
|
|
561
|
+
goroutine 1 [running]:
|
|
562
|
+
main.process()
|
|
563
|
+
/path/to/main.go:45 +0x1a3
|
|
564
|
+
main.main()
|
|
565
|
+
/path/to/main.go:20 +0x2e
|
|
566
|
+
"""
|
|
567
|
+
|
|
568
|
+
parsed = ErrorContext.parse_go_panic(error_text)
|
|
569
|
+
|
|
570
|
+
assert parsed['language'] == 'go'
|
|
571
|
+
assert parsed['error_type'] == 'panic'
|
|
572
|
+
assert 'index out of range' in parsed['error_message']
|
|
573
|
+
assert len(parsed['frames']) >= 2
|
|
574
|
+
|
|
575
|
+
# Check that we found the files
|
|
576
|
+
files = [f['file'] for f in parsed['frames']]
|
|
577
|
+
assert any('main.go' in f for f in files)
|
|
578
|
+
|
|
579
|
+
def test_parse_rust_panic(self) -> None:
|
|
580
|
+
"""Test Rust panic message parsing."""
|
|
581
|
+
error_text = """
|
|
582
|
+
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value', src/main.rs:42:9
|
|
583
|
+
stack backtrace:
|
|
584
|
+
0: rust_begin_unwind
|
|
585
|
+
at /rustc/lib.rs:1:1
|
|
586
|
+
1: core::panicking::panic_fmt
|
|
587
|
+
at src/panicking.rs:50:5
|
|
588
|
+
2: myapp::process
|
|
589
|
+
at ./src/lib.rs:100:13
|
|
590
|
+
"""
|
|
591
|
+
|
|
592
|
+
parsed = ErrorContext.parse_rust_panic(error_text)
|
|
593
|
+
|
|
594
|
+
assert parsed['language'] == 'rust'
|
|
595
|
+
assert parsed['error_type'] == 'panic'
|
|
596
|
+
assert 'Result::unwrap()' in parsed['error_message']
|
|
597
|
+
assert len(parsed['frames']) >= 1
|
|
598
|
+
|
|
599
|
+
# First frame should be from the panic location
|
|
600
|
+
frame1 = parsed['frames'][0]
|
|
601
|
+
assert 'main.rs' in frame1['file']
|
|
602
|
+
assert frame1['line'] == 42
|
|
603
|
+
assert frame1['column'] == 9
|
|
604
|
+
|
|
605
|
+
def test_parse_java_stack(self) -> None:
|
|
606
|
+
"""Test Java stack trace parsing."""
|
|
607
|
+
error_text = """
|
|
608
|
+
java.lang.NullPointerException: Cannot invoke method on null object
|
|
609
|
+
at com.example.MyClass.doSomething(MyClass.java:45)
|
|
610
|
+
at com.example.Main.run(Main.java:20)
|
|
611
|
+
at com.example.Main.main(Main.java:10)
|
|
612
|
+
"""
|
|
613
|
+
|
|
614
|
+
parsed = ErrorContext.parse_java_stack(error_text)
|
|
615
|
+
|
|
616
|
+
assert parsed['language'] == 'java'
|
|
617
|
+
assert parsed['error_type'] == 'NullPointerException'
|
|
618
|
+
assert 'Cannot invoke' in parsed['error_message']
|
|
619
|
+
assert len(parsed['frames']) == 3
|
|
620
|
+
|
|
621
|
+
frame1 = parsed['frames'][0]
|
|
622
|
+
assert 'MyClass.java' in frame1['file']
|
|
623
|
+
assert frame1['line'] == 45
|
|
624
|
+
assert 'doSomething' in frame1['function']
|
|
625
|
+
|
|
626
|
+
def test_parse_traceback_auto_detects_language(self) -> None:
|
|
627
|
+
"""Test that parse_traceback auto-detects language."""
|
|
628
|
+
js_error = "Error: test\n at func (app.js:10:5)"
|
|
629
|
+
parsed = ErrorContext.parse_traceback(js_error)
|
|
630
|
+
assert parsed['language'] == 'javascript'
|
|
631
|
+
|
|
632
|
+
go_error = "panic: test\n\tmain.go:15"
|
|
633
|
+
parsed = ErrorContext.parse_traceback(go_error)
|
|
634
|
+
assert parsed['language'] == 'go'
|
|
635
|
+
|
|
636
|
+
def test_format_for_ai_includes_language(self) -> None:
|
|
637
|
+
"""Test that formatted output includes language."""
|
|
638
|
+
error_text = "TypeError: test\n at func (app.js:10:5)"
|
|
639
|
+
formatted = ErrorContext.format_for_ai(error_text)
|
|
640
|
+
|
|
641
|
+
assert 'Language: Javascript' in formatted
|
|
642
|
+
assert 'Error Type: TypeError' in formatted
|
|
643
|
+
assert 'app.js:10:5' in formatted
|
|
508
644
|
|
|
509
645
|
|
|
510
646
|
class TestContextGatherer:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/plugins/diff_editor/__init__.py
RENAMED
|
File without changes
|
{claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/plugins/diff_editor/plugin.py
RENAMED
|
File without changes
|
{claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli/plugins/diff_editor/viewer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_dev_cli-0.8.1 → claude_dev_cli-0.8.2}/src/claude_dev_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|