Skip to content

okazakee/opencode-shepherd

v0.2.4MIT

A Hermes Agent plugin that watches over opencode sessions: alerts when a turn stops, a question waits or a permission is requested, plus a dependency-free CLI to reply from anywhere.

πŸ‘ opencode-shepherd

Keep an eye on your opencode sessions from anywhere: know when they stop, ask a question or need a permission, and answer them without a UI.

A plugin for Hermes with a tiny, dependency-free Python CLI (standard library only) underneath. Works locally, over SSH, and completely on its own.

   sessions on your dev box              you, anywhere
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ opencode server (v2)   β”‚   ssh    β”‚ ocshepherd watch           β”‚
  β”‚ (same API as the TUI)  β”‚ ◄──────► β”‚   β†’ Telegram / terminal    β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚ ocshepherd send / perm / … β”‚
                                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Why

Long agent sessions behave like a slow colleague: they finish a step and wait, they ask before doing something risky, they block on a question. If opencode runs on another machine you find out too late, and a notification you can't reply to doesn't help. ocshepherd does both halves:

  1. Watch: tells you only when something new needs attention.
  2. Reply: send messages, answer questions, allow or deny permissions, from wherever you are.

Install

As a Hermes plugin (skills-only, no Python: your agent learns how to set up and use the CLI for you):

hermes plugins install okazakee/opencode-shepherd

The standalone CLI goes on the machine that can reach the opencode server:

pipx install git+https://github.com/okazakee/opencode-shepherd   # recommended
# or
pip install git+https://github.com/okazakee/opencode-shepherd
  • Requires Python β‰₯ 3.9 and a running opencode v2 (opencode serve, the background service, or a TUI; tested with v2.0.28).
  • Credentials are found automatically (see Good to know).

30-second tour

ocshepherd ping                 # is the server reachable?
ocshepherd list                 # your recent sessions
ocshepherd tail fix-auth 20     # what happened lately in a session
ocshepherd send fix-auth "also add a test for the empty case"
ocshepherd watch                # one check; silent if nothing new

Get notified (watch)

Schedule watch (every couple of minutes is a good default); it stays quiet unless something new happens.

AlertMeaning
⏸️a session finished its turn and waits for input
❓a session is asking a question
πŸ”a session requests a permission
⚠️a turn failed, or the server has been unreachable for several checks
# cron: local machine
*/2 * * * * /usr/local/bin/ocshepherd watch

# cron: Telegram notifications, silent stdout
*/2 * * * * OCSHEPHERD_TELEGRAM_TOKEN=... OCSHEPHERD_TELEGRAM_CHAT=... /usr/local/bin/ocshepherd watch --quiet

# cron: remote machine over SSH (snapshot remotely, diff + notify locally)
*/2 * * * * ssh devbox ocshepherd snapshot | ocshepherd watch --stdin --quiet
  • Notifications go to stdout, Telegram (--telegram-token/--telegram-chat), or any command (--notify-cmd, alert text on stdin; works with ntfy, webhooks, mail...).
  • --loop 120 keeps it running (polling), or use event-driven mode: ocshepherd watch --events streams the opencode event bus and only runs a check when something actually happens (turn boundaries, errors, permissions, questions), plus a periodic safety-net resync. Lighter and faster than polling.
  • Examples in examples/: crontab, systemd unit + timer, event-daemon unit, ntfy wrapper.
  • It knows the difference between "waiting for you" and "waiting for a background job": the latter is never reported, because opencode resumes those sessions by itself.

Reminders

Don't want a question to sit unanswered for hours? Set a reminder interval and watch will re-notify while an item is still waiting:

ocshepherd config set remind_after_minutes 30   # reminders every 30 minutes while waiting

Pause / resume notifications

ocshepherd config set enabled false   # pause: events are recorded silently
ocshepherd config set enabled true    # resume: one recap of what's waiting right now

Configuration

Settings live in ~/.config/ocshepherd/config.json (every key optional) and are read live, no restart needed:

ocshepherd config list
ocshepherd config set telegram_token 123:ABC
ocshepherd config unset remind_after_minutes
KeyDefaultMeaning
enabledtruemaster switch; while off, events are recorded silently, with a recap when re-enabled
remind_after_minutesnullre-notify every N minutes while an item is still waiting
snippet220max chars of last-message snippets
stateXDG statededupe state file path
telegram_token / telegram_chatnulldefault Telegram sink
notify_cmdnullcommand receiving alerts on stdin
resync_minutes30--events: full-check safety net interval
min_interval_seconds15--events: minimum gap between checks

Reply to a session

ocshepherd send fix-auth "text..."          # delivered immediately (steer)
ocshepherd send fix-auth "text..." --queue  # delivered after the current turn
ocshepherd send fix-auth --file prompt.md   # multiline
cat prompt.md | ocshepherd send fix-auth --stdin

ocshepherd forms fix-auth                   # pending questions
ocshepherd form-reply fix-auth --answer q0="yes"

ocshepherd perms fix-auth                   # pending permissions
ocshepherd perm fix-auth once               # once | always | reject

ocshepherd resolve fix-auth                 # what does this fragment match?

Sessions are addressed by a fragment: part of the session id or part of the title (case-insensitive). Ambiguous fragments print the candidates instead of guessing.

Sessions (create, abort, close)

ocshepherd new --dir ~/work/proj --title "notes" --prompt "summarise this repo"
ocshepherd abort fix-auth        # interrupt a running turn
ocshepherd close fix-auth        # show what would be deleted
ocshepherd close fix-auth --yes  # actually delete the session (and its children)

Over SSH (remote machines)

Snapshots are plain JSON, so you can pipe them straight over SSH, with nothing exposed and no new ports:

ssh devbox ocshepherd snapshot | ocshepherd watch --stdin   # watch from here
ssh devbox ocshepherd list                                  # or act remotely
ssh devbox ocshepherd send fix-auth "text..."

How the Hermes plugin works

The plugin is skills-only (no Python): it ships the opencode-shepherd skill, which teaches a Hermes agent how to install ocshepherd where opencode runs, how to set up watching and notifications, and how to reply to sessions on your behalf. The CLI itself stays a standalone distribution that works with or without Hermes.

All commands

CommandWhat it does
pingcheck connectivity + server info
listrecent sessions (RUNNING / child flags)
tail <frag> [n]last messages of a session
send <frag> ...send a message (--queue, --file, --stdin)
forms <frag> / form-reply <frag>inspect / answer pending questions
perms <frag> / perm <frag> ...inspect / reply to permission requests
resolve <frag>resolve a fragment to a session
snapshotfull JSON snapshot (for piping / your own tooling)
watchthe checks that produce notifications (--events for event-driven)
configshow / change persistent settings (enabled, reminders, sinks)
new / abort / closecreate / interrupt / delete sessions

Good to know

  • Credentials are auto-discovered: URL from $OCSHEPHERD_URL β†’ opencode service status β†’ http://127.0.0.1:4096; password from $OCSHEPHERD_PASSWORD β†’ ~/.config/opencode/service.json β†’ $OPENCODE_SERVER_PASSWORD; username defaults to opencode. Override with --url/--password/--username.
  • steer vs queue: send steers by default (delivered even mid-turn); --queue waits for the current turn to end.
  • Repeat safety: alerts are deduplicated by event id, and the first watch run only seeds state, so no old noise.
  • API caveats: built and tested against the opencode v2 server API (spec at http://<host>:<port>/doc). The API is young; if your version is newer, check the spec after upgrades. The v1 server API is not supported. Escape hatch: opencode api GET /api/session.

Security

  • Nothing listens and nothing is exposed: the tool only calls the opencode server over HTTP (or runs over your existing SSH).
  • If you configure a notification sink (Telegram, notify_cmd), the tool makes network calls to that service. Nothing else leaves the machine.
  • Credentials are never printed or logged. On shared machines prefer the env var or service.json over --password (visible in the process list).
  • perm/form-reply are real approvals: treat them like clicking approve in the TUI.

Related tools

  • opencode web / opencode pair / opencode attach: full interactive UIs; use them to drive sessions by hand.
  • opencode-notify: OS notifications on the machine running opencode.
  • @opencode-ai/sdk: the official TypeScript SDK for JS/TS integrations.

Development

python -m unittest discover -s tests -v

License

MIT. See LICENSE.