The shape of it
One supervisor runs per machine. Under it sit N background agents — one per folder you promote. The supervisor is a normal user service: launchd on macOS, systemd --user on Linux. It starts at login, survives reboots, and respawns any agent that crashes.
Bash, Edit, Write all run without a prompt, because no one is at the keyboard to approve them. Configure Pi's tool permissions to taste before you promote a folder to a 24/7 daemon. A tool-approval gate is on the roadmap.1. Install the supervisor (once per machine)
From inside Pi:
/remote-pi install
That single command does two things:
- Installs and activates the user-level supervisor service (
launchd/systemd --user), so it auto-starts at login and after reboot. - Symlinks the
remote-piandpi-supervisordCLIs into~/.local/bin/so you can manage daemons from any shell. If that directory isn't on your$PATH, the command prints the line to add.
This is a separate, explicit opt-in — it is not part of the regular setup wizard. You only run it on machines where you want 24/7 agents.
2. Promote a folder to a daemon
No per-folder setup is needed first — create registers any folder and the supervisor injects the daemon's config at spawn (a fixed assistent workspace, relay on), so the folder needs no .pi/remote-pi/ of its own. To reach the daemon from your phone, just make sure this machine has been paired once — pairing is per-machine, so any earlier /remote-pi pair on it counts. Then register:
remote-pi create ~/Movies --name "Video Editor" # → Daemon registered: id=4e39152d name="Video Editor" cwd=/Users/you/Movies · started
The id is a stable hash of the folder path (sha256(realpath)[:8]), so it survives moves and is the same on every machine. With the supervisor running, create starts the daemon right away — there is no separate start step. It restarts on crash and comes back after a reboot on its own.
createtime. Pairing stays interactive — a daemon reuses the keypair and paired devices already set up on this machine; it doesn't show a QR itself.3. Manage the fleet
Every command works as a Pi slash command (/remote-pi …) and, once the CLI is linked, as a plain shell command (remote-pi …):
remote-pi daemons # list registered daemons + state remote-pi daemon status # pid, uptime, restart count remote-pi daemon send 4e39152d "Cut the first 30s of the latest clip" remote-pi daemon restart 4e39152d # restart one daemon by id remote-pi daemon restart # ...or the whole fleet (no id) remote-pi daemon stop 4e39152d # stop one remote-pi daemon stop # stop all
A daemon receives a prompt as if a user typed it; its response flows back through the same mesh and relay you configured — your phone sees it live, and other agents on the machine see it over the local mesh.
Where the logs are
# Linux journalctl --user -u remote-pi-supervisord -f # macOS tail -f ~/.pi/remote/supervisord.log
Each daemon's output is forwarded into the supervisor log with a [<cwd>] prefix, so one stream shows the whole fleet.
4. Schedule recurring prompts (cron)
A daemon only acts when something prompts it. cronlets the supervisor be that something on a schedule — “every weekday at 9am, summarize the new PRs” — with no one at the keyboard. Jobs target a daemon by id and survive reboots along with the supervisor.
/remote-pi install (step 1) first — without it, cron add warns instead of pretending to schedule. It is the same launchd / systemd service that keeps your daemons alive; there is no second scheduler.With a daemon registered (step 2) and the supervisor running, add a job. The first argument is the daemon id, then a standard five-field cron expression, then the prompt:
# every weekday at 9am, São Paulo time remote-pi cron add 4e39152d "0 9 * * 1-5" "Summarize the new PRs" --tz America/Sao_Paulo # → Cron j_ab12 added → daemon 4e39152d: "0 9 * * 1-5" (America/Sao_Paulo). Next run: …
Runs must be at least 60 seconds apart — a more frequent expression is rejected with a clear message, since every fire spends tokens. Flags on cron add:
--tz Area/City— run in a specific, DST-aware timezone (e.g.America/Sao_Paulo). Defaults to the machine's local time.--wake— if the daemon is stopped when the job fires, start it first, then send. Default: skip (logged asskipped_down).--no-skip-busy— send even if the daemon is mid-turn. By default a fire is skipped while the daemon is still working (skipped_busy) so prompts don't pile up on an unfinished turn.--catchup— after the supervisor was down, fire one missed run on startup. Default off; never replays the whole backlog.
Each job gets an id like j_ab12 (printed by cron add and cron list). Inspect, test, and audit the fleet's schedule:
remote-pi cron list # schedule, enabled, last run/status, next run remote-pi cron run j_ab12 # fire one now, ignoring its schedule remote-pi cron disable j_ab12 # pause without deleting (enable to resume) remote-pi cron log --tail 20 # recent fires AND skips remote-pi cron remove j_ab12 # delete the job
The agent's reply is fire-and-forget into the mesh — your phone and other agents see it live, exactly like a manual daemon send. Cron itself only audits the trigger: every fire and every skip appends one line to ~/.pi/remote/cron.jsonl, which cron log tails. Full subcommand reference is in the docs.
Removing a daemon
remote-pi remove <id> # unregister one daemon (folder config kept) remote-pi uninstall # remove the supervisor service (registry kept)
uninstall is reversible — re-running /remote-pi install later brings every registered daemon back. Full flags and paths are in the reference docs.