CLI reference

The ctrlrelay console script is a Typer app defined at src/ctrlrelay/cli.py. This page is generated by hand from that source — when in doubt, read the file.

Run any command with no arguments (or --help) to see Typer’s auto-generated help.

ctrlrelay [--version] <command> ...
Top-level Purpose
ctrlrelay --version / -v Print the package version and exit.
ctrlrelay version Same, as a subcommand.
ctrlrelay status Show active locks and the 5 most-recent sessions.
ctrlrelay config ... Configuration helpers.
ctrlrelay skills ... Skill discovery / audit.
ctrlrelay bridge ... Manage the Telegram bridge daemon.
ctrlrelay run ... Run a pipeline interactively.
ctrlrelay ci ... CI helpers used by the dev pipeline prompt.
ctrlrelay poller ... Manage the issue-poller daemon.

Note: the repos, export, import, team-export, team-import, setup, manifest, codex-export, codex-import, and codex-install commands are shell-script subcommands of the legacy ./sync wrapper, not the Python ctrlrelay CLI. See the README for those.

Every command that reads config accepts --config / -c to point at a non-default orchestrator.yaml (default: config/orchestrator.yaml).

ctrlrelay config

config validate

ctrlrelay config validate [-c PATH]

Loads config/orchestrator.yaml, validates the pydantic schema, prints the parsed node_id, timezone, transport type, and repo count. Exits non-zero on validation errors.

config repos

ctrlrelay config repos [-c PATH]

Lists configured repositories in a table (name, local path, deploy provider).

ctrlrelay skills

skills audit

ctrlrelay skills audit [-p SKILLS_DIR] [-c PATH]

Audits each skill under paths.skills (or --path) for orchestrator readiness — checks shape, required metadata, common pitfalls. Exits non-zero if any skill fails.

skills list

ctrlrelay skills list [-p SKILLS_DIR] [-c PATH]

Lists discovered skills as a table (name, path).

ctrlrelay bridge

See Telegram bridge for the full setup walkthrough.

bridge start

ctrlrelay bridge start [-c PATH] [-F|--foreground]

Starts the bridge listening on transport.telegram.socket_path. Reads the bot token from the env var named in transport.telegram.bot_token_env (default CTRLRELAY_TELEGRAM_TOKEN). Daemonizes by default — forks a detached process, writes a PID file alongside the socket, and returns to the shell. Pass --foreground / -F under launchd/systemd or when debugging interactively; the process runs in the foreground and still writes its PID file so ctrlrelay bridge status works.

Fails if transport.type is not telegram, the token env var is unset, or a PID file already exists for a live process.

bridge stop

ctrlrelay bridge stop [-c PATH]

Reads the PID file and sends SIGTERM.

bridge status

ctrlrelay bridge status [-c PATH]

Reports running / not-running by consulting the PID file. If the PID file is missing but the socket exists (e.g. the bridge was started by an older launchd plist that pre-dates the PID-file change), exits non-zero with a hint to restart the bridge — a restart on the new version will regenerate the PID file and let status work.

bridge test

ctrlrelay bridge test [-m "MESSAGE"] [-c PATH]

Connects to the bridge socket as a client and sends a one-off message. Useful for confirming end-to-end delivery to your Telegram chat without waiting for a real pipeline run.

ctrlrelay run

run dev

ctrlrelay run dev -i ISSUE [-r REPO] [-c PATH]
Flag Required Default Description
--issue / -i yes GitHub issue number.
--repo / -r when more than one repo is configured Limit to a single owner/repo.
--config / -c no config/orchestrator.yaml Config path.

Acquires the per-repo lock, creates the worktree, spawns Claude with the issue’s title/body in the prompt, and reports the result. If Claude blocks and the Telegram transport is configured, posts the question to your chat and waits for a reply (up to DEFAULT_MAX_BLOCKED_ROUNDS rounds).

run secops

ctrlrelay run secops [-r REPO] [-c PATH]

Runs the secops pipeline across configured repos (or one repo with --repo). Each repo’s run is serialised by the per-repo lock; the overall sweep runs in parallel across repos. Reports per-repo success/failure and exits non-zero if any repo failed.

ctrlrelay ci

ci wait

ctrlrelay ci wait --pr N --repo OWNER/REPO [--timeout 600] [--interval 15]
Flag Default Description
--pr / -p — (required) PR number to wait on.
--repo / -r — (required) Repository as owner/name.
--timeout / -t 600 Hard timeout in seconds.
--interval / -i 15 Seconds between polls.

Polls gh pr checks until every check has left the pending bucket (or the hard timeout is hit), then exits with:

  • 0 — all checks passed (or the repo has no CI configured)
  • 1 — at least one check failed / cancelled / timed_out
  • 2 — hard timeout while checks were still pending

Exists specifically so the dev pipeline’s prompt can point Claude at one correct command instead of asking it to improvise a bash until / while loop — every attempt at improvising those has been inverted-semantics or pipe-swallowed the exit code (see issue #85).

ctrlrelay poller

poller start

ctrlrelay poller start [-c PATH] [-F|--foreground] [-i SECONDS]
Flag Default Description
--interval / -i 300 Seconds between polls.
--foreground / -F off Run in the foreground. Default is to daemonize (fork + return to shell). Pass this under launchd/systemd.
--config / -c config/orchestrator.yaml Config path.

Polls each configured repo for issues assigned to your GitHub user (resolved via gh api user --jq .login). On the first run, seeds the seen-issue set with current assignments so it does not replay your existing backlog.

For each newly assigned issue, runs run_dev_issue(...) (i.e. the dev pipeline) in-process. If transport.type: telegram is configured and the bridge socket exists, sends notifications: 🔔 New issue on detection, ⏸️ Blocked on question, ✅ PR ready on success, ❌ Failed otherwise.

State is persisted to <state_db_dir>/poller_state.json.

poller stop

ctrlrelay poller stop [-c PATH]

Sends SIGTERM to the daemon PID.

poller status

ctrlrelay poller status [-c PATH]

Reports running / not-running.

ctrlrelay status

ctrlrelay status [-c PATH]

Opens the SQLite state DB at paths.state_db and prints:

  • Active locks(repo → session_id) for each held repo lock.
  • Recent sessions — the 5 most-recent rows from the sessions table (id, pipeline, repo, status).

status values: running, done, blocked, failed.

If the DB doesn’t exist yet, prints a hint to run a pipeline first.

ctrlrelay version

ctrlrelay version

Prints the package version (same as ctrlrelay --version).