claude-dev-cli 0.10.0__py3-none-any.whl → 0.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-dev-cli might be problematic. Click here for more details.
- claude_dev_cli/__init__.py +1 -1
- claude_dev_cli/cli.py +308 -53
- claude_dev_cli/core.py +1 -1
- claude_dev_cli/path_utils.py +174 -0
- {claude_dev_cli-0.10.0.dist-info → claude_dev_cli-0.11.0.dist-info}/METADATA +44 -5
- {claude_dev_cli-0.10.0.dist-info → claude_dev_cli-0.11.0.dist-info}/RECORD +10 -9
- {claude_dev_cli-0.10.0.dist-info → claude_dev_cli-0.11.0.dist-info}/WHEEL +0 -0
- {claude_dev_cli-0.10.0.dist-info → claude_dev_cli-0.11.0.dist-info}/entry_points.txt +0 -0
- {claude_dev_cli-0.10.0.dist-info → claude_dev_cli-0.11.0.dist-info}/licenses/LICENSE +0 -0
- {claude_dev_cli-0.10.0.dist-info → claude_dev_cli-0.11.0.dist-info}/top_level.txt +0 -0
claude_dev_cli/__init__.py
CHANGED
claude_dev_cli/cli.py
CHANGED
|
@@ -806,41 +806,83 @@ def generate() -> None:
|
|
|
806
806
|
|
|
807
807
|
|
|
808
808
|
@generate.command('tests')
|
|
809
|
-
@click.argument('
|
|
810
|
-
@click.option('-o', '--output', type=click.Path(), help='Output file path')
|
|
809
|
+
@click.argument('paths', nargs=-1, type=click.Path(exists=True))
|
|
810
|
+
@click.option('-o', '--output', type=click.Path(), help='Output file path (single file only)')
|
|
811
811
|
@click.option('-a', '--api', help='API config to use')
|
|
812
812
|
@click.option('-i', '--interactive', is_flag=True, help='Interactive refinement mode')
|
|
813
813
|
@click.option('--auto-context', is_flag=True, help='Include dependencies and related files')
|
|
814
|
+
@click.option('--max-files', type=int, default=10, help='Maximum files to process (default: 10)')
|
|
814
815
|
@click.pass_context
|
|
815
816
|
def gen_tests(
|
|
816
817
|
ctx: click.Context,
|
|
817
|
-
|
|
818
|
+
paths: tuple,
|
|
818
819
|
output: Optional[str],
|
|
819
820
|
api: Optional[str],
|
|
820
821
|
interactive: bool,
|
|
821
|
-
auto_context: bool
|
|
822
|
+
auto_context: bool,
|
|
823
|
+
max_files: int
|
|
822
824
|
) -> None:
|
|
823
|
-
"""Generate pytest tests for
|
|
825
|
+
"""Generate pytest tests for Python files.
|
|
826
|
+
|
|
827
|
+
Can generate tests for multiple files or directories:
|
|
828
|
+
|
|
829
|
+
cdc generate tests file1.py file2.py
|
|
830
|
+
cdc generate tests src/
|
|
831
|
+
"""
|
|
824
832
|
console = ctx.obj['console']
|
|
833
|
+
from claude_dev_cli.path_utils import expand_paths
|
|
825
834
|
|
|
826
835
|
try:
|
|
836
|
+
if not paths:
|
|
837
|
+
console.print("[yellow]No files specified. Provide file paths.[/yellow]")
|
|
838
|
+
return
|
|
839
|
+
|
|
840
|
+
files = expand_paths(list(paths), max_files=max_files)
|
|
841
|
+
if not files:
|
|
842
|
+
console.print("[yellow]No files found.[/yellow]")
|
|
843
|
+
return
|
|
844
|
+
|
|
845
|
+
# Output only works with single file
|
|
846
|
+
if output and len(files) > 1:
|
|
847
|
+
console.print("[yellow]Warning: --output only works with single file. Ignoring output option.[/yellow]")
|
|
848
|
+
output = None
|
|
849
|
+
|
|
850
|
+
if len(files) > 1:
|
|
851
|
+
console.print(f"\n[bold]Generating tests for {len(files)} file(s):[/bold]")
|
|
852
|
+
for f in files[:5]:
|
|
853
|
+
console.print(f" • {f}")
|
|
854
|
+
if len(files) > 5:
|
|
855
|
+
console.print(f" ... and {len(files) - 5} more")
|
|
856
|
+
console.print()
|
|
857
|
+
|
|
858
|
+
# Build combined prompt
|
|
859
|
+
files_content = ""
|
|
860
|
+
for file_path in files:
|
|
861
|
+
try:
|
|
862
|
+
with open(file_path, 'r') as f:
|
|
863
|
+
content = f.read()
|
|
864
|
+
files_content += f"\n\n## File: {file_path}\n\n```\n{content}\n```\n"
|
|
865
|
+
except Exception as e:
|
|
866
|
+
console.print(f"[yellow]Warning: Could not read {file_path}: {e}[/yellow]")
|
|
867
|
+
|
|
827
868
|
if auto_context:
|
|
828
869
|
from claude_dev_cli.context import ContextGatherer
|
|
829
870
|
|
|
830
871
|
with console.status("[bold blue]Gathering context..."):
|
|
831
872
|
gatherer = ContextGatherer()
|
|
832
|
-
context = gatherer.gather_for_file(
|
|
873
|
+
context = gatherer.gather_for_file(files[0], include_git=False)
|
|
833
874
|
context_info = context.format_for_prompt()
|
|
834
875
|
|
|
835
876
|
console.print("[dim]✓ Context gathered (dependencies, related files)[/dim]")
|
|
836
877
|
|
|
837
|
-
# Use context-aware test generation
|
|
838
878
|
client = ClaudeClient(api_config_name=api)
|
|
839
|
-
|
|
840
|
-
result = client.call(enhanced_prompt)
|
|
879
|
+
prompt = f"{context_info}\n\nFiles:{files_content}\n\nPlease generate comprehensive pytest tests for these files, including fixtures, edge cases, and proper mocking where needed."
|
|
841
880
|
else:
|
|
842
|
-
with console.status("[bold blue]Generating tests..."):
|
|
843
|
-
|
|
881
|
+
with console.status(f"[bold blue]Generating tests for {len(files)} file(s)..."):
|
|
882
|
+
client = ClaudeClient(api_config_name=api)
|
|
883
|
+
prompt = f"Files to test:{files_content}\n\nPlease generate comprehensive pytest tests for these files, including fixtures, edge cases, and proper mocking where needed."
|
|
884
|
+
|
|
885
|
+
result = client.call(prompt)
|
|
844
886
|
|
|
845
887
|
if interactive:
|
|
846
888
|
# Show initial result
|
|
@@ -892,41 +934,83 @@ def gen_tests(
|
|
|
892
934
|
|
|
893
935
|
|
|
894
936
|
@generate.command('docs')
|
|
895
|
-
@click.argument('
|
|
896
|
-
@click.option('-o', '--output', type=click.Path(), help='Output file path')
|
|
937
|
+
@click.argument('paths', nargs=-1, type=click.Path(exists=True))
|
|
938
|
+
@click.option('-o', '--output', type=click.Path(), help='Output file path (single file only)')
|
|
897
939
|
@click.option('-a', '--api', help='API config to use')
|
|
898
940
|
@click.option('-i', '--interactive', is_flag=True, help='Interactive refinement mode')
|
|
899
941
|
@click.option('--auto-context', is_flag=True, help='Include dependencies and related files')
|
|
942
|
+
@click.option('--max-files', type=int, default=10, help='Maximum files to process (default: 10)')
|
|
900
943
|
@click.pass_context
|
|
901
944
|
def gen_docs(
|
|
902
945
|
ctx: click.Context,
|
|
903
|
-
|
|
946
|
+
paths: tuple,
|
|
904
947
|
output: Optional[str],
|
|
905
948
|
api: Optional[str],
|
|
906
949
|
interactive: bool,
|
|
907
|
-
auto_context: bool
|
|
950
|
+
auto_context: bool,
|
|
951
|
+
max_files: int
|
|
908
952
|
) -> None:
|
|
909
|
-
"""Generate documentation for
|
|
953
|
+
"""Generate documentation for files.
|
|
954
|
+
|
|
955
|
+
Can generate docs for multiple files or directories:
|
|
956
|
+
|
|
957
|
+
cdc generate docs file1.py file2.py
|
|
958
|
+
cdc generate docs src/
|
|
959
|
+
"""
|
|
910
960
|
console = ctx.obj['console']
|
|
961
|
+
from claude_dev_cli.path_utils import expand_paths
|
|
911
962
|
|
|
912
963
|
try:
|
|
964
|
+
if not paths:
|
|
965
|
+
console.print("[yellow]No files specified. Provide file paths.[/yellow]")
|
|
966
|
+
return
|
|
967
|
+
|
|
968
|
+
files = expand_paths(list(paths), max_files=max_files)
|
|
969
|
+
if not files:
|
|
970
|
+
console.print("[yellow]No files found.[/yellow]")
|
|
971
|
+
return
|
|
972
|
+
|
|
973
|
+
# Output only works with single file
|
|
974
|
+
if output and len(files) > 1:
|
|
975
|
+
console.print("[yellow]Warning: --output only works with single file. Ignoring output option.[/yellow]")
|
|
976
|
+
output = None
|
|
977
|
+
|
|
978
|
+
if len(files) > 1:
|
|
979
|
+
console.print(f"\n[bold]Generating docs for {len(files)} file(s):[/bold]")
|
|
980
|
+
for f in files[:5]:
|
|
981
|
+
console.print(f" • {f}")
|
|
982
|
+
if len(files) > 5:
|
|
983
|
+
console.print(f" ... and {len(files) - 5} more")
|
|
984
|
+
console.print()
|
|
985
|
+
|
|
986
|
+
# Build combined prompt
|
|
987
|
+
files_content = ""
|
|
988
|
+
for file_path in files:
|
|
989
|
+
try:
|
|
990
|
+
with open(file_path, 'r') as f:
|
|
991
|
+
content = f.read()
|
|
992
|
+
files_content += f"\n\n## File: {file_path}\n\n```\n{content}\n```\n"
|
|
993
|
+
except Exception as e:
|
|
994
|
+
console.print(f"[yellow]Warning: Could not read {file_path}: {e}[/yellow]")
|
|
995
|
+
|
|
913
996
|
if auto_context:
|
|
914
997
|
from claude_dev_cli.context import ContextGatherer
|
|
915
998
|
|
|
916
999
|
with console.status("[bold blue]Gathering context..."):
|
|
917
1000
|
gatherer = ContextGatherer()
|
|
918
|
-
context = gatherer.gather_for_file(
|
|
1001
|
+
context = gatherer.gather_for_file(files[0], include_git=False)
|
|
919
1002
|
context_info = context.format_for_prompt()
|
|
920
1003
|
|
|
921
1004
|
console.print("[dim]✓ Context gathered (dependencies, related files)[/dim]")
|
|
922
1005
|
|
|
923
|
-
# Use context-aware documentation generation
|
|
924
1006
|
client = ClaudeClient(api_config_name=api)
|
|
925
|
-
|
|
926
|
-
result = client.call(enhanced_prompt)
|
|
1007
|
+
prompt = f"{context_info}\n\nFiles:{files_content}\n\nPlease generate comprehensive documentation for these files, including API reference, usage examples, and integration notes."
|
|
927
1008
|
else:
|
|
928
|
-
with console.status("[bold blue]Generating documentation..."):
|
|
929
|
-
|
|
1009
|
+
with console.status(f"[bold blue]Generating documentation for {len(files)} file(s)..."):
|
|
1010
|
+
client = ClaudeClient(api_config_name=api)
|
|
1011
|
+
prompt = f"Files to document:{files_content}\n\nPlease generate comprehensive documentation for these files, including API reference, usage examples, and integration notes."
|
|
1012
|
+
|
|
1013
|
+
result = client.call(prompt)
|
|
930
1014
|
|
|
931
1015
|
if interactive:
|
|
932
1016
|
console.print("\n[bold]Initial Documentation:[/bold]\n")
|
|
@@ -977,22 +1061,65 @@ def gen_docs(
|
|
|
977
1061
|
|
|
978
1062
|
|
|
979
1063
|
@main.command('review')
|
|
980
|
-
@click.argument('
|
|
1064
|
+
@click.argument('paths', nargs=-1, type=click.Path(exists=True))
|
|
981
1065
|
@click.option('-a', '--api', help='API config to use')
|
|
982
1066
|
@click.option('-i', '--interactive', is_flag=True, help='Interactive follow-up questions')
|
|
983
1067
|
@click.option('--auto-context', is_flag=True, help='Automatically include git, dependencies, and related files')
|
|
1068
|
+
@click.option('--max-files', type=int, default=20, help='Maximum files to review (default: 20)')
|
|
984
1069
|
@click.pass_context
|
|
985
1070
|
def review(
|
|
986
1071
|
ctx: click.Context,
|
|
987
|
-
|
|
1072
|
+
paths: tuple,
|
|
988
1073
|
api: Optional[str],
|
|
989
1074
|
interactive: bool,
|
|
990
|
-
auto_context: bool
|
|
1075
|
+
auto_context: bool,
|
|
1076
|
+
max_files: int
|
|
991
1077
|
) -> None:
|
|
992
|
-
"""Review code for bugs and improvements.
|
|
1078
|
+
"""Review code for bugs and improvements.
|
|
1079
|
+
|
|
1080
|
+
Can review multiple files, directories, or auto-detect git changes:
|
|
1081
|
+
|
|
1082
|
+
cdc review file1.py file2.py # Multiple files
|
|
1083
|
+
cdc review src/ # Directory
|
|
1084
|
+
cdc review # Auto-detect git changes
|
|
1085
|
+
"""
|
|
993
1086
|
console = ctx.obj['console']
|
|
1087
|
+
from claude_dev_cli.path_utils import expand_paths, auto_detect_files
|
|
994
1088
|
|
|
995
1089
|
try:
|
|
1090
|
+
# Determine files to review
|
|
1091
|
+
if paths:
|
|
1092
|
+
# Expand paths (handles directories, multiple files)
|
|
1093
|
+
files = expand_paths(list(paths), max_files=max_files)
|
|
1094
|
+
else:
|
|
1095
|
+
# Auto-detect files from git
|
|
1096
|
+
files = auto_detect_files()
|
|
1097
|
+
if files:
|
|
1098
|
+
console.print(f"[dim]Auto-detected {len(files)} file(s) from git changes[/dim]")
|
|
1099
|
+
|
|
1100
|
+
if not files:
|
|
1101
|
+
console.print("[yellow]No files to review. Specify files or make some changes.[/yellow]")
|
|
1102
|
+
return
|
|
1103
|
+
|
|
1104
|
+
# Show files being reviewed
|
|
1105
|
+
if len(files) > 1:
|
|
1106
|
+
console.print(f"\n[bold]Reviewing {len(files)} file(s):[/bold]")
|
|
1107
|
+
for f in files[:10]: # Show first 10
|
|
1108
|
+
console.print(f" • {f}")
|
|
1109
|
+
if len(files) > 10:
|
|
1110
|
+
console.print(f" ... and {len(files) - 10} more")
|
|
1111
|
+
console.print()
|
|
1112
|
+
|
|
1113
|
+
# Build combined prompt for multiple files
|
|
1114
|
+
files_content = ""
|
|
1115
|
+
for file_path in files:
|
|
1116
|
+
try:
|
|
1117
|
+
with open(file_path, 'r') as f:
|
|
1118
|
+
content = f.read()
|
|
1119
|
+
files_content += f"\n\n## File: {file_path}\n\n```\n{content}\n```\n"
|
|
1120
|
+
except Exception as e:
|
|
1121
|
+
console.print(f"[yellow]Warning: Could not read {file_path}: {e}[/yellow]")
|
|
1122
|
+
|
|
996
1123
|
# Gather context if requested
|
|
997
1124
|
context_info = ""
|
|
998
1125
|
if auto_context:
|
|
@@ -1000,31 +1127,24 @@ def review(
|
|
|
1000
1127
|
|
|
1001
1128
|
with console.status("[bold blue]Gathering context..."):
|
|
1002
1129
|
gatherer = ContextGatherer()
|
|
1003
|
-
context
|
|
1130
|
+
# Use first file for context gathering
|
|
1131
|
+
context = gatherer.gather_for_review(files[0])
|
|
1004
1132
|
context_info = context.format_for_prompt()
|
|
1005
1133
|
|
|
1006
1134
|
console.print("[dim]✓ Context gathered (git, dependencies, tests)[/dim]")
|
|
1007
1135
|
|
|
1008
|
-
with console.status("[bold blue]Reviewing
|
|
1009
|
-
|
|
1136
|
+
with console.status(f"[bold blue]Reviewing {len(files)} file(s)..."):
|
|
1137
|
+
client = ClaudeClient(api_config_name=api)
|
|
1010
1138
|
if context_info:
|
|
1011
|
-
|
|
1012
|
-
result = code_review(file_path, api_config_name=api)
|
|
1013
|
-
# The context module already includes the file, so we use it differently
|
|
1014
|
-
client = ClaudeClient(api_config_name=api)
|
|
1015
|
-
enhanced_prompt = f"{context_info}\n\nPlease review this code for bugs and improvements."
|
|
1016
|
-
result = client.call(enhanced_prompt)
|
|
1139
|
+
prompt = f"{context_info}\n\nFiles to review:{files_content}\n\nPlease review this code for bugs and improvements."
|
|
1017
1140
|
else:
|
|
1018
|
-
|
|
1141
|
+
prompt = f"Files to review:{files_content}\n\nPlease review this code for bugs, security issues, and improvements."
|
|
1142
|
+
result = client.call(prompt)
|
|
1019
1143
|
|
|
1020
1144
|
md = Markdown(result)
|
|
1021
1145
|
console.print(md)
|
|
1022
1146
|
|
|
1023
1147
|
if interactive:
|
|
1024
|
-
client = ClaudeClient(api_config_name=api)
|
|
1025
|
-
with open(file_path, 'r') as f:
|
|
1026
|
-
file_content = f.read()
|
|
1027
|
-
|
|
1028
1148
|
console.print("\n[dim]Ask follow-up questions about the review, or 'exit' to quit[/dim]")
|
|
1029
1149
|
|
|
1030
1150
|
while True:
|
|
@@ -1036,7 +1156,7 @@ def review(
|
|
|
1036
1156
|
if not user_input:
|
|
1037
1157
|
continue
|
|
1038
1158
|
|
|
1039
|
-
follow_up_prompt = f"Code review:\n\n{result}\n\
|
|
1159
|
+
follow_up_prompt = f"Code review:\n\n{result}\n\nFiles:{files_content}\n\nUser question: {user_input}"
|
|
1040
1160
|
|
|
1041
1161
|
console.print("\n[bold green]Claude:[/bold green] ", end='')
|
|
1042
1162
|
for chunk in client.call_streaming(follow_up_prompt):
|
|
@@ -1106,49 +1226,95 @@ def debug(
|
|
|
1106
1226
|
|
|
1107
1227
|
|
|
1108
1228
|
@main.command('refactor')
|
|
1109
|
-
@click.argument('
|
|
1110
|
-
@click.option('-o', '--output', type=click.Path(), help='Output file path')
|
|
1229
|
+
@click.argument('paths', nargs=-1, type=click.Path(exists=True))
|
|
1230
|
+
@click.option('-o', '--output', type=click.Path(), help='Output file path (single file only)')
|
|
1111
1231
|
@click.option('-a', '--api', help='API config to use')
|
|
1112
1232
|
@click.option('-i', '--interactive', is_flag=True, help='Interactive refinement mode')
|
|
1113
1233
|
@click.option('--auto-context', is_flag=True, help='Automatically include git, dependencies, and related files')
|
|
1234
|
+
@click.option('--max-files', type=int, default=20, help='Maximum files to refactor (default: 20)')
|
|
1114
1235
|
@click.pass_context
|
|
1115
1236
|
def refactor(
|
|
1116
1237
|
ctx: click.Context,
|
|
1117
|
-
|
|
1238
|
+
paths: tuple,
|
|
1118
1239
|
output: Optional[str],
|
|
1119
1240
|
api: Optional[str],
|
|
1120
1241
|
interactive: bool,
|
|
1121
|
-
auto_context: bool
|
|
1242
|
+
auto_context: bool,
|
|
1243
|
+
max_files: int
|
|
1122
1244
|
) -> None:
|
|
1123
|
-
"""Suggest refactoring improvements.
|
|
1245
|
+
"""Suggest refactoring improvements.
|
|
1246
|
+
|
|
1247
|
+
Can refactor multiple files, directories, or auto-detect git changes:
|
|
1248
|
+
|
|
1249
|
+
cdc refactor file1.py file2.py # Multiple files
|
|
1250
|
+
cdc refactor src/ # Directory
|
|
1251
|
+
cdc refactor # Auto-detect git changes
|
|
1252
|
+
"""
|
|
1124
1253
|
console = ctx.obj['console']
|
|
1254
|
+
from claude_dev_cli.path_utils import expand_paths, auto_detect_files
|
|
1125
1255
|
|
|
1126
1256
|
try:
|
|
1257
|
+
# Determine files to refactor
|
|
1258
|
+
if paths:
|
|
1259
|
+
files = expand_paths(list(paths), max_files=max_files)
|
|
1260
|
+
else:
|
|
1261
|
+
files = auto_detect_files()
|
|
1262
|
+
if files:
|
|
1263
|
+
console.print(f"[dim]Auto-detected {len(files)} file(s) from git changes[/dim]")
|
|
1264
|
+
|
|
1265
|
+
if not files:
|
|
1266
|
+
console.print("[yellow]No files to refactor. Specify files or make some changes.[/yellow]")
|
|
1267
|
+
return
|
|
1268
|
+
|
|
1269
|
+
# Output only works with single file
|
|
1270
|
+
if output and len(files) > 1:
|
|
1271
|
+
console.print("[yellow]Warning: --output only works with single file. Ignoring output option.[/yellow]")
|
|
1272
|
+
output = None
|
|
1273
|
+
|
|
1274
|
+
# Show files being refactored
|
|
1275
|
+
if len(files) > 1:
|
|
1276
|
+
console.print(f"\n[bold]Refactoring {len(files)} file(s):[/bold]")
|
|
1277
|
+
for f in files[:10]:
|
|
1278
|
+
console.print(f" • {f}")
|
|
1279
|
+
if len(files) > 10:
|
|
1280
|
+
console.print(f" ... and {len(files) - 10} more")
|
|
1281
|
+
console.print()
|
|
1282
|
+
|
|
1283
|
+
# Build combined prompt
|
|
1284
|
+
files_content = ""
|
|
1285
|
+
for file_path in files:
|
|
1286
|
+
try:
|
|
1287
|
+
with open(file_path, 'r') as f:
|
|
1288
|
+
content = f.read()
|
|
1289
|
+
files_content += f"\n\n## File: {file_path}\n\n```\n{content}\n```\n"
|
|
1290
|
+
except Exception as e:
|
|
1291
|
+
console.print(f"[yellow]Warning: Could not read {file_path}: {e}[/yellow]")
|
|
1292
|
+
|
|
1127
1293
|
# Gather context if requested
|
|
1128
1294
|
if auto_context:
|
|
1129
1295
|
from claude_dev_cli.context import ContextGatherer
|
|
1130
1296
|
|
|
1131
1297
|
with console.status("[bold blue]Gathering context..."):
|
|
1132
1298
|
gatherer = ContextGatherer()
|
|
1133
|
-
context = gatherer.gather_for_file(
|
|
1299
|
+
context = gatherer.gather_for_file(files[0])
|
|
1134
1300
|
context_info = context.format_for_prompt()
|
|
1135
1301
|
|
|
1136
1302
|
console.print("[dim]✓ Context gathered[/dim]")
|
|
1137
1303
|
|
|
1138
|
-
# Use context-aware refactoring
|
|
1139
1304
|
client = ClaudeClient(api_config_name=api)
|
|
1140
|
-
|
|
1141
|
-
result = client.call(enhanced_prompt)
|
|
1305
|
+
prompt = f"{context_info}\n\nFiles:{files_content}\n\nPlease suggest refactoring improvements."
|
|
1142
1306
|
else:
|
|
1143
|
-
with console.status("[bold blue]Analyzing
|
|
1144
|
-
|
|
1307
|
+
with console.status(f"[bold blue]Analyzing {len(files)} file(s)..."):
|
|
1308
|
+
client = ClaudeClient(api_config_name=api)
|
|
1309
|
+
prompt = f"Files to refactor:{files_content}\n\nPlease suggest refactoring improvements focusing on code quality, maintainability, and performance."
|
|
1310
|
+
|
|
1311
|
+
result = client.call(prompt)
|
|
1145
1312
|
|
|
1146
1313
|
if interactive:
|
|
1147
1314
|
console.print("\n[bold]Initial Refactoring:[/bold]\n")
|
|
1148
1315
|
md = Markdown(result)
|
|
1149
1316
|
console.print(md)
|
|
1150
1317
|
|
|
1151
|
-
client = ClaudeClient(api_config_name=api)
|
|
1152
1318
|
conversation_context = [result]
|
|
1153
1319
|
|
|
1154
1320
|
while True:
|
|
@@ -1233,6 +1399,95 @@ def git_commit(ctx: click.Context, api: Optional[str], auto_context: bool) -> No
|
|
|
1233
1399
|
sys.exit(1)
|
|
1234
1400
|
|
|
1235
1401
|
|
|
1402
|
+
@git.command('review')
|
|
1403
|
+
@click.option('-a', '--api', help='API config to use')
|
|
1404
|
+
@click.option('--staged', is_flag=True, help='Review only staged changes')
|
|
1405
|
+
@click.option('--branch', help='Review changes in branch (e.g., main..HEAD)')
|
|
1406
|
+
@click.option('-i', '--interactive', is_flag=True, help='Interactive follow-up questions')
|
|
1407
|
+
@click.pass_context
|
|
1408
|
+
def git_review(
|
|
1409
|
+
ctx: click.Context,
|
|
1410
|
+
api: Optional[str],
|
|
1411
|
+
staged: bool,
|
|
1412
|
+
branch: Optional[str],
|
|
1413
|
+
interactive: bool
|
|
1414
|
+
) -> None:
|
|
1415
|
+
"""Review git changes.
|
|
1416
|
+
|
|
1417
|
+
Examples:
|
|
1418
|
+
cdc git review --staged # Review staged changes
|
|
1419
|
+
cdc git review # Review all changes
|
|
1420
|
+
cdc git review --branch main..HEAD # Review branch changes
|
|
1421
|
+
"""
|
|
1422
|
+
console = ctx.obj['console']
|
|
1423
|
+
from claude_dev_cli.path_utils import get_git_changes
|
|
1424
|
+
|
|
1425
|
+
try:
|
|
1426
|
+
# Get changed files
|
|
1427
|
+
if branch:
|
|
1428
|
+
files = get_git_changes(commit_range=branch)
|
|
1429
|
+
scope = f"branch {branch}"
|
|
1430
|
+
elif staged:
|
|
1431
|
+
files = get_git_changes(staged_only=True)
|
|
1432
|
+
scope = "staged changes"
|
|
1433
|
+
else:
|
|
1434
|
+
files = get_git_changes(staged_only=False)
|
|
1435
|
+
scope = "all changes"
|
|
1436
|
+
|
|
1437
|
+
if not files:
|
|
1438
|
+
console.print(f"[yellow]No changes found in {scope}.[/yellow]")
|
|
1439
|
+
return
|
|
1440
|
+
|
|
1441
|
+
console.print(f"\n[bold]Reviewing {len(files)} changed file(s) from {scope}:[/bold]")
|
|
1442
|
+
for f in files[:10]:
|
|
1443
|
+
console.print(f" • {f}")
|
|
1444
|
+
if len(files) > 10:
|
|
1445
|
+
console.print(f" ... and {len(files) - 10} more")
|
|
1446
|
+
console.print()
|
|
1447
|
+
|
|
1448
|
+
# Build files content
|
|
1449
|
+
files_content = ""
|
|
1450
|
+
for file_path in files:
|
|
1451
|
+
try:
|
|
1452
|
+
with open(file_path, 'r') as f:
|
|
1453
|
+
content = f.read()
|
|
1454
|
+
files_content += f"\n\n## File: {file_path}\n\n```\n{content}\n```\n"
|
|
1455
|
+
except Exception as e:
|
|
1456
|
+
console.print(f"[yellow]Warning: Could not read {file_path}: {e}[/yellow]")
|
|
1457
|
+
|
|
1458
|
+
# Review
|
|
1459
|
+
with console.status(f"[bold blue]Reviewing {len(files)} file(s)..."):
|
|
1460
|
+
client = ClaudeClient(api_config_name=api)
|
|
1461
|
+
prompt = f"Changed files in {scope}:{files_content}\n\nPlease review these git changes for bugs, security issues, code quality, and potential improvements. Focus on what changed and why it might be problematic."
|
|
1462
|
+
result = client.call(prompt)
|
|
1463
|
+
|
|
1464
|
+
md = Markdown(result)
|
|
1465
|
+
console.print(md)
|
|
1466
|
+
|
|
1467
|
+
if interactive:
|
|
1468
|
+
console.print("\n[dim]Ask follow-up questions about the review, or 'exit' to quit[/dim]")
|
|
1469
|
+
|
|
1470
|
+
while True:
|
|
1471
|
+
user_input = console.input("\n[cyan]You:[/cyan] ").strip()
|
|
1472
|
+
|
|
1473
|
+
if user_input.lower() == 'exit':
|
|
1474
|
+
break
|
|
1475
|
+
|
|
1476
|
+
if not user_input:
|
|
1477
|
+
continue
|
|
1478
|
+
|
|
1479
|
+
follow_up_prompt = f"Code review:\n\n{result}\n\nChanged files:{files_content}\n\nUser question: {user_input}"
|
|
1480
|
+
|
|
1481
|
+
console.print("\n[bold green]Claude:[/bold green] ", end='')
|
|
1482
|
+
for chunk in client.call_streaming(follow_up_prompt):
|
|
1483
|
+
console.print(chunk, end='')
|
|
1484
|
+
console.print()
|
|
1485
|
+
|
|
1486
|
+
except Exception as e:
|
|
1487
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
1488
|
+
sys.exit(1)
|
|
1489
|
+
|
|
1490
|
+
|
|
1236
1491
|
@main.group()
|
|
1237
1492
|
def context() -> None:
|
|
1238
1493
|
"""Context gathering tools and information."""
|
claude_dev_cli/core.py
CHANGED
|
@@ -101,7 +101,7 @@ class ClaudeClient:
|
|
|
101
101
|
system_prompt = project_profile.system_prompt
|
|
102
102
|
|
|
103
103
|
kwargs: Dict[str, Any] = {
|
|
104
|
-
"model":
|
|
104
|
+
"model": resolved_model,
|
|
105
105
|
"max_tokens": max_tokens,
|
|
106
106
|
"temperature": temperature,
|
|
107
107
|
"messages": [{"role": "user", "content": prompt}]
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Path expansion and git change detection utilities."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Set, Optional
|
|
6
|
+
|
|
7
|
+
# Common code file extensions
|
|
8
|
+
CODE_EXTENSIONS = {
|
|
9
|
+
'.py', '.js', '.ts', '.jsx', '.tsx', '.go', '.rs', '.java', '.cpp', '.c',
|
|
10
|
+
'.h', '.hpp', '.cs', '.rb', '.php', '.swift', '.kt', '.scala', '.r',
|
|
11
|
+
'.m', '.mm', '.sh', '.bash', '.zsh', '.fish', '.lua', '.pl', '.sql',
|
|
12
|
+
'.html', '.css', '.scss', '.sass', '.less', '.vue', '.svelte'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_code_file(path: Path) -> bool:
|
|
17
|
+
"""Check if file is a code file based on extension."""
|
|
18
|
+
return path.suffix.lower() in CODE_EXTENSIONS
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def expand_paths(
|
|
22
|
+
paths: List[str],
|
|
23
|
+
max_files: Optional[int] = None,
|
|
24
|
+
recursive: bool = True
|
|
25
|
+
) -> List[Path]:
|
|
26
|
+
"""Expand paths (files, directories, globs) to list of code files.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
paths: List of file/directory paths
|
|
30
|
+
max_files: Maximum number of files to return (None = unlimited)
|
|
31
|
+
recursive: Whether to recursively search directories
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
List of Path objects for code files
|
|
35
|
+
"""
|
|
36
|
+
result_files: Set[Path] = set()
|
|
37
|
+
|
|
38
|
+
for path_str in paths:
|
|
39
|
+
path = Path(path_str).resolve()
|
|
40
|
+
|
|
41
|
+
if not path.exists():
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
if path.is_file():
|
|
45
|
+
# Add single file
|
|
46
|
+
result_files.add(path)
|
|
47
|
+
elif path.is_dir():
|
|
48
|
+
# Expand directory
|
|
49
|
+
if recursive:
|
|
50
|
+
# Recursively find all code files
|
|
51
|
+
for file_path in path.rglob('*'):
|
|
52
|
+
if file_path.is_file() and is_code_file(file_path):
|
|
53
|
+
result_files.add(file_path)
|
|
54
|
+
if max_files and len(result_files) >= max_files:
|
|
55
|
+
break
|
|
56
|
+
else:
|
|
57
|
+
# Only direct children
|
|
58
|
+
for file_path in path.glob('*'):
|
|
59
|
+
if file_path.is_file() and is_code_file(file_path):
|
|
60
|
+
result_files.add(file_path)
|
|
61
|
+
if max_files and len(result_files) >= max_files:
|
|
62
|
+
break
|
|
63
|
+
|
|
64
|
+
if max_files and len(result_files) >= max_files:
|
|
65
|
+
break
|
|
66
|
+
|
|
67
|
+
# Sort for consistent ordering
|
|
68
|
+
return sorted(result_files)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_git_changes(
|
|
72
|
+
staged_only: bool = False,
|
|
73
|
+
include_untracked: bool = False,
|
|
74
|
+
commit_range: Optional[str] = None
|
|
75
|
+
) -> List[Path]:
|
|
76
|
+
"""Get list of changed files from git.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
staged_only: Only return staged files
|
|
80
|
+
include_untracked: Include untracked files
|
|
81
|
+
commit_range: Git commit range (e.g., "main..HEAD")
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
List of Path objects for changed files
|
|
85
|
+
"""
|
|
86
|
+
files: Set[Path] = set()
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
if commit_range:
|
|
90
|
+
# Get files changed in commit range
|
|
91
|
+
result = subprocess.run(
|
|
92
|
+
['git', 'diff', '--name-only', commit_range],
|
|
93
|
+
capture_output=True,
|
|
94
|
+
text=True,
|
|
95
|
+
check=True
|
|
96
|
+
)
|
|
97
|
+
for line in result.stdout.strip().split('\n'):
|
|
98
|
+
if line:
|
|
99
|
+
path = Path(line)
|
|
100
|
+
if path.exists():
|
|
101
|
+
files.add(path)
|
|
102
|
+
elif staged_only:
|
|
103
|
+
# Get only staged files
|
|
104
|
+
result = subprocess.run(
|
|
105
|
+
['git', 'diff', '--cached', '--name-only'],
|
|
106
|
+
capture_output=True,
|
|
107
|
+
text=True,
|
|
108
|
+
check=True
|
|
109
|
+
)
|
|
110
|
+
for line in result.stdout.strip().split('\n'):
|
|
111
|
+
if line:
|
|
112
|
+
path = Path(line)
|
|
113
|
+
if path.exists():
|
|
114
|
+
files.add(path)
|
|
115
|
+
else:
|
|
116
|
+
# Get all modified files (staged + unstaged)
|
|
117
|
+
result = subprocess.run(
|
|
118
|
+
['git', 'diff', '--name-only', 'HEAD'],
|
|
119
|
+
capture_output=True,
|
|
120
|
+
text=True,
|
|
121
|
+
check=True
|
|
122
|
+
)
|
|
123
|
+
for line in result.stdout.strip().split('\n'):
|
|
124
|
+
if line:
|
|
125
|
+
path = Path(line)
|
|
126
|
+
if path.exists():
|
|
127
|
+
files.add(path)
|
|
128
|
+
|
|
129
|
+
if include_untracked:
|
|
130
|
+
# Add untracked files
|
|
131
|
+
result = subprocess.run(
|
|
132
|
+
['git', 'ls-files', '--others', '--exclude-standard'],
|
|
133
|
+
capture_output=True,
|
|
134
|
+
text=True,
|
|
135
|
+
check=True
|
|
136
|
+
)
|
|
137
|
+
for line in result.stdout.strip().split('\n'):
|
|
138
|
+
if line:
|
|
139
|
+
path = Path(line)
|
|
140
|
+
if path.exists():
|
|
141
|
+
files.add(path)
|
|
142
|
+
except subprocess.CalledProcessError:
|
|
143
|
+
# Not a git repo or git command failed
|
|
144
|
+
return []
|
|
145
|
+
|
|
146
|
+
return sorted(files)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def auto_detect_files(cwd: Optional[Path] = None) -> List[Path]:
|
|
150
|
+
"""Auto-detect files to process based on git status.
|
|
151
|
+
|
|
152
|
+
Priority:
|
|
153
|
+
1. Staged files
|
|
154
|
+
2. Modified files (staged + unstaged)
|
|
155
|
+
3. All code files in current directory
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
List of Path objects, empty list if none found
|
|
159
|
+
"""
|
|
160
|
+
if cwd is None:
|
|
161
|
+
cwd = Path.cwd()
|
|
162
|
+
|
|
163
|
+
# Try staged files first
|
|
164
|
+
files = get_git_changes(staged_only=True)
|
|
165
|
+
if files:
|
|
166
|
+
return files
|
|
167
|
+
|
|
168
|
+
# Try all modified files
|
|
169
|
+
files = get_git_changes(staged_only=False)
|
|
170
|
+
if files:
|
|
171
|
+
return files
|
|
172
|
+
|
|
173
|
+
# Fallback: all code files in current directory (non-recursive)
|
|
174
|
+
return expand_paths([str(cwd)], recursive=False)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-dev-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.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
|
|
@@ -46,7 +46,7 @@ Dynamic: license-file
|
|
|
46
46
|
|
|
47
47
|
[](https://badge.fury.io/py/claude-dev-cli)
|
|
48
48
|
[](https://www.python.org/downloads/)
|
|
49
|
-
[](https://github.com/thinmanj/claude-dev-cli)
|
|
50
50
|
[](https://opensource.org/licenses/MIT)
|
|
51
51
|
[](https://github.com/thinmanj/homebrew-tap)
|
|
52
52
|
[](https://github.com/psf/black)
|
|
@@ -72,6 +72,19 @@ A powerful command-line tool for developers using Claude AI with multi-API routi
|
|
|
72
72
|
- `smart`: Claude Sonnet 4 ($3.00/$15.00 per Mtok) - default
|
|
73
73
|
- `powerful`: Claude Opus 4 ($15.00/$75.00 per Mtok)
|
|
74
74
|
|
|
75
|
+
### 📁 Multi-File Support (v0.11.0+)
|
|
76
|
+
- **Batch Processing**: Review, refactor, test, or document multiple files at once
|
|
77
|
+
- **Directory Support**: Process all code files in a directory with `--max-files` limit
|
|
78
|
+
- **Auto-Detection**: Commands auto-detect git changes when no files specified
|
|
79
|
+
- `cdc review` → reviews staged files, falls back to modified files, then current directory
|
|
80
|
+
- **Git Integration**: New `cdc git review` command for reviewing changes
|
|
81
|
+
- `--staged`: Review only staged changes
|
|
82
|
+
- `--branch <range>`: Review branch changes (e.g., `main..HEAD`)
|
|
83
|
+
- **Multi-Language**: Supports 25+ file extensions (Python, JS/TS, Go, Rust, Java, C++, etc.)
|
|
84
|
+
- **Smart Display**: Shows file list preview (first 5-10 files, then "... and N more")
|
|
85
|
+
- Commands with multi-file support:
|
|
86
|
+
- `review`, `refactor`, `generate tests`, `generate docs`
|
|
87
|
+
|
|
75
88
|
### 🧪 Developer Tools
|
|
76
89
|
- **Test Generation**: Automatic pytest test generation for Python code
|
|
77
90
|
- **Code Review**: Comprehensive code reviews with security, performance, and best practice checks
|
|
@@ -232,36 +245,62 @@ cdc generate tests -m smart mymodule.py # Balanced approach
|
|
|
232
245
|
### 3. Developer Commands
|
|
233
246
|
|
|
234
247
|
```bash
|
|
235
|
-
# Generate tests
|
|
248
|
+
# Generate tests (single file)
|
|
236
249
|
cdc generate tests mymodule.py -o tests/test_mymodule.py
|
|
237
250
|
|
|
251
|
+
# Generate tests for multiple files (NEW in v0.11.0)
|
|
252
|
+
cdc generate tests file1.py file2.py file3.py
|
|
253
|
+
cdc generate tests src/ --max-files 10
|
|
254
|
+
|
|
238
255
|
# Generate tests with interactive refinement
|
|
239
256
|
cdc generate tests mymodule.py --interactive
|
|
240
257
|
|
|
241
258
|
# Generate tests with context (includes dependencies, related files) - NEW in v0.8.1
|
|
242
259
|
cdc generate tests mymodule.py --auto-context
|
|
243
260
|
|
|
244
|
-
# Code review
|
|
261
|
+
# Code review (single file)
|
|
245
262
|
cdc review mymodule.py
|
|
246
263
|
|
|
264
|
+
# Code review multiple files (NEW in v0.11.0)
|
|
265
|
+
cdc review file1.py file2.py file3.py
|
|
266
|
+
cdc review src/ # Review entire directory
|
|
267
|
+
cdc review # Auto-detect git changes (staged → modified → current dir)
|
|
268
|
+
|
|
247
269
|
# Code review with auto-context (includes git, dependencies, tests)
|
|
248
270
|
cdc review mymodule.py --auto-context
|
|
249
271
|
|
|
250
272
|
# Code review with interactive follow-up questions
|
|
251
273
|
cdc review mymodule.py --interactive
|
|
252
274
|
|
|
275
|
+
# Review git changes (NEW in v0.11.0)
|
|
276
|
+
cdc git review --staged # Review only staged changes
|
|
277
|
+
cdc git review --branch main..HEAD # Review branch changes
|
|
278
|
+
cdc git review # Review all modified files
|
|
279
|
+
|
|
253
280
|
# Debug errors with intelligent error parsing
|
|
254
281
|
python script.py 2>&1 | cdc debug --auto-context
|
|
255
282
|
|
|
256
|
-
# Generate documentation
|
|
283
|
+
# Generate documentation (single file)
|
|
257
284
|
cdc generate docs mymodule.py
|
|
258
285
|
|
|
286
|
+
# Generate docs for multiple files (NEW in v0.11.0)
|
|
287
|
+
cdc generate docs file1.py file2.py file3.py
|
|
288
|
+
cdc generate docs src/ --max-files 10
|
|
289
|
+
|
|
259
290
|
# Generate docs with interactive refinement
|
|
260
291
|
cdc generate docs mymodule.py --interactive
|
|
261
292
|
|
|
262
293
|
# Generate docs with context (includes dependencies) - NEW in v0.8.1
|
|
263
294
|
cdc generate docs mymodule.py --auto-context
|
|
264
295
|
|
|
296
|
+
# Refactor (single file)
|
|
297
|
+
cdc refactor legacy_code.py
|
|
298
|
+
|
|
299
|
+
# Refactor multiple files (NEW in v0.11.0)
|
|
300
|
+
cdc refactor file1.py file2.py file3.py
|
|
301
|
+
cdc refactor src/
|
|
302
|
+
cdc refactor # Auto-detect git changes
|
|
303
|
+
|
|
265
304
|
# Refactor with context (includes related files)
|
|
266
305
|
cdc refactor legacy_code.py --auto-context
|
|
267
306
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
claude_dev_cli/__init__.py,sha256=
|
|
2
|
-
claude_dev_cli/cli.py,sha256=
|
|
1
|
+
claude_dev_cli/__init__.py,sha256=XSFQv1m1nRRX-T9POeBmjHrA0pylgusfmwtA92XCEzo,470
|
|
2
|
+
claude_dev_cli/cli.py,sha256=RkoR4ocGUfP5A37yl_xObdNdd0QKeyqvEf2YOq1CL0U,79000
|
|
3
3
|
claude_dev_cli/commands.py,sha256=RKGx2rv56PM6eErvA2uoQ20hY8babuI5jav8nCUyUOk,3964
|
|
4
4
|
claude_dev_cli/config.py,sha256=ZnPvzwlXsoY9YhqTl4S__fwY1MzJXKIaYK0nIIelNXk,19978
|
|
5
5
|
claude_dev_cli/context.py,sha256=1TlLzpREFZDEIuU7RAtlkjxARKWZpnxHHvK283sUAZE,26714
|
|
6
|
-
claude_dev_cli/core.py,sha256
|
|
6
|
+
claude_dev_cli/core.py,sha256=4tKBgPQzvhM-jtlHaIy2K54vc2yIb4ycNDPLpoIoqN0,6621
|
|
7
7
|
claude_dev_cli/history.py,sha256=26EjNW68JuFQJhUp1j8UdB19S-eYz3eqevkpCOATwP0,10510
|
|
8
|
+
claude_dev_cli/path_utils.py,sha256=FFwweSkXe9OiG2Dej_UDKcY8-ZCYjL89ow6c7LZGe80,5564
|
|
8
9
|
claude_dev_cli/secure_storage.py,sha256=KcZuQMLTbQpMAi2Cyh-_JkNcK9vHzAITOgjTcM9sr98,8161
|
|
9
10
|
claude_dev_cli/template_manager.py,sha256=wtcrNuxFoJLJIPmIxUzrPKrE8kUvdqEd53EnG3jARhg,9277
|
|
10
11
|
claude_dev_cli/templates.py,sha256=lKxH943ySfUKgyHaWa4W3LVv91SgznKgajRtSRp_4UY,2260
|
|
@@ -17,9 +18,9 @@ claude_dev_cli/plugins/base.py,sha256=H4HQet1I-a3WLCfE9F06Lp8NuFvVoIlou7sIgyJFK-
|
|
|
17
18
|
claude_dev_cli/plugins/diff_editor/__init__.py,sha256=gqR5S2TyIVuq-sK107fegsutQ7Z-sgAIEbtc71FhXIM,101
|
|
18
19
|
claude_dev_cli/plugins/diff_editor/plugin.py,sha256=M1bUoqpasD3ZNQo36Fu_8g92uySPZyG_ujMbj5UplsU,3073
|
|
19
20
|
claude_dev_cli/plugins/diff_editor/viewer.py,sha256=1IOXIKw_01ppJx5C1dQt9Kr6U1TdAHT8_iUT5r_q0NM,17169
|
|
20
|
-
claude_dev_cli-0.
|
|
21
|
-
claude_dev_cli-0.
|
|
22
|
-
claude_dev_cli-0.
|
|
23
|
-
claude_dev_cli-0.
|
|
24
|
-
claude_dev_cli-0.
|
|
25
|
-
claude_dev_cli-0.
|
|
21
|
+
claude_dev_cli-0.11.0.dist-info/licenses/LICENSE,sha256=DGueuJwMJtMwgLO5mWlS0TaeBrFwQuNpNZ22PU9J2bw,1062
|
|
22
|
+
claude_dev_cli-0.11.0.dist-info/METADATA,sha256=5fvuAb55hqZwnB_j-wzQNFv6ZXZbdnvEbAzt9pvmxtM,21330
|
|
23
|
+
claude_dev_cli-0.11.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
24
|
+
claude_dev_cli-0.11.0.dist-info/entry_points.txt,sha256=zymgUIIVpFTARkFmxAuW2A4BQsNITh_L0uU-XunytHg,85
|
|
25
|
+
claude_dev_cli-0.11.0.dist-info/top_level.txt,sha256=m7MF6LOIuTe41IT5Fgt0lc-DK1EgM4gUU_IZwWxK0pg,15
|
|
26
|
+
claude_dev_cli-0.11.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|