Turn one narrated screen recording into a draft automation spec a developer can build an agent from.
The bottleneck in automating office routines isn't capture — it's getting the why (the branches, exceptions, judgment calls) out of the person who does the work. So the worker performs the routine once while narrating their decisions out loud. The narration is the product; capture is plumbing.
watchme is the plumbing. It takes the recording and produces a folder that local
Claude Code reads and synthesizes into the spec. The tool itself makes no API
calls — that keeps v0 free of API keys and lets the synthesis prompt be tuned
conversationally.
v0 scope. Personal experiment. Capture uses macOS's own recorder —
watchme recorddrives it for you, or record manually (QuickTime works too) and pass the file. No recorder daemon, no keylogger, no Accessibility tree, no redaction layer. Those earn their place only after synthesis is proven to work. Seeroutine-capture-v0-brief.md.
brew install ffmpeg whisper-cpp # ffmpeg + local transcription (Metal-accelerated)
# a whisper.cpp model. small (multilingual) is the safe default — it auto-detects the
# narration language. Use small.en only if you know the narration is English.
mkdir -p ~/.cache/whisper-cpp
curl -L -o ~/.cache/whisper-cpp/ggml-small.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.binwatchme auto-discovers models in ~/.cache/whisper-cpp/ and picks a multilingual one
unless you pass --language en.
Then either run it directly (no Python deps):
python3 watchme.py process ~/Desktop/my-recording.mov…or install the watchme command:
pipx install . # or: uv tool install .
watchme process ~/Desktop/my-recording.movopenai-whisper works too — if whisper is on your PATH and no whisper.cpp is
found, watchme uses it automatically.
Before the first recording, run:
watchme setupIt checks ffmpeg/ffprobe and local Whisper/model first, then walks permissions in strict
sequence: Screen & System Audio Recording, restart, and real read access to the folder
where macOS saves recordings. It emits a status before macOS can show consent UI and never
stacks the Screen Recording and Desktop-folder prompts. Use --no-open for a
diagnostic-only check, or --open screen|microphone|files|full-disk to open a specific
pane.
After automated checks pass, run a short watchme record --no-process smoke test.
Microphone access is requested by macOS the first time you choose a mic in the capture
toolbar; watchme setup --open microphone opens the pane for review. If Files & Folders
is enabled but an agent host still cannot read Desktop, add its command helper under Full
Disk Access too, restart the host, and rerun setup.
watchme record # record → transcribe → timeline, in one goA watchme popup explains the flow and then hands over to Apple's native capture UI
(the ⌘⇧5 toolbar): pick full screen or an area, choose your mic under Options, click
Record, narrate, and stop with the ⏹ button in the menu bar. The toolbar hands
the file to the system Screenshot service, so watchme watches the service's save
location (Options ▸ Save to, Desktop by default) and adopts the newest recording
automatically; then the pipeline below runs and the .mov is kept in the run folder as
recording.mov.
Why Apple's UI and not our own stop button: anything watchme draws during recording would be captured — macOS 15+ has no public API to exempt a window — so watchme's popup closes before capture starts. Apple's toolbar dismisses before recording begins too. The one trace left in a full-screen recording is the menu bar's tiny stop icon and your final click on it (the menu bar is ordinary screen content); recording a selected portion below the menu bar keeps even that out of frame.
The "Ready to record?" popup is a styled SwiftUI panel (frosted glass over an ambient
backdrop) that walks you through the capture UI: numbered steps with screenshots showing
which toolbar button to click (Record Entire Screen) and the Stop Recording
button to end with. watchme compiles it on first use from assets/watchme-dialog.swift
into ~/.cache/watchme/. It needs Apple's command-line Swift tools (swiftc, present
with the Xcode Command Line Tools); if they're missing — or on failure, or off macOS —
watchme falls back to a plain system dialog automatically. The images live in
~/.cache/watchme/ (dialog-bg.png, step-record.png, step-stop.png — bundled
defaults; the backdrop was generated with the Higgsfield CLI); drop your own PNGs there
to reskin or to match your own menu bar.
One-time setup is handled by watchme setup; watchme record also refuses to start if
Screen Recording or access to the configured save folder is missing, so it cannot wait
indefinitely for a recording it is not allowed to retrieve.
Options: --plain records the full screen with no native UI (stop with any key in the
terminal — useful over SSH/scripts); --no-process just saves the .mov;
--save PATH keeps the recording at PATH instead of inside the run folder. All
process options (below) work on record too.
watchme ships as a Claude Code plugin so you can run the whole loop from inside the repo you want to automate — capture → spec → implementation — without leaving the session.
Prerequisites: ffmpeg + a local whisper + a model — see Install above. The commands also check for these and tell you what's missing.
Run these /plugin commands inside Claude Code (needs access to the private
wearevolt/watchme repo; auth via gh or an SSH key):
/plugin marketplace add wearevolt/watchme
/plugin install watchme@watchme
The commands are then available in every repo. Update later with /plugin update watchme@watchme; refresh the catalog with /plugin marketplace update watchme.
# quick, one session only (loads in place, always reflects your working copy):
claude --plugin-dir /path/to/watchme
# persistent, from a local path (copies the folder into the plugin cache):
# /plugin marketplace add /path/to/watchme
# /plugin install watchme@watchmeUse --plugin-dir while hacking on the plugin itself; use the GitHub install for daily
use (a git source pulls only tracked files, leaving your .watchme/ runs behind).
In any repo, the simplest way is the single entry point:
/watchme:watchme # no args → record now; then spec; then offer to build
/watchme:watchme setup # guided prerequisites + macOS permissions
/watchme:watchme /path/to/recording.mov # a recording → process it
/watchme:watchme .watchme/<run>/spec.md # a spec → implement it
/watchme:watchme routes to the right step by what you pass (it's the first match when
you type /watchme — Claude Code namespaces every plugin command, so there is no bare
/watchme). The granular commands stay available if you prefer to drive each step:
/watchme:record # record right now, then process + spec
/watchme:setup # diagnose/setup before the first recording
/watchme:process /path/to/recording.mov # process an existing recording
/watchme:implement # newest spec, or pass .watchme/<run>/spec.md
/watchme:setup checks dependencies and guides macOS permission grants.
/watchme:record starts the native macOS recorder (stop with ⏹ in the menu bar);
/watchme:process takes a file you already have. Either way the pipeline runs into a
scratch .watchme/<date>-<slug>/ folder in the current repo, then Claude reads the
timeline, views the frames, writes spec.md — and offers to build it. /watchme:implement
resolves the spec's open questions with you and implements it in the current repo,
following its conventions. Spec and implementation are separate on purpose: you review the
spec before any code is written.
Requirements are the same as the CLI (ffmpeg + whisper + a model); the command checks them and tells you what's missing.
watchme also ships as a Codex CLI skill at codex/watchme/ — a self-contained skill
directory (its own SKILL.md + a bundled copy of watchme.py and assets/), so it works
with no separate CLI install. Codex uses the same Agent Skills format, so the same
setup → record → spec → implement flow is available there.
Install it from this repo with Codex's built-in skill-installer (ask Codex to "install
the watchme skill from wearevolt/watchme", or run its helper directly):
install-skill-from-github.py --repo wearevolt/watchme --path codex/watchme
That drops the skill into ~/.codex/skills/watchme/ (works for private repos via your git
creds / GITHUB_TOKEN); it's available on Codex's next turn. You can also just copy
codex/watchme/ into ~/.codex/skills/ (global) or .codex/skills/ (per-project, shared
via git). Then tell Codex to set up WatchMe, record a routine, process a .mov, or
implement a spec.md — it routes accordingly. Recording needs macOS + the same
prerequisites. Frame-aware
synthesis works — the current Codex CLI views the local frame images directly (verified on
Codex 0.144.5), so the spec gets the same on-screen context as the Claude path; a Codex
build without local-image support would degrade to transcript-only.
The Codex skill's
watchme.py/assets/are a copy — the repo root is canonical. After changing either, runscripts/sync-codex-skill.shto refresh the copy before committing.
watchme setup # check deps + guide macOS privacy permissions
--no-open # report only; do not open System Settings
--open PANE # screen, microphone, files, or full-disk
watchme record [options] # record now (native macOS recorder), then process
--plain # no native UI: record full screen, stop with any key here
--no-process # just save the .mov, skip the pipeline
--save PATH # keep the .mov at PATH (default: run folder/recording.mov)
# …plus all `process` options below
watchme process RECORDING [options]
--runs-dir DIR base dir holding per-run folders (default: runs)
--name SLUG explicit run-folder slug (skips claude auto-naming)
--out DIR exact output dir (overrides --runs-dir and auto-naming)
--scene-threshold F starting ffmpeg scene-change threshold (default 0.2, self-adapts)
--min-frames N min frames to aim for (default 14)
--max-frames N max frames to aim for (default 40)
--frame-width N max frame width in px (default 1600; Retina gets downscaled)
--model PATH|NAME ggml model path (whisper.cpp) or model name (openai-whisper)
--whisper-bin PATH override whisper binary
--language CODE narration language, or 'auto' (default: config value, else auto)
--translate translate non-English narration to English in the transcript
--verbose stream whisper's backend/model logs (hidden by default)The narration language is auto-detected up front (first ~45s) before transcription, so you normally don't set anything — watchme picks the right multilingual model and tells you what it found. You can also:
watchme detect RECORDING # just report the detected language + confidence
watchme config # show current settings
watchme config language ru # pin a language (accepts codes or names; validated)
watchme config language # interactive set, with suggestions on bad input
watchme config languages russian # search the supported languagesSet values are validated against the languages whisper accepts — an invalid value is
rejected with "did you mean …?" suggestions rather than saved. Precedence:
--language flag → pinned config → auto-detect.
Each run gets its own folder named runs/<recording-date>-<slug>/. The slug is generated
by the local claude CLI from the transcript (e.g.
2026-07-01-triage-disliked-llm-answers), so runs are self-describing and never clobber
each other. If claude isn't installed or times out, watchme falls back to a slug built
from the transcript's own words. Override with --name, or force an exact path with
--out.
- Transcribe (local) — extracts 16 kHz mono audio and runs whisper, keeping
segment timestamps (
{start, end, text}). Timestamps are what make alignment work. - Sample frames — extracts frames on scene change (not fixed-rate) via ffmpeg,
adapting the threshold to land in the ~20–40 meaningful-frame band. Each frame keeps
its timestamp in the filename (
frame_0036.png). - Interleave — merges frames and narration into one chronological
timeline.md, so what was on screen and what was said about it sit together in order. That alignment is what recovers intent instead of just actions.
runs/2026-07-01-triage-disliked-llm-answers/
transcript.json # {start, end, text} narration segments
frames/ # frame_0000.png, frame_0032.png, … (scene-change screenshots)
timeline.md # interleaved narration + frame refs — what Claude Code reads
synthesis-prompt.md # the task you give Claude Code (trivially editable — tune this)
spec.md # written by Claude Code in the synthesis step (below)
cd runs/<the-run-folder>
claude # then: "follow synthesis-prompt.md"Claude Code reads timeline.md, views the frames, and writes spec.md — a
SKILL.md-style scaffold with: Goal & trigger · Inputs · Systems touched (mapped to
candidate MCPs/integrations) · Process (happy path) · Decision branches & exceptions
(the high-value part) · Open questions (what to clarify on a re-record).
Run it once on one real narrated routine. If the spec captures the branches and exceptions correctly and a developer could start building from it, the core loop is validated. If it's mush, that's been learned for the cost of one recording.