Skip to content

CLI

The screenci CLI keeps the workflow small: initialize a project, iterate locally with screenci test, refine videos with screenci preview and the web editor, export the finished videos with screenci export, and manage public delivery when needed. Run commands from inside your screenci/ project directory. Most commands resolve screenci.config.ts from the current directory unless you pass --config <path>.

Command overview

Command Purpose
screenci start <code> Set up a workspace from a setup code created in the web app
screenci context Print the organisation’s AI context for this project
screenci login [url] Sign in to your own app once and reuse that session in every recording
screenci logout Forget the signed-in session saved on this machine
screenci merge-complete Report that sources pulled by a “Move to repository” code were committed
screenci init [name] Scaffold a ScreenCI project
screenci test [playwrightArgs] Run .screenci.ts files locally without final recording
screenci preview [pattern] Record live previews and print their links
screenci export [patterns...] Produce finished videos: re-record every requested video, render, and download
screenci info Print the last record run’s URLs and render status
screenci make-public <videoId> Enable public delivery for a video
screenci make-private <videoId> Disable public delivery for a video
screenci delete <videoId> Permanently delete a video and its renders

screenci start <code>

The entry point of the web-app flow. The Add project, Add video, Edit, and Move to repository buttons in the app produce a prompt that carries a one-time setup code and points the agent at the brief for that button (screenci.com/add-project.md, /add-video.md, /edit-video.md, or /merge-sources.md); the coding agent runs the command in the repository of the app to record, or in an empty folder:

Terminal window
npx screenci@latest start SC-7K3Q-M9XA

It runs without a config file (there is no island yet) and does the following:

  1. Exchanges the code for a project-scoped SCREENCI_SECRET and a personal and writes it into the workspace’s env file. The secret only addresses that project. A code belongs to the first machine that exchanges it (rerunning there resumes it until a run lands; other machines are refused) and expires 24 hours after it was created.
  2. Reads the organisation’s AI context (with the project’s overrides) and locates the repository: the current checkout when its origin is the configured repository, else a shallow clone in .screenci/repo (refreshed when it exists). A failed clone is reported and the command continues when the site answers.
  3. Prepares the workspace, screenci/ inside the repository when one exists there, else ./screenci:
    • Absent, new project: scaffolds the island like init --yes (agent skills on, no GitHub workflow) and writes projectId into screenci.config.ts.
    • Absent, existing service-managed project: pulls the project’s latest uploaded sources, installs dependencies and the Playwright browser, and installs the agent skills at the repository root. Binary media is not pulled; recordings reuse the project’s uploaded assets.
    • Present, same projectId: syncs the sources in place. Local files that differ from the project’s sources are refused (listed, nothing written) unless --force is given.
    • Present, repository-managed (no projectId), same project name: used as is; the agent commits its change to git.
    • Present, another project: refuses; use --dir.
    • Merge code: pulls the sources into screenci/ inside the repository, removes projectId from the config, and writes .screenci/pending-merge.json for merge-complete.
  4. Looks for a signed-in session on this machine (.screenci/auth/default.json, saved by login) and reports it in the brief. Nothing about your own product’s credentials comes from, or goes to, ScreenCI.
  5. Probes the site (the prompt’s app URL, else the context’s site URL). When nothing answers and the agent may not start the app, the brief says STOP and the command exits with code 2; see Running the app locally.
  6. Prints a brief for the agent (the task, the repository, the site, how to sign in, the team’s notes, which script to edit, the commands to run) followed by one machine-readable JSON line ({"status":"started", ...}, or "stopped" with a stop object).

Options:

  • --name <projectName> names a new project. Precedence: this flag, then the name typed in the web dialog, then the current folder name.
  • --dir <path> uses another workspace folder than ./screenci.
  • --force overwrites local files that differ from the project’s sources.
  • --package-manager <npm|pnpm|yarn> overrides the auto-detected manager.
  • --agent <name> targets a specific coding agent for the skills install.
  • --skip-site-check records even when the site does not answer.
  • --no-clone never clones the repository when running outside it.
  • -v, --verbose prints underlying command output.

After start, the usual commands run from inside the workspace: test, preview, export. For a service-managed project they also upload the island’s text sources (see Source sync) and report the finished run, which is what makes the originating browser tab open the result.

screenci context

Prints the organisation’s AI context as resolved for the workspace’s project (repository URL, site URL, whether the agent may start the app, whether the site needs a sign-in, the team’s notes), whether a signed-in session is saved on this machine, the branding new videos start from, and one JSON line (with a branding key). When the branding voice is cloned from a sample, the sample is saved as branding/<file> in the workspace and the JSON line names it as brandingSamplePath. The shared branding assets are listed with their kind and guide, saved as branding/<name>, and named in the JSON line as brandingAssetPaths. Runs inside a workspace; uses SCREENCI_SECRET from the env file. The session line is read from disk, not from the service.

Terminal window
npx screenci context
npx screenci context --json # the JSON line only

screenci login

Captures the signed-in session of your own app, so recordings start signed in and your video scripts contain no sign-in steps. It opens a real browser you sign in to yourself, which is why two-factor, single sign-on, passkeys, and magic links all work. See Signing In for the whole picture.

Run it from the screenci/ workspace. It needs no ScreenCI account and no secret: an address is enough.

Terminal window
npx screenci login https://app.example.com # opens a browser, returns at once
npx screenci login # uses use.baseURL from the config
npx screenci login --wait # blocks until you finish signing in
npx screenci login --done # finish it now, from the terminal
npx screenci login --status # what is saved, and until when
npx screenci login --cancel # close it, save nothing

The command returns immediately so whoever started it can tell you to sign in. Finish by clicking I’m signed in on the small ScreenCI card floating over the page (drag it if it is in the way); closing the window finishes it too.

An agent then runs --wait, which blocks until you do, so your click actually reaches it. Without that the agent has ended its turn and the click tells it nothing. --wait gives up after eight minutes and says the sign-in is still going, so it fits inside an agent’s command timeout and can simply be run again. --done is the other direction: it finishes the sign-in right now, for when you say you are done rather than clicking.

Options:

  • --profile <name> names the session, for a video that needs a second role. Pick it for a run with SCREENCI_AUTH_PROFILE=<name>.
  • --timeout <minutes> closes the browser by itself after this long (default 30).
  • -c, --config <path> points at another screenci.config.ts.

The session is written to .screenci/auth/<profile>.json, owner-readable only and gitignored, and screenci.config.ts picks it up automatically. It never leaves the machine: ScreenCI stores no credential and no session for your app. Nothing prints its contents, --status included.

On a machine with no display (a server, a container, CI) the command refuses and points at CI Setup, which is where an unattended session comes from.

screenci logout

Forgets the saved session on this machine.

Terminal window
npx screenci logout
npx screenci logout --profile admin

screenci merge-complete

The last step of Move to repository. Run it from the screenci/ workspace inside the repository after committing and pushing the sources start pulled:

Terminal window
npx screenci merge-complete --pr https://github.com/acme/app/pull/42

The commit (HEAD), branch, and repository URL (origin) come from git, and the pulled source bundle from .screenci/pending-merge.json; --commit, --branch, --git-url, and --bundle override them. ScreenCI then marks the project as repository-managed and shows an In repository badge on every version recorded from those sources.

screenci init

Create a new ScreenCI project in the current directory:

Terminal window
npm init screenci@latest

The project name defaults to the repository root directory name.

Connecting to an existing account

No account or secret is needed to scaffold, record, or preview: without one, preview uploads under a local, anonymous trial session and prints the web preview link, then you sign up to keep it. Recording an anonymous trial agrees to the Terms, which the CLI prints before it starts. Exporting requires an account with an active paid subscription.

To connect the project to an account you already have, pass your SCREENCI_SECRET (from your secrets page) as init’s positional argument, and it writes the secret straight into screenci/.env:

Terminal window
npm init screenci@latest PASTE_YOUR_SCREENCI_SECRET_HERE

You can also skip this and copy SCREENCI_SECRET into screenci/.env by hand at any point before recording.

When using npm init, pass extra initializer flags after --:

Terminal window
npm init screenci@latest -- --yes --package-manager pnpm
npm init screenci@latest -- --yes --package-manager yarn

The package manager is auto-detected from the npm_config_user_agent environment variable (set automatically when you run pnpm create or yarn create), lockfile presence (pnpm-lock.yaml, yarn.lock), or the packageManager field in package.json. Use --package-manager to override.

To keep setup fast, init only prompts for the choices that genuinely vary: the project name, whether to add the GitHub Actions workflow, and whether to install AI agent skills (the ScreenCI skill plus playwright-cli). Everything else is applied at a sensible default and can be steered with a flag.

Common options:

  • -y, --yes accepts all defaults and skips every prompt
  • --package-manager <npm|pnpm|yarn> overrides auto-detected package manager
  • --agent <name> passes an agent name to the selected skills install command
  • -v, --verbose prints underlying command output

Flags to override an auto-applied default without going interactive:

  • --no-github-workflow skips the GitHub Actions workflow (and its prompt)
  • --no-skills skips both AI agent skills (and their prompt)
  • --no-playwright-cli keeps the ScreenCI skill but drops playwright-cli (the skill and the @playwright/cli dev dependency)
  • --no-react skips React overlay support (react/react-dom and JSX)
  • --no-playwright-browsers skips installing the Chromium shell
  • --playwright-os-deps installs Playwright operating system dependencies (off by default because it may require sudo)

Applied defaults: create the GitHub Actions workflow, install dependencies, add React overlay support, install the Chromium shell, skip OS dependency installation, and install the AI agent skills (ScreenCI + playwright-cli).

The AI skills install is best effort: if it fails (for example on an older Node.js runtime), init warns, prints the command to retry it later, and continues with the rest of the setup.

Use this command in Manual Setup & First Video.

screenci test [playwrightArgs...]

Run videos locally without the final recording pipeline:

Terminal window
npx screenci test
npx screenci test recordings/onboarding.screenci.ts
npx screenci test --grep "billing"
npx screenci test --ui

Use this during normal authoring. Most trailing arguments are forwarded to Playwright.

Plain screenci test disables recording-style waits so authoring runs stay fast. That includes ScreenCI cursor/camera pauses and page.waitForTimeout(...), which is collapsed to 0ms. Use Playwright locator or load-state waits for real application readiness. Use --mock-record when you need to preview the same pacing that a real recording will capture.

Common Playwright examples that also work here:

Terminal window
npx screenci test --project=chromium
npx screenci test --grep "onboarding"
npx screenci test --ui

--mock-record

Terminal window
npx screenci test --mock-record

This keeps recording-like pacing enabled without starting the real recording capture path. Use it when test passes but a real recording exposes timing differences.

If you want that behavior by default for a project, set test.mockRecord: true in screenci.config.ts.

screenci export [patterns...]

Produce finished videos and download them. export is the one-shot command (also the one CI runs): it pulls any queued editor edits into the sources, re-records every requested video (sources can carry changes a hash check would miss, so export always captures fresh footage), waits for the renders to finish, and downloads the outputs into ./exports/:

Terminal window
npx screenci export
npx screenci export "Onboarding"
npx screenci export --grep "billing"
npx screenci export --languages fi,en

Positional arguments are title patterns (multiple are OR-combined, like playwright test <pattern>); with no patterns, every video is exported in every language. Downloads are named <title>.<lang>.mp4 (screenshots use .png). A service-managed project uploads its island sources first (see Source sync). The command polls render status every 5 seconds for up to 30 minutes and exits 0 only when every requested video rendered and downloaded. After a run it prints the video’s page in the app: a run with exactly one rendered pass links that video’s overview page with the run preselected (<app>/project/<projectId>/video/<videoId>?export=<recordId>); several passes (several videos, or one video exported in several languages) link the run’s page (<app>/export/<recordId>), which links each version.

--languages <langs>

Export only specific language versions of videos declared with video.languages([...]). Pass a comma-separated list:

Terminal window
npx screenci export --languages fi
npx screenci export --languages fi,en

Per-language videos record and render only the requested languages, so a run never produces more than you asked for. Videos that do not declare those languages are skipped. A shared-mode recording ({ mode: 'shared' }) is a single capture and is not split by this filter. See Languages for the recording API.

-o, --output <dir>

Directory for the downloaded files. Defaults to exports:

Terminal window
npx screenci export -o dist/videos

--no-wait

Start the renders and exit immediately, without waiting for them or downloading files. Use this when the finished videos are consumed from the web (public URLs or embeds) and the run only needs to publish fresh footage:

Terminal window
npx screenci export --no-wait

The command still records, uploads, and dispatches the renders, and prints the results URL. It exits 0 as soon as the upload succeeds; render failures are visible on the results page, not in the exit code. Without this flag, export waits for every render and exits non-zero if any of them fails.

--share

Instead of downloading files, share each finished version with a permanent, version-pinned public URL and print the URLs:

Terminal window
npx screenci export --share

The command records, renders, and waits like a normal export, then shares the run’s finished versions and prints one public URL per video and language (plus thumbnail and subtitle URLs when they exist). A shared version keeps serving that exact render until it is unshared or deleted from the app: it is exempt from version retention, and the URL works even when the video’s own public URL is switched off. See version-pinned URLs for the URL shape and behavior.

Shared versions count against your organization’s shared-version limit (1000 by default, shown on the billing page). When the limit is reached the remaining versions are not shared and the command exits non-zero with the refusal message.

--share needs finished renders, so combining it with --no-wait is an error. It exits 0 only when every requested render finished and was shared.

--force

Deprecated no-op, kept so existing scripts do not break: export always re-records every requested video.

Behavior:

  • enables recording timing for every requested video
  • writes local output into .screenci/
  • holds a per-project run lock at .screenci/.record.lock while recording and uploading. If another screenci preview or screenci export run is already active, the second run exits non-zero instead of sharing the same .screenci output. Stale locks are reclaimed automatically when the pid is gone or the lock is older than the maximum run TTL
  • requires an account with an active paid subscription: without a SCREENCI_SECRET, export refuses before recording and prints a sign-up link (the anonymous trial is preview-only; use screenci preview for the free live preview, see Anonymous Trial). Once you have signed up, re-running export in the same folder links your account automatically. Set SCREENCI_SECRET from the project .env, the environment, or by running init with it
  • uploads only the recordings requested by this run (for example the videos matched by patterns, --grep, or --languages), with or without SCREENCI_SECRET
  • exits non-zero unless every requested video rendered and downloaded. If a requested video is missing its recording.mp4 or another failure occurs, export does not save .screenci/last-record.json and does not print a success URL for unrelated output
  • prints any informational notices returned by the service in cyan after upload (occasional, non-error status messages such as maintenance or timing notes). These never affect the exit code
  • new accounts choose a plan before exporting: exports need an active Starter, Pro, or Business subscription. Higher tiers raise export and active-video limits and unlock multiple languages

Relevant options:

  • -c, --config <path>
  • -v, --verbose
  • -g, --grep <pattern> (same filter as Playwright’s --grep)

Important restriction:

  • --retries is not supported because ScreenCI forces retries to 0

screenci preview [pattern]

Records live previews and prints their links: it re-records and uploads the live preview of every matched video (all declared videos when no pattern is given), prints the link, and exits. A preview always records fresh footage; there is no freshness skip. A run with exactly one recording pass prints that video’s overview page (<app>/project/<projectId>/video/<videoId>); several passes (several videos, or one video recorded in several languages) print the run listing page (<app>/preview/<recordId>), which links each video and language.

Terminal window
screenci preview

Filter videos by passing a title pattern, the same way playwright test <pattern> does; --grep does the same and takes precedence when both are given.

Terminal window
screenci preview "Auto-zoom"

Right before recording a preview, the CLI sends a best-effort “preview recording started” notice to the backend, so an open web preview page shows a live “Recording preview…” indicator and refreshes automatically when the new preview lands.

Source sync

For a service-managed project (projectId in screenci.config.ts, written by screenci start) or a project with uploadSources: true, preview and export upload the island’s text sources right before recording, and report the finished run afterwards:

  • The bundle holds screenci.config.ts, package.json, tsconfig.json, .prettierrc, .gitignore, README.md, the package-manager workspace files, and every text file under recordings/. Env files, lockfiles, node_modules, .screenci, exports, and binary media are never included. Files over 256 KB and anything past 2 MB in total are skipped with a warning.
  • The bundle is content addressed: an unchanged island is not re-uploaded. Its canonical form is { "files": [{ "path", "content" }] } sorted by path (plain code-unit order), hashed with SHA-256 over the JSON text.
  • Both steps are best effort. A failed upload warns and the recording still runs (the next run retries); the run report never fails a command.
  • Anonymous previews (no SCREENCI_SECRET) never sync sources.
  • A project-scoped secret that pins a different project than the island’s projectId (an env file copied from another workspace) stops the run before recording, with the pinned project’s name in the message.

A run prints one line per phase: the record announcement, the upload results, one line per video whose editor-uploaded media (overlays, audio, narration audio, cloned voices) shapes the render, and the video link:

Recording: Search for Alfa Forni Moderno 2
...
✔ Uploaded "Search for Alfa Forni Moderno 2 [en]"
✔ Uploaded "Search for Alfa Forni Moderno 2 [et]"
Editor-uploaded media for "Search for Alfa Forni Moderno 2" applies at render time; recordings always run from code.
Open the live preview for "Search for Alfa Forni Moderno 2" at:
<app>/project/<projectId>/video/<videoId>

Options:

  • -c, --config <path>: path to the ScreenCI config file.
  • -g, --grep <pattern>: only manage videos whose title matches this pattern (the same filter as Playwright’s --grep, and the same as a positional pattern above). The record pass is limited to the matching videos.
  • -v, --verbose: verbose output.

Setup (with an account): none beyond SCREENCI_SECRET. That secret is the only credential ScreenCI needs; there is nothing else to paste.

Without an account, no setup is needed either: screenci preview runs under a local, anonymous trial session (see Anonymous Trial). Signing up claims the trial, and the next screenci command picks up your new SCREENCI_SECRET (written into screenci/.env).

screenci info

Terminal window
npx screenci info

Lists every video in the project with its public URLs as JSON, keyed by video name and language code. It is where you get the remote videoId for make-public, make-private, and delete.

When this machine has recorded a successful run, info also reports that run. Each screenci export upload stores its unique record id in .screenci/last-record.json; info then attaches, only to the videos produced by that run, a per-language latestRecord with the run’s render status and its record-pinned URLs, plus a latestRecordId on the video. Videos that were not part of the run are still listed, with static URLs only. This is the command to use in CI to gate on rendering or grab links right after an export.

{
"projectName": "My Product",
"videos": {
"Onboarding": {
"videoId": "kh74…",
"latestRecordId": "5f1c…",
"isPublic": true,
"languages": {
"en": {
"static": {
"video": "https://api.screenci.com/public/kh74…/en/video",
"thumbnail": "https://api.screenci.com/public/kh74…/en/thumbnail",
"subtitle": "https://api.screenci.com/public/kh74…/en/subtitle",
"screenshot": "https://api.screenci.com/public/kh74…/en/screenshot"
},
"download": {
"video": "https://api.screenci.com/cli/download/kh74…/en/video",
"thumbnail": "https://api.screenci.com/cli/download/kh74…/en/thumbnail",
"subtitle": "https://api.screenci.com/cli/download/kh74…/en/subtitle",
"screenshot": "https://api.screenci.com/cli/download/kh74…/en/screenshot"
},
"latestRecord": {
"status": "finished",
"video": "https://api.screenci.com/public/kh74…/records/5f1c…/en/video",
"thumbnail": "https://api.screenci.com/public/kh74…/records/5f1c…/en/thumbnail",
"subtitle": "https://api.screenci.com/public/kh74…/records/5f1c…/en/subtitle",
"screenshot": "https://api.screenci.com/public/kh74…/records/5f1c…/en/screenshot",
"download": {
"video": "https://api.screenci.com/cli/download/kh74…/records/5f1c…/en/video",
"thumbnail": "https://api.screenci.com/cli/download/kh74…/records/5f1c…/en/thumbnail",
"subtitle": "https://api.screenci.com/cli/download/kh74…/records/5f1c…/en/subtitle",
"screenshot": "https://api.screenci.com/cli/download/kh74…/records/5f1c…/en/screenshot"
}
}
}
}
}
}
}

Each language exposes up to three URL sets:

  • static: stable, public URLs that always follow the currently selected version
  • download: authenticated download URLs for the selected version (see below)
  • latestRecord: this run’s render status plus public URLs pinned to it via a records/<recordId> path segment (an immutable contract: the exact run, or 404, see resolution rules). Once finished, it also carries record-pinned download URLs.

Each URL set carries one entry per asset: video, thumbnail, subtitle, and screenshot. A version is either a video or a screenshot, never both, so use the video asset for video recordings and the screenshot asset for screenshot recordings: the asset that does not match the version’s type 404s when fetched.

static is null for videos without public delivery enabled. latestRecord carries the render status even for private videos, but its public URLs are then null.

The download URLs are private: they require your X-ScreenCI-Secret header, so they are not embeddable, and they work even for private videos. Use them to archive a render permanently, before older versions are pruned (see Keep a render forever):

Terminal window
curl -H "X-ScreenCI-Secret: $SCREENCI_SECRET" \
"https://api.screenci.com/cli/download/kh74…/records/5f1c…/en/video" \
-o video.mp4

Render status (under latestRecord.status) is one of:

  • finished: a render finished and is servable
  • rendering: a render is still in progress (or has not started)
  • failed: a render failed and none for that language is still in progress

Because rendering happens after upload, poll info until each latestRecord reaches finished (or gate your pipeline on failed).

Not recorded on this machine? If there is no .screenci/last-record.json (for example a fresh checkout that has never run screenci export), info does not fail. It simply omits latestRecordId and the latestRecord fields and prints the project-wide listing with static URLs only.

screenci make-public <videoId>

Terminal window
npx screenci make-public kh74…

Enables public delivery for a video. Get the ID from screenci info.

When you make a video public, ScreenCI starts it in the same mode as the app:

  • public delivery is enabled for the video
  • auto-select latest is enabled
  • the latest finished render for each language becomes the active public output

That means make-public is the CLI equivalent of turning on Enable public URL in the dashboard.

screenci make-private <videoId>

Terminal window
npx screenci make-private kh74…

Disables public delivery for a video.

This is the CLI equivalent of turning off Enable public URL in the dashboard.

screenci delete <videoId>

Terminal window
npx screenci delete kh74…

Permanently deletes a video and all of its renders. Use the same videoId that make-public and make-private accept (copy it from screenci info).

This is irreversible, so the command prints the video’s name and asks you to confirm before deleting. Pass -y (or --yes) to skip the prompt in CI or scripts:

Terminal window
npx screenci delete kh74… --yes

You can only delete videos that belong to your own project’s organization.

What the CLI does not do

The CLI currently covers:

  • workspace setup from a web-app setup code with start
  • reading the organisation’s AI context with context
  • signing in to your own app with login and logout
  • finishing a move to the repository with merge-complete
  • project scaffolding with init
  • project auth setup (handled automatically on first export)
  • local iteration with test
  • live previews and editing sessions with preview
  • finished videos with export
  • per-run URLs and render status with info
  • public visibility changes with make-public and make-private
  • permanent video deletion with delete

Manual version pinning is currently handled in the app UI:

  • turn off Auto-select latest version
  • open a language section
  • choose the version to mark as Selected

Shared environment and config behavior

These commands support --config <path>:

  • context
  • login
  • logout
  • merge-complete
  • test
  • preview
  • export
  • info
  • make-public
  • make-private
  • delete

SCREENCI_SECRET is used for:

  • auth bootstrap and persistence
  • uploads
  • per-run URLs and render status (info)
  • public delivery changes
  • video deletion (delete)

If envFile is configured in screenci.config.ts, the CLI loads it automatically. Otherwise it falls back to the project .env. The CLI resolves envFile by evaluating the config the same way Playwright does, so a dynamic value like envFile: isLocal ? '.env.local' : '.env' picks the right file at run time.

That env file is the recommended place to keep SCREENCI_SECRET and other runtime variables your setup needs. Your ElevenLabs key is not kept here: add it on the Branding page in the app instead (see Narration).