gitarsenal-cli 1.9.85 → 1.9.87
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.
- package/.venv_status.json +1 -1
- package/kill_claude/TUI_IMPROVEMENTS.md +1 -1
- package/kill_claude/__pycache__/claude_code_agent.cpython-313.pyc +0 -0
- package/kill_claude/claude_code_agent.py +358 -13
- package/kill_claude/tools/__pycache__/bash_output_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/bash_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/edit_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/exit_plan_mode_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/glob_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/grep_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/kill_bash_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/ls_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/multiedit_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/notebook_edit_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/read_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/task_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/todo_write_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/web_fetch_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/web_search_tool.cpython-313.pyc +0 -0
- package/kill_claude/tools/__pycache__/write_tool.cpython-313.pyc +0 -0
- package/package.json +1 -1
- package/python/test_modalSandboxScript.py +12 -11
package/.venv_status.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"created":"2025-08-
|
|
1
|
+
{"created":"2025-08-20T13:10:12.929Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
|
|
@@ -109,7 +109,7 @@ When you run `query "read main.py"`, you now see:
|
|
|
109
109
|
|
|
110
110
|
### Enhanced Todo Management
|
|
111
111
|
Todo lists are now displayed as professional tables with:
|
|
112
|
-
- Status indicators (⏳ Pending, 🔄 In Progress,
|
|
112
|
+
- Status indicators (⏳ Pending, 🔄 In Progress, ✓ Completed)
|
|
113
113
|
- Organized columns
|
|
114
114
|
- Clean borders and styling
|
|
115
115
|
|
|
Binary file
|
|
@@ -33,8 +33,11 @@ from rich.text import Text
|
|
|
33
33
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
34
34
|
from rich.tree import Tree
|
|
35
35
|
from rich.syntax import Syntax
|
|
36
|
-
from rich.prompt import Prompt
|
|
36
|
+
from rich.prompt import Prompt, Confirm, IntPrompt
|
|
37
37
|
from rich import print as rprint
|
|
38
|
+
from rich.layout import Layout
|
|
39
|
+
from rich.live import Live
|
|
40
|
+
from pathlib import Path
|
|
38
41
|
|
|
39
42
|
def load_tool_modules():
|
|
40
43
|
"""Load all tool modules from the tools directory."""
|
|
@@ -444,7 +447,7 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
|
|
|
444
447
|
todo_table.add_column("Task", style="cyan")
|
|
445
448
|
|
|
446
449
|
for i, todo in enumerate(todos, 1):
|
|
447
|
-
status_emoji = {"pending": "⏳ Pending", "in_progress": "🔄 In Progress", "completed": "
|
|
450
|
+
status_emoji = {"pending": "⏳ Pending", "in_progress": "🔄 In Progress", "completed": "✓ Completed"}.get(todo.get("status", "pending"), "❓ Unknown")
|
|
448
451
|
content = todo.get("content", "No description")
|
|
449
452
|
if len(content) > 80:
|
|
450
453
|
content = content[:80] + "..."
|
|
@@ -557,7 +560,7 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
|
|
|
557
560
|
|
|
558
561
|
# Success panel
|
|
559
562
|
success_panel = Panel(
|
|
560
|
-
f"
|
|
563
|
+
f"✓ [bold green]Tool {tool_name} completed successfully[/bold green]",
|
|
561
564
|
border_style="green",
|
|
562
565
|
padding=(0, 1)
|
|
563
566
|
)
|
|
@@ -566,7 +569,7 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
|
|
|
566
569
|
console.print(result_panel)
|
|
567
570
|
else:
|
|
568
571
|
console.print(Panel(
|
|
569
|
-
"
|
|
572
|
+
"✓ [bold green]Tool completed successfully[/bold green]\n[dim](no output)[/dim]",
|
|
570
573
|
title=f"Tool {tool_name}",
|
|
571
574
|
border_style="green",
|
|
572
575
|
padding=(0, 1)
|
|
@@ -577,7 +580,7 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
|
|
|
577
580
|
|
|
578
581
|
except Exception as e:
|
|
579
582
|
console.print(Panel(
|
|
580
|
-
f"
|
|
583
|
+
f"✗ [bold red]Tool {tool_name} failed:[/bold red]\n{str(e)}",
|
|
581
584
|
border_style="red",
|
|
582
585
|
padding=(0, 1)
|
|
583
586
|
))
|
|
@@ -617,6 +620,10 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
|
|
|
617
620
|
return self._get_bash_output(tool_input)
|
|
618
621
|
elif tool_name == "KillBash":
|
|
619
622
|
return self._kill_bash(tool_input)
|
|
623
|
+
elif tool_name == "WebSearch":
|
|
624
|
+
return self._web_search(tool_input)
|
|
625
|
+
elif tool_name == "WebFetch":
|
|
626
|
+
return self._web_fetch(tool_input)
|
|
620
627
|
else:
|
|
621
628
|
return f"Tool {tool_name} not implemented"
|
|
622
629
|
|
|
@@ -844,6 +851,60 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
|
|
|
844
851
|
except Exception as e:
|
|
845
852
|
return f"Error searching: {str(e)}"
|
|
846
853
|
|
|
854
|
+
def _web_search(self, tool_input: Dict[str, Any]) -> str:
|
|
855
|
+
"""Handle WebSearch tool - search the web for information."""
|
|
856
|
+
query = tool_input.get('query', '')
|
|
857
|
+
allowed_domains = tool_input.get('allowed_domains', [])
|
|
858
|
+
blocked_domains = tool_input.get('blocked_domains', [])
|
|
859
|
+
|
|
860
|
+
if not query:
|
|
861
|
+
return "Error: No search query provided"
|
|
862
|
+
|
|
863
|
+
# Since we don't have actual web search capability, return a mock response
|
|
864
|
+
# In a real implementation, this would use a search API like Google, Bing, or DuckDuckGo
|
|
865
|
+
domain_filter = ""
|
|
866
|
+
if allowed_domains:
|
|
867
|
+
domain_filter = f" (restricted to: {', '.join(allowed_domains)})"
|
|
868
|
+
elif blocked_domains:
|
|
869
|
+
domain_filter = f" (excluding: {', '.join(blocked_domains)})"
|
|
870
|
+
|
|
871
|
+
return f"""Web search results for: "{query}"{domain_filter}
|
|
872
|
+
|
|
873
|
+
⚠️ Note: WebSearch is not fully implemented in this demo version.
|
|
874
|
+
|
|
875
|
+
In a production environment, this would:
|
|
876
|
+
• Search the web using APIs like Google Search API, Bing API, or similar
|
|
877
|
+
• Return formatted search results with titles, snippets, and URLs
|
|
878
|
+
• Support domain filtering as specified
|
|
879
|
+
• Provide up-to-date information beyond the AI's knowledge cutoff
|
|
880
|
+
|
|
881
|
+
Query processed: "{query}"
|
|
882
|
+
Domain restrictions: {domain_filter if domain_filter else "None"}"""
|
|
883
|
+
|
|
884
|
+
def _web_fetch(self, tool_input: Dict[str, Any]) -> str:
|
|
885
|
+
"""Handle WebFetch tool - fetch content from a URL."""
|
|
886
|
+
url = tool_input.get('url', '')
|
|
887
|
+
prompt = tool_input.get('prompt', '')
|
|
888
|
+
|
|
889
|
+
if not url:
|
|
890
|
+
return "Error: No URL provided"
|
|
891
|
+
|
|
892
|
+
# Since we don't have actual web fetching capability, return a mock response
|
|
893
|
+
# In a real implementation, this would fetch the URL content and process it
|
|
894
|
+
return f"""WebFetch results for: {url}
|
|
895
|
+
|
|
896
|
+
⚠️ Note: WebFetch is not fully implemented in this demo version.
|
|
897
|
+
|
|
898
|
+
In a production environment, this would:
|
|
899
|
+
• Fetch content from the specified URL
|
|
900
|
+
• Convert HTML to markdown if needed
|
|
901
|
+
• Process the content with the provided prompt: "{prompt}"
|
|
902
|
+
• Return the AI model's analysis of the fetched content
|
|
903
|
+
• Handle redirects and various content types
|
|
904
|
+
|
|
905
|
+
URL to fetch: {url}
|
|
906
|
+
Processing prompt: "{prompt}" """
|
|
907
|
+
|
|
847
908
|
def process_query(self, user_input: str) -> str:
|
|
848
909
|
"""
|
|
849
910
|
Main method to process user queries using the Anthropic API.
|
|
@@ -929,7 +990,7 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
|
|
|
929
990
|
break
|
|
930
991
|
except Exception as e:
|
|
931
992
|
console.print(Panel(
|
|
932
|
-
f"[bold red]
|
|
993
|
+
f"[bold red]✗ Error:[/bold red] {str(e)}",
|
|
933
994
|
border_style="red"
|
|
934
995
|
))
|
|
935
996
|
|
|
@@ -1007,7 +1068,7 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
|
|
|
1007
1068
|
config_table = Table(show_header=False, box=None)
|
|
1008
1069
|
config_table.add_column("Setting", style="yellow")
|
|
1009
1070
|
config_table.add_column("Value", style="green")
|
|
1010
|
-
config_table.add_row("API Key", "
|
|
1071
|
+
config_table.add_row("API Key", "✓ Set" if self.api_key else "✗ Missing")
|
|
1011
1072
|
config_table.add_row("Model", "claude-sonnet-4-20250514")
|
|
1012
1073
|
config_table.add_row("Working Directory", self.working_dir)
|
|
1013
1074
|
config_table.add_row("Git Repository", "Yes" if self.is_git_repo else "No")
|
|
@@ -1061,7 +1122,7 @@ def interactive(
|
|
|
1061
1122
|
agent.interactive_mode()
|
|
1062
1123
|
except ValueError as e:
|
|
1063
1124
|
console.print(Panel(
|
|
1064
|
-
f"[bold red]
|
|
1125
|
+
f"[bold red]✗ Configuration Error:[/bold red]\n{str(e)}\n\n" +
|
|
1065
1126
|
"[yellow]To fix this:[/yellow]\n" +
|
|
1066
1127
|
"1. Get your Anthropic API key from: https://console.anthropic.com/\n" +
|
|
1067
1128
|
"2. Set environment variable: [cyan]export ANTHROPIC_API_KEY=your_key_here[/cyan]\n" +
|
|
@@ -1072,7 +1133,7 @@ def interactive(
|
|
|
1072
1133
|
raise typer.Exit(1)
|
|
1073
1134
|
except Exception as e:
|
|
1074
1135
|
console.print(Panel(
|
|
1075
|
-
f"[bold red]
|
|
1136
|
+
f"[bold red]✗ Error:[/bold red] {str(e)}",
|
|
1076
1137
|
border_style="red"
|
|
1077
1138
|
))
|
|
1078
1139
|
raise typer.Exit(1)
|
|
@@ -1109,7 +1170,7 @@ def query(
|
|
|
1109
1170
|
|
|
1110
1171
|
except ValueError as e:
|
|
1111
1172
|
console.print(Panel(
|
|
1112
|
-
f"[bold red]
|
|
1173
|
+
f"[bold red]✗ Configuration Error:[/bold red]\n{str(e)}\n\n" +
|
|
1113
1174
|
"[yellow]To fix this:[/yellow]\n" +
|
|
1114
1175
|
"1. Get your Anthropic API key from: https://console.anthropic.com/\n" +
|
|
1115
1176
|
"2. Set environment variable: [cyan]export ANTHROPIC_API_KEY=your_key_here[/cyan]\n" +
|
|
@@ -1120,7 +1181,7 @@ def query(
|
|
|
1120
1181
|
raise typer.Exit(1)
|
|
1121
1182
|
except Exception as e:
|
|
1122
1183
|
console.print(Panel(
|
|
1123
|
-
f"[bold red]
|
|
1184
|
+
f"[bold red]✗ Error:[/bold red] {str(e)}",
|
|
1124
1185
|
border_style="red"
|
|
1125
1186
|
))
|
|
1126
1187
|
raise typer.Exit(1)
|
|
@@ -1137,7 +1198,7 @@ def setup():
|
|
|
1137
1198
|
system_info.add_row("Platform", os.uname().sysname if hasattr(os, 'uname') else 'unknown')
|
|
1138
1199
|
system_info.add_row("Working Directory", os.getcwd())
|
|
1139
1200
|
system_info.add_row("Git Repository", "Yes" if os.path.exists('.git') else "No")
|
|
1140
|
-
system_info.add_row("API Key Status", "
|
|
1201
|
+
system_info.add_row("API Key Status", "✓ Set" if os.getenv('ANTHROPIC_API_KEY') else "✗ Missing")
|
|
1141
1202
|
|
|
1142
1203
|
# Setup instructions
|
|
1143
1204
|
setup_instructions = """[bold yellow]1. Get your API key:[/bold yellow]
|
|
@@ -1152,7 +1213,10 @@ def setup():
|
|
|
1152
1213
|
[bold yellow]4. Run the agent:[/bold yellow]
|
|
1153
1214
|
[cyan]python claude_code_agent.py interactive[/cyan]
|
|
1154
1215
|
or
|
|
1155
|
-
[cyan]python claude_code_agent.py query "your question"[/cyan]
|
|
1216
|
+
[cyan]python claude_code_agent.py query "your question"[/cyan]
|
|
1217
|
+
|
|
1218
|
+
[bold cyan]5. Quick setup:[/bold cyan]
|
|
1219
|
+
[cyan]python claude_code_agent.py configure[/cyan]"""
|
|
1156
1220
|
|
|
1157
1221
|
console.print(Panel(
|
|
1158
1222
|
Columns([
|
|
@@ -1164,6 +1228,287 @@ def setup():
|
|
|
1164
1228
|
padding=(1, 1)
|
|
1165
1229
|
))
|
|
1166
1230
|
|
|
1231
|
+
@app.command()
|
|
1232
|
+
def configure(
|
|
1233
|
+
reset: bool = typer.Option(False, "--reset", "-r", help="Reset configuration to defaults")
|
|
1234
|
+
):
|
|
1235
|
+
"""🔧 Interactive configuration wizard"""
|
|
1236
|
+
console = Console()
|
|
1237
|
+
config_file = Path.home() / ".claude_code_agent.json"
|
|
1238
|
+
|
|
1239
|
+
if reset:
|
|
1240
|
+
if config_file.exists():
|
|
1241
|
+
config_file.unlink()
|
|
1242
|
+
console.print("✓ [green]Configuration reset to defaults[/green]")
|
|
1243
|
+
return
|
|
1244
|
+
|
|
1245
|
+
console.print(Panel(
|
|
1246
|
+
"[bold blue]🔧 Claude Code Agent Configuration Wizard[/bold blue]\n" +
|
|
1247
|
+
"This will help you set up your preferences and API credentials.",
|
|
1248
|
+
title="Configuration Wizard",
|
|
1249
|
+
border_style="blue"
|
|
1250
|
+
))
|
|
1251
|
+
|
|
1252
|
+
# Load existing config
|
|
1253
|
+
config = {}
|
|
1254
|
+
if config_file.exists():
|
|
1255
|
+
with open(config_file) as f:
|
|
1256
|
+
config = json.load(f)
|
|
1257
|
+
console.print(f"[dim]Found existing config at: {config_file}[/dim]")
|
|
1258
|
+
|
|
1259
|
+
# API Key configuration
|
|
1260
|
+
current_api_key = config.get('api_key') or os.getenv('ANTHROPIC_API_KEY')
|
|
1261
|
+
if current_api_key:
|
|
1262
|
+
console.print(f"✓ [green]API Key is already configured[/green]")
|
|
1263
|
+
if not Confirm.ask("Would you like to update it?"):
|
|
1264
|
+
api_key = current_api_key
|
|
1265
|
+
else:
|
|
1266
|
+
api_key = typer.prompt("Enter your Anthropic API key", hide_input=True)
|
|
1267
|
+
else:
|
|
1268
|
+
console.print("✗ [red]API Key not found[/red]")
|
|
1269
|
+
api_key = typer.prompt("Enter your Anthropic API key", hide_input=True)
|
|
1270
|
+
|
|
1271
|
+
# Model selection
|
|
1272
|
+
models = [
|
|
1273
|
+
"claude-sonnet-4-20250514",
|
|
1274
|
+
"claude-3-5-sonnet-20241022",
|
|
1275
|
+
"claude-3-opus-20240229",
|
|
1276
|
+
"claude-3-haiku-20240307"
|
|
1277
|
+
]
|
|
1278
|
+
current_model = config.get('default_model', models[0])
|
|
1279
|
+
console.print(f"\nCurrent model: [cyan]{current_model}[/cyan]")
|
|
1280
|
+
|
|
1281
|
+
model_choice = typer.prompt(
|
|
1282
|
+
"Select model",
|
|
1283
|
+
type=typer.Choice(models),
|
|
1284
|
+
default=current_model
|
|
1285
|
+
)
|
|
1286
|
+
|
|
1287
|
+
# Timeout configuration
|
|
1288
|
+
current_timeout = config.get('default_timeout', 120)
|
|
1289
|
+
timeout = IntPrompt.ask(
|
|
1290
|
+
"Default timeout (seconds)",
|
|
1291
|
+
default=current_timeout,
|
|
1292
|
+
show_default=True
|
|
1293
|
+
)
|
|
1294
|
+
|
|
1295
|
+
# Tool preferences
|
|
1296
|
+
console.print("\n[bold]Tool Preferences:[/bold]")
|
|
1297
|
+
use_progress_bars = Confirm.ask(
|
|
1298
|
+
"Show progress bars for long operations?",
|
|
1299
|
+
default=config.get('use_progress_bars', True)
|
|
1300
|
+
)
|
|
1301
|
+
|
|
1302
|
+
auto_todos = Confirm.ask(
|
|
1303
|
+
"Automatically create todo lists for complex tasks?",
|
|
1304
|
+
default=config.get('auto_todos', True)
|
|
1305
|
+
)
|
|
1306
|
+
|
|
1307
|
+
syntax_highlighting = Confirm.ask(
|
|
1308
|
+
"Enable syntax highlighting for code files?",
|
|
1309
|
+
default=config.get('syntax_highlighting', True)
|
|
1310
|
+
)
|
|
1311
|
+
|
|
1312
|
+
# Save configuration
|
|
1313
|
+
new_config = {
|
|
1314
|
+
'api_key': api_key,
|
|
1315
|
+
'default_model': model_choice,
|
|
1316
|
+
'default_timeout': timeout,
|
|
1317
|
+
'use_progress_bars': use_progress_bars,
|
|
1318
|
+
'auto_todos': auto_todos,
|
|
1319
|
+
'syntax_highlighting': syntax_highlighting,
|
|
1320
|
+
'configured_at': str(datetime.now())
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
# Ensure config directory exists
|
|
1324
|
+
config_file.parent.mkdir(exist_ok=True)
|
|
1325
|
+
|
|
1326
|
+
with open(config_file, 'w') as f:
|
|
1327
|
+
json.dump(new_config, f, indent=2)
|
|
1328
|
+
|
|
1329
|
+
# Show summary
|
|
1330
|
+
summary_table = Table(title="Configuration Summary", show_header=True)
|
|
1331
|
+
summary_table.add_column("Setting", style="yellow")
|
|
1332
|
+
summary_table.add_column("Value", style="green")
|
|
1333
|
+
|
|
1334
|
+
summary_table.add_row("API Key", "✓ Configured" if api_key else "✗ Missing")
|
|
1335
|
+
summary_table.add_row("Model", model_choice)
|
|
1336
|
+
summary_table.add_row("Timeout", f"{timeout}s")
|
|
1337
|
+
summary_table.add_row("Progress Bars", "✓ Enabled" if use_progress_bars else "✗ Disabled")
|
|
1338
|
+
summary_table.add_row("Auto Todos", "✓ Enabled" if auto_todos else "✗ Disabled")
|
|
1339
|
+
summary_table.add_row("Syntax Highlighting", "✓ Enabled" if syntax_highlighting else "✗ Disabled")
|
|
1340
|
+
summary_table.add_row("Config File", str(config_file))
|
|
1341
|
+
|
|
1342
|
+
console.print(Panel(
|
|
1343
|
+
summary_table,
|
|
1344
|
+
title="✓ Configuration Complete",
|
|
1345
|
+
border_style="green"
|
|
1346
|
+
))
|
|
1347
|
+
|
|
1348
|
+
@app.command()
|
|
1349
|
+
def browse():
|
|
1350
|
+
"""📁 Interactive file browser"""
|
|
1351
|
+
console = Console()
|
|
1352
|
+
current_dir = Path.cwd()
|
|
1353
|
+
|
|
1354
|
+
while True:
|
|
1355
|
+
# List directory contents
|
|
1356
|
+
items = []
|
|
1357
|
+
if current_dir.parent != current_dir: # Not root
|
|
1358
|
+
items.append((".. (parent directory)", current_dir.parent, "dir"))
|
|
1359
|
+
|
|
1360
|
+
for item in sorted(current_dir.iterdir()):
|
|
1361
|
+
if item.is_dir():
|
|
1362
|
+
items.append((f"{item.name}/", item, "dir"))
|
|
1363
|
+
else:
|
|
1364
|
+
size = item.stat().st_size
|
|
1365
|
+
size_str = f"{size:,} bytes" if size < 1024 else f"{size//1024:,} KB"
|
|
1366
|
+
items.append((f"{item.name} ({size_str})", item, "file"))
|
|
1367
|
+
|
|
1368
|
+
# Create selection table
|
|
1369
|
+
table = Table(title=f"Directory: {current_dir}", show_header=True)
|
|
1370
|
+
table.add_column("#", style="dim", width=3)
|
|
1371
|
+
table.add_column("Type", width=4)
|
|
1372
|
+
table.add_column("Name", style="cyan")
|
|
1373
|
+
|
|
1374
|
+
for i, (display_name, path, item_type) in enumerate(items, 1):
|
|
1375
|
+
icon = "📁" if item_type == "dir" else "📄"
|
|
1376
|
+
table.add_row(str(i), icon, display_name)
|
|
1377
|
+
|
|
1378
|
+
console.clear()
|
|
1379
|
+
console.print(table)
|
|
1380
|
+
|
|
1381
|
+
# Get user choice
|
|
1382
|
+
console.print("\n[dim]Commands: [cyan]number[/cyan] to select, [cyan]q[/cyan] to quit, [cyan]r[/cyan] to read file[/dim]")
|
|
1383
|
+
choice = typer.prompt("Select item")
|
|
1384
|
+
|
|
1385
|
+
if choice.lower() == 'q':
|
|
1386
|
+
break
|
|
1387
|
+
elif choice.lower() == 'r':
|
|
1388
|
+
file_num = typer.prompt("File number to read", type=int)
|
|
1389
|
+
if 1 <= file_num <= len(items):
|
|
1390
|
+
selected_path = items[file_num - 1][1]
|
|
1391
|
+
if selected_path.is_file():
|
|
1392
|
+
try:
|
|
1393
|
+
# Read file directly without requiring API key
|
|
1394
|
+
with open(selected_path, 'r', encoding='utf-8') as f:
|
|
1395
|
+
lines = f.readlines()
|
|
1396
|
+
|
|
1397
|
+
# Format with line numbers
|
|
1398
|
+
formatted_lines = []
|
|
1399
|
+
for i, line in enumerate(lines[:100], 1): # Limit to 100 lines
|
|
1400
|
+
clean_line = line.rstrip('\n\r')
|
|
1401
|
+
if len(clean_line) > 2000:
|
|
1402
|
+
clean_line = clean_line[:2000] + "..."
|
|
1403
|
+
formatted_lines.append(f"{i:>5}→{clean_line}")
|
|
1404
|
+
|
|
1405
|
+
content = '\n'.join(formatted_lines)
|
|
1406
|
+
if len(lines) > 100:
|
|
1407
|
+
content += f"\n... (showing first 100 lines of {len(lines)} total)"
|
|
1408
|
+
|
|
1409
|
+
console.print(Panel(
|
|
1410
|
+
content,
|
|
1411
|
+
title=f"📄 {selected_path.name}",
|
|
1412
|
+
border_style="blue"
|
|
1413
|
+
))
|
|
1414
|
+
typer.prompt("Press Enter to continue")
|
|
1415
|
+
except Exception as e:
|
|
1416
|
+
console.print(f"[red]✗ Error reading file: {e}[/red]")
|
|
1417
|
+
else:
|
|
1418
|
+
try:
|
|
1419
|
+
choice_num = int(choice)
|
|
1420
|
+
if 1 <= choice_num <= len(items):
|
|
1421
|
+
selected_path = items[choice_num - 1][1]
|
|
1422
|
+
if selected_path.is_dir():
|
|
1423
|
+
current_dir = selected_path
|
|
1424
|
+
else:
|
|
1425
|
+
console.print(f"Selected file: [cyan]{selected_path}[/cyan]")
|
|
1426
|
+
break
|
|
1427
|
+
except ValueError:
|
|
1428
|
+
console.print("[red]Invalid choice[/red]")
|
|
1429
|
+
typer.prompt("Press Enter to continue")
|
|
1430
|
+
|
|
1431
|
+
@app.command()
|
|
1432
|
+
def templates():
|
|
1433
|
+
"""🎨 Manage project templates"""
|
|
1434
|
+
console = Console()
|
|
1435
|
+
|
|
1436
|
+
# Built-in templates
|
|
1437
|
+
builtin_templates = {
|
|
1438
|
+
"python-basic": {
|
|
1439
|
+
"description": "Basic Python project structure",
|
|
1440
|
+
"files": {
|
|
1441
|
+
"main.py": "#!/usr/bin/env python3\n\nif __name__ == '__main__':\n print('Hello, World!')\n",
|
|
1442
|
+
"requirements.txt": "",
|
|
1443
|
+
"README.md": "# Project\n\nDescription here.\n"
|
|
1444
|
+
}
|
|
1445
|
+
},
|
|
1446
|
+
"python-package": {
|
|
1447
|
+
"description": "Python package with setup.py",
|
|
1448
|
+
"files": {
|
|
1449
|
+
"setup.py": "from setuptools import setup, find_packages\n\nsetup(\n name='my-package',\n version='0.1.0',\n packages=find_packages(),\n)\n",
|
|
1450
|
+
"my_package/__init__.py": "",
|
|
1451
|
+
"my_package/main.py": "def main():\n pass\n",
|
|
1452
|
+
"requirements.txt": "",
|
|
1453
|
+
"README.md": "# My Package\n"
|
|
1454
|
+
}
|
|
1455
|
+
},
|
|
1456
|
+
"claude-agent": {
|
|
1457
|
+
"description": "Claude Code Agent project structure",
|
|
1458
|
+
"files": {
|
|
1459
|
+
"main.py": "#!/usr/bin/env python3\nfrom claude_code_agent import ClaudeCodeAgent\n\ndef main():\n agent = ClaudeCodeAgent()\n agent.interactive_mode()\n\nif __name__ == '__main__':\n main()\n",
|
|
1460
|
+
"requirements.txt": "anthropic>=0.25.0\ntyper>=0.12.0\nrich>=13.0.0\n",
|
|
1461
|
+
"README.md": "# Claude Agent Project\n\nA project using Claude Code Agent.\n",
|
|
1462
|
+
".env.example": "ANTHROPIC_API_KEY=your_key_here\n"
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
# List available templates
|
|
1468
|
+
table = Table(title="Available Templates", show_header=True)
|
|
1469
|
+
table.add_column("Name", style="cyan")
|
|
1470
|
+
table.add_column("Description", style="yellow")
|
|
1471
|
+
table.add_column("Files", style="green")
|
|
1472
|
+
|
|
1473
|
+
for name, template in builtin_templates.items():
|
|
1474
|
+
file_count = len(template["files"])
|
|
1475
|
+
table.add_row(name, template["description"], f"{file_count} files")
|
|
1476
|
+
|
|
1477
|
+
console.print(table)
|
|
1478
|
+
|
|
1479
|
+
template_name = typer.prompt("Select template to create")
|
|
1480
|
+
if template_name not in builtin_templates:
|
|
1481
|
+
console.print(f"[red]✗ Template '{template_name}' not found[/red]")
|
|
1482
|
+
return
|
|
1483
|
+
|
|
1484
|
+
project_name = typer.prompt("Project name")
|
|
1485
|
+
project_dir = Path.cwd() / project_name
|
|
1486
|
+
|
|
1487
|
+
if project_dir.exists():
|
|
1488
|
+
if not Confirm.ask(f"Directory {project_name} exists. Continue?"):
|
|
1489
|
+
return
|
|
1490
|
+
|
|
1491
|
+
project_dir.mkdir(exist_ok=True)
|
|
1492
|
+
template = builtin_templates[template_name]
|
|
1493
|
+
|
|
1494
|
+
# Create files with progress bar
|
|
1495
|
+
with Progress() as progress:
|
|
1496
|
+
task = progress.add_task("Creating project...", total=len(template["files"]))
|
|
1497
|
+
|
|
1498
|
+
for file_path, content in template["files"].items():
|
|
1499
|
+
full_path = project_dir / file_path
|
|
1500
|
+
full_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1501
|
+
full_path.write_text(content)
|
|
1502
|
+
progress.advance(task)
|
|
1503
|
+
|
|
1504
|
+
console.print(Panel(
|
|
1505
|
+
f"✓ [green]Project '{project_name}' created successfully![/green]\n" +
|
|
1506
|
+
f"Location: [cyan]{project_dir}[/cyan]\n" +
|
|
1507
|
+
f"Template: [yellow]{template_name}[/yellow]",
|
|
1508
|
+
title="Project Created",
|
|
1509
|
+
border_style="green"
|
|
1510
|
+
))
|
|
1511
|
+
|
|
1167
1512
|
def main():
|
|
1168
1513
|
"""Main entry point - delegate to Typer app."""
|
|
1169
1514
|
app()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -322,7 +322,8 @@ def ssh_container_function(ssh_password=None, repo_url=None, repo_name=None, set
|
|
|
322
322
|
# Process complete lines immediately
|
|
323
323
|
while '\n' in stdout_buffer:
|
|
324
324
|
line, stdout_buffer = stdout_buffer.split('\n', 1)
|
|
325
|
-
|
|
325
|
+
# Add subtle color to agent output for better readability
|
|
326
|
+
print(line, flush=True) # Use default terminal colors
|
|
326
327
|
elif stream == process.stderr:
|
|
327
328
|
chunk = stream.read(1024)
|
|
328
329
|
if chunk is not None and chunk:
|
|
@@ -816,32 +817,32 @@ def show_usage_examples():
|
|
|
816
817
|
|
|
817
818
|
print("Basic Container Creation with Agent")
|
|
818
819
|
print("┌────────────────────────────────────────────────────────────────────────┐")
|
|
819
|
-
print("│ gitarsenal --gpu A10G --repo
|
|
820
|
+
print("│ gitarsenal --gpu A10G --repo https://github.com/username/repo.git │")
|
|
820
821
|
print("│ # Agent will intelligently clone and setup the repository │")
|
|
821
822
|
print("└────────────────────────────────────────────────────────────────────────┘\n")
|
|
822
823
|
|
|
823
824
|
print("With Persistent Storage")
|
|
824
825
|
print("┌────────────────────────────────────────────────────────────────────────────────────┐")
|
|
825
|
-
print("│ gitarsenal --gpu A10G --repo
|
|
826
|
+
print("│ gitarsenal --gpu A10G --repo https://github.com/username/repo.git \\ │")
|
|
826
827
|
print("│ --volume-name my-persistent-volume │")
|
|
827
828
|
print("└────────────────────────────────────────────────────────────────────────────────────┘\n")
|
|
828
829
|
|
|
829
830
|
print("With Multiple GPUs")
|
|
830
831
|
print("┌────────────────────────────────────────────────────────────────────────────────────┐")
|
|
831
832
|
print("│ gitarsenal --gpu A100-80GB --gpu-count 4 \\ │")
|
|
832
|
-
print("│ --repo
|
|
833
|
+
print("│ --repo https://github.com/username/repo.git │")
|
|
833
834
|
print("└────────────────────────────────────────────────────────────────────────────────────┘\n")
|
|
834
835
|
|
|
835
836
|
print("Intelligent Repository Setup (default)")
|
|
836
837
|
print("┌────────────────────────────────────────────────────────────────────────────────────┐")
|
|
837
|
-
print("│ gitarsenal --gpu A10G --repo
|
|
838
|
+
print("│ gitarsenal --gpu A10G --repo https://github.com/username/repo.git │")
|
|
838
839
|
print("│ # Agent analyzes repo and sets up environment automatically │")
|
|
839
840
|
print("└────────────────────────────────────────────────────────────────────────────────────┘\n")
|
|
840
841
|
|
|
841
842
|
print("With Manual Setup Commands (Advanced)")
|
|
842
843
|
print("┌────────────────────────────────────────────────────────────────────────────────────┐")
|
|
843
844
|
print("│ gitarsenal --gpu A10G --setup-commands \"pip install torch\" \"python train.py\" │")
|
|
844
|
-
print("│ # Only use when not providing --repo
|
|
845
|
+
print("│ # Only use when not providing --repo (bypasses Agent) │")
|
|
845
846
|
print("└────────────────────────────────────────────────────────────────────────────────────┘\n")
|
|
846
847
|
|
|
847
848
|
print("Development Mode (Skip Authentication)")
|
|
@@ -863,16 +864,16 @@ def show_usage_examples():
|
|
|
863
864
|
print(" • Without --gpu: Shows interactive GPU selection menu")
|
|
864
865
|
print()
|
|
865
866
|
print("Repository Setup Behavior:")
|
|
866
|
-
print(" • With --repo
|
|
867
|
-
print(" • Without --repo
|
|
868
|
-
print(" • Legacy --setup-commands: Only used when --repo
|
|
867
|
+
print(" • With --repo Agent intelligently clones and sets up repository")
|
|
868
|
+
print(" • Without --repo: Manual container setup (no automatic repository setup)")
|
|
869
|
+
print(" • Legacy --setup-commands: Only used when --repo not provided")
|
|
869
870
|
print()
|
|
870
871
|
print("Examples:")
|
|
871
872
|
print(" # Intelligent repository setup (recommended):")
|
|
872
|
-
print(" gitarsenal --gpu A10G --repo
|
|
873
|
+
print(" gitarsenal --gpu A10G --repo https://github.com/username/repo.git")
|
|
873
874
|
print()
|
|
874
875
|
print(" # Development mode (skip authentication):")
|
|
875
|
-
print(" gitarsenal --skip-auth --gpu A10G --repo
|
|
876
|
+
print(" gitarsenal --skip-auth --gpu A10G --repo https://github.com/username/repo.git")
|
|
876
877
|
print()
|
|
877
878
|
print(" # Manual setup (advanced users):")
|
|
878
879
|
print(" gitarsenal --gpu A10G --setup-commands \"pip install torch\" \"python train.py\"")
|