Skip to content

CLI reference

The paddock command, all of it. For the why and the walkthroughs, see Running as a service, Updating and Config profiles; this page is the lookup table.

Terminal window
npm i -g @edspencer/paddock # installs `paddock`
npx @edspencer/paddock # or run it without installing
paddock [serve] [options] run the server in this terminal
paddock service <action> [options] install|uninstall|status|start|stop|restart|logs
paddock busy [--json] is the running instance busy?
paddock update [--check] [--to <v>] install a newer Paddock
paddock doctor [--json] [--offline] checks, with a fix for each problem
paddock config <show|eject> [options] what this instance's config resolved to
paddock -v | --version print the version and exit
paddock -h | --help the full help

Every command takes --help (paddock service --help, paddock update --help, …), which is the same text as this page in more words.

The CLI follows the CLI contract (SPEC 0.2) that Ed’s local tools share, so these hold for every command:

  • The command comes first, flags after it. paddock serve --port 7300 and paddock service install --port 7300 use the same flag grammar.
  • A flag a command can’t act on is a usage error (exit 2), not something quietly ignored: --force on service uninstall, --json on serve, --offline on anything but doctor. The message names the commands it does apply to.
  • --json prints exactly one JSON document on stdout, and every word of prose goes to stderr. The exit code is the same with or without it.
  • Colour is on for a terminal and off otherwise. NO_COLOR (any non-empty value) turns it off; FORCE_COLOR turns it on, or off with 0 or false.
  • Only update, update --check and doctor contact the npm registry, and only when you run them. Nothing else in the CLI or the server checks for updates.

Runs the server in this terminal until Ctrl-C. paddock start is an alias, kept for good, and a bare paddock (or npx @edspencer/paddock) runs serve too.

Terminal window
paddock serve
paddock serve --port 7300 --open
paddock serve -d ~/paddock-work # a second, separate instance
OptionWhat it does
-p, --port <port>the HTTP/WS port, 1–65535 (default 7233, or $PORT)
--host <host>the bind address (default 127.0.0.1, or $HOST). A routable address needs auth: see Binding & network exposure
-d, --data-dir <path>the instance: projects, transcripts and state (default ~/.paddock, or $PADDOCK_DATA_DIR)
-o, --openopen the app in your browser once it is listening
-q, --quietprint only the first line, Paddock <version>: <url>
--verboseshow the server’s own logs, which are quiet (warn) by default

The first line on stdout is Paddock <version>: <url>, and nothing comes before it, so a script can read the URL from it. The server’s logs and any first-run notice go to stderr. The data dir alone decides which instance you get: the directory you run it from makes no difference. A port that is already in use exits 1, with a suggestion of another.

Runs Paddock in the background as a per-user service: a launchd LaunchAgent on macOS, a systemd --user unit on Linux. Not on Windows yet. The full story is Running as a service.

CommandWhat it does
paddock service installwrite the unit, enable it, start it, and wait until it answers
paddock service uninstallstop it and remove the unit. Your data and logs stay
paddock service startstart an installed service, and wait until it answers
paddock service stopstop it until service start, or the next login or boot. It stays installed
paddock service restartstop it and start it again, re-reading the unit and config
paddock service statusinstalled? running? answering? which version?
paddock service logsthe last 50 lines of <data-dir>/logs/service.log
OptionOnWhat it does
-p, --port <port>installthe port recorded in the unit (default 7233)
--host <host>installthe bind address recorded in the unit (default 127.0.0.1)
-d, --data-dir <path>installa data dir separate from your terminal’s instance. Left out of the unit unless you pass it or PADDOCK_DATA_DIR is set
--verboseinstallrecord the server’s own logs, not just warnings
--dry-runinstallprint the unit on stdout (and where it would go on stderr), and write nothing
--from-checkoutinstallinstall from a git checkout or npm link anyway
--refreshinstallre-write the unit from the settings the installed one records; exit 4 if there is none
--forcestop, restartgo ahead even though Paddock is busy
--jsonall but logsone document on stdout: ok, action, error on a failure, and the same fields as status --json
-f, --followlogskeep printing what gets appended, until Ctrl-C
-n, --lines <n>logshow many lines to print first (default 50)

Running install again keeps the port, host, data dir and verbosity the installed unit records, unless a flag gives new ones, and restarts the service onto the new unit. It refuses, with exit 1, from an npx cache, from a git checkout without --from-checkout, and when something other than Paddock holds the port.

stop and restart refuse with exit 75 while Paddock is busy unless you add --force, which prints the same message prefixed Overriding:. start and uninstall don’t ask. status --json reports installed, running, answering, installedVersion, runningVersion, port, url, pid, unit, log, warnings, and update when a check has found one.

Asks the running instance whether it is in the middle of something a restart would lose: a chat turn (a human turn, a fired trigger, a cron fire), a background task a live session still holds, or a scheduled job. An idle session, an open tab or a pending sweep is not busy.

Terminal window
paddock busy # a sentence, and the exit code
paddock busy --json # { running, busy, url, version, counts }
OptionWhat it does
-d, --data-dir <path>which instance to ask (default ~/.paddock, or $PADDOCK_DATA_DIR)
--jsonone JSON document on stdout; prose on stderr

This command is for its exit code: 0 not busy, 75 busy, 3 nothing answering, and 1 when something answered but not as Paddock’s busy check. It finds the instance through <data-dir>/runtime.json and never guesses a port. 75 is the code Watchtower’s pre-update hook reads as “skip this one”; see Docker and Watchtower. Run it from a terminal: a Paddock chat that runs it counts itself as busy.

The same answer is GET /api/busy over HTTP, with no authentication, since it carries counts and nothing else.

Installs a newer Paddock, restarts the service onto it, waits for it to answer, and puts the old version back if it doesn’t. Step by step, and what each failure means, is on Updating.

Terminal window
paddock update # the latest release, asking first
paddock update --check --json # what a new version would be; changes nothing
paddock update --to 0.82.1 # a version: the only way to go back
paddock update --to next # a dist-tag
paddock update --yes # from a script
OptionWhat it does
--checkask the registry, report, and change nothing. Exits 0 either way; scripts read updateAvailable from --json
--to <version|tag>a version or dist-tag instead of latest. Required to move to a lower version
-y, --yesdon’t ask. Required without a terminal, where update otherwise exits 2
--forceskip the busy check, and nothing else: the health check and the rollback still run
--jsonone JSON document on stdout; prose on stderr

An install that isn’t Paddock’s to replace (Docker, npx, a git checkout, a dependency of another project, a yarn classic global, a global folder you can’t write to, or anything with PADDOCK_UPDATE_HINT set) exits 5 and prints the command that would work. A global npm, pnpm, bun or volta install updates in place.

Checks that Paddock can run here, one line each, with a fix: under every warning and failure. Each line starts ✓ (fine), ! (a warning), ✗ (a failure) or - (doesn’t apply here). There’s an example on Updating.

OptionWhat it does
--jsonone document on stdout: { ok, version, checks: [{ id, status, message, fix? }] }. The update notice goes to stderr
--offlineskip the update check, the only one that uses the network
-d, --data-dir <path>which instance to check (default: the service’s, else $PADDOCK_DATA_DIR, else ~/.paddock)
CheckWhat it looks at
node-enginesthis Node satisfies the package’s engines (Node 22 or later)
install-methodhow Paddock was installed, and whether paddock update can update it
unitthe service: installed, running and answering, its Node and CLI still there, the same install as this CLI
running-versionthe running Paddock is the installed version
portthe port is free, or held by Paddock
lingeron Linux, whether the service keeps running after you log out
data-homethe data dir is writable
configpaddock.config.yaml and the environment load as serve would load them
projectsthe projects folder is writable
claude-logina Claude login Paddock can use: which kind, never its value
claude-clia claude on PATH, which the sweeper always needs, and chats and triggers on driveMode: batch
updatea newer version on the registry (skipped with --offline)

It exits 1 if any check failed; warnings don’t count. Apart from the update check’s cache, <data-dir>/update-check.json, it writes nothing, and never creates a data dir.

What this instance’s configuration resolved to, and where each value came from. The detail is on Config profiles.

CommandWhat it does
paddock config showthe decisions: your profile, the keys your config file sets, the variables your environment sets
paddock config ejectpreview what freezing the resolution into paddock.config.yaml would write. Writes nothing
OptionOnWhat it does
-d, --data-dir <path>bothwhich instance to inspect (default ~/.paddock, or $PADDOCK_DATA_DIR)
--resolvedshowevery effective value, not just the ones someone set
--jsonshowthe whole report as JSON, long values in full
--show-sensitiveshowprint the values of fields marked sensitive
--writeejectactually write the file
--include-envejectalso freeze values an environment variable currently supplies
FlagWhat it does
-v, --versionprint the Paddock version and exit
-h, --helpprint the help and exit. After a command (paddock doctor --help), that command’s help

The codes are the CLI contract’s (SPEC §2), and mean the same in every tool that follows it.

CodeMeaningFrom
0done; running and answering; up to date; not busyeverything
1failed. Also: an install or start that never answered, a refused service install, a doctor check that failedeverything
2usage: an unknown command or flag, a bad value, a flag the command can’t act on, or update with no terminal and no --yes. The usage follows the messageeverything
3not running: installed but not running or not answering, or nothing answers. From update: a foreground serve is still answeringservice status, busy, update
4not installed: there is no service unitservice status, start, stop, restart, logs, install --refresh
5this install can’t update itself; the right command was printedupdate
6the update failed and was rolled back; the old version is running againupdate
7the update failed and couldn’t be rolled back; a person is neededupdate
75busy: refused, and why was printed. Try later, or use --forcebusy, update, service stop, restart

3 and 4 are the codes systemctl status and LSB init scripts use. 75 is EX_TEMPFAIL from sysexits.h.

What the CLI itself reads. The server reads many more, all on Environment variables.

VariableRead byWhat it does
PADDOCK_DATA_DIRevery commandthe data dir when -d isn’t given (default ~/.paddock). service install records it in the unit if it is set
PORTservethe port when -p isn’t given (default 7233)
HOSTservethe bind address when --host isn’t given (default 127.0.0.1)
LOG_LEVEL, HERDCTL_LOG_LEVELserveleft alone if set; otherwise serve sets both to warn unless --verbose
PADDOCK_IN_DOCKERupdate, doctorset to 1 by the image: update prints the image to pull and exits 5
PADDOCK_UPDATE_HINTupdate, doctorfor an install something else manages: update prints it and exits 5
npm_config_registryupdate, doctorthe registry to ask; else npm config get registry, else https://registry.npmjs.org
XDG_CONFIG_HOMEservicewhere the systemd user unit goes (default ~/.config)
NO_COLOR, FORCE_COLORevery commandcolour off, or forced on (FORCE_COLOR=0 or false forces it off)
PathWhat it is
~/.paddock/the default data dir: projects, transcripts, state and paddock.config.yaml
<data-dir>/runtime.jsonwritten 0600 while serve is listening, removed when it stops: how busy and update find the instance
<data-dir>/logs/service.logthe service’s log, rotated by Paddock at 8 MB, keeping three older generations
<data-dir>/update-check.jsonwhat the last update --check or doctor found, which service status repeats
<data-dir>/update.lockheld while update runs, so two updates can’t interleave
~/Library/LaunchAgents/net.edspencer.paddock.plistthe service unit on macOS
~/.config/systemd/user/paddock.servicethe service unit on Linux