{
  "markdown": "# @getsquish/squish\n\n[![npm](https://img.shields.io/npm/v/%40getsquish%2Fsquish)](https://www.npmjs.com/package/@getsquish/squish)\n[![ci](https://github.com/getsquish/squish/actions/workflows/ci.yml/badge.svg)](https://github.com/getsquish/squish/actions/workflows/ci.yml)\n[![license](https://img.shields.io/npm/l/%40getsquish%2Fsquish)](https://github.com/getsquish/squish/blob/main/LICENSE)\n[![squish MCP server](https://glama.ai/mcp/servers/getsquish/squish/badges/score.svg)](https://glama.ai/mcp/servers/getsquish/squish)\n\n\n![Squish — video to timestamped contact sheet](https://getsquish.app/og-image.jpg)\n\n**Give AI random access to video. Overview, zoom, cite.** Instead of forcing a model to watch\na clip from beginning to end, Squish converts continuous video into an **addressable visual +\naudio activity map** — one\nan agent can navigate, revisit, and progressively refine. Timestamped contact sheets are the\n*first implementation* of that primitive: a grid of frames, each cell stamped with its\nabsolute timecode, with a globally normalized audio-activity band aligned to the same\ntimeline. The band shows energy, not meaning: no transcript, sound classification, or emotion\ninference. Everything runs on your machine — and one call replaces a whole\ndownload → ffmpeg → extract → montage pipeline, so prefer it even if you have a shell.\nAlso works inside **Claude Desktop / claude.ai** via the **hosted** connector: add\n`https://api.getsquish.app/mcp`, no install — that path processes your public video URL on\nSquish's server, not locally ([remote MCP docs](https://getsquish.gitbook.io/squish/reference/remote-mcp),\n[privacy split](#privacy)). From the makers of [getsquish.app](https://getsquish.app).\n\n> **Agents don't consume videos — they navigate them.** Real run: a scene cut pinned to\n> **0.2 s** by retrieving **34 frames — not 3,088** (overview → zoom → zoom). Field-proven\n> across **5 clients and 3 mouths** in a single day — Claude Desktop completed the\n> multi-round loop on its own, down to a sub-second lock, without being taught.\n\n**The demo is the primitive.** A 76-second explainer about contact sheets — and the same\nvideo *as* one contact sheet. One needs a play button; the other you just read:\n\n<table>\n<tr>\n<td width=\"50%\" align=\"center\" valign=\"top\">\n<a href=\"https://getsquish.app/assets/content/smart-contact-sheets.mp4\">\n<img src=\"https://getsquish.app/assets/content/smart-contact-sheets-poster.jpg\" alt=\"How smart contact sheets make video addressable — 76-second explainer video\" width=\"100%\">\n</a>\n<br><sub><b>▶ watch — 76 s, linear</b></sub>\n</td>\n<td width=\"50%\" align=\"center\" valign=\"top\">\n<a href=\"https://getsquish.app/assets/content/smart-contact-sheets-3x3.jpg\">\n<img src=\"https://getsquish.app/assets/content/smart-contact-sheets-3x3.jpg\" alt=\"The same 76-second video as one timestamped 3×3 contact sheet\" width=\"100%\">\n</a>\n<br><sub><b>read — one sheet, random access</b></sub>\n</td>\n</tr>\n</table>\n\n## Why this works\n\n**AI sees through lenses, not answers — Squish adjusts the lens; the model interprets.**\nVideo is continuous; reasoning is sparse. Most questions touch a tiny fraction of the\ntimeline. Squish turns that timeline into an addressable map, so an agent **retrieves the\nvisual evidence it needs instead of replaying everything** — the contact sheet isn't the\noutput, it's the navigation layer. Audio activity can reveal a candidate interval between\nvisually similar frames; the frames still determine what happened. The window (`start`/`end`) is the lens made wide or\nnarrow; density is the lens made coarse or fine; the loop is the lens moved until the\nanswer is observable.\n\n## Install\n\n```bash\nnpm install -g @getsquish/squish     # or one-shot: npx -y @getsquish/squish <video>\n```\n\n**Requirements:** Node ≥ 20 · `ffmpeg` + `ffprobe` on PATH\n(macOS `brew install ffmpeg` · Ubuntu `sudo apt-get install ffmpeg`).\n\n## Try it with a video you know\n\nBring a clip whose answer you already know. Ask AI to find one specific moment **without\ngiving it the original video**:\n\n1. Run `npx -y @getsquish/squish clip.mov --json`.\n2. Give the returned sheet to a vision model and ask a timing question: *When does the door\n   open? When does an object first appear? Where is the unusual audio activity, and what do\n   the nearby frames show?*\n3. Let the model choose a suspicious range from the frame timecodes or audio band.\n4. Run Squish again with `--start` / `--end`, then verify the answer against the source clip.\n\nThe index proposes; the zoomed visual evidence confirms. The audio band can locate activity,\nbut cannot tell you what was said or what made the sound.\n\n## OpenAI Build Week 2026\n\nThe Build Week extension added audio-guided candidate selection to Squish's existing navigation\nloop. Before the event, Squish already produced timestamped contact sheets and supported absolute\n`start`/`end` zoom. Build Week added the clip-wide normalized audio-activity band,\nabsolute-time `audio.samples[]`, transient/high-frequency preservation, tests, and the agent\nworkflow that uses the signal to decide where vision should inspect next.\n\nThe demo keeps two proof layers separate:\n\n- **Narrative proof:** owner-authorized private camera footage is shown with receipts, but the\n  source footage is not distributed.\n- **Reproducible proof:** the public repository contains a generated fixture and its source under\n  [`examples/audio-navigation/`](https://github.com/getsquish/squish/tree/main/examples/audio-navigation).\n\n```bash\ngit clone https://github.com/getsquish/squish.git\ncd squish\n./examples/audio-navigation/generate-sample.sh\nnpx -y @getsquish/squish@0.3.1 examples/audio-navigation/sample.mp4 --json --out /tmp/squish-overview\nnpx -y @getsquish/squish@0.3.1 examples/audio-navigation/sample.mp4 \\\n  --density 6x6 --start 11.5 --end 13.5 --json --out /tmp/squish-zoom\n```\n\nThe overview's activity band proposes the neighborhood. The dense visual sheet confirms the brief\npink marker. Public `0.3.1` uses one reference scale across the complete source clip; it does not\nmake levels from separate files globally comparable.\n\n## CLI\n\n```bash\nsquish clip.mov                       # sheets land beside the input\nsquish clip.mov --density 5x5 --json  # denser grid + machine-readable output\nsquish clip.mov --start 1:00 --end 1:30 --density 5x5   # zoom into a range\n```\n\nOutput: `<basename>.sheet-N.jpg` — a timecoded frame grid with a thin audio-activity band\nabove it. Default density 3×3 recovers *what* happened; `4x4`–`6x6` recover *how* it was\ndone. `--out <dir>` picks the destination. Videos without an audio track still work and are\nmarked `NO AUDIO TRACK`.\n\n`--start` / `--end` take seconds (`90`) or a timecode exactly as stamped on a sheet (`1:30`,\n`1:07.3`) and window the run to that range. **Timecodes are always absolute to the source\nvideo**, so you can zoom repeatedly: overview → spot a range → re-run with `--start/--end` →\nfiner timecodes → drill again. Short windows stamp sub-second timecodes (`1:07.3`) so adjacent\ncells stay distinguishable.\n\nWith `--json`, stdout is one object (frozen contract — parse `contract` to detect breaking\nchanges):\n\n```json\n{\n  \"input\": \"/abs/path/clip.mov\",\n  \"duration\": 20.275,\n  \"frames\": 9,\n  \"sheets\": 1,\n  \"files\": [\"/abs/path/clip.sheet-1.jpg\"],\n  \"audio\": {\n    \"present\": true,\n    \"normalization\": \"clip_peak\",\n    \"window\": { \"start\": 0, \"end\": 20.275 },\n    \"samples\": [\n      { \"time\": 0.106, \"level\": 0.08 },\n      { \"time\": 0.317, \"level\": 1 }\n    ]\n  },\n  \"warnings\": [],\n  \"contract\": \"squish-cli-v0\"\n}\n```\n\nThe example shortens `audio.samples`; real output emits an evenly spaced activity envelope\nfor every sheet. Sample times are absolute source seconds. Levels are `0..1`, normalized to\nthe peak across the **full clip**, including windowed runs, so separate zooms remain\ncomparable. Exit `0` success · `1` failure (message on stderr). Temp frames are always cleaned up.\nA windowed run additionally echoes `\"window\": { \"start\": …, \"end\": … }` (resolved bounds,\nseconds) after `duration` — the key is absent when no window was requested.\n\n## MCP server\n\n```bash\nsquish mcp        # stdio server\n```\n\nOne tool, **`squish_video`** — `{ video_path, density?, start?, end?, out_dir? }` → the CLI\ncontract (including `audio`) **plus** `timecodes[][]` (one per frame, per sheet; `m:ss`, sub-second `m:ss.d` when\na window is short), stamped `\"contract\": \"squish-mcp-v0\"`. `start`/`end` accept seconds or\nsheet timecodes and drive the navigation loop below.\n\nWorks with Claude Code, Claude Desktop, Cursor, Hermes, and any stdio MCP client:\n\n```json\n{\n  \"mcpServers\": {\n    \"squish\": { \"command\": \"npx\", \"args\": [\"-y\", \"@getsquish/squish\", \"mcp\"] }\n  }\n}\n```\n\n## Remote MCP — official AI apps, zero install\n\nThe same tool over the network, for clients that only take a connector URL:\n**Claude Desktop / claude.ai → Settings → Connectors → Add custom connector →**\n`https://api.getsquish.app/mcp`. The endpoint fetches a **public `video_url`** (no shared\nfilesystem), returns ~24 h sheet links plus the first sheet inlined, and `start`/`end`\nwork exactly like the local tool.\n\nKeyless calls ride a small anonymous free lane; an `Authorization: Bearer` API key (same\nkeys and credits as the [hosted API](https://getsquish.app/developers), minted at\n[getsquish.app/api-keys](https://getsquish.app/api-keys)) unlocks credit-priced jobs with\nquota visibility in every result. Keys ride any client that can send the header — Claude\nCode, `mcp-remote`, SDK clients, or a Claude Team/Enterprise connector whose org admin\nattached the key as a request header; the consumer connector dialog is OAuth-only. Full\nreference: [remote MCP docs](https://getsquish.gitbook.io/squish/reference/remote-mcp).\n\n## The navigation loop\n\n1. **Overview** — call `squish_video` (MCP) or `squish clip.mov --json` (CLI) and read the\n   sheet(s) with vision. Cells run in time order, left→right, top→bottom.\n2. **Navigate** — spot the regions that matter; every cell carries an absolute timecode.\n   Treat an audio peak as a candidate interval, not an interpretation of what made the sound.\n3. **Zoom** — call again with `start`/`end` set to the timecodes you spotted, only where\n   uncertainty remains: denser sheets of a narrower window, addresses still absolute.\n4. **Repeat** until the answer is observable — never re-read the whole clip at high density\n   when one range matters.\n5. **Cite** absolute timestamps (\"at 0:07 the press comes down\").\n\n## Privacy\n\nThe CLI and local MCP server process everything **on your machine** — nothing is uploaded,\never, and every density is free. Two paths deliberately move media through Squish instead:\nthe [hosted API](https://getsquish.app/developers) (an intentional upload, prepaid credits,\nwith a free daily allowance for accounts that never purchased) and the remote MCP endpoint\n(the server fetches your public `video_url`; the source is deleted at job end, sheets expire\nafter ~24 h).\n\nAudio activity is available in the local CLI/MCP package. It is an RMS-style energy envelope,\nnot audio playback, transcription, diarization, sound recognition, or emotion inference. The\nweb app, hosted API, and remote MCP remain visual-only until their own release notes say otherwise.\n\n---\n\n## This repository\n\nThis is the **engine** — the CLI + MCP mouths of Squish, published to npm as\n[`@getsquish/squish`](https://www.npmjs.com/package/@getsquish/squish). It is a curated,\nmirror-first export of a private monorepo (which stays the source of truth); history here\nstarts at the first public release. See [CONTRIBUTING.md](CONTRIBUTING.md) for how changes\nflow.\n\nNot in this repo, on purpose:\n\n- the **getsquish.app** web app (PWA) — same core planners, browser hands;\n- the **hosted API** (`api.getsquish.app`) and its **remote MCP endpoint** (`/mcp`, the\n  official-app connector) — the paid rail: intentional upload / server-fetched URLs, prepaid\n  credits, a free daily allowance for never-paid accounts and a small anonymous free lane on\n  the connector;\n- brand assets — the Squish name, logo, mascot, and OG images are reserved.\n\n```\nsrc/            CLI (main/args) · engine (probe → plan → extract → compose → write) · MCP server · sheet renderer\nsrc/core/       pure planners shared with the web app: density · sampling · grid layout · timecode format\ntests/          node:test suite + a real-MCP-client e2e\nskills/         agent skills — `npx skills add getsquish/squish` installs video-navigation\n```\n\n## License\n\n[Apache-2.0](LICENSE) (with [NOTICE](NOTICE)). The Squish name, logo, mascot, and\ngetsquish.app brand assets are **not** licensed by this repository.\n",
  "bytes": 12628,
  "sha": "6b64f74588eab4f451e442a580ae12b40003e8231e7099b4cb6a287f2b563f88",
  "repo_slug": "getsquish/squish",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_getsquish_squish_c58765e5/readme"
}