Command Line Interface (CLI)

Command Book includes a CLI that lets you run, list, create, edit, and monitor saved commands directly from your terminal. Define a command once in Command Book and execute it from either the GUI or CLI.

# Run a saved command
$ commandbook run talk-python-dev

# List all saved commands
$ commandbook list

# Show one command's full config + status
$ commandbook details talk-python-dev

# Run an arbitrary command without saving it
$ commandbook run --command "npm run dev" --name web --dir .

# Create a new command interactively
$ commandbook new

# Edit an existing command
$ commandbook edit talk-python-dev

# Check whether a command is running
$ commandbook status talk-python-dev

# Stop a running command
$ commandbook stop talk-python-dev

# Open the GUI app
$ commandbook open

Why a CLI?

Command Book's GUI lets you configure commands with precision: working directories, pre-commands, environment variables, auto-restart behavior, and more. The CLI brings all of that to your terminal with zero extra setup.

When you run commandbook run <command-name>, the CLI executes the command exactly as if you clicked Run in the GUI. It reads the same saved configuration and replicates every detail:

  • Working directory -- cds to the configured path before execution
  • Pre-commands -- Runs setup steps (like git pull) before the main command
  • Environment variables -- Applies any configured env vars to the process
  • Auto-restart -- Restarts on crash with the same delay you set in the GUI
  • Login shell -- Uses your shell's PATH, aliases, and environment

Instead of remembering cd ~/projects/talk-python && git pull && python app.py --reload, you run:

$ commandbook run talk-python-dev

One command, fully configured, every time. Define it once in Command Book, run it from wherever you prefer -- the GUI or the terminal.

The CLI also opens the door to automation and integration. Shell scripts, CI pipelines, Claude Code, and other agentic coding tools can all invoke your saved commands with commandbook run. Your carefully configured commands become accessible to any tool that can call a shell command.

To help your AI agent use the CLI effectively, add https://commandbookapp.com/docs/ai-guide.md to your project's agent instructions (e.g. CLAUDE.md, .cursorrules, or equivalent) -- it's a concise reference written specifically for LLMs. See the AI Agents guide for full setup with Claude Code, Codex, and Cursor.


Installation

The CLI ships inside the Command Book app bundle. Install it from the GUI:

From Settings

  1. Open Command Book
  2. Go to Settings (⌘,)
  3. Select the CLI Tools tab
  4. Click Install CLI Tools

From the Menu Bar

  1. Open Command Book
  2. Click File → Install CLI Tools

What Happens

The installer creates a symlink at ~/.local/bin/commandbook pointing to the CLI binary inside the app bundle. No admin privileges required.

If ~/.local/bin is not in your PATH, Command Book shows instructions to add it:

export PATH="$HOME/.local/bin:$PATH"

Add that line to your ~/.zshrc (or ~/.bashrc), then restart your terminal or run source ~/.zshrc.

Uninstalling

Use Settings → CLI Tools → Remove CLI Tools or File → Remove CLI Tools to remove the symlink.


Commands

commandbook list

Display all saved commands in a formatted table.

$ commandbook list

SLUG                  NAME                    COMMAND
────────────────────────────────────────────────────────────────────────────────
api-server            API Server              npm run dev
docker-postgres       Docker Postgres         docker compose up db
valkey-cache          Valkey Cache            valkey-server
talk-python-dev       Talk Python (dev)       python app.py

Commands are sorted alphabetically by name. If no commands exist, you'll see a message suggesting commandbook new.


commandbook run [slug]

Run a saved command in the foreground, attached to your terminal.

With a slug:

$ commandbook run talk-python-dev

Talk Python (dev)
────────────────────
Pre-command:  git pull
Command:      python app.py --reload
Directory:    ~/projects/talk-python
────────────────────

▶ Running pre-command: git pull
Already up to date.

▶ Running: python app.py
 * Serving Flask app 'app'
 * Running on http://127.0.0.1:5000
^C
Interrupted.

Without a slug (interactive picker):

$ commandbook run

Select a command to run:

  1) API Server              npm run dev
  2) Docker Postgres         docker compose up db
  3) Redis Cache             redis-server
  4) Talk Python (dev)       python app.py

Enter number (1-4): 4

Without a saved command (ad-hoc):

Run an arbitrary command without saving it first by passing --command:

$ commandbook run --command "python -m http.server 8000" --name file-server --dir .

The process is fully managed — commandbook status file-server and commandbook stop file-server work just like a saved command — but it is not added to your saved command list, so one-off runs don't clutter the GUI sidebar. This is ideal for automation and AI agents that need to launch a throwaway process. Once it exits, it leaves no trace.

Options:

Flag Description
--dir <path> Override the working directory (defaults to the current directory for ad-hoc runs)
--command "<cmd>" Run an arbitrary command ad-hoc instead of a saved slug (not saved)
--name <handle> Name/handle for an ad-hoc run, used by status/stop (derived from the command if omitted)

Behavior:

  • Prints a header with the command name, working directory, and configuration
  • Streams output to your terminal in real-time with full ANSI color support
  • Runs through your login shell, so your PATH, aliases, and environment are available
  • If the command has auto-restart enabled and crashes (non-zero exit), the CLI restarts it automatically after the configured delay
  • Ctrl+C sends SIGINT to the child process (intentional stop, no auto-restart)
  • Exit code matches the child process exit code
  • Exits 404 if the slug is unknown

Pre-commands run before the main command. If a pre-command fails (non-zero exit), the main command does not start.


commandbook details <slug> [--json]

Show a single saved command's full configuration — the same fields as the app's edit page — together with its current run status. Use it to inspect exactly what a slug will run before launching it; list only shows a truncated command, and status only shows whether it's running.

$ commandbook details talk-python-dev

Talk Python (dev)  ● running
────────────────────────────
Slug            talk-python-dev
Command         python app.py --reload
Pre-command     git pull
Working dir     ~/projects/talk-python
Environment     2 vars: NODE_ENV, PORT
Auto-restart    on (5s delay)
Created         2026-05-01 14:22
Updated         2026-06-10 09:03
Last run        2026-06-18 08:40

PID    UPTIME    SOURCE
───────────────────────
81234  12m       app

With --json, emits the full configuration plus a nested status block (the same shape as status --json):

$ commandbook details talk-python-dev --json
{
  "slug" : "talk-python-dev",
  "name" : "Talk Python (dev)",
  "command" : "python app.py --reload",
  "preCommand" : "git pull",
  "workingDirectory" : "~/projects/talk-python",
  "environmentKeys" : [ "NODE_ENV", "PORT" ],
  "autoRestart" : true,
  "restartDelaySeconds" : 5,
  "createdAt" : "2026-05-01T14:22:00Z",
  "updatedAt" : "2026-06-10T09:03:00Z",
  "lastRunAt" : "2026-06-18T08:40:00Z",
  "status" : { "state" : "running", "instances" : [ { "pid" : 81234, "startedAt" : "2026-06-18T08:40:00Z", "source" : "app", "uptimeSeconds" : 720 } ] }
}

Privacy: environment variable values are never printed — only their names (environmentKeys). To view or change a value, open the command in the GUI editor.

Exit codes: 0 when the command exists (running or stopped) · 404 if the slug is not found.


commandbook new

Create a new command interactively.

$ commandbook new

Create a new command
────────────────────

Name: Talk Python (dev)
Command: python app.py
Working directory [/Users/you/projects/talk-python]: ~/projects/talk-python
Pre-command (optional): git pull
Auto-restart on crash? (y/N): y

✓ Command 'talk-python-dev' created successfully.

Prompts:

  1. Name (required) -- Display name for the command
  2. Command (required) -- The shell command to run
  3. Working directory (optional) -- Defaults to current directory
  4. Pre-command (optional) -- Runs before the main command each time
  5. Auto-restart on crash (optional) -- Defaults to No
  6. Auto-restart delay (if auto-restart is yes) -- Seconds, defaults to 5

The CLI validates that command executables exist in your PATH and warns (but still allows saving) if they're not found:

Command: assetbuilder --run build_assets.py && python app.py --reload
⚠ Warning: 'assetbuilder' not found in PATH. The command may fail at runtime.

commandbook edit <slug>

Edit an existing command interactively. Current values are shown in brackets -- press Enter to keep them.

$ commandbook edit talk-python-dev

Edit command: Talk Python (dev)
────────────────────────────────

Name [Talk Python (dev)]: Talk Python (development)
Command [python app.py]: python -m flask run
Working directory [~/projects/talk-python]:
Pre-command [git pull]:
Auto-restart on crash? (y/N) [n]: y
Auto-restart delay (seconds) [5]: 3

✓ Command 'talk-python-development' updated successfully.

If the name changes and the slug would change, the CLI confirms:

⚠ Slug will change: talk-python-dev → talk-python-development
Proceed? (Y/n):

commandbook status [slug] [--json]

Show whether a saved command is currently running. Without a slug, lists every process Command Book manages.

With a slug:

$ commandbook status talk-python-dev

Talk Python (dev)  ● running

PID    UPTIME    SOURCE
───────────────────────
81234  12m       app

Without a slug (list all):

$ commandbook status

NAME               STATE    PID    UPTIME  SOURCE
────────────────────────────────────────────────────────
API Server         running  81234  12m     app
Talk Python (dev)  running  82345  3m      cli

Machine-readable output (--json):

$ commandbook status talk-python-dev --json
{
  "slug" : "talk-python-dev",
  "state" : "running",
  "instances" : [
    {
      "pid" : 81234,
      "startedAt" : "2026-06-17T10:02:00Z",
      "source" : "app",
      "uptimeSeconds" : 720
    }
  ]
}

Options:

Flag Description
--json Emit structured JSON instead of a human-readable table

Exit codes (scriptable, HTTP-flavored):

Code Meaning
0 Running — at least one live instance found
204 Known but not running (the command exists, nothing is live)
44 Slug not found (printed as 404 in the message/JSON)
244 Runtime registry error (printed as 500)

For the no-arg form (commandbook status), exit code is always 0 — it's a listing, not a predicate.

Framing for agents:

Command Book is the front door for starting and stopping long-running servers. If all processes are launched through Command Book (CLI run or the GUI app), then status always has a truthful answer. AI agents (Claude, etc.) can interrogate the environment cleanly:

if commandbook status django-project; then
    echo "Server is up"
else
    echo "Server is not running"
fi

commandbook wait <slug> [--for running|stopped] [--port n] [--http url] [--timeout s] [--json]

Blocks until a command reaches a state, then returns the instant it does. The timeout is only a ceiling — never a fixed delay. This is the clean replacement for until commandbook status <slug>; do sleep 1; done, which has no timeout and can't distinguish "still booting" from "already crashed."

$ commandbook run talk-python-dev &
$ commandbook wait talk-python-dev --http http://127.0.0.1:5000
● talk-python-dev — ready (http://127.0.0.1:5000) in 0.4s

What it waits for:

Form Returns when
commandbook wait <slug> the process is registered and alive (or has already exited cleanly)
… --port <n> a TCP connection to 127.0.0.1:<n> succeeds
… --http <url> the URL returns a 2xx/3xx response
… --for stopped the command is no longer running (returns its exit code)

Options:

Option Description
--for <running\|stopped> Condition to wait for (default running)
--port <n> Ready only once this TCP port on 127.0.0.1 accepts a connection
--http <url> Ready only once this URL returns 2xx/3xx (e.g. http://127.0.0.1:5000)
--timeout <seconds> Ceiling before giving up (default 30)
--interval <seconds> Poll cadence (default 0.1)
--json Emit structured JSON instead of a human-readable line

--port and --http make running mean ready: wait returns only once the process is alive and the endpoint answers — so you can drop a separate curl-retry loop. If the process exits before the endpoint comes up, wait fails fast rather than waiting out the timeout. (For http:// localhost, wait speaks raw HTTP so it isn't subject to App Transport Security.)

A plain commandbook wait <slug> also returns 0 if the command has already exited cleanly — handy for short commands. A non-zero exit is propagated as wait's own exit code. Use --for stopped to block until a one-shot command finishes and read its exit code.

Exit codes:

Code Meaning
0 Condition met (running / ready / clean exit / stopped)
124 Timed out before the condition was met
205 --http/--port target exited before becoming ready
44 Slug not found (printed as 404)
244 Runtime registry error (printed as 500)

JSON output (--json) carries state (running · ready · exited · stopped · timeout), waitedSeconds, an exitCode once the command has ended, and the live instances:

{ "slug": "talk-python-dev", "state": "ready", "waitedSeconds": 0.34,
  "instances": [ { "pid": 81234, "startedAt": "2026-06-18T08:40:00Z", "source": "cli", "uptimeSeconds": 0 } ] }

commandbook stop [slug] [--all] [--force]

Stop running command(s) that Command Book manages.

With a slug:

$ commandbook stop talk-python-dev
Stopped Talk Python (dev) (pid 81234).

Stop everything:

$ commandbook stop --all
Stopped API Server (pid 82345).
Stopped Talk Python (dev) (pid 81234).
2 processes stopped.

Force-kill immediately:

$ commandbook stop talk-python-dev --force
Stopped Talk Python (dev) (pid 81234).

Machine-readable output (--json):

$ commandbook stop talk-python-dev --json
{
  "slug" : "talk-python-dev",
  "requested" : 1,
  "stopped" : [ { "pid" : 81234, "name" : "Talk Python (dev)", "signal" : "SIGINT" } ],
  "failed" : [],
  "exitCode" : 0
}

signal is the signal that actually stopped each process (SIGINT, SIGTERM, or SIGKILL); anything that survived is listed under failed. For stop --all --json, slug is null and the arrays aggregate every target.

Options:

Flag Description
--all Stop every running Command Book process
--force Skip the graceful signal sequence and send SIGKILL immediately
--json Emit a machine-readable result (slug, requested count, stopped/failed, exit code)

Behavior:

  • Sends SIGINT → SIGTERM → SIGKILL to the process group (dev-server-friendly: Django, Flask, and similar tools do their cleanest shutdown on Ctrl+C)
  • Sets a stop-requested flag in the runtime registry before signaling, so auto-restart is suppressed — both in the CLI run loop and in the GUI app
  • Works whether or not the GUI app is running (reads the registry and signals directly)
  • Stops ad-hoc processes (started with run --command) by their --name slug, too

Exit codes:

Code Meaning
0 Stopped at least one instance
1 Matched live processes but none could be killed
204 Known but not running (nothing to stop)
44 Slug not found (printed as 404)
244 Runtime registry error (printed as 500)

commandbook open

Open the Command Book GUI application.

$ commandbook open
Opening Command Book...

If Command Book is already running, it brings the window to the foreground.


commandbook --help

Show help for all commands.

$ commandbook --help

Command Book CLI - Run saved commands from your terminal

USAGE:
    commandbook <command> [options]

COMMANDS:
    list                List all saved commands
    run [slug]          Run a saved command (interactive picker if no slug)
    new                 Create a new command
    edit <slug>         Edit an existing command
    status [slug]       Show whether a command is running (no slug = list all)
    stop [slug]         Stop running command(s)
    open                Open the GUI app

OPTIONS:
    --help          Show this help message
    --version       Show version information

commandbook --version

Show the version number (matches the GUI app version).

$ commandbook --version
Command Book 1.0.0

Slugs

Slugs are URL-style identifiers generated from command names. They're how you reference commands in the CLI.

Name Slug
Talk Python (dev) talk-python-dev
API Server api-server
Docker - Postgres DB docker-postgres-db
My App v2.0 my-app-v2.0

Slugs are always unique. If two commands would produce the same slug, a numeric suffix is appended: talk-python-dev, talk-python-dev-2, talk-python-dev-3.

Use commandbook list to see all slugs.


Shared Database

The CLI and GUI share the same SQLite database. Commands you create in the GUI appear in commandbook list, and commands you create with commandbook new appear in the GUI. Changes in either are immediately visible to the other.

The database location follows the GUI's storage settings. The storage location is shown in Settings → Storage, the CLI uses the same path.


Troubleshooting

PATH: commandbook: command not found

The CLI is installed at ~/.local/bin/commandbook. If your shell doesn't find it, add ~/.local/bin to your PATH:

# Add to ~/.zshrc or ~/.bashrc
export PATH="$HOME/.local/bin:$PATH"

Then restart your terminal or run source ~/.zshrc.

You can verify the install in Settings → CLI Tools, which shows the PATH status.

Database Not Found

Error: Command Book database not found. Please run the app first to initialize.

This means the CLI can't find the SQLite database. Open Command Book at least once to create it. If you've moved the database via Settings → Storage, the CLI reads the same setting automatically.

Command Not Found (Slug)

Error: Command 'foo' not found. Run 'commandbook list' to see available commands.

Check the exact slug with commandbook list. Slugs are generated from command names and may differ from what you expect (e.g., "My API Server" becomes my-api-server).

Working Directory Doesn't Exist

Error: Working directory '/path/to/dir' does not exist.

The saved working directory has been moved or deleted. Update it with commandbook edit <slug> or use the --dir flag to override:

commandbook run my-command --dir ~/new/path

CLI Not Working After Moving the App

The CLI symlink points to the binary inside the app bundle. If you move Command Book.app to a different location, reinstall the CLI tools from Settings → CLI Tools or File → Install CLI Tools.