Skip to content

CLI reference

Every command and flag of pinrail, from the same definition pinrail --help prints. For the commands in use, see The CLI.

Every command accepts these.

ArgumentDescription
--url <URL>The server’s URL [default: PINRAIL_URL, then the address in the running server’s server.json, then http://127.0.0.1:4747]. Env: PINRAIL_URL.
--prettyIndent the JSON output. Requires --json.
--jsonPrint JSON instead of Markdown, for a script or tool that processes the result. PINRAIL_JSON=1 sets this for a whole session. Env: PINRAIL_JSON.
--verboseAlso report each step on stderr, such as the origin read from git, the server started, the files uploaded and where files were written. Env: PINRAIL_VERBOSE.

Submit a review, and with --wait, wait for the decision and print it.

The options build the review: the plugin, --title, and the payload from --data. Alternatively, give the whole request as one JSON file with --request, in the form the API takes:

{
"plugin": "list",
"title": "Sentry triage",
"origin": {"repo": "acme"},
"payload": {"groups": []}
}

The keys are plugin, title, payload, origin, revises, expires_at and requested_by. Options override the file’s keys, and --data replaces its payload, so you can send a new round with the same file, --revises and the id of the earlier round. You can leave out the plugin argument when the file names one.

To send files with the payload, use --attach, for a plugin that accepts files (pinrail plugins shows which plugins do). The payload refers to each file as {“$attachment”: ""}. The name is the file’s own name, or the name given after =:

pinrail submit model --data models.json \
--attach out/pivot.glb --attach out/v2.glb=column.glb

In a --request file, list the files under “attachments”, such as {“pivot.glb”: “out/pivot.glb”}, with paths relative to the file. An entry can also be {“path”: …, “media_type”: …}, as in a plugin’s fixtures, so you can send a fixture unchanged. Pinrail checks the submission before it uploads anything, and it does not upload a file that the app already has.

Terminal window
pinrail submit [PLUGIN] [OPTIONS]
ArgumentDescription
<PLUGIN>The plugin that defines this kind of review, such as review.
--title <TITLE>The title the inbox shows for the review.
--request <JSON|FILE|->The whole request as JSON, given inline, as a file path, or as - for stdin. Other options override its keys.
--origin <ORIGIN>Where the review comes from, as comma-separated key=value pairs. The keys are repo (the project, as owner/name), ref (a branch or pull request), workflow and run_id (what asked for the review), and url (a link back, which may contain commas). In a git checkout, repo defaults to the remote, and ref defaults to the branch when the repo is the checkout’s own. For example: repo=acme/api,ref=42,url=….
--data <JSON|FILE|->The payload as JSON, given inline, as a file path, or as - for stdin.
--attach <PATH[=NAME]>A file to send with the payload. The payload refers to it as {“$attachment”: ""}, where the name is the file’s own name unless another name follows =. Repeat the option for more files.
--revises <REVISES>The id of the review that this one is a new round of.
--expires-at <EXPIRES_AT>The time after which the review expires, as an ISO 8601 timestamp.
--requested-by <REQUESTED_BY>Who is asking, shown on the review as its requester, with the agent’s icon when the app knows the agent [default: the coding agent the command runs under, detected from the variables it sets (claude-code, codex, cursor, antigravity, grok, opencode, kimi), or else pinrail-cli]. Env: PINRAIL_REQUESTED_BY.
--sample <NAME>Send one of the plugin’s sample reviews, which show what the plugin looks like, instead of a payload: the one named, or else its first. --title and --origin still apply.
--waitWait until the review ends, whether or not it is decided (see wait).
--dry-runRun every check on the submission without creating a review. The command exits with 0 if the review would be accepted, and with 2 and the violations if not.
--timeout <TIMEOUT>With --wait, stop waiting after this many seconds and exit with 4. 0 waits indefinitely. PINRAIL_TIMEOUT sets this for every wait.
--decision-out <FILE>With --wait, also write decision.data to this file as JSON once the review is decided. pinrail show shows the decision at any time.
--no-startDo not start the server when it is not running.

Wait until a review ends, then print its decision as Markdown, or the whole review as JSON with --json.

Terminal window
pinrail wait <ID> [OPTIONS]
ArgumentDescription
<ID>The review’s id.
--timeout <TIMEOUT>Stop waiting after this many seconds and exit with 4. 0 waits indefinitely. PINRAIL_TIMEOUT sets this for every wait.
--decision-out <FILE>Also write decision.data to this file as JSON once the review is decided. pinrail show shows the decision at any time.

Print a review as Markdown, or the whole review as JSON with --json, including its payload and decision.

Terminal window
pinrail show <ID>
ArgumentDescription
<ID>The review’s id.

List every round of a review, oldest first, without payloads.

Terminal window
pinrail rounds <ID>
ArgumentDescription
<ID>The review’s id.

Print a review’s event log.

Terminal window
pinrail events <ID>
ArgumentDescription
<ID>The review’s id.

List reviews, newest first, without payloads.

By default, the command lists only pending reviews and, in a git checkout, only those of its project. The project is named the way submit names it: the remote’s owner/name, or the folder’s name when there is no remote. Outside a git checkout, the command lists the pending reviews of every project. Use --status and --repo to choose other reviews, and --all to list every review.

Terminal window
pinrail list [OPTIONS]
ArgumentDescription
--status <STATUS>The status to list: pending, decided, withdrawn, discarded or expired. Separate several with commas [default: pending, unless --all].
--repo <REPO>The project, as the origin’s repo. Use - for reviews that name no project [default: the git checkout’s project, unless --all].
--workflow <WORKFLOW>The workflow that asked for the review, as the origin’s workflow.
--ref <REFERENCE>The branch, pull request or other ref, as the origin’s ref.
--run-id <RUN_ID>The run that asked for the review, as the origin’s run_id.
--plugin <PLUGIN>List only reviews of this plugin.
--search <WORDS>Words that must all appear in the title, payload, plugin, requester, origin or the name of the person who decided.
--cursor <CURSOR>List only reviews older than this id.
--limit <LIMIT>The most reviews to list. Use --cursor to list the next page.
--allList every review, of any status and project unless --status or --repo limits them, and every page unless --limit caps the number.
--include-revisedInclude rounds that a later round revises.

Record a decision on behalf of the person, from a test or a tool.

An agent never decides a review that it submitted. Only the person does.

Terminal window
pinrail decide <ID> --data <JSON|FILE|-> [OPTIONS]
ArgumentDescription
<ID>The review’s id.
--data <JSON|FILE|->The decision as JSON, given inline, as a file path, or as - for stdin. Required.
--note <NOTE>A note to the agent that requested the review.

Withdraw a pending review. A command that is waiting on it exits with 3.

Terminal window
pinrail withdraw <ID> [OPTIONS]
ArgumentDescription
<ID>The review’s id.
--reason <REASON>Why the review is no longer needed.

Discard a pending review on behalf of the person.

This does what discarding does in the app. A command that is waiting on the review exits with 5 and tells the agent to stop. An agent never discards a review that it submitted.

Terminal window
pinrail discard <ID> [OPTIONS]
ArgumentDescription
<ID>The review’s id.
--reason <REASON>The reason, which the agent receives.
--by <BY>Who discards the review [default: the server’s user].

List the installed plugins and when to use each one.

The listing includes each plugin’s install record and settings. pinrail plugins describe <name> shows one plugin in full.

Terminal window
pinrail plugins [COMMAND]

Install an official plugin by name, or a plugin from a folder or a zip.

An official plugin, such as list, is installed from the plugins the app carries, at the newest version it has. A name is taken as a folder when a folder of that name exists here. Installing again, from the same place or another, replaces the installed plugin: this is how a plugin is upgraded.

Terminal window
pinrail plugins install <SOURCE> [OPTIONS]
ArgumentDescription
<SOURCE>An official plugin’s name, such as list, or a plugin’s folder, or a zip of it.
--linkServe the folder directly instead of copying it, while you develop the plugin.

Remove an installed plugin. The versions that existing reviews render with are kept.

Terminal window
pinrail plugins remove <NAME>
ArgumentDescription
<NAME>The plugin’s name.

Show what an agent needs to submit a review with a plugin.

The description includes the payload schema, an example payload and the files the plugin accepts. With --json, it also includes the decision schema. pinrail plugins lists the plugins and says when to use each one.

Terminal window
pinrail plugins describe <NAME> [OPTIONS]
ArgumentDescription
<NAME>The plugin’s name.
--payload-schemaPrint only the payload’s JSON schema, for a tool that checks or builds payloads.
--examplePrint only the example payload, as a starting point for your own.
--decision-schemaPrint only the decision’s JSON schema, which describes decision.data, for processing the decision with --json. Without --json, the decision is printed as Markdown.

Create a new plugin.

The new folder contains a manifest, schemas, a sample review, a view, and the SDK’s types. The plain template’s view needs no build. The typescript template, or ts, and the react template write the view in src/, which npm run build turns into view/. --playwright adds a first test of the view, run with Playwright under the SDK’s harness. --link installs the plugin as a link right away, which needs a view, so it takes the plain template only.

Terminal window
pinrail plugins new <NAME> [OPTIONS]
ArgumentDescription
<NAME>The plugin’s name, which starts with a lowercase letter followed by letters, digits, _ or -.
--dir <DIR>The folder to write the plugin to [default: ./].
--template <TEMPLATE>How the view is written. One of plain, typescript, react. Default: plain.
--playwrightAdd a first test of the view, with package.json and Playwright’s configuration.
--linkInstall the plugin as a link after writing it, so the app serves the folder directly.

Print one of the plugin SDK’s JSON Schemas.

The manifest’s schema, which every plugin’s manifest.json is checked against, or the attachment schema, which a payload schema copies to describe a field that names an attached file. They are the schemas this command was built with, and need no app.

Terminal window
pinrail plugins schema [WHICH]
ArgumentDescription
<WHICH>Which schema to print. One of manifest, attachment. Default: manifest.

Check a plugin folder without installing it.

The command reports why the app would refuse the folder, and each feature the app would drop. It also checks the plugin’s recorded decisions, fixtures/.decided.json: their payload and decision must pass the plugin’s schemas, and when .decided.md is beside one, the Markdown the app renders from it must equal that file. It exits with 0 if the app would accept the plugin and every recorded decision checks out, and with 2 if not.

Terminal window
pinrail plugins check [DIR] [OPTIONS]
ArgumentDescription
<DIR>The plugin’s folder. Default: ..
--since <DIR>The release before this one, as a folder: also report what this release’s schemas no longer accept of what that one took, which semantic versioning allows only in a version that announces it.
--update-fixturesWrite each recorded decision’s .decided.md from what the app renders, creating it if it is missing, before comparing. Review the difference before you commit it.

List the files a review carries, or save one of them.

Terminal window
pinrail attachments <COMMAND>

List the files a review carries, with their names, sizes, media types and hashes.

Terminal window
pinrail attachments list <ID>
ArgumentDescription
<ID>The review’s id.

Save a file that a review carries.

Terminal window
pinrail attachments get <ID> <NAME> [OPTIONS]
ArgumentDescription
<ID>The review’s id.
<NAME>The file’s name on the review.
--output <PATH|->The path to write the file to, or - for stdout [default: the file’s name, in the current directory].
--forceReplace an existing file.

Write every review as a JSON file in a directory.

Terminal window
pinrail export <DIR>
ArgumentDescription
<DIR>The directory to write to. It is created if it does not exist.

Start the server if it is not running, and print its URL.

Terminal window
pinrail serve

Print the briefs on using Pinrail as an agent.

Without a path, the command prints the main brief, which is Pinrail’s agent skill, and a menu of the other briefs. With a path, such as pinrail docs building, it prints that brief.

Terminal window
pinrail docs [PATH] [OPTIONS]
ArgumentDescription
<PATH>The brief to print, as the menu names it [default: the main brief].
--treePrint the path and subject of every brief, as a tree.

Open a review in the app, or its preview in a browser with --browser.

The command opens the link pinrail://reviews/ with the system’s default handler, which passes it to the Pinrail app, and the app shows the review. The command prints the address it opened. An id that the app does not have is refused with exit code 2, and nothing opens.

--browser opens /preview/reviews/ instead. The preview shows the review in the plugin’s view, served by the running app to this computer only. Use it to try a view while you build a plugin. Its hand-over button checks the decision against the plugin’s schema and shows it, but decides nothing. Only the person decides, in the app.

Terminal window
pinrail open <ID> [OPTIONS]
ArgumentDescription
<ID>The review’s id.
--browserOpen the preview in the default browser, to try a view while building a plugin.
CodeMeaning
0Done. For wait and submit --wait, the review was decided.
1Error: bad arguments, the app could not be reached, a file could not be read or written, or the app failed on its side. wait and submit --wait keep waiting through an error inside the app until it answers again.
2The app refused the request, for example a payload the plugin’s schema rejects, or a folder that plugins check would not install. Why is on stderr.
3The review was withdrawn by the agent, or expired, before anyone decided.
4--timeout ran out. The review is still pending.
5The person discarded the review: stop the work it was gating, and do not ask again.
VariableUsed for
PINRAIL_DATA_DIRWhere the running server’s server.json is, to find it; also where a server the CLI starts keeps its data.
PINRAIL_JSON--json. Print JSON instead of Markdown, for a script or tool that processes the result. PINRAIL_JSON=1 sets this for a whole session.
PINRAIL_PORTThe port to try when no server advertises itself. Defaults to 4747.
PINRAIL_REQUESTED_BY--requested-by <REQUESTED_BY>. Who is asking, shown on the review as its requester, with the agent’s icon when the app knows the agent [default: the coding agent the command runs under, detected from the variables it sets (claude-code, codex, cursor, antigravity, grok, opencode, kimi), or else pinrail-cli].
PINRAIL_SERVER_CMDHow to start a server when none is running, run through sh -c. submit, serve, plugins, plugins describe, plugins check and plugins new --link use it.
PINRAIL_TIMEOUTHow many seconds wait and submit --wait wait before they give up with exit 4, when --timeout is not given. Unset or 0 waits without a limit.
PINRAIL_URL--url <URL>. The server’s URL [default: PINRAIL_URL, then the address in the running server’s server.json, then http://127.0.0.1:4747].
PINRAIL_VERBOSE--verbose. Also report each step on stderr, such as the origin read from git, the server started, the files uploaded and where files were written.