Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
24fdad1
fix(export): synthesize default timeline for un-edited recordings
richiemcilroy Jun 4, 2026
203d4c5
feat(cli-install): add shared CLI install library
richiemcilroy Jun 4, 2026
e97f48a
build(scripts): add build-desktop-binaries script
richiemcilroy Jun 4, 2026
b1403f8
refactor(scripts): delegate build-cap-muxer to build-desktop-binaries
richiemcilroy Jun 4, 2026
0e22809
build(desktop): bundle cap-cli as external binary
richiemcilroy Jun 4, 2026
50697fa
chore(ci): use build-desktop-binaries in workflows
richiemcilroy Jun 4, 2026
910d606
feat(cli): add credentials module for upload auth
richiemcilroy Jun 4, 2026
fa97372
feat(cli): add session module for detached recordings
richiemcilroy Jun 4, 2026
e944495
feat(cli): add targets command module
richiemcilroy Jun 4, 2026
e746da4
feat(cli): add screenshot command module
richiemcilroy Jun 4, 2026
bbf9865
feat(cli): add recordings list command module
richiemcilroy Jun 4, 2026
c654452
feat(cli): add project inspect and validate module
richiemcilroy Jun 4, 2026
bee1e22
feat(cli): add export command module
richiemcilroy Jun 4, 2026
14369fe
feat(cli): add upload command module
richiemcilroy Jun 4, 2026
2c30c83
feat(cli): add doctor diagnostics module
richiemcilroy Jun 4, 2026
db23a7f
feat(cli): add guide manifest module for agents
richiemcilroy Jun 4, 2026
4268dda
feat(cli): expand record command with detach and JSON
richiemcilroy Jun 4, 2026
b8f0ac6
feat(cli): add subcommands, JSON output, and agent help
richiemcilroy Jun 4, 2026
44c5d2e
test(cli): add integration tests
richiemcilroy Jun 4, 2026
e2d5de0
docs(cli): add README and agent skill
richiemcilroy Jun 4, 2026
e1fa655
feat(desktop): add Tauri CLI install commands
richiemcilroy Jun 4, 2026
b45cbb8
feat(desktop): add CLI settings page
richiemcilroy Jun 4, 2026
84aaac3
feat(desktop): wire CLI settings route and nav
richiemcilroy Jun 4, 2026
db73dce
feat(desktop): regenerate tauri bindings for CLI install
richiemcilroy Jun 4, 2026
fddf91c
feat(web): add install-cli shell script route
richiemcilroy Jun 4, 2026
8240f71
feat(web): add install-cli PowerShell route
richiemcilroy Jun 4, 2026
96dd69d
feat(web): add CLI install section to download page
richiemcilroy Jun 4, 2026
ceb86d6
docs(web): document CLI in llms.txt
richiemcilroy Jun 4, 2026
ab2e49b
chore: update Cargo.lock
richiemcilroy Jun 4, 2026
6dd3358
build(cli): add Windows dependency for process liveness check
richiemcilroy Jun 5, 2026
e9c35bb
feat(cli): add Windows process_alive check
richiemcilroy Jun 5, 2026
f73be52
fix(cli): handle fast short recordings in session ready wait
richiemcilroy Jun 5, 2026
3d6dddb
fix(cli): preserve recording start time in session file
richiemcilroy Jun 5, 2026
80c4676
refactor(cli): remove user id from credentials status output
richiemcilroy Jun 5, 2026
c4949b8
fix(cli): reject non-MP4 files on direct upload
richiemcilroy Jun 5, 2026
242ec42
fix(cli-install): recognize Cap-managed shims with canonical path com…
richiemcilroy Jun 5, 2026
21d275e
fix(web): recognize existing Cap CLI shim with alternate path
richiemcilroy Jun 5, 2026
882caf3
fix(web): write CLI shim batch file in OEM encoding
richiemcilroy Jun 5, 2026
8cad912
docs(cli): clarify detach recording event behavior in guide
richiemcilroy Jun 5, 2026
9f985b4
fix(cli): include error field in project validation JSON output
richiemcilroy Jun 5, 2026
6ad907d
fix(cli): stop detached recording when start event delivery fails
richiemcilroy Jun 5, 2026
fc1366f
fix(cli): preserve session metadata when recording worker errors
richiemcilroy Jun 5, 2026
b5953f9
fix(cli): treat exited worker with Recording status as failure
richiemcilroy Jun 5, 2026
134af6c
fix(cli): only terminate live process on record stop
richiemcilroy Jun 5, 2026
b3c96b2
fix(cli): harden upload video metadata probing
richiemcilroy Jun 5, 2026
3d818b5
Track export progress and guard zero-frame output
richiemcilroy Jun 5, 2026
6312d69
Update manifest.json
richiemcilroy Jun 5, 2026
e737e63
feat(recording): add shared recording defaults module
richiemcilroy Jun 5, 2026
9162bd3
refactor(recording): use default instant mode fps constant
richiemcilroy Jun 5, 2026
725defd
feat(recording): add remux_if_needed for fragmented recordings
richiemcilroy Jun 5, 2026
13768d4
refactor(desktop): source general settings defaults from recording
richiemcilroy Jun 5, 2026
5131887
refactor(desktop): apply RecordingDefaults when starting recordings
richiemcilroy Jun 5, 2026
f18916b
feat(cli): align studio recording defaults and remux after stop
richiemcilroy Jun 5, 2026
5647642
feat(cli): remux fragmented recordings before export
richiemcilroy Jun 5, 2026
96a08b0
fix(cli): canonicalize symlink path in doctor version info
richiemcilroy Jun 5, 2026
58b9e4f
fix(cli-install): detect and repoint cap-managed shims
richiemcilroy Jun 5, 2026
9fff9c3
style(web): format workflow manifest indentation
richiemcilroy Jun 5, 2026
32078f5
fix(cli-install): resolve shim symlink and detect persisted Windows PATH
richiemcilroy Jun 5, 2026
e912a3d
fix(cli): require --detach in agent workflow docs and harden inputs
richiemcilroy Jun 5, 2026
a1bbbd6
fix(web): use HOME_DIR under set -u in install-cli script
richiemcilroy Jun 5, 2026
90ec98e
Support env-prefixed Windows shim targets
richiemcilroy Jun 5, 2026
d03c38b
clippy
richiemcilroy Jun 5, 2026
eff8357
fix: import tauri manager for export command
richiemcilroy Jun 5, 2026
5ec47f1
fix: gate crash sentinel record on macos
richiemcilroy Jun 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ jobs:
shell: bash
run: node scripts/setup.js

- name: Build cap-muxer sidecar
- name: Build desktop binaries
shell: bash
run: ./scripts/build-cap-muxer.sh ${{ matrix.settings.target }}
run: ./scripts/build-desktop-binaries.sh ${{ matrix.settings.target }}

- name: Run Clippy
run: cargo clippy --workspace --all-features --locked -- -D warnings
Expand Down Expand Up @@ -217,9 +217,9 @@ jobs:
env:
RUST_TARGET_TRIPLE: ${{ matrix.settings.target }}

- name: Build cap-muxer sidecar
- name: Build desktop binaries
shell: bash
run: ./scripts/build-cap-muxer.sh ${{ matrix.settings.target }}
run: ./scripts/build-desktop-binaries.sh ${{ matrix.settings.target }}

- name: Build app
working-directory: apps/desktop
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@ jobs:
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_KEYCHAIN: ${{ runner.temp }}/build.keychain

- name: Build cap-muxer sidecar
- name: Build desktop binaries
shell: bash
run: ./scripts/build-cap-muxer.sh ${{ matrix.settings.target }}
run: ./scripts/build-desktop-binaries.sh ${{ matrix.settings.target }}

- name: Build app
working-directory: apps/desktop
Expand Down
38 changes: 33 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 23 additions & 7 deletions apps/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,44 @@ edition = "2024"

[dependencies]
clap = { version = "4.5.23", features = ["derive"] }
cap-utils = { path = "../../crates/utils" }
clap_complete = "4.5.38"
cap-project = { path = "../../crates/project" }
cap-rendering = { path = "../../crates/rendering" }
cap-editor = { path = "../../crates/editor" }
cap-media = { path = "../../crates/media" }
cap-flags = { path = "../../crates/flags" }
cap-recording = { path = "../../crates/recording" }
cap-export = { path = "../../crates/export" }
cap-camera = { path = "../../crates/camera" }
cap-cli-install = { path = "../../crates/cli-install" }
scap-targets = { path = "../../crates/scap-targets" }
serde = { workspace = true }
serde_json = "1.0.133"
tokio.workspace = true
tokio = { workspace = true, features = ["signal"] }
tokio-util = { version = "0.7", features = ["io"] }
uuid = { version = "1.11.1", features = ["v4"] }
ffmpeg = { workspace = true }
reqwest = { version = "0.12.24", features = ["json", "stream"] }
kameo = "0.17.2"
flume = { workspace = true }
futures = { workspace = true }
dirs = "6.0.0"
image = "0.25.2"
tracing.workspace = true
tracing-subscriber = "0.3.19"
flume.workspace = true
workspace-hack = { version = "0.1", path = "../../crates/workspace-hack" }

[target.'cfg(unix)'.dependencies]
libc = "0.2"

[target.'cfg(windows)'.dependencies]
windows = { workspace = true, features = [
"Win32_Foundation",
"Win32_System_Threading",
] }

[target.'cfg(target_os = "macos")'.dependencies]
cidre = { workspace = true }
scap-screencapturekit = { path = "../../crates/scap-screencapturekit" }

[dev-dependencies]
tempfile = "3"

[lints]
workspace = true
85 changes: 85 additions & 0 deletions apps/cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# cap CLI

Cap screen recording, driven from the command line. The `cap` binary is built for automation and AI
coding agents (Claude Code, Codex, OpenCode, Cursor): every command speaks JSON, errors are
machine-readable, and recordings have an explicit start/stop lifecycle.

## Install

- **From Cap Desktop:** Settings → Command Line → Install CLI (links the bundled binary onto your PATH).
- **Script:** `curl -fsSL https://cap.so/install-cli.sh | sh` (Windows PowerShell: `irm https://cap.so/install-cli.ps1 | iex`).

The desktop app and the CLI share the same binary, so the CLI is always in sync with the installed app.

## Agent skill (Claude Code / Agent SDK)

A ready-made skill lives at [`skill/cap/SKILL.md`](./skill/cap/SKILL.md). Install it so agents reach for
Cap proactively (e.g. "record a repro of this bug"):

```sh
cp -r apps/cli/skill/cap ~/.claude/skills/cap # or a project's .claude/skills/
```

The skill is intentionally thin — it delegates the authoritative contract to `cap guide --json`, so it
never drifts from the binary. Agents that don't consume skills (Codex, OpenCode, Cursor) get the same
information from `cap --help` and `cap guide --json` directly.

## The output convention (read this first)

- Pass `--json` (a global flag) to **any** command for machine-readable JSON on **stdout**. A command's
own `--format json` works too; `--json` is the order-insensitive shortcut (`cap --json targets` and
`cap targets --json` both work).
- **stdout** is the authoritative result. **stderr** is human-readable logs plus a final
`error: <message>` line on failure.
- Failures exit **non-zero**. In `--json` mode a final object/event carries an `error` string field, so
a single `"error" in obj` check detects failure across every command. clap usage/parse errors exit `2`.
- `record` and `export` stream **newline-delimited JSON (NDJSON)** events on stdout.
- Fetch the full machine-readable contract any time with **`cap guide --json`**.

## Authentication

`cap upload` authenticates automatically by **reusing the login Cap Desktop already stored** — if the
user is signed into the desktop app, there is no key to fetch or set. Check with `cap auth status --json`
(`{"authenticated":true,"source":"desktop","server":"…","userId":"…"}`); it never prints the secret.

For headless/CI (or to override), set `CAP_API_KEY` to a Cap auth key from Settings. The target server
is taken from `CAP_SERVER_URL`, else Cap Desktop's configured server, else `https://cap.so`.

## Environment variables

| Variable | Used by | Notes |
| --- | --- | --- |
| `CAP_API_KEY` | `upload` | Overrides auth with a Cap auth key (Settings). Optional when signed into Cap Desktop. |
| `CAP_SERVER_URL` | `upload` | Cap server base URL. Defaults to Cap Desktop's server, else `https://cap.so`. |
| `CAP_NO_MODIFY_PATH` | `desktop install-cli` | Set to skip editing shell profiles / user PATH. |

## Typical agent workflow

```sh
cap doctor --json # verify permissions & capture readiness (exits 0; read `ok`/`captureReady`)
cap targets --json # discover screens/windows/cameras/mics (ids feed the next steps)
cap record start --screen <id> --json --detach # start in the background -> {"type":"started","recordingId","pid","path"}
# ... the agent performs whatever it needs to capture ...
cap record stop --id <recordingId> --json # finalize -> {"type":"stopped","path","recordingMetaExists":true}
cap project validate <path.cap> --json # confirm the recording is complete before exporting
cap export <path.cap> --output out.mp4 --json
cap upload out.mp4 --json # -> {"type":"uploaded","id","link"} (auto-auth via Cap Desktop)
```

`cap upload <path.cap> --export --json` will export a project to its default output and upload it in one
step.

## Commands

- `cap record start` / `record stop` / `record status` — record (foreground, or `--detach` for background) and manage sessions.
- `cap export` — render a `.cap` project to mp4/gif/mov. Here `--format` selects the **container**; use `--json` for machine-readable output.
- `cap screenshot` — capture a still of a screen/window (`--json` → `{path,width,height}`).
- `cap targets` (`screens`/`windows`/`cameras`/`mics`) — enumerate capture inputs.
- `cap project inspect` / `validate` / `config get|set` — inspect and edit `.cap` projects.
- `cap recordings list` — list `.cap` recordings in the desktop library.
- `cap upload` — upload a `.cap` project or video file and get a shareable link.
- `cap doctor` / `version` / `guide` — diagnostics, version info, and the agent capability manifest.
- `cap desktop status|install-cli|uninstall-cli` — manage the `cap` shim on PATH.
- `cap completions <shell>` — shell completion scripts (bash/zsh/fish/powershell).

Run `cap --help` or `cap <command> --help` for full flag documentation.
80 changes: 80 additions & 0 deletions apps/cli/skill/cap/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
name: cap
description: >-
Record the screen, capture screenshots, and create shareable video links from the command line using
the Cap CLI. Use when the user wants to record a screen demo or bug repro, capture a screenshot of a
screen/window, produce a Loom-style shareable video link, or automate/script screen recording. Requires
the `cap` command to be installed (Cap Desktop, https://cap.so).
---

# Cap CLI

`cap` is a command-line screen recorder built to be driven by agents. Every command takes `--json` for
machine-readable output on stdout; stderr stays human-readable; failures exit non-zero and, in `--json`
mode, print a final object/event with an `error` field.

**`cap guide --json` is the authoritative contract** (output convention, env vars, exit codes, and every
command's output mode + event tags). Prefer it over guessing, and `cap <command> --help` for flags.

## First: check it's available

```sh
cap doctor --json
```

- If `cap` is not found, it isn't installed. Tell the user to install Cap Desktop from
https://cap.so/download, then enable the CLI (Settings → Command Line), or run
`curl -fsSL https://cap.so/install-cli.sh | sh` (Windows: `irm https://cap.so/install-cli.ps1 | iex`).
- Read `captureReady` and `permissions.screenRecording` from the output. On macOS, recording fails until
Screen Recording permission is granted in System Settings.

## Discover capture targets

```sh
cap targets --json # screens, windows, cameras, mics
```

Use a screen's `id` (or a window's `id`) for `--screen`/`--window`, a camera's `deviceId` for `--camera`,
and a mic's `name` for `--mic`.

## Record (background lifecycle — the usual agent pattern)

When you don't know the duration in advance: start detached, do the work, then stop.

```sh
cap record start --screen <id> --json --detach # -> {"type":"started","recordingId","pid","path"}
# ... perform the actions to capture ...
cap record stop --id <recordingId> --json # -> {"type":"stopped","path","recordingMetaExists":true}
cap record status --json # list active sessions
```

A `stopped` event is only a complete recording when `recordingMetaExists` is `true`.

Fixed-length alternative (no detach): `cap record start --screen <id> --duration 10 --json`.

## Screenshot

```sh
cap screenshot --screen <id> --path shot.png --json # -> {"path","width","height"}
```

## Export and share

```sh
cap project validate <path.cap> --json # confirm the recording is complete
cap export <path.cap> --output out.mp4 --json # render (here --format means container: mp4|gif|mov)
cap upload out.mp4 --json # -> {"type":"uploaded","id","link"}
```

`cap upload` authenticates automatically by reusing the user's Cap Desktop login — check with
`cap auth status --json`. If it reports `authenticated:false`, tell the user to sign into Cap Desktop,
or set `CAP_API_KEY` (a Cap auth key from Settings) for headless use. `cap upload <path.cap> --export
--json` exports then uploads in one step.

## Conventions to rely on

- Add `--json` to any command; it overrides each command's `--format`.
- Detect failure with a single check: the process exits non-zero and the JSON carries an `error` field.
- `record` and `export` stream newline-delimited JSON (NDJSON) events; everything else returns one object.
- `doctor`, `project validate`, and `recordings list` are reports — branch on their fields
(`ok`/`captureReady`, `valid`), not just the exit code.
Loading
Loading