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 (fromlist) or a case-insensitive substring matched against the window's app name or title (first match wins — use a numeric id fromlistif there's ambiguity). Without--out, saves to~/Movies/ScreenRecordings/<session>-<timestamp>.mov.stop <session>— sendsSIGINTto the recording process, which is howscreencapture -vfinalizes 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"
# ... drive the app under test, reproduce the issue ...
scripts/record.sh stop bug-repro
# -> prints the .mov path; attach/inspect it as evidence
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 toCoreGraphics/AppKit— no Python dependencies, no compiled helper.CGWindowListCopyWindowInfomust be walked manually viaCFArrayGetValueAtIndex+ObjC.castRefToObject; callingObjC.deepUnwrapdirectly 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 inrecord.shsince the agent-driven start/stop flow is the point of this skill.