claude-dev-cli 0.8.1__py3-none-any.whl → 0.8.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ Features:
9
9
  - Interactive and single-shot modes
10
10
  """
11
11
 
12
- __version__ = "0.8.1"
12
+ __version__ = "0.8.2"
13
13
  __author__ = "Julio"
14
14
  __license__ = "MIT"
15
15
 
claude_dev_cli/cli.py CHANGED
@@ -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')
claude_dev_cli/context.py CHANGED
@@ -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 parse_traceback(error_text: str) -> Dict[str, Any]:
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
- parts.append(f" {i}. {frame.get('file', 'unknown')}:{frame.get('line', '?')} in {frame.get('function', 'unknown')}")
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.1
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**: Structured Python traceback 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 (NEW in v0.8.0)
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?"
@@ -1,8 +1,8 @@
1
- claude_dev_cli/__init__.py,sha256=0g1KP2ohJ45KCYW9EkcRcTA885E4Gqm1kDQbmIOdC-k,469
2
- claude_dev_cli/cli.py,sha256=FjVq9QanwxM8WqSZPPM4-b50v9Ycy9ZDHEg9gBo-kCk,51304
1
+ claude_dev_cli/__init__.py,sha256=zz-bOjjR01C_VWxFEsIWbdJui0kBuDLpDfJV7yAFFRc,469
2
+ claude_dev_cli/cli.py,sha256=BjTST8JSd8quldFxs09ZzlAPGt-rFIYhKUCWfj1pIn4,55100
3
3
  claude_dev_cli/commands.py,sha256=RKGx2rv56PM6eErvA2uoQ20hY8babuI5jav8nCUyUOk,3964
4
4
  claude_dev_cli/config.py,sha256=OLx0xWDf1RIK6RIxl5OKVS4aOSMZZOKxBDmzfQX-muk,9745
5
- claude_dev_cli/context.py,sha256=Z3QYq4ZHAqpuv_xPZtXcBeWf0LCelzkybj8cBz2nBAo,19523
5
+ claude_dev_cli/context.py,sha256=1TlLzpREFZDEIuU7RAtlkjxARKWZpnxHHvK283sUAZE,26714
6
6
  claude_dev_cli/core.py,sha256=yaLjEixDvPzvUy4fJ2UB7nMpPPLyKACjR-RuM-1OQBY,4780
7
7
  claude_dev_cli/history.py,sha256=iQlqgTnXCsyCq5q-XaDl7V5MyPKQ3bx7o_k76-xWSAA,6863
8
8
  claude_dev_cli/secure_storage.py,sha256=TK3WOaU7a0yTOtzdP_t_28fDRp2lovANNAC6MBdm4nQ,7096
@@ -17,9 +17,9 @@ claude_dev_cli/plugins/base.py,sha256=H4HQet1I-a3WLCfE9F06Lp8NuFvVoIlou7sIgyJFK-
17
17
  claude_dev_cli/plugins/diff_editor/__init__.py,sha256=gqR5S2TyIVuq-sK107fegsutQ7Z-sgAIEbtc71FhXIM,101
18
18
  claude_dev_cli/plugins/diff_editor/plugin.py,sha256=M1bUoqpasD3ZNQo36Fu_8g92uySPZyG_ujMbj5UplsU,3073
19
19
  claude_dev_cli/plugins/diff_editor/viewer.py,sha256=1IOXIKw_01ppJx5C1dQt9Kr6U1TdAHT8_iUT5r_q0NM,17169
20
- claude_dev_cli-0.8.1.dist-info/licenses/LICENSE,sha256=DGueuJwMJtMwgLO5mWlS0TaeBrFwQuNpNZ22PU9J2bw,1062
21
- claude_dev_cli-0.8.1.dist-info/METADATA,sha256=3QFSQUey2f4wMFgtrImtuPgQoD_-Bk2me891eCrCyhY,15223
22
- claude_dev_cli-0.8.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- claude_dev_cli-0.8.1.dist-info/entry_points.txt,sha256=zymgUIIVpFTARkFmxAuW2A4BQsNITh_L0uU-XunytHg,85
24
- claude_dev_cli-0.8.1.dist-info/top_level.txt,sha256=m7MF6LOIuTe41IT5Fgt0lc-DK1EgM4gUU_IZwWxK0pg,15
25
- claude_dev_cli-0.8.1.dist-info/RECORD,,
20
+ claude_dev_cli-0.8.2.dist-info/licenses/LICENSE,sha256=DGueuJwMJtMwgLO5mWlS0TaeBrFwQuNpNZ22PU9J2bw,1062
21
+ claude_dev_cli-0.8.2.dist-info/METADATA,sha256=4Pyai7Is7NJezxG5ypTm5rML1wEqIlOwHI5GZDlFFs0,15708
22
+ claude_dev_cli-0.8.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ claude_dev_cli-0.8.2.dist-info/entry_points.txt,sha256=zymgUIIVpFTARkFmxAuW2A4BQsNITh_L0uU-XunytHg,85
24
+ claude_dev_cli-0.8.2.dist-info/top_level.txt,sha256=m7MF6LOIuTe41IT5Fgt0lc-DK1EgM4gUU_IZwWxK0pg,15
25
+ claude_dev_cli-0.8.2.dist-info/RECORD,,