---
name: switchroom-cli
description: "Run switchroom CLI operations on existing agents: logs, update, restart, version, config inspection, scheduled tasks, and Telegram plugin reference. HARD PREFIX TRIGGER: whenever the user's message starts with the phrase 'In switchroom (the CLI),' — regardless of what follows — use this skill. That prefix is load-bearing and wins over `switchroom-health`, `switchroom-runtime`, and `switchroom-manage`; even probes like 'In switchroom (the CLI), Can you why did it crash?', 'In switchroom (the CLI), Please sync my config.', and 'In switchroom (the CLI), Upgrade switchroom, please.' MUST route here, not to switchroom-runtime or switchroom-health. Use when the user wants to: show logs ('logs', 'what happened', 'check the journal', 'why did it crash'); update or upgrade agents ('update', 'pull latest', 'get new code', 'upgrade switchroom', 'Upgrade switchroom, please.'); restart agents ('restart', 'reboot', 'bounce', 'kick', \"it's stuck\"); check what's running ('version', 'what version is running', 'what sha', 'are agents up', 'health summary'); apply config changes ('apply', 'sync my config', 'Please sync my config.', 'apply my config changes', 'I just edited switchroom.yaml'); inspect an agent's effective config ('what model is X using', 'how is <agent> configured', 'show the cascade'); list scheduled tasks ('cron', 'timers', 'what runs automatically', 'scheduled tasks'); or ask about Telegram-plugin features ('what MCP tools does the bot have', 'how does reply work'). Also fires on verbatim phrasings: 'Please sync my config.', 'Could you check the journal for me?', 'Upgrade switchroom, please.', 'apply my config changes', 'what version is running', 'Can you why did it crash?', 'show me the logs', 'check the cron jobs', \"what's scheduled\". Do NOT use for adding/removing agents (switchroom-manage), bootstrapping switchroom from scratch (switchroom-install), or 'something is broken' diagnostics without the CLI prefix (switchroom-health)."
allowed-tools: Bash(switchroom *) Bash(docker *) Bash(docker compose *)
---

# Switchroom CLI operations

This skill is the reference for running `switchroom` CLI commands against existing agents. Each section below is triggered by a distinct user intent — jump to the relevant one rather than walking top-to-bottom.

**Four commands to know:**
- `switchroom update` — full operator path: pulls images + applies config + recreates containers + runs doctor (since v0.7.8 / #918). What you want 95% of the time.
- `switchroom apply` — config-only reconcile: refresh per-agent scaffolds and (re)write `~/.switchroom/compose/docker-compose.yml` without touching running containers. Use when you want to inspect the generated compose before bringing the fleet up yourself.
- `switchroom restart [agent]` — bounces a stuck or wedged agent
- `switchroom version` — shows what's running (versions + health summary)

**Prerequisite:** the `switchroom` CLI must be on `PATH`. If it isn't, direct the user to the `switchroom-install` skill.

---

## Logs — "show me the logs", "what happened", "why did it crash"

Fetch recent journal output when the user wants to see what an agent did or is debugging a specific crash.

### Step 1 — Identify the agent

If the user didn't name one, ask: *"Which agent do you want logs for?"* Then list available agents:

```bash
switchroom agent list
```

### Step 2 — Tail the logs

Default is the last 20 lines. User can specify a number. Use the CLI if available; fall back to `docker compose logs` when it's not:

```bash
switchroom agent logs <name> [--lines 50]
# or, when switchroom CLI isn't reachable:
docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml logs --tail 50 switchroom-<name>
```

### Step 3 — Present output

Include the last ~20 lines verbatim, then summarise what you see (crash, stall, normal chatter). If the user asked "why did it crash" and you don't see a clear cause, say so and offer `/doctor` as the next step.

---

## Update — "update", "pull latest", "get new code", "upgrade"

Use the `switchroom update` verb (since v0.7.8 / #918). It collapses pull + apply + recreate + doctor into one command.

```bash
switchroom update                # pull images + apply + recreate + run doctor
switchroom update --check        # dry-run: print the plan, exit 0
switchroom update --status       # read-only: CLI version + image/container ages
switchroom update --rebuild      # source-checkout users: also git pull + npm build
```

`switchroom update` is the operator path. The CLI self-elevates via sudo internally for the per-agent scaffold dirs that need root — no need for `sudo HOME=… PATH=…` incantations.

If you only need the config-reconcile half without restarting agents, `switchroom apply` writes `~/.switchroom/compose/docker-compose.yml` and refreshes per-agent scaffolds without touching running containers. The operator runs the docker bring-up themselves.

From inside an agent's Telegram DM, the same flow is available as `/upgradestatus` (read-only) and `/update apply` (admin-gated).

---

## Restart — "restart", "reboot", "bounce", "it's stuck"

Restart one agent or all. Also covers "refresh", "kick", "kill and restart", "stop and start".

### Step 1 — Identify the agent

If the user didn't name one, ask which. Accept `all` or no argument as "all agents".

### Step 2 — Run the restart

```bash
# Restart a specific agent (drains in-flight turn by default):
switchroom restart <name>

# Restart all agents:
switchroom restart

# Skip drain — SIGTERM immediately:
switchroom restart <name> --force
```

The `switchroom restart` top-level command reconciles + restarts and prints the health summary. It uses drain semantics by default (waits up to 60s for an in-flight turn to complete before cycling).

For the lower-level per-agent restart without reconcile, `switchroom agent restart <name>` is also available.

### Step 3 — Confirm

Report the outcome. If the agent is being restarted via Telegram (`/restart` handler), the user will see a `🔄 Restarting <name>…` ack followed by a `🎛️ Switchroom restarted — ready` message. Don't double-post.

---

## Version / health summary — "version", "what sha", "are agents up", "health check"

Show switchroom version, claude-code version, and the running status of all agents.

```bash
switchroom version
```

Output format:
```
✓ claude-code 2.1.119
✓ switchroom 0.3.0 / 7278044 (clean)
✓ klanker → up 5m, on 7278044
✓ gymbro → up 4h, on 7278044
✓ foreman → up 2d, on 7278044
```

No side effects. Safe to run at any time.

---

## Config inspection — "what model is X using", "show config for <agent>"

Surface the resolved effective config for a specific agent. Use for "how is X configured", "what tools does X have", or cascade-resolution questions.

### Step 1 — Pick the right inspector

For a high-level view (model, profile, topic, status, uptime):

```bash
switchroom agent list --json
```

For the full merged settings file (tools, hooks, MCP servers):

```bash
cat ~/.switchroom/agents/<name>/.claude/settings.json
```

For the **exact prompt + system message** an agent sends Claude on its next turn:

```bash
switchroom debug turn <name>
```

For the rendered workspace bootstrap block (CLAUDE.md, SOUL.md, skills wiring):

```bash
switchroom workspace render <name>
```

`debug turn` and `workspace render` are the authoritative answers when the user asks "why is X behaving this way" or "what is X actually being told".

### Step 2 — Explain the cascade

Agent config resolves through `defaults → extends profile → agent-specific`, with later keys winning. If the user asks *why* a value is set the way it is, walk them through which layer contributed it.

---

## Auth — "share my Pro account across agents", "auth verbs", "who's logged into what"

The **Anthropic account is the unit of authentication** — one OAuth flow per account, then "use this account on these agents" is fleet-wide config (not another OAuth round per agent). The `switchroom-auth-broker` daemon is the sole writer of every `credentials.json`; agents are passive readers. See `docs/auth.md` for the full model.

### CLI verbs

```bash
# Add an account (one OAuth flow per account, ever)
switchroom auth add <label> --from-oauth                   # interactive OAuth
switchroom auth add <label> --from-agent <name>            # seed from an existing agent's creds
switchroom auth add <label> --from-credentials <path>      # import a credentials.json
switchroom auth add <label> --from-oauth --replace         # re-auth an existing label (drift recovery)

# See the state of the fleet
switchroom auth list                                       # accounts + health + which one is active
switchroom auth show                                       # full snapshot (fleet + agents + consumers)
switchroom auth show <agent>                               # one agent's effective account + override

# Move the fleet to a different account
switchroom auth use <label>                                # fleet-wide active swap
switchroom auth rotate                                     # cycle to next non-exhausted in fallback_order

# Manage accounts
switchroom auth rm <label>                                 # refused if it's the only account

# Edge case: per-agent override (opts one agent out of the fleet active)
switchroom auth agent override <agent> <label>
switchroom auth agent override <agent> --clear

# Diagnostics (broker owns the refresh loop; this just forces a tick)
switchroom auth refresh                                    # all accounts
switchroom auth refresh <label>                            # one account
```

### Schema

```yaml
auth:
  active: me@example.com              # fleet-wide active account
  fallback_order:                     # ordered cycle list for `auth rotate`
    - me@example.com
    - work
    - personal

agents:
  ziggy: {}                           # inherits fleet active
  clerk:
    admin: true                       # gates /agents, /restart, /update AND admin /auth verbs
  klanker:
    auth:
      override: work                  # opt-out (edge case)
```

Most agents need no `auth:` block — they inherit `auth.active`. The pre-RFC-H per-agent `auth.accounts: [...]` and `auth_label:` fields are gone; replace them with fleet-wide `auth.active` + (rarely) `agents.<name>.auth.override`.

### Telegram surface

Three commands the gateway recognises in any agent chat:

- `/auth show` — fleet snapshot. Open to any agent (read-only).
- `/auth use <label>` — admin agents only.
- `/auth rotate` — admin agents only.

These replaced the v0.7-era `/auth dashboard` UI (a 1100-LOC slot-model promote UI, deleted in the broker rollout).

### When auth-related questions come in

- "I want one Pro/Max subscription on multiple agents" → that's just the default. `switchroom auth add me@example.com --from-oauth`, then `switchroom auth use me@example.com`. Every agent in the fleet inherits.
- "An agent's auth expired" → `switchroom auth list` first. If broker thinks the account is healthy but the agent isn't getting it, force a tick with `switchroom auth refresh` (diagnostic; the broker normally handles this on its own loop).
- "I hit a quota" → `switchroom auth rotate` cycles to the next non-exhausted account in `fallback_order`. Quota state is per-account and fans out in seconds across every agent on that account.

---

## Scheduled tasks — "what cron runs", "show me the timers"

List cron jobs and scheduled tasks.

### Step 1 — Show live timers

Since Phase 4 (#893) cron runs **in-container** as the `agent-scheduler`
sidecar inside each agent — the old `switchroom-<agent>-scheduler` /
`switchroom-cron` singleton container no longer exists. Inspect fired
jobs in the agent's own log; scheduler lines are prefixed
`agent-scheduler:`:

```bash
docker logs --tail 100 switchroom-<agent> 2>&1 | grep agent-scheduler:
```

### Step 2 — Show declared schedule entries

From `switchroom.yaml`, the `schedule:` array under each agent specifies `cron` + `prompt`. (A `model:` field may appear but is **ignored** — since the v0.8 cron-fold-in the fire runs in the agent's existing session and uses the agent's configured model, not a per-task one. Don't tell the user a per-task model is honoured.) Cron expressions are evaluated in the agent's resolved timezone (the `switchroom.timezone` / per-agent `timezone` cascade), not hard-coded UTC. Read the relevant agent block and enumerate the entries with their next-fire times in that zone.

### Step 3 — A schedule change is NOT live until the agent restarts

The in-container `agent-scheduler` reads its entries **once at boot**. Editing the `schedule:` array in `switchroom.yaml`, or adding/removing an entry via the agent-config `schedule add` / `schedule remove` tools, writes the change to disk but does **not** register it in the running scheduler. The same is true for `skill_install` and `.mcp.json` changes — claude loads skills and MCP servers at process start.

So whenever you (or the user) change a schedule, skill, or MCP config:

- The `schedule add` / `skill_install` tool result includes `restart_required: true` and a `restart_hint`. **Surface it.** Tell the user plainly that the change is on disk but won't take effect until `switchroom agent restart <name>` (or `switchroom restart <name>` for the reconcile+restart path).
- Never report a just-added schedule or skill as already active. It is staged, not live.

### Step 4 — Missed runs while the agent was offline

If the user asks whether scheduled runs were missed during downtime: the scheduler replays fires missed within the last ~30 min on boot, but runs older than that window are **not** re-run (cron is not a queue). It is not silent about this — on boot it emits a one-time notice listing every schedule that had a skipped run, delivered as a normal turn and recorded in `agent-scheduler:` log lines / `/state/agent/scheduler.jsonl`. Check those to answer honestly; never claim a run happened if the skip notice says it was dropped.

---

## Telegram plugin reference — "what MCP tools", "how does reply work"

The `switchroom-telegram` plugin is an enhanced fork of the official Telegram MCP plugin and is the default for all switchroom agents. It exposes **10 MCP tools** (all prefixed `mcp__switchroom-telegram__`):

| Tool | Purpose |
|---|---|
| `reply` | Send a text/photo message, with optional `reply_to` for threaded quotes |
| `stream_reply` | Incrementally stream a long reply (edits the same message as tokens arrive) |
| `react` | Emoji reaction on an inbound or outbound message |
| `edit_message` | Modify an earlier bot message's text |
| `delete_message` | Remove an earlier bot message |
| `forward_message` | Forward a message from another chat |
| `pin_message` | Pin a message in the current chat |
| `send_typing` | Show the "typing…" indicator |
| `download_attachment` | Save a Telegram file attachment to the agent's inbox |
| `get_recent_messages` | Fetch recent history for context |

Additional features:
- **Status reactions** — 👀 queued → 🤔 thinking → 👨‍💻 tool → 🔥 streaming → 👍 done
- **Progress cards** — pinned, live-updating tool-step summary
- **SQLite history** — enables quote-reply defaults
- **PI-safe envelope** — inbound text wrapped in `<channel source="telegram">` for prompt-injection safety
- **Inline approvals** — tool permissions surface as ✅/❌ buttons or via `/approve` `/deny` `/pending`
- **Slash commands** — `/new`, `/reset`, `/approve`, `/deny`, `/pending`, `/restart`, `/update`, `/version`, `/logs`, `/doctor`, `/auth`, `/switchroomhelp` (see `TELEGRAM_MENU_COMMANDS` in `telegram-plugin/welcome-text.ts`)
- **`/auth`** — three chat commands: `/auth show` (read-only, open to any agent), `/auth use <label>` and `/auth rotate` (admin agents only). Backed by the auth-broker — see the **Auth** section above and `docs/auth.md` for the full model.
- **Access control** — `dmPolicy: pairing | allowlist | disabled` per agent

---

## Rule of thumb

If the user is asking **"do X"**, this is your skill. If they're asking **"why is X broken"**, switch to `switchroom-health`. If they're asking **"how do I add/remove an agent"**, switch to `switchroom-manage`. If they're new and don't have switchroom yet, switch to `switchroom-install`.
