| name | screen-recorder |
| description | Start and stop a native macOS screen recording of a specific window (not the whole screen) from the command line. Use when a user wants to record an app window, a demo, a bug repro, or a test run on a Mac, and wants it driven programmatically by an agent (explicit start/stop) rather than via the Cmd+Shift+5 UI. |
| license | MIT |
Screen Recorder
Record a single macOS window to a .mov file using only the built-in screencapture binary — no third-party app, no accessibility permission beyond one-time Screen Recording approval. Designed to be driven by an agent: one command to start, one to stop, named sessions so multiple recordings can run concurrently.
Prerequisites (one-time, per machine)
The terminal app that runs these scripts (Terminal.app, iTerm2, VS Code's integrated terminal, etc.) needs Screen Recording permission: System Settings → Privacy & Security → Screen Recording → enable the terminal app → restart that terminal app once.
Without this, record.sh start will report the process exiting immediately.
Commands
scripts/record.sh list
scripts/record.sh start <session> [--window <id|substring>] [--out <path>] [--audio]
scripts/record.sh stop <session>
scripts/record.sh status [session]
list — prints on-screen windows as JSON (id, app, title, x, y, width, height). Use this to find a window's id or a substring to match on.
start <session> — begins recording. <session> is any name you choose; use it later to stop or check that specific recording. Without --window, records whichever window belongs to the frontmost (currently active) app — specifically its largest window, so small notification/helper windows owned by the same app aren't picked by accident. With --window, pass either a numeric window id (from list) or a case-insensitive substring matched against the window's app name or title (first match wins — use a numeric id from list if there's ambiguity). Without --out, saves to ~/Movies/ScreenRecordings/<session>-<timestamp>.mov.
stop <session> — sends SIGINT to the recording process, which is how screencapture -v finalizes and closes the video file cleanly (there is no other stop signal for it). Prints the file path.
status [session] — running/not-running for one session, or a list of all known sessions.
Typical agent flow
scripts/record.sh start bug-repro --window "MyApp"
scripts/record.sh stop bug-repro
Multiple sessions can run at once — pass distinct session names. Each session's PID and output path are tracked in $MOINSEN_RECORDER_STATE_DIR (defaults to ~/.local/state/moinsen-screen-recorder).
How it works (for context, not required reading)
- Window enumeration and the frontmost-app lookup use
osascript -l JavaScript (JXA) bridging to CoreGraphics/AppKit — no Python dependencies, no compiled helper. CGWindowListCopyWindowInfo must be walked manually via CFArrayGetValueAtIndex + ObjC.castRefToObject; calling ObjC.deepUnwrap directly on the array result segfaults on current macOS — this was verified by hand, don't "simplify" it back.
- Recording itself is
screencapture -v -l<windowid> <file>, confirmed by direct test to crop to the window's bounds, not the full (multi-monitor) desktop.
-V<seconds> (capital V) exists for a fixed-duration recording instead of manual stop, if ever needed — not exposed in record.sh since the agent-driven start/stop flow is the point of this skill.