What it does
[Granola](https://granola.ai) is the AI meeting-notes desktop app for macOS and Windows. Its public API (`https://public-api.granola.ai/v1`) is currently read-only — `GET /v1/notes`, `GET /v1/notes/:id`, `GET /v1/folders` and nothing else. This sink takes the practical path: it writes a per-run markdown bundle (`note.md` + a `frames/` folder) into a configurable import directory (`~/Documents/Granola Imports/` by default), pre-formatted with a heading, meeting details, container tags, timestamped transcript segments, and embedded frame thumbnails. Drag the folder into Granola or open `note.md` and paste — the markdown is shaped to render cleanly inside Granola's editor. When Granola ships a write endpoint, the sink will swap the file-drop path for an HTTP POST without touching the markdown formatter.
When to reach for it
- Pipe meeting recordings (Zoom, Loom, screen captures) through peepshow and into your Granola workspace as native notes
- Build a paper trail of every video an agent watched alongside your live-attended meetings — same import dir, same review workflow
- Skip context-switching: drop a video at Claude / Cursor, the agent watches + summarises, Granola gets the structured note for follow-up
Install
npm i -g peepshowUse it
peepshow ./standup.mp4 --sink granolaMake it automatic
Register the sink once — every run fires it afterward. Scope by --when so it only runs for matching videos.
peepshow sinks add granola
peepshow sinks add granola --when extension=mp4,mov
peepshow sinks add granola --when path=/Volumes/Work/Configuration
GRANOLA_IMPORT_DIRAbsolute path to the import directory. Default: `~/Documents/Granola Imports`. Created on first write.GRANOLA_TITLE_PREFIXPrepended to every note title — useful for distinguishing peepshow imports from meetings (e.g. `peepshow: `).GRANOLA_COPY_FRAMESDefault `1`; set `0` to embed frames by absolute path instead of copying them into the bundle.GRANOLA_API_KEYReserved for the future Granola write endpoint. When set, the note records its presence; the sink still uses the file-drop path until the upstream API supports note creation.
Use with an LLM agent
Every peepshow sink reads its config from env vars and receives a single JSON payload on stdin. An LLM agent (Claude Code, Cursor, Windsurf, Gemini, Codex) can drive the Granola sink automatically when three things are true:
- the env vars below are exported in the agent's shell (or a project
.envit can load), - the
peepshowCLI is onPATH— install withnpm i -g peepshow, - a peepshow auto-sink is registered for the run (optional but recommended — makes invocation zero-argument).
1. Set the environment
This sink has no required env vars — it writes to a local path. Pass the destination via --sink-arg:
peepshow ./standup.mp4 --sink granola2. Register as an auto-sink
peepshow sinks add granola
peepshow sinks add granola --when extension=mp4,mov3. Example LLM session
You → drop a
.movinto Claude Code.Claude → auto-invokes
/peepshow:slides ./clip.mov. peepshow extracts frames + audio, theGranolasink forwards the run to a new page in your knowledge base. Claude replies with a summary and a link to the created record.
The transcript text is embedded alongside the frame gallery on the created page.
Write your own
A sink is any executable that reads the --emit json payload on stdin. Shell, Node, Python, Go — the spec's in docs/PLUGINS.md. Register persistent ones with peepshow sinks add-cmd 'your-command'.