Vision-based auto-approval system for Claude Code CLI using MiniCPM-V vision model. Features: - Automatic detection and response to approval prompts - Screenshot capture and vision analysis via Ollama - Support for multiple screenshot tools (scrot, gnome-screenshot, etc.) - Configurable timing and behavior - Debug mode for troubleshooting - Comprehensive documentation Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Jean-Philippe Brule <jp@svrnty.io>
124 lines
3.6 KiB
Python
124 lines
3.6 KiB
Python
"""
|
|
Screenshot capture functionality
|
|
"""
|
|
|
|
import subprocess
|
|
import tempfile
|
|
import shutil
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from . import config
|
|
|
|
|
|
class ScreenshotError(Exception):
|
|
"""Raised when screenshot capture fails"""
|
|
pass
|
|
|
|
|
|
def find_screenshot_tool() -> Optional[str]:
|
|
"""Find available screenshot tool on the system"""
|
|
for tool in config.SCREENSHOT_TOOLS:
|
|
if shutil.which(tool):
|
|
return tool
|
|
return None
|
|
|
|
|
|
def take_screenshot() -> Optional[str]:
|
|
"""
|
|
Take screenshot of active terminal window
|
|
|
|
Returns:
|
|
Path to screenshot file, or None if capture failed
|
|
"""
|
|
tool = find_screenshot_tool()
|
|
|
|
if not tool:
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] No screenshot tool found. Tried: {', '.join(config.SCREENSHOT_TOOLS)}")
|
|
return None
|
|
|
|
# Create temporary file
|
|
cache_dir = config.get_cache_dir()
|
|
screenshot_path = cache_dir / f"screenshot_{int(subprocess.check_output(['date', '+%s']).decode().strip())}.png"
|
|
|
|
try:
|
|
if tool == "scrot":
|
|
# Capture active window
|
|
subprocess.run(
|
|
["scrot", "-u", str(screenshot_path)],
|
|
check=True,
|
|
capture_output=True,
|
|
timeout=config.SCREENSHOT_TIMEOUT
|
|
)
|
|
elif tool == "gnome-screenshot":
|
|
# Capture active window
|
|
subprocess.run(
|
|
["gnome-screenshot", "-w", "-f", str(screenshot_path)],
|
|
check=True,
|
|
capture_output=True,
|
|
timeout=config.SCREENSHOT_TIMEOUT
|
|
)
|
|
elif tool == "import":
|
|
# ImageMagick - capture root window
|
|
subprocess.run(
|
|
["import", "-window", "root", str(screenshot_path)],
|
|
check=True,
|
|
capture_output=True,
|
|
timeout=config.SCREENSHOT_TIMEOUT
|
|
)
|
|
elif tool == "maim":
|
|
# Capture active window
|
|
subprocess.run(
|
|
["maim", "-i", "$(xdotool getactivewindow)", str(screenshot_path)],
|
|
shell=True,
|
|
check=True,
|
|
capture_output=True,
|
|
timeout=config.SCREENSHOT_TIMEOUT
|
|
)
|
|
else:
|
|
return None
|
|
|
|
if screenshot_path.exists():
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Screenshot saved to {screenshot_path}")
|
|
return str(screenshot_path)
|
|
else:
|
|
return None
|
|
|
|
except subprocess.TimeoutExpired:
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Screenshot timeout with tool: {tool}")
|
|
return None
|
|
except subprocess.CalledProcessError as e:
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Screenshot failed: {e}")
|
|
return None
|
|
except Exception as e:
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Unexpected error: {e}")
|
|
return None
|
|
|
|
|
|
def cleanup_old_screenshots(max_age_seconds: int = 3600):
|
|
"""
|
|
Clean up old screenshots from cache directory
|
|
|
|
Args:
|
|
max_age_seconds: Maximum age of screenshots to keep (default 1 hour)
|
|
"""
|
|
import time
|
|
|
|
cache_dir = config.get_cache_dir()
|
|
current_time = time.time()
|
|
|
|
for screenshot in cache_dir.glob("screenshot_*.png"):
|
|
if current_time - screenshot.stat().st_mtime > max_age_seconds:
|
|
try:
|
|
screenshot.unlink()
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Cleaned up old screenshot: {screenshot}")
|
|
except Exception as e:
|
|
if config.DEBUG:
|
|
print(f"[DEBUG] Failed to cleanup {screenshot}: {e}")
|