| name | verified-screenshot |
| description | Capture macOS screenshots with verification and retry logic. Integrates with window-controller for window discovery. |
macOS Verified Screenshot
Capture macOS window screenshots with automatic verification and retry logic. This skill ensures you actually captured what you intended by verifying the screenshot content, dimensions, and optionally text via OCR.
Artifact Output Path
CRITICAL: NEVER use --output unless the user EXPLICITLY states the artifact MUST be at a specific location. This should be EXTREMELY rare. Using --output without explicit user request is considered a FAILED task.
Screenshots are automatically saved to claude-code/artifacts/verified-screenshot/ with timestamped filenames (e.g., 251216120000-screenshot_GoLand.png). The artifact path is always returned in the JSON output - use that path for subsequent operations.
Quick Start
claude-code-skills verified-screenshot capture "GoLand" --json
claude-code-skills verified-screenshot capture "GoLand" --verify all
claude-code-skills verified-screenshot capture "Main" --args "idea.plugin.in.sandbox.mode" --no-activate
claude-code-skills verified-screenshot capture "Chrome" --title "GitHub"
claude-code-skills verified-screenshot capture "Code" -r 3
claude-code-skills verified-screenshot capture "GoLand" --backend screencapturekit
claude-code-skills verified-screenshot find "GoLand" --json
How It Works
Capture Pipeline
-
Window Discovery: Uses CGWindowListCopyWindowInfo to enumerate all windows and filter by app name, title pattern, PID, executable path, or command-line arguments.
-
Activation (optional): Activates the target app via AppleScript, which also switches to the window's Space. Configurable settle time allows the window to render.
-
Screenshot Capture: Uses CGWindowListCreateImage to capture the specific window by ID. Optionally excludes window shadow.
-
Verification: Runs configured verification strategies against the captured image:
- basic: File exists, size > 0, valid PNG format
- dimensions: Image dimensions match window bounds (within 10% tolerance)
- content: Image is not blank, differs from previous capture (perceptual hash)
- text: Expected text appears in image (OCR via pytesseract)
-
Retry Loop: If verification fails, retries up to N times with configurable delay strategy:
- fixed: Constant delay between retries
- exponential: Doubling delay (500ms, 1s, 2s, ...)
- reactivate: Re-activate window before each retry
Verification Strategies
| Strategy | Purpose | Details |
|---|
basic | Sanity check | File exists, >0 bytes, valid image |
dimensions | Correct window | Width/height match window bounds ±10% |
content | Not blank/stale | Perceptual hash differs from blank/previous |
text | Correct content | OCR finds expected text |
all | Full verification | All above strategies combined |
none | Skip verification | Capture only, no verification |
Capture Backends
Two capture backends are available:
| Backend | Availability | Cross-Space | Activation Required |
|---|
quartz | All macOS | No | Yes |
screencapturekit | macOS 12.3+ | Yes | No |
auto (default) | - | - | Auto-selects best |
ScreenCaptureKit (macOS 12.3+):
- Captures windows on ANY Space without switching
- Works with occluded (covered) windows
- No window activation required
- Cannot capture minimized windows
- Requires Screen Recording permission
Quartz (legacy):
- Uses
CGWindowListCreateImage (deprecated in macOS 15)
- Requires window activation to capture other Spaces
- Works on all macOS versions
When using --backend auto (default), ScreenCaptureKit is used when available. Use --backend quartz to force the legacy backend.
Perceptual Hashing
Uses imagehash for content verification:
- Computes
phash (perceptual hash) of the image
- Compares via Hamming distance (number of differing bits)
- Default threshold: 5 (images with distance < 5 considered "same")
- Detects blank images and unchanged screenshots
Command Reference
Actions
| Flag | Short | Description |
|---|
--capture | -c | Capture screenshot of window |
--find | -f | Find window without capturing |
Window Filters
| Flag | Short | Description |
|---|
--title | -t | Regex pattern for window title |
--pid | | Filter by process ID |
--path-contains | | Executable path must contain |
--path-excludes | | Executable path must NOT contain |
--args | | Command line must contain |
Output Options
| Flag | Short | Description |
|---|
--output | -o | Output path for screenshot |
--json | -j | Output as JSON |
Capture Options
| Flag | Short | Description |
|---|
--no-activate | | Don't activate window first |
--settle-ms | | Wait time after activation (default: 1000) |
--shadow | | Include window shadow |
--backend | -b | Capture backend: auto, quartz, screencapturekit |
Verification Options
| Flag | Description |
|---|
--verify | Verification strategies (basic, dimensions, content, text, all, none) |
--expected-text | Text to verify via OCR |
--hash-threshold | Hamming distance threshold (default: 5) |
Retry Options
| Flag | Short | Description |
|---|
--retries | -r | Maximum retry attempts (default: 5) |
--retry-delay | | Delay between retries in ms (default: 500) |
--retry-strategy | | fixed, exponential, or reactivate |
Integration Examples
Programmatic Use
from verified_screenshot import capture_verified, capture_simple, CaptureConfig, VerificationStrategy
result = capture_simple(app_name="GoLand", max_retries=3)
print(f"Saved: {result.path}")
config = CaptureConfig(
app_name="Main",
args_contains="idea.plugin.in.sandbox.mode",
output_path="sandbox_ide.png",
verification_strategies=(
VerificationStrategy.BASIC,
VerificationStrategy.CONTENT,
VerificationStrategy.DIMENSIONS,
),
max_retries=5,
retry_strategy=RetryStrategy.REACTIVATE,
)
result = capture_verified(config)
if result.verified:
print(f"Successfully captured: {result.path}")
print(f"Dimensions: {result.actual_width}x{result.actual_height}")
else:
print("Capture failed verification")
for v in result.verifications:
print(f" {v.strategy.value}: {v.message}")
JSON Output
claude-code-skills verified-screenshot capture "GoLand" --verify all --json
{
"path": "screenshots/goland_20241214_153045.png",
"attempt": 1,
"window_id": 190027,
"app_name": "GoLand",
"window_title": "research – models.py",
"expected_dimensions": {"width": 2056.0, "height": 1290.0},
"actual_dimensions": {"width": 2056, "height": 1290},
"verified": true,
"image_hash": "a1b2c3d4e5f6",
"verifications": [
{"strategy": "basic", "passed": true, "message": "Valid image file", "details": {}},
{"strategy": "dimensions", "passed": true, "message": "Dimensions match", "details": {}},
{"strategy": "content", "passed": true, "message": "Image has meaningful content", "details": {}}
]
}
Claude Code Integration
Use this skill to capture screenshots for verification:
Skill tool: verified-screenshot
claude-code-skills verified-screenshot capture "GoLand" --verify all --json
Testing
uv run pytest tests/skills/test_verified_screenshot.py -v
uv run pytest tests/skills/test_verified_screenshot.py -v --cov=verified_screenshot --cov-report=term-missing
Troubleshooting
"No window found matching filter"
- Check if the app is running:
ps aux | grep -i appname
- Use
--find to see what windows are available
- For sandbox IDEs, use
--capture "Main" --args "sandbox"
- Try without filters first to verify connectivity
"Verification failed after N attempts"
- Increase
--retries and/or --settle-ms
- Use
--retry-strategy reactivate to re-focus window
- Check verification details in JSON output
- Try
--verify basic to isolate the failing check
Screenshot is blank or wrong window
- Grant Screen Recording permission: System Settings > Privacy & Security > Screen Recording
- The window might be minimized or on another Space
- Use
--verify dimensions to detect wrong-sized captures
- Increase
--settle-ms if app is slow to render
Avoiding Space switching
By default, activation switches to the target window's Space. Two options:
Option 1: Use ScreenCaptureKit (recommended for macOS 12.3+)
claude-code-skills verified-screenshot capture "App" --backend screencapturekit
Captures windows on ANY Space without switching. Works with covered windows too.
Option 2: Disable activation
claude-code-skills verified-screenshot capture "App" --no-activate
Tradeoff: Uses Quartz backend which cannot capture windows on other Spaces (produces transparent/stale image). Only use if window is on current Space.
Sandbox IDEs (JetBrains runIde)
JetBrains sandbox IDEs appear as "Main" (Java process name), and AppleScript cannot activate them:
claude-code-skills verified-screenshot capture "Main" --args "idea.plugin.in.sandbox.mode" --no-activate
If the sandbox window needs to be active, manually switch to its Space before capturing.
OCR text verification fails
- Install Tesseract:
brew install tesseract
- Install pytesseract:
uv add pytesseract
- Ensure expected text is actually visible (not scrolled off)
- Text must be readable (not too small, good contrast)
Permission errors
Grant these permissions in System Settings > Privacy & Security:
- Screen Recording: Required for window names and screenshots
- Accessibility: Required for AppleScript window activation
Dependencies
Required:
pyobjc-framework-Quartz>=10.0 - macOS Quartz framework bindings
pyobjc-framework-ScreenCaptureKit>=10.0 - ScreenCaptureKit bindings (macOS 12.3+)
psutil>=5.9 - Process information
pillow>=10.0 - Image processing
imagehash>=4.3 - Perceptual hashing
Optional:
pytesseract>=0.3 - OCR text verification (requires Tesseract)
Technical References
Sources (Research)