# Screen Recorder

> 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.

- Skill: `moinsen-dev/screen-recorder` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add moinsen-dev/screen-recorder`
- Raw SKILL.md: https://api.skillmd.com/api/skills/moinsen-dev/screen-recorder/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: MIT
- Author: moinsen-dev (https://skillmd.com/u/moinsen-dev)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/moinsen-dev/screen-recorder

---


# 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

```bash
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

```bash
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.

