Prompt Runner exposes the same CLI through three entry points:

  • mix prompt_runner ...
  • mix run run_prompts.exs -- ...
  • ./prompt_runner ... after mix escript.build

All commands operate on a packet directory. If you omit the directory, Prompt Runner uses the current working directory.

Setup Commands

Initialize the global profile store:

mix prompt_runner init
mix prompt_runner template list

Create and inspect profiles:

mix prompt_runner profile new codex-fast --provider codex --model gpt-5.6-luna --reasoning high
mix prompt_runner profile list

Packet Authoring Commands

Create a packet:

mix prompt_runner packet new demo \
  --profile simulated-default \
  --provider simulated \
  --model simulated-demo \
  --repo app=/path/to/repo \
  --default-repo app \
  --prompt-template from-adr

mix prompt_runner prompt new 01 \
  --packet demo \
  --phase 1 \
  --name "Capture runtime boundaries" \
  --targets app \
  --commit "docs: add runtime boundaries summary"

mix prompt_runner checklist sync demo

Packet-local templates can override the home-scoped templates created by init. List visible templates for a packet with:

mix prompt_runner template list demo

Use the packet manifest's recovery: block for the full policy surface. The CLI flags are convenience shorthands for common resume/retry/repair defaults.

Packet Inspection Commands

mix prompt_runner packet explain demo      # resolved manifest metadata
mix prompt_runner packet lint demo         # authoring hazards
mix prompt_runner packet doctor demo       # authoring gaps
mix prompt_runner packet preflight demo    # runtime readiness

The four are complementary and in increasing order of what they touch:

  • explain prints the packet's repos, phases, and resolved options as JSON.
  • lint is static. It reports constructs that load, run, and silently produce a wrong answer: an id that does not match its filename prefix, a verify command with no timeout, a target naming a repo that does not exist. Exits non-zero on errors. See Packet Linting.
  • doctor reports authoring gaps — no prompts, no default repo, a prompt with no targets or no verifier items, scaffold placeholders left in a body.
  • preflight is the runtime gate used before provider execution. It checks packet repo paths and git readiness, prints JSON, exits non-zero when the run should not start, and is called automatically by run unless --skip-preflight is explicit.

packet lint flags:

  • --strict — promote every warning to an error, which is what CI wants
  • --json — machine-readable report

Execution Commands

List and plan:

mix prompt_runner list demo
mix prompt_runner plan demo
mix prompt_runner plan demo --provider simulated --model simulated-demo

plan accepts the same override flags as run, so it reports the plan run would actually build. Before 0.9.0 it parsed no flags at all and always reported the packet's own provider and model.

Run everything:

mix prompt_runner run demo
mix prompt_runner run demo --skip-preflight

Preview without starting a provider:

mix prompt_runner run demo --dry-run

--dry-run prints, per prompt, the resolved provider, model, working directory, permission mode, target repos, and the commit message that would be used. It starts nothing.

Run only deterministic contracts, without opening a provider or changing packet progress:

prompt_runner verify demo 01 02
prompt_runner verify --packet demo --workspace workspace.yml --json

The workspace form binds logical repositories and installed contract artifacts to the current operator's independent workspace.

When the prepared workspace manifest declares its default packet, the id is the whole target:

prompt_runner plan operator-packet --remaining
prompt_runner verify operator-packet 01
prompt_runner start operator-packet --remaining --no-commit
prompt_runner status operator-packet
prompt_runner control events operator-packet
prompt_runner watch operator-packet
prompt_runner stop operator-packet

All explicit --workspace MANIFEST --packet PACKET_DIR forms remain supported. See Operator Workspaces for the strict packet binding.

Run specific prompts:

mix prompt_runner run demo 01 02
mix prompt_runner run demo --phase 2

Resuming

mix prompt_runner run demo --remaining

--remaining runs every prompt whose recorded status is not completed, in order. That includes prompts earlier than the furthest one that finished: if 03 failed while 04 succeeded, --remaining runs 03 and 05, and says so.

A prompt with no recorded status is remaining — the absence of a record is not evidence of success. A missing progress store is a new run. An existing store that cannot be read or parsed stops selection instead of silently turning a resume into a full rerun.

When --remaining selects nothing, the run says so rather than exiting zero in silence.

Intentionally replacing a run generation

A failed or interrupted run is bound to the packet content it started with. Changing a prompt, dependency edge, or contract makes an ordinary resume fail closed. After reviewing and committing that packet change, start a fresh run generation explicitly while preserving completed-prompt progress:

mix prompt_runner run demo --remaining --new-run

The prior run directory and append-only journal remain intact and receive a run_superseded record naming the new identity. --new-run is never inferred from a fingerprint mismatch; without the flag the mismatch remains an error.

Pre-flight verification

Under --remaining, each prompt's verify contract is evaluated before the provider is invoked. If it already passes, the prompt is marked completed with no session, and its state records session_ran: false and source: "preflight_verify". This is what makes a prompt idempotent and a resume cheap: finished work re-verifies in seconds instead of being re-done.

Two contracts are never pre-flighted:

  • one with no evaluable clause, which would pass vacuously
  • one containing changed_paths_only, which reads git status --porcelain and so passes vacuously against a clean tree — including the clean tree that exists before any session has run

--verify-first and --no-verify-first state it explicitly either way. Naming a prompt id is a request to run it, so pre-flight is off for explicit ids unless --verify-first is given.

--continue

--continue is an API option, not a CLI switch. It resumes from last_completed + 1, so it steps over any earlier prompt that failed or never ran. When it does, the runner names the prompts being skipped and points at --remaining. Its behaviour is unchanged — some callers want exactly that.

Let each session own its commits:

mix prompt_runner run demo --no-commit

Repair a failed prompt from stored verifier state:

mix prompt_runner repair --packet demo 02

Print packet-local runtime status JSON:

mix prompt_runner status demo

For a prepared operator workspace, address it by manifest id and get a compact human summary:

prompt_runner status operator-packet

prompt_runner status with no argument discovers the workspace when the current directory is related to exactly one prepared manifest, declared source checkout, or independent clone. It refuses an ambiguous match and asks for the id. The report shows only relevant dimensions: prompt counts for multi-prompt runs, iteration progress for an agent-controlled loop, and attempt details for retry or repair.

Use the structured form for automation:

prompt_runner status operator-packet --json
prompt_runner status --workspace /path/to/workspace.yml --json

The explicit --workspace form remains the deterministic interface for service scripts. Workspace ids and current-directory discovery come from operator-owned reference records written by workspace prepare, not from environment-specific aliases or recursive filesystem search.

Agent-Controlled Linear Runs

When the packet enables agent_control, the running provider receives these iteration-scoped commands:

prompt_runner agent-control progress --cursor P09R.2 --unit C --summary "generic runtime ownership is in progress"
prompt_runner agent-control continue
prompt_runner agent-control repeat --reason "P09R.2 unit B is complete; next is unit C runtime ownership"
prompt_runner agent-control finish --reason "the packet objective is complete"
prompt_runner agent-control blocked --reason "exact external blocker"

The provider publishes progress repeatedly; a human only runs status. Progress is nonterminal and never consumes the first-wins directive. The commands are rejected outside a live controlled invocation. finish does not trust the provider's claim: Prompt Runner runs the packet-level completion_verify contract and starts a fresh iteration with its failures if the request was premature. See Agent-Controlled Linear Runs.

Supervision

mix prompt_runner watch demo
mix prompt_runner watch demo --interval 300
mix prompt_runner watch demo --once --json

One compact line per interval:

WATCH 16:57Z runner=UP prompt=11 quiet=0min repos=3 dirty=0 commits=27

Liveness comes from the .prompt_runner/run.pid file the runner writes for the duration of a run, and quiet time comes from file mtimes. See Supervising A Long Run for why both matter.

Useful Execution Flags

run and plan both accept:

  • --provider
  • --model
  • --log-mode
  • --log-meta
  • --events-mode
  • --tool-output
  • --thinking (show | hide)

  • --diff (none | stat | full)

  • --cli-confirmation
  • --runtime-store
  • --committer
  • --skip-preflight
  • --no-commit
  • --dry-run
  • --all
  • --remaining
  • --verify-first / --no-verify-first
  • --phase N

watch accepts:

  • --interval SECONDS (default 900)
  • --once
  • --json

packet lint accepts:

  • --strict
  • --json

packet new accepts:

  • --repo NAME=PATH (repeatable)
  • --default-repo
  • --prompt-template
  • --profile
  • --provider
  • --model
  • --reasoning
  • --permission
  • --resume-attempts
  • --retry-attempts
  • --retry-base-delay-ms
  • --retry-max-delay-ms
  • --retry-jitter
  • --auto-repair
  • --repair-attempts
  • --cli-confirmation

prompt new accepts:

  • --packet
  • --phase
  • --name
  • --targets
  • --commit
  • --template

Example:

mix prompt_runner run demo \
  --provider codex \
  --model gpt-5.6-luna \
  --log-mode compact \
  --cli-confirmation require

Escript

Build once:

mix escript.build

Then use the same commands:

./prompt_runner run demo
./prompt_runner watch demo --once
./prompt_runner status demo