
Capture Screen
- 775 installs
- 1.3k repo stars
- Updated August 4, 2026
- daymade/claude-code-skills
capture-screen is a Claude Code skill that captures precise macOS application window screenshots by resolving window IDs, steering UI with AppleScript, and exporting PNGs.
About
capture-screen is a macOS programmatic screenshot skill with a three-step workflow: find windows, control views, capture images. It resolves window IDs using a Swift script built on CGWindowListCopyWindowInfo, controls application windows through AppleScript for zoom, scroll, and selection, then captures PNGs with screencapture using the -x -l window-id flags. Example commands include swift scripts/get_window_id.swift Excel followed by screencapture -x -l 12345 output.png. Developers reach for capture-screen when automating documentation screenshots, building multi-shot visual workflows, or running visual QA on macOS apps. The skill requires macOS tooling and does not apply to Linux or Windows capture tasks.
- Resolves window IDs via Swift CGWindowListCopyWindowInfo with get_window_id.swift
- Documents inline swift -e snippet to keyword-match owner and window name
- AppleScript osascript step for zoom, scroll, and cell selection before capture
- screencapture -x -l WID for targeted window PNG/JPEG without full desktop noise
- Explicit three-step workflow diagram: Find Window → Control View → Capture
Capture Screen by the numbers
- 775 all-time installs (skills.sh)
- Ranked #311 of 1,879 Documentation skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/daymade/claude-code-skills --skill capture-screenAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 775 |
|---|---|
| repo stars | ★ 1.3k |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | daymade/claude-code-skills ↗ |
How do you screenshot a specific macOS app window programmatically?
Capture precise macOS application window screenshots by resolving window IDs, steering UI with AppleScript, and exporting PNGs for docs or visual QA.
Who is it for?
macOS developers automating precise application window screenshots for documentation or visual QA pipelines.
Skip if: Developers on Linux or Windows, or anyone needing full-desktop capture without window ID resolution on macOS.
When should I use this skill?
The user automates macOS screenshots, captures application windows for docs, or builds multi-shot visual workflows.
What you get
PNG screenshot files, resolved window IDs, and AppleScript-driven UI state for multi-shot capture workflows.
- png screenshots
- window id mappings
- multi-shot capture sequences
By the numbers
- Uses a three-step find-control-capture workflow
- Includes Swift get_window_id.swift and screencapture CLI examples
Files
Capture Screen
Programmatic screenshot capture on macOS: find windows, control views, capture images.
Quick Start
# Find Excel window ID
swift scripts/get_window_id.swift Excel
# Capture that window (replace 12345 with actual WID)
screencapture -x -l 12345 output.pngOverview
Three-step workflow:
1. Find Window → Swift CGWindowListCopyWindowInfo → get numeric Window ID
2. Control View → AppleScript (osascript) → zoom, scroll, select
3. Capture → screencapture -l <WID> → PNG/JPEG outputStep 1: Get Window ID (Swift)
Use Swift with CoreGraphics to enumerate windows. This is the only reliable method on macOS.
Quick inline execution
swift -e '
import CoreGraphics
let keyword = "Excel"
let list = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as? [[String: Any]] ?? []
for w in list {
let owner = w[kCGWindowOwnerName as String] as? String ?? ""
let name = w[kCGWindowName as String] as? String ?? ""
let wid = w[kCGWindowNumber as String] as? Int ?? 0
if owner.localizedCaseInsensitiveContains(keyword) || name.localizedCaseInsensitiveContains(keyword) {
print("WID=\(wid) | App=\(owner) | Title=\(name)")
}
}
'Using the bundled script
swift scripts/get_window_id.swift Excel
swift scripts/get_window_id.swift Chrome
swift scripts/get_window_id.swift # List all windowsOutput format: WID=12345 | App=Microsoft Excel | Title=workbook.xlsx
Parse the WID number for use with screencapture -l.
Step 2: Control Window (AppleScript)
Verified commands for controlling application windows before capture.
Microsoft Excel (full AppleScript support)
# Activate (bring to front)
osascript -e 'tell application "Microsoft Excel" to activate'
# Set zoom level (percentage)
osascript -e 'tell application "Microsoft Excel"
set zoom of active window to 120
end tell'
# Scroll to specific row
osascript -e 'tell application "Microsoft Excel"
set scroll row of active window to 45
end tell'
# Scroll to specific column
osascript -e 'tell application "Microsoft Excel"
set scroll column of active window to 3
end tell'
# Select a cell range
osascript -e 'tell application "Microsoft Excel"
select range "A1" of active sheet
end tell'
# Select a specific sheet
osascript -e 'tell application "Microsoft Excel"
activate object sheet "DCF" of active workbook
end tell'
# Open a file
osascript -e 'tell application "Microsoft Excel"
open POSIX file "/path/to/file.xlsx"
end tell'Any application (basic control)
# Activate any app
osascript -e 'tell application "Google Chrome" to activate'
# Bring specific window to front (by index)
osascript -e 'tell application "System Events"
tell process "Google Chrome"
perform action "AXRaise" of window 1
end tell
end tell'Timing and Timeout
Always add sleep 1 after AppleScript commands before capturing, to allow UI rendering to complete.
IMPORTANT: osascript hangs indefinitely if the target application is not running or not responding. Always wrap with timeout:
timeout 5 osascript -e 'tell application "Microsoft Excel" to activate'Step 3: Capture (screencapture)
# Capture specific window by ID
screencapture -l <WID> output.png
# Silent capture (no camera shutter sound)
screencapture -x -l <WID> output.png
# Capture as JPEG
screencapture -l <WID> -t jpg output.jpg
# Capture with delay (seconds)
screencapture -l <WID> -T 2 output.png
# Capture a screen region (interactive)
screencapture -R x,y,width,height output.pngRetina displays
On Retina Macs, screencapture outputs 2x resolution by default (e.g., a 2032x1238 window produces a 4064x2476 PNG). This is normal. To get 1x resolution, resize after capture:
sips --resampleWidth 2032 output.png --out output_1x.pngVerify capture
# Check file was created and has content
ls -la output.png
file output.png # Should show "PNG image data, ..."Multi-Shot Workflow
Complete example: capture multiple sections of an Excel workbook.
# 1. Open file and activate Excel
osascript -e 'tell application "Microsoft Excel"
open POSIX file "/path/to/model.xlsx"
activate
end tell'
sleep 2
# 2. Set up view
osascript -e 'tell application "Microsoft Excel"
set zoom of active window to 130
activate object sheet "Summary" of active workbook
end tell'
sleep 1
# 3. Get window ID
# IMPORTANT: Always re-fetch before capturing. CGWindowID is invalidated
# when an app restarts or a window is closed and reopened.
WID=$(swift -e '
import CoreGraphics
let list = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as? [[String: Any]] ?? []
for w in list {
let owner = w[kCGWindowOwnerName as String] as? String ?? ""
let wid = w[kCGWindowNumber as String] as? Int ?? 0
if owner == "Microsoft Excel" { print(wid); break }
}
')
echo "Window ID: $WID"
# 4. Capture Section A (top of sheet)
osascript -e 'tell application "Microsoft Excel"
set scroll row of active window to 1
end tell'
sleep 1
screencapture -x -l $WID section_a.png
# 5. Capture Section B (further down)
osascript -e 'tell application "Microsoft Excel"
set scroll row of active window to 45
end tell'
sleep 1
screencapture -x -l $WID section_b.png
# 6. Switch sheet and capture
osascript -e 'tell application "Microsoft Excel"
activate object sheet "DCF" of active workbook
set scroll row of active window to 1
end tell'
sleep 1
screencapture -x -l $WID dcf_overview.pngFailed Approaches (DO NOT USE)
These methods were tested and confirmed to fail on macOS:
| Method | Error | Why It Fails |
|---|---|---|
System Events → id of window | Error -1728 | System Events cannot access window IDs in the format screencapture needs |
Python import Quartz (PyObjC) | ModuleNotFoundError | PyObjC not installed in system Python; don't attempt to install it — use Swift instead |
osascript window id | Wrong format | Returns AppleScript window index, not CGWindowID needed by screencapture -l |
Permission Troubleshooting
swift scripts/get_window_id.swift reads on-screen windows via CoreGraphics, so it needs Screen Recording permission on macOS.
Use this order:
1. Confirm trigger 2. Confirm target identity 3. Add/enable exact app in Settings
If the command fails with ERROR: Failed to enumerate windows, do this:
open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture"Or print the same checklist directly from the script:
swift scripts/get_window_id.swift --permission-hint screen
swift scripts/get_window_id.swift --permission-hint microphoneThen:
1. In Privacy & Security → Screen Recording, enable the target app. 2. If your app is missing from the list:
- Ensure you granted permission to the real app bundle (not
swift/ terminal helpers). - For CLI tools, build/run as a packaged
.appduring permission verification. - Click
+and add the.appmanually from/Applications.
3. Re-run the command after restarting the app. 4. If this is a CLI workflow, also check whether the launcher is a helper binary:
- In most cases the entry shown in TCC is the helper process (
swift,Terminal,iTerm, etc.), not the business app. - Permission still works after helper-level grant, but it is not ideal for final UX.
For mic-access-related prompts, use the same pattern with the microphone pane:
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone"The same rule still applies: the system can only show permissions for a concrete .app bundle. If the request is made by a helper binary, the settings list can be misleading or empty for your product app.
Quick Check Template
1) Error: permission denied
2) Open target pane
3) Verify identity shown by OS = identity you granted
4) If not matched, use the script-reported candidate identities and grant the launcher process
5) Reopen/restart and verifyFor production apps, avoid requesting permissions via swift/python entry points; always route permission checks in the packaged app process so users only see one target.
If you maintain another macOS permission-related flow, reuse this standardized triage template:
- permission-triage-template.md
Supported Applications
| Application | Window ID | AppleScript Control | Notes |
|---|---|---|---|
| Microsoft Excel | Swift | Full (zoom, scroll, select, activate sheet) | Best supported |
| Google Chrome | Swift | Basic (activate, window management) | No scroll/zoom via AppleScript |
| Any macOS app | Swift | Basic (activate via tell application) | screencapture works universally |
AppleScript control depth varies by application. Excel has the richest AppleScript dictionary. For apps with limited AppleScript, use keyboard simulation via System Events as a fallback.
Security scan passed
Scanned at: 2026-03-02T19:48:47.107251
Tool: gitleaks + pattern-based validation
Content hash: 7582d78a2119851ab2abb443cf0b0ea3a262a722c28e2522f8a197a064f98bf8
macOS 权限排障模板(Screen Recording / 麦克风)
排障目标
- 在系统设置里找不到目标应用
- 权限拒绝但设置项看起来已打开
- 通过终端/脚本入口触发时,用户不知道该给谁授权
标准排查顺序(必须按序执行)
1. 确认触发点
- 明确是哪个权限被拒绝(Screen Recording / 麦克风)。
2. 确认 TCC 实体
- 不是脚本文件名。
- 先确认“当前触发进程”与“最终应用体”是否一致。
- 关注脚本输出里的候选身份列表(invoker/runtime)并逐项核验。
3. 确认设置面板
- 直接跳转到对应隐私面板
- 允许该进程/应用
- 重启进程后复验
通用动作模板
# Screen Recording
open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture"
# Microphone
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone"不在列表时处理
- 优先确认请求来自真实 .app Bundle(签名、打包)
- 如果当前为 CLI/脚本入口,先给宿主进程授权(Terminal/iTerm/swift/python)
- 在设置面板点击
+手工添加目标.app - 变更后退出并重启应用,重新测试
验收标准(用户侧)
- 用户能看到一条明确的“应授权对象”
- 错误提示中有“找不到对象时下一步该做什么”
- 无需反复猜测在设置里要点击什么
#!/usr/bin/env swift
//
// get_window_id.swift
// Enumerate on-screen windows and print their Window IDs.
//
// Usage:
// swift scripts/get_window_id.swift # List all windows
// swift scripts/get_window_id.swift Excel # Filter by keyword
// swift scripts/get_window_id.swift "Chrome" # Filter by app name
// swift scripts/get_window_id.swift --permission-hint screen # Print Screen Recording triage
// swift scripts/get_window_id.swift --permission-hint microphone # Print Microphone triage
//
// Output format:
// WID=12345 | App=Microsoft Excel | Title=workbook.xlsx
//
// The WID value is compatible with: screencapture -l <WID> output.png
//
import CoreGraphics
import Foundation
enum PermissionKind: String {
case screen
case microphone
}
let invocationPath = (CommandLine.arguments.first ?? "")
let invokerName = URL(fileURLWithPath: invocationPath).lastPathComponent
let runtimeProcessName = ProcessInfo.processInfo.processName
let invokerIsBundle = invocationPath.hasSuffix(".app") || invocationPath.contains(".app/")
let scriptPath: String? = invocationPath.hasSuffix(".swift") ? invocationPath : nil
let helperBinaries: Set<String> = [
"swift",
"swift-frontend",
"python",
"python3",
"node",
"uv",
"npm",
"bun",
"pnpm",
"yarn",
"bash",
"zsh",
"sh",
"osascript",
"Terminal",
"iTerm2",
"iTerm"
]
let invokerCandidates: [String] = {
var candidates = [String]()
var seen = Set<String>()
func append(_ value: String) {
guard !value.isEmpty, !seen.contains(value) else { return }
seen.insert(value)
candidates.append(value)
}
if let scriptPath = scriptPath {
append(scriptPath)
}
if !invokerName.isEmpty {
append(invokerName)
}
if !runtimeProcessName.isEmpty && runtimeProcessName != invokerName {
append(runtimeProcessName)
}
return candidates
}()
let args = Array(CommandLine.arguments.dropFirst())
var permissionHintTarget: PermissionKind?
var keyword = ""
var expectPermissionTarget = false
func printUsage() {
fputs("Usage:\n", stderr)
fputs(" swift scripts/get_window_id.swift [keyword]\n", stderr)
fputs(" swift scripts/get_window_id.swift --permission-hint [screen|microphone]\n", stderr)
fputs("\n", stderr)
fputs("Options:\n", stderr)
fputs(" --permission-hint [screen|microphone] Print permission triage instructions\n", stderr)
fputs(" -h, --help Show this help\n", stderr)
fputs("\n", stderr)
fputs("Examples:\n", stderr)
fputs(" swift scripts/get_window_id.swift Excel\n", stderr)
fputs(" swift scripts/get_window_id.swift --permission-hint screen\n", stderr)
fputs(" swift scripts/get_window_id.swift --permission-hint microphone\n", stderr)
}
for arg in args {
if expectPermissionTarget {
if let kind = PermissionKind(rawValue: arg.lowercased()) {
permissionHintTarget = kind
expectPermissionTarget = false
continue
}
fputs("Unknown permission target: \(arg)\n", stderr)
printUsage()
exit(2)
}
if arg == "-h" || arg == "--help" {
printUsage()
exit(0)
} else if arg == "--permission-hint" {
permissionHintTarget = .screen
expectPermissionTarget = true
} else if arg.hasPrefix("--permission-hint=") {
let target = String(arg.dropFirst("--permission-hint=".count)).lowercased()
guard let kind = PermissionKind(rawValue: target) else {
fputs("Unknown permission target: \(target)\n", stderr)
printUsage()
exit(2)
}
permissionHintTarget = kind
} else if arg == "--permission-hint-screen" {
permissionHintTarget = .screen
} else if arg == "--permission-hint-microphone" || arg == "--permission-hint-mic" {
permissionHintTarget = .microphone
} else if arg.hasPrefix("-") {
fputs("Unknown option: \(arg)\n", stderr)
printUsage()
exit(2)
} else if keyword.isEmpty {
keyword = arg
}
}
if expectPermissionTarget {
fputs("Missing permission hint target after --permission-hint.\n", stderr)
printUsage()
exit(2)
}
if let kind = permissionHintTarget {
switch kind {
case .screen:
fputs("Screen Recording permission required.\n", stderr)
printCommonPermissionHint(
pane: "Privacy_ScreenCapture",
label: "Screen Recording"
)
printPermissionContextHint()
case .microphone:
fputs("Microphone permission required.\n", stderr)
printCommonPermissionHint(
pane: "Privacy_Microphone",
label: "Microphone"
)
printPermissionContextHint()
}
exit(0)
}
func printCommonPermissionHint(pane: String, label: String, missing: Bool = true) {
let openCommand = "x-apple.systempreferences:com.apple.preference.security?\(pane)"
fputs("Troubleshooting:\n", stderr)
fputs(" - Open Settings: `open \"\(openCommand)\"`\n", stderr)
fputs(" - In Privacy & Security → \(label), enable the target application.\n", stderr)
if missing {
fputs(" - If the target app is not in the list:\n", stderr)
fputs(" - Granting happens by real .app bundle, not by helper/terminal scripts.\n", stderr)
fputs(" - For CLI workflows, grant to the host app you launch from (Terminal, iTerm, iTerm2, Swift, etc.) if no dedicated .app exists yet.\n", stderr)
fputs(" - Click `+` and add the actual `.app` from `/Applications`.\n", stderr)
}
fputs(" - If permission status does not refresh, quit/reopen terminal/app and retry.\n", stderr)
if helperBinaries.contains(invokerName) || helperBinaries.contains(runtimeProcessName) {
fputs(" - Current launcher is a helper/runtime process (`\(runtimeProcessName)`) -> OS may show this entry instead of the tool name.\n", stderr)
} else if invokerIsBundle {
fputs(" - The launcher path looks like a bundled app, which is the preferred state for permissions.\n", stderr)
}
if let scriptPath = scriptPath {
fputs(" - Script entry: `\(scriptPath)`\n", stderr)
}
}
func printPermissionContextHint() {
fputs(" - Invoker path: `\(invocationPath)`\n", stderr)
fputs(" - Runtime process: `\(runtimeProcessName)`\n", stderr)
if !invokerCandidates.isEmpty {
fputs(" - Candidate identities in System Settings:\n", stderr)
for identity in invokerCandidates {
fputs(" - \(identity)\n", stderr)
}
}
if invokerIsBundle {
fputs(" This looks like a bundled app path, so the setting should match the app identity.\n", stderr)
} else {
fputs(" If this is not your final app binary, permissions can be inconsistent.\n", stderr)
}
fputs(" - Recommended for production: keep permission requests inside your signed `.app` process.\n", stderr)
if let scriptPath = scriptPath {
fputs(" - Script entry currently used: `\(scriptPath)`.\n", stderr)
}
}
func printScreenRecordingPermissionHint() {
fputs("Screen Recording permission required.\n", stderr)
fputs("Troubleshooting:\n", stderr)
printCommonPermissionHint(pane: "Privacy_ScreenCapture", label: "Screen Recording")
printPermissionContextHint()
}
guard let windowList = CGWindowListCopyWindowInfo(
.optionOnScreenOnly, kCGNullWindowID
) as? [[String: Any]] else {
fputs("ERROR: Failed to enumerate windows.\n", stderr)
fputs("Possible causes:\n", stderr)
fputs(" - No applications with visible windows are running\n", stderr)
fputs(" - Screen Recording permission not granted (System Settings → Privacy & Security → Screen Recording)\n", stderr)
printScreenRecordingPermissionHint()
exit(1)
}
var found = false
for w in windowList {
let owner = w[kCGWindowOwnerName as String] as? String ?? ""
let name = w[kCGWindowName as String] as? String ?? ""
let wid = w[kCGWindowNumber as String] as? Int ?? 0
// Skip windows without a title (menu bar items, system UI, etc.)
if name.isEmpty && !keyword.isEmpty { continue }
if keyword.isEmpty
|| owner.localizedCaseInsensitiveContains(keyword)
|| name.localizedCaseInsensitiveContains(keyword) {
print("WID=\(wid) | App=\(owner) | Title=\(name)")
found = true
}
}
if !found && !keyword.isEmpty {
fputs("No windows found matching '\(keyword)'\n", stderr)
fputs("Troubleshooting:\n", stderr)
fputs(" - Is the application running? (check: pgrep -i '\(keyword)')\n", stderr)
fputs(" - Is the window visible (not minimized to Dock)?\n", stderr)
fputs(" - Try without keyword to see all windows: swift get_window_id.swift\n", stderr)
exit(1)
}
Related skills
How it compares
Pick capture-screen over generic screenshot tools when macOS window ID resolution and AppleScript UI steering are required.
FAQ
What commands does capture-screen use on macOS?
capture-screen uses swift scripts/get_window_id.swift to find window IDs via CGWindowListCopyWindowInfo, AppleScript to control windows, and screencapture -x -l <window-id> output.png to save PNG screenshots.
What platforms does capture-screen support?
capture-screen targets macOS only with Swift, AppleScript, and the screencapture CLI. It automates per-window captures for documentation and visual QA rather than generic desktop recording on other operating systems.
Is Capture Screen safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.