gitarsenal-cli 1.9.70 → 1.9.71

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 CHANGED
@@ -1 +1 @@
1
- {"created":"2025-08-15T05:04:01.624Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
1
+ {"created":"2025-08-15T05:31:11.835Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
@@ -372,7 +372,7 @@ The following {len(TOOL_SCHEMAS)} tools are loaded and available:
372
372
 
373
373
  # Execute tool calls
374
374
  if tool_calls:
375
- print(f"šŸ¤– Claude requested {len(tool_calls)} tool call(s)")
375
+ print(f"šŸ¤– Agent requested {len(tool_calls)} tool call(s)")
376
376
  tool_results = []
377
377
 
378
378
  for i, tool_call in enumerate(tool_calls, 1):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitarsenal-cli",
3
- "version": "1.9.70",
3
+ "version": "1.9.71",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -541,28 +541,89 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
541
541
  print("šŸŽ‰ AGENT OUTPUT (LIVE)")
542
542
  print("="*60)
543
543
 
544
- # Use Popen for real-time output streaming
544
+ # Use Popen for real-time output streaming with optimizations
545
+ import sys
546
+ import select
547
+ import fcntl
548
+ import os as os_module
549
+
545
550
  process = subprocess.Popen(
546
- ["python", "/python/kill_claude/claude_code_agent.py", claude_prompt],
551
+ ["python", "-u", "/python/kill_claude/claude_code_agent.py", claude_prompt], # -u for unbuffered output
547
552
  cwd="/root",
548
553
  stdout=subprocess.PIPE,
549
- stderr=subprocess.STDOUT, # Merge stderr into stdout
554
+ stderr=subprocess.PIPE, # Keep separate for better handling
550
555
  text=True,
551
- bufsize=1, # Line buffered
552
- universal_newlines=True
556
+ bufsize=0, # Unbuffered for fastest output
557
+ universal_newlines=True,
558
+ env=dict(os.environ, PYTHONUNBUFFERED='1') # Force unbuffered Python output
553
559
  )
554
560
 
555
- # Stream output in real-time
561
+ # Make stdout and stderr non-blocking for faster reading
562
+ def make_non_blocking(fd):
563
+ flags = fcntl.fcntl(fd, fcntl.F_GETFL)
564
+ fcntl.fcntl(fd, fcntl.F_SETFL, flags | os_module.O_NONBLOCK)
565
+
566
+ make_non_blocking(process.stdout)
567
+ make_non_blocking(process.stderr)
568
+
569
+ # Stream output in real-time with robust error handling
556
570
  try:
557
- while True:
558
- output = process.stdout.readline()
559
- if output == '' and process.poll() is not None:
560
- break
561
- if output:
562
- print(output.rstrip())
571
+ stdout_buffer = ""
572
+ stderr_buffer = ""
573
+
574
+ while process.poll() is None:
575
+ try:
576
+ # Use select for efficient I/O multiplexing with error handling
577
+ ready, _, _ = select.select([process.stdout, process.stderr], [], [], 0.1) # 100ms timeout
578
+
579
+ for stream in ready:
580
+ try:
581
+ if stream == process.stdout:
582
+ chunk = stream.read(1024) # Read in chunks for efficiency
583
+ if chunk is not None and chunk:
584
+ stdout_buffer += chunk
585
+ # Process complete lines immediately
586
+ while '\n' in stdout_buffer:
587
+ line, stdout_buffer = stdout_buffer.split('\n', 1)
588
+ print(line, flush=True) # Force immediate flush
589
+ elif stream == process.stderr:
590
+ chunk = stream.read(1024)
591
+ if chunk is not None and chunk:
592
+ stderr_buffer += chunk
593
+ # Process complete lines immediately
594
+ while '\n' in stderr_buffer:
595
+ line, stderr_buffer = stderr_buffer.split('\n', 1)
596
+ print(f"STDERR: {line}", flush=True)
597
+ except (BlockingIOError, OSError, ValueError):
598
+ # Handle various I/O errors gracefully
599
+ continue
600
+ except (select.error, OSError):
601
+ # If select fails, fall back to simple polling
602
+ time.sleep(0.1)
603
+ continue
604
+
605
+ # Process any remaining output after process ends
606
+ try:
607
+ # Read any remaining data from streams
608
+ remaining_stdout = process.stdout.read()
609
+ remaining_stderr = process.stderr.read()
610
+
611
+ if remaining_stdout:
612
+ stdout_buffer += remaining_stdout
613
+ if remaining_stderr:
614
+ stderr_buffer += remaining_stderr
615
+
616
+ # Output remaining buffered content
617
+ if stdout_buffer.strip():
618
+ print(stdout_buffer.strip(), flush=True)
619
+ if stderr_buffer.strip():
620
+ print(f"STDERR: {stderr_buffer.strip()}", flush=True)
621
+ except (OSError, ValueError):
622
+ # Handle cases where streams are already closed
623
+ pass
563
624
 
564
- # Wait for process to complete and get return code
565
- return_code = process.wait(timeout=600) # 10 minute timeout
625
+ # Get final return code
626
+ return_code = process.returncode
566
627
 
567
628
  print("\n" + "="*60)
568
629
  if return_code == 0:
@@ -576,9 +637,48 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
576
637
  process.kill()
577
638
  process.wait()
578
639
  except Exception as stream_error:
579
- print(f"\nāš ļø Error streaming output: {stream_error}")
580
- process.kill()
581
- process.wait()
640
+ # print(f"\nāš ļø Error with advanced streaming: {stream_error}")
641
+ # print("šŸ”„ Falling back to simple streaming...")
642
+ pass
643
+
644
+ # Fallback to simple readline approach
645
+ try:
646
+ # Restart the process with simpler streaming
647
+ if process.poll() is None:
648
+ process.kill()
649
+ process.wait()
650
+
651
+ fallback_process = subprocess.Popen(
652
+ ["python", "-u", "/python/kill_claude/claude_code_agent.py", claude_prompt],
653
+ cwd="/root",
654
+ stdout=subprocess.PIPE,
655
+ stderr=subprocess.STDOUT,
656
+ text=True,
657
+ bufsize=1,
658
+ universal_newlines=True
659
+ )
660
+
661
+ # Simple line-by-line reading
662
+ while True:
663
+ line = fallback_process.stdout.readline()
664
+ if line == '' and fallback_process.poll() is not None:
665
+ break
666
+ if line:
667
+ print(line.rstrip(), flush=True)
668
+
669
+ return_code = fallback_process.returncode
670
+
671
+ print("\n" + "="*60)
672
+ if return_code == 0:
673
+ print("āœ… Agent completed successfully!")
674
+ else:
675
+ print(f"āš ļø Agent exited with code: {return_code}")
676
+ print("="*60)
677
+
678
+ except Exception as fallback_error:
679
+ print(f"\nāŒ Fallback streaming also failed: {fallback_error}")
680
+ print("āš ļø Agent may have completed, but output streaming failed")
681
+ return_code = 1
582
682
 
583
683
  except Exception as e:
584
684
  print(f"āŒ Error during repository setup: {e}")