gitarsenal-cli 1.9.65 → 1.9.67
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/bin/gitarsenal.js +24 -0
- package/kill_claude/.claude/settings.local.json +9 -0
- package/kill_claude/README.md +265 -0
- package/kill_claude/__pycache__/bash_output_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/bash_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/claude_code_agent.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/edit_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/exit_plan_mode_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/glob_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/grep_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/kill_bash_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/ls_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/multiedit_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/notebook_edit_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/read_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/task_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/todo_write_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/web_fetch_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/web_search_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/write_tool.cpython-313.pyc +0 -0
- package/kill_claude/claude_code_agent.py +848 -0
- package/kill_claude/prompts/claude-code-agent-prompts.md +65 -0
- package/kill_claude/prompts/claude-code-environment-context.md +100 -0
- package/kill_claude/prompts/claude-code-git-workflows.md +151 -0
- package/kill_claude/prompts/claude-code-hook-system.md +94 -0
- package/kill_claude/prompts/claude-code-response-formatting.md +79 -0
- package/kill_claude/prompts/claude-code-security-constraints.md +87 -0
- package/kill_claude/prompts/claude-code-system-prompt.md +136 -0
- package/kill_claude/prompts/claude-code-system-reminders.md +50 -0
- package/kill_claude/prompts/claude-code-task-workflows.md +114 -0
- package/kill_claude/prompts/claude-code-thinking-mode-prompts.md +39 -0
- package/kill_claude/prompts/claude-code-tool-prompts.md +339 -0
- package/kill_claude/prompts/claude-code-tool-usage-policies.md +87 -0
- package/kill_claude/requirements.txt +1 -0
- package/kill_claude/tools/__init__.py +1 -0
- 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/kill_claude/tools/bash_output_tool.py +47 -0
- package/kill_claude/tools/bash_tool.py +79 -0
- package/kill_claude/tools/edit_tool.py +60 -0
- package/kill_claude/tools/exit_plan_mode_tool.py +42 -0
- package/kill_claude/tools/glob_tool.py +46 -0
- package/kill_claude/tools/grep_tool.py +105 -0
- package/kill_claude/tools/kill_bash_tool.py +38 -0
- package/kill_claude/tools/ls_tool.py +44 -0
- package/kill_claude/tools/multiedit_tool.py +111 -0
- package/kill_claude/tools/notebook_edit_tool.py +64 -0
- package/kill_claude/tools/read_tool.py +61 -0
- package/kill_claude/tools/task_tool.py +67 -0
- package/kill_claude/tools/todo_write_tool.py +114 -0
- package/kill_claude/tools/web_fetch_tool.py +55 -0
- package/kill_claude/tools/web_search_tool.py +58 -0
- package/kill_claude/tools/write_tool.py +46 -0
- package/lib/sandbox.js +3 -0
- package/package.json +1 -1
- package/python/test_modalSandboxScript.py +144 -295
- package/python/gitarsenal.py +0 -488
|
@@ -162,7 +162,10 @@ if token_id is None or token_secret is None:
|
|
|
162
162
|
# Explicitly set the environment variables again to be sure
|
|
163
163
|
os.environ["MODAL_TOKEN_ID"] = token_id
|
|
164
164
|
os.environ["MODAL_TOKEN_SECRET"] = token_secret
|
|
165
|
-
|
|
165
|
+
if openai_api_key:
|
|
166
|
+
os.environ["OPENAI_API_KEY"] = openai_api_key
|
|
167
|
+
if anthropic_api_key:
|
|
168
|
+
os.environ["ANTHROPIC_API_KEY"] = anthropic_api_key
|
|
166
169
|
# Also set the old environment variable for backward compatibility
|
|
167
170
|
os.environ["MODAL_TOKEN"] = token_id
|
|
168
171
|
|
|
@@ -198,7 +201,11 @@ def get_stored_credentials():
|
|
|
198
201
|
# Now modify the create_modal_ssh_container function to use the PersistentShell
|
|
199
202
|
def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_commands=None,
|
|
200
203
|
volume_name=None, timeout_minutes=60, ssh_password=None, interactive=False, gpu_count=1):
|
|
201
|
-
"""Create a Modal SSH container with GPU support and
|
|
204
|
+
"""Create a Modal SSH container with GPU support and intelligent repository setup.
|
|
205
|
+
|
|
206
|
+
When repo_url is provided, uses Claude Code Agent for intelligent repository setup.
|
|
207
|
+
The setup_commands parameter is maintained for backwards compatibility but ignored when using Claude Code Agent.
|
|
208
|
+
"""
|
|
202
209
|
|
|
203
210
|
# Use interactive mode if specified
|
|
204
211
|
if interactive:
|
|
@@ -346,6 +353,8 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
346
353
|
|
|
347
354
|
# Get the current directory path for mounting local Python sources
|
|
348
355
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
356
|
+
# Get the gitarsenal-cli root directory for kill_claude files
|
|
357
|
+
gitarsenal_root = os.path.dirname(current_dir)
|
|
349
358
|
# print(f"🔍 Current directory for mounting: {current_dir}")
|
|
350
359
|
|
|
351
360
|
# Use a more stable CUDA base image and avoid problematic packages
|
|
@@ -378,12 +387,30 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
378
387
|
|
|
379
388
|
# Set up a nice bash prompt
|
|
380
389
|
"echo 'export PS1=\"\\[\\e[1;32m\\]modal:\\[\\e[1;34m\\]\\w\\[\\e[0m\\]$ \"' >> /root/.bashrc",
|
|
390
|
+
|
|
391
|
+
# Create directories for Claude Code Agent
|
|
392
|
+
"mkdir -p /python/kill_claude/tools /python/kill_claude/prompts",
|
|
381
393
|
)
|
|
394
|
+
# Mount all local files (must be done after run_commands)
|
|
382
395
|
.add_local_file(os.path.join(current_dir, "shell.py"), "/python/shell.py") # Mount shell.py
|
|
383
396
|
.add_local_file(os.path.join(current_dir, "command_manager.py"), "/python/command_manager.py") # Mount command_manager.py
|
|
384
397
|
.add_local_file(os.path.join(current_dir, "fetch_modal_tokens.py"), "/python/fetch_modal_tokens.py") # Mount fetch_modal_token.py
|
|
385
398
|
.add_local_file(os.path.join(current_dir, "llm_debugging.py"), "/python/llm_debugging.py") # Mount llm_debugging.py
|
|
386
399
|
.add_local_file(os.path.join(current_dir, "credentials_manager.py"), "/python/credentials_manager.py") # Mount credentials_manager.py
|
|
400
|
+
# Mount Claude Code Agent and its dependencies
|
|
401
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "claude_code_agent.py"), "/python/kill_claude/claude_code_agent.py")
|
|
402
|
+
# Mount the tools and prompts directories manually with key files
|
|
403
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "tools", "__init__.py"), "/python/kill_claude/tools/__init__.py")
|
|
404
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "tools", "bash_tool.py"), "/python/kill_claude/tools/bash_tool.py")
|
|
405
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "tools", "read_tool.py"), "/python/kill_claude/tools/read_tool.py")
|
|
406
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "tools", "write_tool.py"), "/python/kill_claude/tools/write_tool.py")
|
|
407
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "tools", "ls_tool.py"), "/python/kill_claude/tools/ls_tool.py")
|
|
408
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "tools", "edit_tool.py"), "/python/kill_claude/tools/edit_tool.py")
|
|
409
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "tools", "grep_tool.py"), "/python/kill_claude/tools/grep_tool.py")
|
|
410
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "tools", "glob_tool.py"), "/python/kill_claude/tools/glob_tool.py")
|
|
411
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "tools", "todo_write_tool.py"), "/python/kill_claude/tools/todo_write_tool.py")
|
|
412
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "prompts", "claude-code-system-prompt.md"), "/python/kill_claude/prompts/claude-code-system-prompt.md")
|
|
413
|
+
.add_local_file(os.path.join(gitarsenal_root, "kill_claude", "prompts", "claude-code-agent-prompts.md"), "/python/kill_claude/prompts/claude-code-agent-prompts.md")
|
|
387
414
|
|
|
388
415
|
)
|
|
389
416
|
print("✅ SSH image built successfully")
|
|
@@ -409,8 +436,8 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
409
436
|
serialized=True,
|
|
410
437
|
volumes=volumes_config if volumes_config else None,
|
|
411
438
|
)
|
|
412
|
-
def ssh_container_function(ssh_password=None, repo_url=None, repo_name=None, setup_commands=None, openai_api_key=None, stored_credentials=None):
|
|
413
|
-
"""Start SSH container with password authentication and
|
|
439
|
+
def ssh_container_function(ssh_password=None, repo_url=None, repo_name=None, setup_commands=None, openai_api_key=None, anthropic_api_key=None, stored_credentials=None):
|
|
440
|
+
"""Start SSH container with password authentication and intelligent repository setup using Claude Code Agent."""
|
|
414
441
|
import subprocess
|
|
415
442
|
import time
|
|
416
443
|
import os
|
|
@@ -487,273 +514,96 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
487
514
|
# Start SSH service
|
|
488
515
|
subprocess.run(["service", "ssh", "start"], check=True)
|
|
489
516
|
|
|
490
|
-
#
|
|
491
|
-
if
|
|
492
|
-
print(
|
|
493
|
-
api_key = os.environ.get("OPENAI_API_KEY")
|
|
494
|
-
processed_commands = preprocess_commands_with_llm(setup_commands, stored_credentials, api_key)
|
|
495
|
-
print(f"⚙️ Running {len(processed_commands)} preprocessed setup commands with dynamic command list...")
|
|
496
|
-
|
|
497
|
-
# Display all setup commands that will be executed
|
|
498
|
-
print("\n" + "="*80)
|
|
499
|
-
print("📋 SETUP COMMANDS TO BE EXECUTED")
|
|
500
|
-
print("="*80)
|
|
501
|
-
for i, cmd in enumerate(processed_commands, 1):
|
|
502
|
-
print(f" {i:2d}. {cmd}")
|
|
503
|
-
print("="*80 + "\n")
|
|
517
|
+
# Use Claude Code Agent for intelligent repository setup
|
|
518
|
+
if repo_url:
|
|
519
|
+
print("🤖 Using Claude Code Agent for intelligent repository setup...")
|
|
504
520
|
|
|
505
|
-
#
|
|
506
|
-
|
|
521
|
+
# Set up environment variables for the Claude Code Agent
|
|
522
|
+
if openai_api_key:
|
|
523
|
+
os.environ['OPENAI_API_KEY'] = openai_api_key
|
|
524
|
+
if anthropic_api_key:
|
|
525
|
+
os.environ['ANTHROPIC_API_KEY'] = anthropic_api_key
|
|
507
526
|
|
|
508
|
-
#
|
|
509
|
-
|
|
527
|
+
# Set up Anthropic API key from stored credentials
|
|
528
|
+
anthropic_api_key = None
|
|
529
|
+
if stored_credentials:
|
|
530
|
+
# Look for Anthropic API key in various possible names
|
|
531
|
+
for key_name in ['ANTHROPIC_API_KEY', 'anthropic_api_key', 'anthropic-api-key']:
|
|
532
|
+
if key_name in stored_credentials:
|
|
533
|
+
anthropic_api_key = stored_credentials[key_name]
|
|
534
|
+
os.environ['ANTHROPIC_API_KEY'] = anthropic_api_key
|
|
535
|
+
print(f"✅ Set Anthropic API key from stored credentials")
|
|
536
|
+
break
|
|
537
|
+
|
|
538
|
+
if not anthropic_api_key:
|
|
539
|
+
print("⚠️ No Anthropic API key found in stored credentials")
|
|
540
|
+
print("💡 Claude Code Agent will require an Anthropic API key for operation")
|
|
510
541
|
|
|
511
542
|
try:
|
|
512
|
-
|
|
513
|
-
shell.start()
|
|
543
|
+
print("🔧 Running Claude Code Agent for repository setup...")
|
|
514
544
|
|
|
515
|
-
|
|
516
|
-
|
|
545
|
+
print("\n" + "="*80)
|
|
546
|
+
print("🤖 CLAUDE CODE AGENT REPOSITORY SETUP")
|
|
547
|
+
print("="*80)
|
|
548
|
+
print(f"Repository: {repo_url}")
|
|
549
|
+
print(f"Working Directory: /root")
|
|
550
|
+
if stored_credentials:
|
|
551
|
+
print(f"Available Credentials: {len(stored_credentials)} items")
|
|
552
|
+
print("="*80 + "\n")
|
|
517
553
|
|
|
518
|
-
#
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
break
|
|
525
|
-
|
|
526
|
-
# Box everything printed in this iteration into one block
|
|
527
|
-
with _boxed_capture():
|
|
528
|
-
# Print status before executing
|
|
529
|
-
cmd_manager.print_status()
|
|
530
|
-
|
|
531
|
-
# Periodically analyze and update the command list
|
|
532
|
-
if commands_since_analysis >= 3 and cmd_type == 'main':
|
|
533
|
-
print("\n🔍 Periodic command list analysis...")
|
|
534
|
-
api_key = os.environ.get("OPENAI_API_KEY")
|
|
535
|
-
cmd_manager.update_command_list_with_llm(api_key)
|
|
536
|
-
commands_since_analysis = 0
|
|
537
|
-
|
|
538
|
-
# Execute the command
|
|
539
|
-
if cmd_type == 'main':
|
|
540
|
-
cmd_text = next_cmd['command']
|
|
541
|
-
cmd_index = next_cmd['index']
|
|
542
|
-
success, stdout, stderr, execution_time = _execute_with_box(
|
|
543
|
-
shell, cmd_text, timeout=100,
|
|
544
|
-
title_line=f"📋 Executing main command {cmd_index + 1}/{cmd_manager.total_commands}: {cmd_text}",
|
|
545
|
-
box=False
|
|
546
|
-
)
|
|
547
|
-
|
|
548
|
-
# Check if the command was aborted due to waiting for input and an alternative was suggested
|
|
549
|
-
if not success and "Command aborted - requires user input" in stderr and shell.suggested_alternative:
|
|
550
|
-
alternative_cmd = shell.suggested_alternative
|
|
551
|
-
print(f"🔄 Command aborted due to input requirement. Adding suggested alternative: {alternative_cmd}")
|
|
552
|
-
|
|
553
|
-
# Add the alternative command with high priority
|
|
554
|
-
cmd_manager.add_command_dynamically(alternative_cmd, priority='high')
|
|
555
|
-
|
|
556
|
-
# Clear the suggested alternative
|
|
557
|
-
shell.suggested_alternative = None
|
|
558
|
-
# Check if the command should be removed as suggested by LLM
|
|
559
|
-
elif not success and stderr.startswith("Command removed -"):
|
|
560
|
-
reason = stderr.replace("Command removed -", "").strip()
|
|
561
|
-
print(f"🚫 Removed command as suggested by LLM: {reason}")
|
|
562
|
-
# We don't need to do anything else, just mark it as executed and move on
|
|
563
|
-
|
|
564
|
-
# Mark command as executed
|
|
565
|
-
cmd_manager.mark_command_executed(
|
|
566
|
-
cmd_index, 'main', success, stdout, stderr, execution_time
|
|
567
|
-
)
|
|
568
|
-
|
|
569
|
-
# Increment counter for periodic analysis
|
|
570
|
-
commands_since_analysis += 1
|
|
571
|
-
|
|
572
|
-
if not success:
|
|
573
|
-
print(f"⚠️ Command failed, attempting LLM debugging...")
|
|
574
|
-
|
|
575
|
-
# Call LLM for debugging
|
|
576
|
-
try:
|
|
577
|
-
current_dir = shell.get_cwd()
|
|
578
|
-
|
|
579
|
-
# Use unified LLM debugging function
|
|
580
|
-
fix_command = call_llm_for_debug(cmd_text, stderr, current_dir=current_dir, sandbox=shell)
|
|
581
|
-
|
|
582
|
-
if fix_command:
|
|
583
|
-
# Get the current debug model to show the correct provider name
|
|
584
|
-
from llm_debugging import get_current_debug_model
|
|
585
|
-
current_model = get_current_debug_model()
|
|
586
|
-
print(f"🔧 {current_model.capitalize()} suggested fix command: {fix_command}")
|
|
587
|
-
|
|
588
|
-
# Add the fix to the command list manager
|
|
589
|
-
fix_index = cmd_manager.add_suggested_fix(cmd_text, fix_command, "LLM suggested fix")
|
|
590
|
-
|
|
591
|
-
# Execute the fix command
|
|
592
|
-
fix_success, fix_stdout, fix_stderr, fix_execution_time = _execute_with_box(
|
|
593
|
-
shell, fix_command, timeout=300,
|
|
594
|
-
title_line=f"🔧 Executing suggested fix: {fix_command}",
|
|
595
|
-
box=False
|
|
596
|
-
)
|
|
597
|
-
|
|
598
|
-
# Mark fix command as executed
|
|
599
|
-
cmd_manager.mark_command_executed(
|
|
600
|
-
fix_index, 'fix', fix_success, fix_stdout, fix_stderr, fix_execution_time
|
|
601
|
-
)
|
|
602
|
-
|
|
603
|
-
if fix_success:
|
|
604
|
-
print(f"✅ Fix command succeeded")
|
|
605
|
-
|
|
606
|
-
# Check if we should skip the original command
|
|
607
|
-
api_key = os.environ.get("OPENAI_API_KEY")
|
|
608
|
-
should_skip, skip_reason = cmd_manager.should_skip_original_command(
|
|
609
|
-
cmd_text, fix_command, fix_stdout, fix_stderr, api_key
|
|
610
|
-
)
|
|
611
|
-
|
|
612
|
-
if should_skip:
|
|
613
|
-
print(f"🔄 Skipping original command: {skip_reason}")
|
|
614
|
-
|
|
615
|
-
# Mark the original command as successful without running it
|
|
616
|
-
cmd_manager.mark_command_executed(
|
|
617
|
-
cmd_index, 'main', True,
|
|
618
|
-
f"Command skipped after successful fix: {skip_reason}",
|
|
619
|
-
"", execution_time
|
|
620
|
-
)
|
|
621
|
-
|
|
622
|
-
print(f"✅ Original command marked as successful (skipped)")
|
|
623
|
-
|
|
624
|
-
# After a successful fix and skipping the original command,
|
|
625
|
-
# analyze and update the entire command list
|
|
626
|
-
print("\n🔍 Analyzing and updating remaining commands based on fix results...")
|
|
627
|
-
cmd_manager.update_command_list_with_llm(api_key)
|
|
628
|
-
else:
|
|
629
|
-
# Retry the original command
|
|
630
|
-
print(f"🔄 Retrying original command: {cmd_text}")
|
|
631
|
-
retry_success, retry_stdout, retry_stderr, retry_execution_time = _execute_with_box(
|
|
632
|
-
shell, cmd_text, timeout=300,
|
|
633
|
-
title_line=f"🔄 Retrying command: {cmd_text}",
|
|
634
|
-
box=False
|
|
635
|
-
)
|
|
636
|
-
|
|
637
|
-
# Update the original command status
|
|
638
|
-
cmd_manager.mark_command_executed(
|
|
639
|
-
cmd_index, 'main', retry_success, retry_stdout, retry_stderr, retry_execution_time
|
|
640
|
-
)
|
|
641
|
-
|
|
642
|
-
if retry_success:
|
|
643
|
-
print(f"✅ Original command succeeded after fix!")
|
|
644
|
-
|
|
645
|
-
# After a successful fix and successful retry,
|
|
646
|
-
# analyze and update the entire command list
|
|
647
|
-
print("\n🔍 Analyzing and updating remaining commands based on fix results...")
|
|
648
|
-
cmd_manager.update_command_list_with_llm(api_key)
|
|
649
|
-
else:
|
|
650
|
-
print(f"⚠️ Original command still failed after fix, continuing...")
|
|
651
|
-
else:
|
|
652
|
-
print(f"❌ Fix command failed: {fix_stderr}")
|
|
653
|
-
print(f"⚠️ First fix attempt failed; continuing without web search...")
|
|
654
|
-
|
|
655
|
-
# No web-search retry; continue
|
|
656
|
-
print(f"⚠️ Continuing with remaining commands...")
|
|
657
|
-
else:
|
|
658
|
-
# Get the current debug model to show the correct provider name
|
|
659
|
-
from llm_debugging import get_current_debug_model
|
|
660
|
-
current_model = get_current_debug_model()
|
|
661
|
-
print(f"❌ No fix suggested by {current_model.capitalize()}")
|
|
662
|
-
print(f"⚠️ Continuing with remaining commands...")
|
|
663
|
-
|
|
664
|
-
except Exception as debug_e:
|
|
665
|
-
print(f"❌ LLM debugging failed: {debug_e}")
|
|
666
|
-
print(f"⚠️ Continuing with remaining commands...")
|
|
667
|
-
|
|
668
|
-
elif cmd_type == 'fix':
|
|
669
|
-
cmd_text = next_cmd['fix_command']
|
|
670
|
-
cmd_index = next_cmd['index']
|
|
671
|
-
success, stdout, stderr, execution_time = _execute_with_box(
|
|
672
|
-
shell, cmd_text, timeout=300,
|
|
673
|
-
title_line=f"🔧 Executing fix command {cmd_index + 1}: {cmd_text}",
|
|
674
|
-
box=False
|
|
675
|
-
)
|
|
676
|
-
|
|
677
|
-
# Check if the fix command was aborted due to waiting for input and an alternative was suggested
|
|
678
|
-
if not success and "Command aborted - requires user input" in stderr and shell.suggested_alternative:
|
|
679
|
-
alternative_cmd = shell.suggested_alternative
|
|
680
|
-
print(f"🔄 Fix command aborted due to input requirement. Adding suggested alternative: {alternative_cmd}")
|
|
681
|
-
|
|
682
|
-
# Add the alternative command with high priority
|
|
683
|
-
cmd_manager.add_command_dynamically(alternative_cmd, priority='high')
|
|
684
|
-
|
|
685
|
-
# Clear the suggested alternative
|
|
686
|
-
shell.suggested_alternative = None
|
|
687
|
-
# Check if the fix command should be removed as suggested by LLM
|
|
688
|
-
elif not success and stderr.startswith("Command removed -"):
|
|
689
|
-
reason = stderr.replace("Command removed -", "").strip()
|
|
690
|
-
print(f"🚫 Removed fix command as suggested by LLM: {reason}")
|
|
691
|
-
# We don't need to do anything else, just mark it as executed and move on
|
|
692
|
-
|
|
693
|
-
# Mark fix command as executed
|
|
694
|
-
cmd_manager.mark_command_executed(
|
|
695
|
-
cmd_index, 'fix', success, stdout, stderr, execution_time
|
|
696
|
-
)
|
|
554
|
+
# Call Claude Code Agent directly as subprocess with real-time output
|
|
555
|
+
claude_prompt = f"clone and setup {repo_url}"
|
|
556
|
+
print(f"🚀 Executing: python /python/kill_claude/claude_code_agent.py \"{claude_prompt}\"")
|
|
557
|
+
print("\n" + "="*60)
|
|
558
|
+
print("🎉 CLAUDE CODE AGENT OUTPUT (LIVE)")
|
|
559
|
+
print("="*60)
|
|
697
560
|
|
|
698
|
-
#
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
561
|
+
# Use Popen for real-time output streaming
|
|
562
|
+
process = subprocess.Popen(
|
|
563
|
+
["python", "/python/kill_claude/claude_code_agent.py", claude_prompt],
|
|
564
|
+
cwd="/root",
|
|
565
|
+
stdout=subprocess.PIPE,
|
|
566
|
+
stderr=subprocess.STDOUT, # Merge stderr into stdout
|
|
567
|
+
text=True,
|
|
568
|
+
bufsize=1, # Line buffered
|
|
569
|
+
universal_newlines=True
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
# Stream output in real-time
|
|
573
|
+
try:
|
|
574
|
+
while True:
|
|
575
|
+
output = process.stdout.readline()
|
|
576
|
+
if output == '' and process.poll() is not None:
|
|
577
|
+
break
|
|
578
|
+
if output:
|
|
579
|
+
print(output.rstrip())
|
|
703
580
|
|
|
704
|
-
#
|
|
705
|
-
|
|
706
|
-
current_model = get_current_debug_model()
|
|
707
|
-
api_key = get_api_key(current_model)
|
|
581
|
+
# Wait for process to complete and get return code
|
|
582
|
+
return_code = process.wait(timeout=600) # 10 minute timeout
|
|
708
583
|
|
|
709
|
-
|
|
710
|
-
|
|
584
|
+
print("\n" + "="*60)
|
|
585
|
+
if return_code == 0:
|
|
586
|
+
print("✅ Claude Code Agent completed successfully!")
|
|
587
|
+
else:
|
|
588
|
+
print(f"⚠️ Claude Code Agent exited with code: {return_code}")
|
|
589
|
+
print("="*60)
|
|
711
590
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
shell, cmd_text, timeout=300,
|
|
721
|
-
title_line=f"🔧 Executing additional fix: {cmd_text}",
|
|
722
|
-
box=False
|
|
723
|
-
)
|
|
724
|
-
|
|
725
|
-
# Check if the fix command was aborted due to waiting for input and an alternative was suggested
|
|
726
|
-
if not success and "Command aborted - requires user input" in stderr and shell.suggested_alternative:
|
|
727
|
-
alternative_cmd = shell.suggested_alternative
|
|
728
|
-
print(f"🔄 Additional fix command aborted due to input requirement. Adding suggested alternative: {alternative_cmd}")
|
|
729
|
-
|
|
730
|
-
# Add the alternative command with high priority
|
|
731
|
-
cmd_manager.add_command_dynamically(alternative_cmd, priority='high')
|
|
732
|
-
|
|
733
|
-
# Clear the suggested alternative
|
|
734
|
-
shell.suggested_alternative = None
|
|
735
|
-
# Check if the additional fix command should be removed as suggested by LLM
|
|
736
|
-
elif not success and stderr.startswith("Command removed -"):
|
|
737
|
-
reason = stderr.replace("Command removed -", "").strip()
|
|
738
|
-
print(f"🚫 Removed additional fix command as suggested by LLM: {reason}")
|
|
739
|
-
# We don't need to do anything else, just mark it as executed and move on
|
|
740
|
-
|
|
741
|
-
# Mark fix command as executed
|
|
742
|
-
cmd_manager.mark_command_executed(
|
|
743
|
-
fix_index, 'fix', success, stdout, stderr, execution_time
|
|
744
|
-
)
|
|
745
|
-
|
|
746
|
-
# Print final status
|
|
747
|
-
print("\n" + "="*60)
|
|
748
|
-
print("🎉 SETUP COMMANDS EXECUTION COMPLETED")
|
|
749
|
-
print("="*60)
|
|
750
|
-
cmd_manager.print_status()
|
|
591
|
+
except subprocess.TimeoutExpired:
|
|
592
|
+
print("\n⚠️ Claude Code Agent timed out after 10 minutes")
|
|
593
|
+
process.kill()
|
|
594
|
+
process.wait()
|
|
595
|
+
except Exception as stream_error:
|
|
596
|
+
print(f"\n⚠️ Error streaming output: {stream_error}")
|
|
597
|
+
process.kill()
|
|
598
|
+
process.wait()
|
|
751
599
|
|
|
752
600
|
except Exception as e:
|
|
753
|
-
print(f"❌ Error during setup
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
601
|
+
print(f"❌ Error during repository setup: {e}")
|
|
602
|
+
print("⚠️ Proceeding without setup...")
|
|
603
|
+
import traceback
|
|
604
|
+
traceback.print_exc()
|
|
605
|
+
else:
|
|
606
|
+
print("⚠️ No repository URL provided, skipping setup")
|
|
757
607
|
|
|
758
608
|
# Create SSH tunnel
|
|
759
609
|
with modal.forward(22, unencrypted=True) as tunnel:
|
|
@@ -791,15 +641,17 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
791
641
|
with app.run():
|
|
792
642
|
# Get the API key from environment
|
|
793
643
|
api_key = os.environ.get("OPENAI_API_KEY")
|
|
644
|
+
# print(f"🔐 API key: {api_key}")
|
|
794
645
|
|
|
795
646
|
# Get stored credentials from local file
|
|
796
647
|
stored_credentials = get_stored_credentials()
|
|
648
|
+
# print(f"🔐 Stored credentials: {stored_credentials}")
|
|
797
649
|
if stored_credentials:
|
|
798
650
|
print(f"🔐 Found {len(stored_credentials)} stored credentials to send to container")
|
|
799
651
|
else:
|
|
800
652
|
print("⚠️ No stored credentials found")
|
|
801
653
|
|
|
802
|
-
ssh_container_function.remote(ssh_password, repo_url, repo_name, setup_commands, api_key, stored_credentials)
|
|
654
|
+
ssh_container_function.remote(ssh_password, repo_url, repo_name, setup_commands, api_key, anthropic_api_key, stored_credentials)
|
|
803
655
|
|
|
804
656
|
# Clean up Modal token after container is successfully created
|
|
805
657
|
cleanup_modal_token()
|
|
@@ -1256,17 +1108,12 @@ def show_usage_examples():
|
|
|
1256
1108
|
print("│ gitarsenal --auth # Interactive auth management │")
|
|
1257
1109
|
print("└────────────────────────────────────────────────────────────────────────────────────┘\n")
|
|
1258
1110
|
|
|
1259
|
-
print("Basic Container Creation")
|
|
1111
|
+
print("Basic Container Creation with Claude Code Agent")
|
|
1260
1112
|
print("┌────────────────────────────────────────────────────────────────────────┐")
|
|
1261
1113
|
print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git │")
|
|
1114
|
+
print("│ # Claude Code Agent will intelligently clone and setup the repository │")
|
|
1262
1115
|
print("└────────────────────────────────────────────────────────────────────────┘\n")
|
|
1263
1116
|
|
|
1264
|
-
print("With Setup Commands")
|
|
1265
|
-
print("┌────────────────────────────────────────────────────────────────────────────────────────────────────┐")
|
|
1266
|
-
print("│ gitarsenal --gpu A100 --repo-url https://github.com/username/repo.git \\ │")
|
|
1267
|
-
print("│ --setup-commands \"pip install -r requirements.txt\" \"python setup.py install\" │")
|
|
1268
|
-
print("└────────────────────────────────────────────────────────────────────────────────────────────────────┘\n")
|
|
1269
|
-
|
|
1270
1117
|
print("With Persistent Storage")
|
|
1271
1118
|
print("┌────────────────────────────────────────────────────────────────────────────────────┐")
|
|
1272
1119
|
print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git \\ │")
|
|
@@ -1279,21 +1126,16 @@ def show_usage_examples():
|
|
|
1279
1126
|
print("│ --repo-url https://github.com/username/repo.git │")
|
|
1280
1127
|
print("└────────────────────────────────────────────────────────────────────────────────────┘\n")
|
|
1281
1128
|
|
|
1282
|
-
print("
|
|
1129
|
+
print("Intelligent Repository Setup (default)")
|
|
1283
1130
|
print("┌────────────────────────────────────────────────────────────────────────────────────┐")
|
|
1284
1131
|
print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git │")
|
|
1132
|
+
print("│ # Claude Code Agent analyzes repo and sets up environment automatically │")
|
|
1285
1133
|
print("└────────────────────────────────────────────────────────────────────────────────────┘\n")
|
|
1286
1134
|
|
|
1287
|
-
print("
|
|
1288
|
-
print("┌────────────────────────────────────────────────────────────────────────────────────┐")
|
|
1289
|
-
print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git \\ │")
|
|
1290
|
-
print("│ --no-gitingest │")
|
|
1291
|
-
print("└────────────────────────────────────────────────────────────────────────────────────┘\n")
|
|
1292
|
-
|
|
1293
|
-
print("With Original API")
|
|
1135
|
+
print("With Manual Setup Commands (Advanced)")
|
|
1294
1136
|
print("┌────────────────────────────────────────────────────────────────────────────────────┐")
|
|
1295
|
-
print("│ gitarsenal --gpu A10G --
|
|
1296
|
-
print("│
|
|
1137
|
+
print("│ gitarsenal --gpu A10G --setup-commands \"pip install torch\" \"python train.py\" │")
|
|
1138
|
+
print("│ # Only use when not providing --repo-url (bypasses Claude Code Agent) │")
|
|
1297
1139
|
print("└────────────────────────────────────────────────────────────────────────────────────┘\n")
|
|
1298
1140
|
|
|
1299
1141
|
print("Development Mode (Skip Authentication)")
|
|
@@ -1314,15 +1156,20 @@ def show_usage_examples():
|
|
|
1314
1156
|
print(" • With --gpu: Uses specified GPU without prompting")
|
|
1315
1157
|
print(" • Without --gpu: Shows interactive GPU selection menu")
|
|
1316
1158
|
print()
|
|
1159
|
+
print("Repository Setup Behavior:")
|
|
1160
|
+
print(" • With --repo-url: Claude Code Agent intelligently clones and sets up repository")
|
|
1161
|
+
print(" • Without --repo-url: Manual container setup (no automatic repository setup)")
|
|
1162
|
+
print(" • Legacy --setup-commands: Only used when --repo-url not provided")
|
|
1163
|
+
print()
|
|
1317
1164
|
print("Examples:")
|
|
1318
|
-
print(" #
|
|
1165
|
+
print(" # Intelligent repository setup (recommended):")
|
|
1319
1166
|
print(" gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git")
|
|
1320
1167
|
print()
|
|
1321
|
-
print(" # Subsequent runs (automatic login):")
|
|
1322
|
-
print(" gitarsenal --repo-url https://github.com/username/repo.git")
|
|
1323
|
-
print()
|
|
1324
1168
|
print(" # Development mode (skip authentication):")
|
|
1325
|
-
print(" gitarsenal --skip-auth --repo-url https://github.com/username/repo.git")
|
|
1169
|
+
print(" gitarsenal --skip-auth --gpu A10G --repo-url https://github.com/username/repo.git")
|
|
1170
|
+
print()
|
|
1171
|
+
print(" # Manual setup (advanced users):")
|
|
1172
|
+
print(" gitarsenal --gpu A10G --setup-commands \"pip install torch\" \"python train.py\"")
|
|
1326
1173
|
|
|
1327
1174
|
def make_api_request_with_retry(url, payload, max_retries=2, timeout=180):
|
|
1328
1175
|
"""Make an API request with retry mechanism."""
|
|
@@ -1984,9 +1831,9 @@ if __name__ == "__main__":
|
|
|
1984
1831
|
parser.add_argument('--volume-name', type=str, help='Name of the Modal volume for persistent storage')
|
|
1985
1832
|
parser.add_argument('--timeout', type=int, default=60, help='Container timeout in minutes (default: 60)')
|
|
1986
1833
|
parser.add_argument('--ssh-password', type=str, help='SSH password (random if not provided)')
|
|
1987
|
-
parser.add_argument('--use-api', action='store_true', help='Fetch setup commands from original API')
|
|
1988
|
-
parser.add_argument('--use-gitingest', action='store_true', default=True, help='Use gitingest approach
|
|
1989
|
-
parser.add_argument('--no-gitingest', action='store_true', help='Disable gitingest approach
|
|
1834
|
+
parser.add_argument('--use-api', action='store_true', help='[DEPRECATED] Fetch setup commands from original API (use --repo-url for Claude Code Agent instead)')
|
|
1835
|
+
parser.add_argument('--use-gitingest', action='store_true', default=True, help='[DEPRECATED] Use gitingest approach (Claude Code Agent is now used when --repo-url is provided)')
|
|
1836
|
+
parser.add_argument('--no-gitingest', action='store_true', help='[DEPRECATED] Disable gitingest approach (no longer needed with Claude Code Agent)')
|
|
1990
1837
|
parser.add_argument('--show-examples', action='store_true', help='Show usage examples')
|
|
1991
1838
|
parser.add_argument('--list-gpus', action='store_true', help='List available GPU types with their specifications')
|
|
1992
1839
|
parser.add_argument('--interactive', action='store_true', help='Run in interactive mode with prompts')
|
|
@@ -2118,12 +1965,14 @@ if __name__ == "__main__":
|
|
|
2118
1965
|
else:
|
|
2119
1966
|
print(f"GPU Type: {gpu_type}")
|
|
2120
1967
|
print(f"Volume: {args.volume_name or 'None'}")
|
|
2121
|
-
if args.
|
|
1968
|
+
if args.repo_url:
|
|
1969
|
+
print("Repository Setup: Claude Code Agent (intelligent)")
|
|
1970
|
+
elif args.use_api:
|
|
2122
1971
|
print("Setup Commands: Auto-detect from repository")
|
|
2123
1972
|
elif args.setup_commands:
|
|
2124
1973
|
print(f"Setup Commands: {len(args.setup_commands)} custom commands")
|
|
2125
1974
|
else:
|
|
2126
|
-
print("Setup Commands:
|
|
1975
|
+
print("Setup Commands: None")
|
|
2127
1976
|
|
|
2128
1977
|
# Confirm settings (skip if --yes specified)
|
|
2129
1978
|
if not getattr(args, 'yes', False):
|
|
@@ -2203,15 +2052,15 @@ if __name__ == "__main__":
|
|
|
2203
2052
|
args.use_gitingest = use_gitingest
|
|
2204
2053
|
|
|
2205
2054
|
try:
|
|
2206
|
-
#
|
|
2055
|
+
# Setup commands are no longer used when repo_url is provided (Claude Code Agent handles setup)
|
|
2207
2056
|
setup_commands = args.setup_commands or []
|
|
2208
2057
|
|
|
2209
|
-
#
|
|
2210
|
-
if args.repo_url
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
print(
|
|
2058
|
+
# Repository setup approach
|
|
2059
|
+
if args.repo_url:
|
|
2060
|
+
print("🤖 Repository setup will be handled by Claude Code Agent in container")
|
|
2061
|
+
setup_commands = [] # Claude Code Agent will handle setup intelligently
|
|
2062
|
+
else:
|
|
2063
|
+
print("⚠️ No repository URL provided - setup commands may be needed manually")
|
|
2215
2064
|
|
|
2216
2065
|
|
|
2217
2066
|
# Parse setup commands from JSON if provided
|
|
@@ -2265,7 +2114,7 @@ if __name__ == "__main__":
|
|
|
2265
2114
|
try:
|
|
2266
2115
|
with open(args.setup_script, 'r') as f:
|
|
2267
2116
|
script_content = f.read().strip()
|
|
2268
|
-
# Convert script to individual
|
|
2117
|
+
# Convert script to individual commandsr
|
|
2269
2118
|
script_commands = [line.strip() for line in script_content.split('\n')
|
|
2270
2119
|
if line.strip() and not line.strip().startswith('#')]
|
|
2271
2120
|
setup_commands.extend(script_commands)
|