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"
# ... 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 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.
1---2name: screen-recorder3description: 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.4license: MIT5---67# Screen Recorder89Record 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.1011## Prerequisites (one-time, per machine)1213The 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.1415Without this, `record.sh start` will report the process exiting immediately.1617## Commands1819```bash20scripts/record.sh list21scripts/record.sh start <session> [--window <id|substring>] [--out <path>] [--audio]22scripts/record.sh stop <session>23scripts/record.sh status [session]24```2526- `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.27- `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`.28- `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.29- `status [session]` — running/not-running for one session, or a list of all known sessions.3031## Typical agent flow3233```bash34scripts/record.sh start bug-repro --window "MyApp"35# ... drive the app under test, reproduce the issue ...36scripts/record.sh stop bug-repro37# -> prints the .mov path; attach/inspect it as evidence38```3940Multiple 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`).4142## How it works (for context, not required reading)4344- 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.45- 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.46- `-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.