Wrap a command in a PTY and control it from another terminal. The command runs in the background under a worker that owns the PTY and records its output. From anywhere you can read the output, screenshot the screen, send input, wait for exit, or attach interactively.
Useful for scripts and coding agents that need to drive a command they didn't start.
$ babysit run -d --json -- make local-ci # {"id":"ab12"}
$ babysit log -s ab12 --tail 20
$ babysit screenshot -s ab12
$ babysit wait -s ab12babysit -- make local-ci is the interactive shorthand. For scripting, use
run -d --json and capture the id.
The command runs under a background worker that owns the PTY, logs all output, and serves a Unix control socket. Your terminal is just attached to it, so you can detach, re-attach, and query from other terminals.
State lives in ~/.babysit/sessions/<id>/. Set $BABYSIT_DIR (absolute path) to
change the root. status, log, and screenshot work after the worker exits;
send, key, restart, and kill need it alive.
-s <id> selects a session; there is no "most recent" fallback. Inside the
wrapped command the id is exported as $BABYSIT_SESSION_ID.
Pass an OpenSSH destination directly to global --host:
$ babysit --host user@devbox run -- pi
$ babysit --host user@devbox list
$ babysit --host user@devbox attach -s ab12SSH config aliases work too, so --host dev uses the Host dev entry from
~/.ssh/config (including its user, port, ProxyJump, and keys). The remote host
must already have a compatible babysit in its non-interactive PATH.
A foreground remote run starts one detached worker on the remote machine and
then attaches to it. SSH server-alive probes detect a silent transport failure
(such as Wi-Fi loss) within roughly 10 seconds. Babysit then clears the stale
local display and shows the host, session, disconnect reason, offline duration,
raw-log offset, retry attempt, and backoff while reconnecting. The same process
and PTY continue remotely. Once connected, babysit replays the bounded raw
backlog to rebuild the terminal display, then resumes offset-based live output.
Input typed while offline is discarded. Detach at any time with
Ctrl-\ Ctrl-\, including while reconnecting; pass attach --no-reconnect to
make a transport loss fatal.
Sessions using run --view-cmd have a transformed stream without stable raw
byte offsets, so their remote attach returns an error after transport loss
instead of replaying duplicate output. run -d starts the remote worker without
attaching.
--host local is the default and runs directly without SSH. Any other value is
passed to ssh as its destination. The embedding API remains local-only.
babysit is also a library. Everything is reached through a Babysit context — an
explicit handle to a state root. The library never reads the environment to find
its root; you pass it in. ($BABYSIT_DIR is consulted only by the babysit
binary, via Babysit::from_env.)
use babysit::Babysit;
# async fn demo() -> anyhow::Result<()> {
let bs = Babysit::new("/path/to/state"); // explicit root, no env
let id = /* spawn */ "job".to_string();
for s in bs.list_sessions().await? {
println!("{} {}", s.id, s.state);
}
bs.kill(Some(id), false).await?;
# Ok(()) }Embedders (e.g. looop) compute their own
root and call Babysit::new, so a babysit-backed tool never has to touch
$BABYSIT_DIR or share the global ~/.babysit.
Pick one.
From crates.io:
cargo install babysitWith Nix flakes:
nix profile install github:yusukeshib/babysitPrebuilt binary via the install script:
curl -fsSL https://raw.githubusercontent.com/yusukeshib/babysit/main/install.sh | shThe install script drops a prebuilt binary in ~/.local/bin (override with
BABYSIT_INSTALL_DIR, pin with BABYSIT_VERSION). babysit upgrade
self-updates cargo/binary installs; Nix installs are managed by Nix.
Run without installing: nix run github:yusukeshib/babysit -- -- make local-ci.
| Command | Description |
|---|---|
run |
Wrap a command in a PTY (babysit -- <cmd> shorthand; -d detached; --json prints the id) |
list (ls) |
List sessions |
status |
Session state and exit code |
log |
Show output; --tail, --grep, --follow, --since |
screenshot (shot) |
Render the current screen; --format plain|ansi|json, --trim |
send |
Send text to stdin (-n no newline; --json) |
key |
Send named keys (Enter, Up, Esc, C-c, F1, …) |
expect |
Block until a regex appears (--screen matches the rendered TUI) |
wait-idle |
Block until output is quiet for --settle |
wait |
Block until exit, return the exit code |
resize |
Resize the terminal (COLSxROWS) |
flag / unflag |
Flag a session for attention / clear it |
restart |
Restart the command |
kill |
Terminate the command's process group (graceful signal, then forced escalation) and return only after exit is confirmed |
attach / detach |
Attach your terminal (detach: Ctrl-\ Ctrl-\) / detach others |
prune |
Delete finished or dead sessions |
upgrade |
Self-update |
config |
Shell completions: eval "$(babysit config zsh|bash)" |
Each command documents its own flags and gotchas: babysit help <command>.
babysit --help covers the model and the typical agent loop.
On Unix, kill covers the isolated process group created for the wrapped
command. A descendant that deliberately daemonizes into a different process
group or session is outside that boundary.
cargo build --release # target/release/babysit