Fixtureframedocs
CLI

Command reference

Every command the CLI answers to.

Run ff help for this list in the terminal, or ff help <command> for one command in detail. Both read from the same list in the CLI source, so they cannot disagree with each other — though they can drift from this page, which is written by hand.

For commands and agent prompts in execution order, see From sign-in to a published flow. /ff-context is an agent skill that writes APP_CONTEXT.md; it is not a CLI subcommand.

Setup

CommandWhat it does
ff initCreate the .fixtureframe workspace directory in this folder.
ff doctorCheck this workspace's configuration and report what is missing.
ff session initSign in to the target app interactively and save authentication state.
ff session statusInspect saved-session metadata without showing cookie values.
ff session clearRemove the workspace's saved session.
ff agent installInstall or refresh the coding-agent skills.
ff adapter list | init | inspectList, scaffold, or inspect auth adapters.

Authoring

CommandWhat it does
ff record [adapter]Record yourself using the application and save it as a flow.
ff deck <deck.pptx>Turn a slide deck into a draft flow: one step per slide, notes as narration.
ff media <file...>Turn pictures and clips you already have into a flow: one step per file.
ff publish <flow.json>Save a local flow file as a flow, or as the next version of one.
ff survey [adapter]Survey an environment read-only and store the map as authoring evidence.
ff discover <adapter>Explore an application with an adapter and save what it found.
ff scenario create | preview <adapter>Create or preview a capture scenario for an adapter.
ff sample interactiveCreate a sample interactive demo to exercise the player.

deck takes four shapes: a .pptx file, a .pdf file, a Google Slides link, and a public link to a deck anywhere else. Files are uploaded from here; a link is handed over and fetched by the control plane, which is the only side that can refuse one safely. Only the conversion happens there — laying a deck out faithfully needs an office suite.

media needs none of that, so it works locally: .png, .jpg, .webp, .svg, .mp4 and .webm, in the order given.

publish uploads any asset the file names by path and saves it as a URL, so a flow written by hand — pictures beside it, narration and card text written in it — is complete rather than dependent on the machine that wrote it. See Flow files.

record and script both photograph each step as they capture it, and publish those frames with the flow. Frames normally come from a run — a recording proves the steps, and only a replay proves they still resolve — but a flow nothing has run yet would otherwise open in Studio as a storyboard of rows saying "No frame", which is unreviewable for want of a picture the browser was already showing. A run's own frames replace them the moment one exists.

record --narrate records your microphone while you walk. The words are transcribed and written onto the steps as narration, so a walkthrough explained aloud arrives narrated rather than silent. It is off by default — a microphone is not visible the way a screen is — and it pauses when the recording does, so nothing said while you sign in by hand is kept.

Data

CommandWhat it does
ff profile create <name>Write a data profile from what a flow actually needs.
ff profile listThe profiles in this workspace, and what each one fills.

A flow declares parameters, a profile supplies their values, and the parameter name is the join between them — so a profile written by hand is a guess at names some flow chose, and when the guess is wrong the two simply fail to meet. No error, no run, just a value nobody filled.

profile create --from <flow.json> reads the flow and works out what it needs. A flow that declares parameters keeps them, because a declaration is a decision somebody already made. Otherwise the values are proposed from the flow itself — deterministically, with no browser and no model, so the same flow gives the same answer every time and the reason for each is printed beside it.

Names already used in the organisation are preferred over new ones, which is the whole point of the join: a merchant in one flow and a vendor in another are never filled by the same profile, and nothing reports it when they fail to.

Three kinds of value are named and then left out. A generated one differs per run by design. A secret is supplied by the runner's environment and never written to a file that travels. A route is where a flow runs rather than who it runs for, and belongs to the environment.

The profile is written to .fixtureframe/profiles/data/<name>.json and the record is saved to your organisation. By default only the parameter names are sent: the values stay in the file, which is what lets demo data that may not leave a network stay inside it. --inline sends the values too, so Studio can show and edit them; --local-only publishes nothing.

Keeping a repository of flows in sync

CommandWhat it does
ff pullFetch the control plane's flows into this working copy.
ff pushSave changed flow files to the control plane as new versions.

publish saves one file you were just told about. pull and push are for a directory of flows kept under review: they work out which side of each flow moved since the two last agreed, and carry only that. Running either twice does nothing the second time, which is what makes them safe from a merge hook.

Both refuse rather than guess when both sides moved, and both exit non-zero when anything was left alone — a conflict reported only on stdout is a conflict a pipeline never notices. --force picks a side: pull --force takes the control plane's, push --force takes the file's.

What the two sides last agreed on is recorded in .fixtureframe/flow-sync.json, beside config.json. Commit it with the flow files. It stores two hashes per flow — the file's and the saved flow's — which is what lets an edit be told from a conflict; without it, every difference looks like both. Two hashes rather than one because a flow file that names pictures by path is meant to differ from the saved flow, which carries the URLs they were uploaded to.

ff pull --dry-run     # what would come down
ff pull               # take it
ff push --dry-run     # what would go up
ff push               # save it

Both group every file by outcome and end with one summary line, so nothing is left implicit — a file that was considered and found unchanged is listed as such, rather than being absent and indistinguishable from one the command never reached.

Would create (1):
  .fixtureframe/flows/onboarding.flow.json  Onboarding

Would save as a new version (1):
  .fixtureframe/flows/submit-an-expense.flow.json  Submit an expense v2

Left alone (1):
  .fixtureframe/flows/download-a-report.flow.json — "Download a report" changed on the control plane. Pull it, or push --force to overwrite.

3 flow files: 1 created, 1 saved, 1 left alone.

What actually travels

Only the flow definition. pull writes *.flow.json and nothing else; push sends the same file back as a new version.

Run outputs — story plans, render plans, transcripts, captions, video, captured frames, DOM captures — are not synced in either direction. They belong to a run rather than to a flow: a run produces them, they are uploaded as artifacts under the run that made them, and they are read back with ff status <run-id> or in Studio's gallery. A flow file is the input; those are the evidence of one execution of it.

Two things travel alongside the definition, in one direction each:

  • Up: any asset the definition names by path — a picture, a clip, a music bed. push uploads it to your organisation's library and saves the flow with a URL, exactly as publish does, and only for the flows it is actually saving. --keep-paths declines.
  • Neither: .fixtureframe/flow-sync.json is written locally and never sent. It is bookkeeping for this checkout, and it belongs in git rather than in the control plane.

pull does not download asset files. A pulled definition carries the library URLs, which is what makes it renderable anywhere; the originals stay wherever their author kept them.

Checking, before anything costs anything

These are free. They open no browser, spend nothing, and change nothing — which is the point: each answers a question that would otherwise be answered by a capture, two to four minutes later.

CommandWhat it does
ff brief <brief.yaml>Check a brief, and show who agreed to what.
ff lint <walk.mjs>Check a walk against the surveyed product without running it.
ff suggestRead the repository for the walkthroughs it has not written yet.
ff diagnose <run-id>What went wrong in a run, for whoever is going to fix it.

lint never surveys. Without a survey of the environment it says so and stops, because five of its eight rules need one — and a rule that cannot match reports nothing, which reads exactly like a rule that found nothing wrong. ff survey fills it in, once per environment.

brief and lint both exit 2 when the thing they checked has problems, and 1 when the command itself did not run. Every command here takes --json.

Coding agents

CommandWhat it does
ff agent installInstall the skills and the permission table for the agents this repository uses.

See the agentic pipeline for what these instruct, and installing the skills for what lands where.

Execution

CommandWhat it does
ff validate <flow.json>Check a flow file against the canonical schema without running it.
ff script <walk.mjs>Record a flow by running a Playwright script rather than walking it yourself.
ff run [flow.json]Execute a governed run and publish what it produces.
ff status [run-id]Show what a run is doing, or what it produced.

script reads a brief named after the walk before it runs anything. If that brief's beats are not confirmed, the walk is refused — capturing from beats nobody agreed to spends a vendor call and leaves state behind for a story nobody has read. No brief at all is a warning, unless the project sets require_brief in .fixtureframe/config.json.

Flags every control-plane command takes

FlagMeaning
--api-url <url>Control plane to talk to. Defaults to FIXTUREFRAME_API_URL.
--environment <name>Environment by name, as shown in Settings.
--environment-id <id>Environment by id, when two share a name.
--directory <path>Workspace directory holding .fixtureframe. Defaults to the current one.

ff suggest is the one command that also reads a second directory — the product's code, which is routinely a different checkout from the workspace holding the credentials.

FlagMeaning
--source <path>The code to read for end-to-end tests. Defaults to source in .fixtureframe/config.json, then the current directory.
--include <path>Another directory holding browser tests, for a project that keeps them somewhere unconventional. Repeat for each.
--catalog <path>An existing catalogue whose decisions — including every rejection — must be honoured.

The source fallback resolves against the current directory rather than the workspace, so pointing --directory at one place and standing in another scans where you are standing. See Working in a session.

Flags the asset-uploading commands take

record, script, media, publish and push all put files in your organisation's library. record and script upload the frames they photographed — and, for record --narrate, the take they recorded — through the same route a hand-written flow's pictures take. The flow file keeps its relative paths, which is what makes it re-publishable; the copy that leaves carries URLs.

FlagMeaning
--organization <id>Whose library to upload to. Derived from the API key; needed only when the key names several.
--assets-dir <path>publish and push: where local asset paths resolve from. Defaults to each flow file's own folder.
--keep-pathspublish and push: save local paths as written, without uploading.
--local-onlyWrite the flow file and save nothing. media still uploads its files; the others upload nothing.

push uploads only for the flows it is actually saving, so an unchanged flow that names a picture costs nothing.

On this page