# babysit

[English README](README.md)

ローカルコマンドに API を生やすツール。外部の AI エージェント（Claude
Code, Codex, …）がライブ出力と終了状態を、`gcloud` や `kubectl` を
クエリするのと同じ感覚でクエリできる。

想定している 2 つの使い方:

1. **エージェントに run を渡す** — コマンドをラップしてセッション ID を
   渡すと、エージェントがライブ出力 / 終了状態を必要な時に引く（下記）。
2. **エージェントにオーケストレートさせる** — エージェントループがタスクを
   バックグラウンドで起動し（`babysit run -d`）、一覧し（`babysit ls`）、
   ログを読み（`babysit log`）、終了を待ち合わせる（`babysit wait`）。
   詳しくは[エージェントをバックグラウンドで回す](#エージェントをバックグラウンドで回す)。

**自分のシェル** — 普段通りに動かしたいコマンドをラップする。babysit が
セッション ID を表示し、コマンドはそのまま透過的に実行される:

```console
$ babysit -- make local-ci
babysit session ab12: make local-ci
  babysit log -s ab12 --tail 200
  babysit status -s ab12
Running tests...
✓ test_a
✗ test_b: assertion failed
make: *** [local-ci] Error 1
```

**別ターミナルのエージェント** — セッション ID（`ab12`）を渡せば、必要な
タイミングで状態を引きにいける:

```console
$ babysit status -s ab12
session: ab12
cmd:     make local-ci
state:   exit:2
exit:    2

$ babysit log -s ab12 --tail 3
✓ test_a
✗ test_b: assertion failed
make: *** [local-ci] Error 1
```

babysit 自身は監視を一切行わず、ラップされたコマンドを小さな
CLI / ファイル API として公開するだけ。いつ・どう使うかはエージェントが
決める。

## プロンプト例

セッション ID を渡したら、走り続けるコマンドを見ててくれる同僚に
頼むような感覚でプロンプトを書ける:

> `babysit` っていう CLI でセッション `ab12` を見ててほしい。
> `make local-ci` が終わったら教えて、コケてたらどのテストがどう
> 落ちたか要約して。

> `babysit` コマンドでセッション `ab12` を見張っといて。何かおかしく
> なった時だけ知らせて。

エージェントが自身のループで `babysit status` / `babysit log` を叩く
仕組みで、babysit 側からプッシュ通知を出すわけではない。

## なぜ作ったか

リモート実行系のプラットフォーム（`gcloud`, `kubectl`, CI など）には、
AI エージェントがログや状態を取得するための API が用意されている。
一方ローカル実行にはそれがない。ターミナルで走っているコマンドは、その
TTY に既に貼り付いているエージェント以外からはブラックボックスで、実行
中の処理を解析しようと思うと結局スクロールバックを手でコピペすること
になる。

babysit はそのギャップを埋める。ローカルのコマンドを一度ラップすれば、
ライブの出力と終了状態が、エージェントが既に扱い慣れている小さな CLI
からクエリ可能になる。スクレイピングも画面共有も追加の常駐プロセスも
いらない。

## インストール

```
curl -fsSL https://raw.githubusercontent.com/yusukeshib/babysit/main/install.sh | sh
```

チェックサム検証つきでバイナリを `~/.local/bin/babysit` に置く
（`BABYSIT_INSTALL_DIR` で配置先、`BABYSIT_VERSION=v0.2.4` で
バージョンを上書き可能）。macOS / Linux の x86_64 / aarch64 対応。

または [GitHub Releases](https://github.com/yusukeshib/babysit/releases)
からビルド済みバイナリを直接取ってくるか、ソースからインストールする:

```
cargo install --git https://github.com/yusukeshib/babysit
```

Nix（flakes）を使う場合:

```
nix run github:yusukeshib/babysit        # インストールせずに実行
nix profile install github:yusukeshib/babysit
```

インストール後は `babysit upgrade` で最新リリースに自己アップデート
できる。

## サブコマンド

```
$ babysit help
Wrap a shell command in a PTY and expose it to external agents via subcommands

Usage: babysit <COMMAND>

Commands:
  run      Wrap a shell command in a PTY and expose it via the other subcommands
  list     List all babysit sessions
  status   Show status of a session
  log      Show recent output from the wrapped command
  restart  Restart the wrapped command
  kill     Terminate the wrapped command
  send     Send text to the wrapped command's stdin (newline appended)
  wait     Block until the wrapped command exits, then return its exit code
  attach   Attach your terminal to a session (detach with Ctrl-\ Ctrl-\)
  detach   Detach any terminal currently attached to a session
  prune    Delete sessions whose wrapped command has finished or whose owner died
  upgrade  Self-update to the latest version
  config   Print shell integration (completions)
  help     Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version
```

各コマンドのフラグやエイリアスは `babysit help <command>` で見られる。
`babysit -- <cmd>` は `babysit run <cmd>` の短縮形。

セッション ID はデフォルトで自動採番されるが、`run` に `--id <id>` を
渡せば自分で指定できる（例: `babysit run --id ci -- make local-ci`）。
ID は一意で、使える文字は英数字・`-`・`_`・`.` のみ。

`-s <id>` は `--session <id>` の短縮形で、ID か `latest` という文字列を
受け付ける。ラップされたコマンドの内側からは `$BABYSIT_SESSION_ID`
経由でセッションが暗黙に決まるので、フラグは省略可能。

## アタッチ / デタッチ

ラップしたコマンドは常に PTY を所有するバックグラウンドワーカー
の下で走り、見ている端末はそのワーカーに**アタッチ**しているだけ
（tmux 風）。なのでコマンドを止めずに出たり入ったりできる:

```console
$ babysit run -- make local-ci    # アタッチして実行（自動アタッチ）
…                                 # Ctrl-\ Ctrl-\ でデタッチ
$ babysit attach -s ci            # あとでどこからでも再アタッチ
$ babysit detach -s ci            # アタッチ中の端末を外す（コマンドは継続）
```

- **デタッチホットキー:** `Ctrl-\ Ctrl-\`（Ctrl-バックスラッシュを 2 回）。
  コマンドを走らせたままシェルに戻る。（Ctrl-Q はフロー制御、
  Ctrl-P は pi 等の TUI が使うので避けた。）
- **フルスクリーンの TUI**（エディタ、`pi` など）は拡張キーボード
  モードを有効化して制御キーを別エンコードするため、ホットキーが
  検出されないことがある。その場合は別端末から `babysit detach -s <id>`
  でデタッチする（キー入力に依存しないので必ず効く）。デタッチ時に
  babysit が端末（alt-screen/mouse など）をリセットするのでシェルは使える状態で戻る。
- `babysit attach -s <id>` は直近の出力を再生してからライブを
  ストリームし、キー入力・リサイズを転送する。
- `babysit detach -s <id>` は別の端末からクライアントを外す。

### デタッチで起動（`-d`）

`babysit -d -- <cmd>`（または `babysit run -d -- <cmd>`）はコマンドを
バックグラウンドで起動して即座に戻り、アタッチはしない:

```console
$ babysit -d --id ci -- make local-ci
babysit session ci: make local-ci
  babysit log -s ci --tail 200
  babysit attach -s ci
$ # すぐにプロンプトが戻る。エージェントが ci をポーリングしてもよいし、アタッチしてもよい
```

ワーカーはこのシェルが終了しても生き残る。出力はアタッチの有無に
関わらずセッションログに記録されるので、`babysit log`/`status`/`send`/`kill`
はどちらでも同じように動く。

`status` と `log` は babysit 自身が終了した後でも動く（ディスク上の
状態ファイルにフォールバックする）。`restart`, `kill`, `send` はライブの
コントロールソケットが必要で、babysit プロセスが既にいない場合は失敗
する。

`babysit <unknown>` は未知のサブコマンドとして扱われ（`did you mean …?`
のヒント付き）、ラップ実行とはみなされない。ラップしたい場合は
`babysit -- <cmd>` か `babysit run <cmd>` を使うこと。

## シェル統合

`babysit config` を eval すると補完が有効になる:

```sh
# zsh (~/.zshrc)
eval "$(babysit config zsh)"

# bash (~/.bashrc)
eval "$(babysit config bash)"
```

サブコマンドとそのエイリアス、各コマンドのフラグ、そして最も便利な点として
`-s` のセッション ID 補完（`~/.babysit/sessions` を直接読む。`latest` も含む）
が効くようになる。`babysit run` はシェル本来のコマンド補完に委譲する。

## エージェントをバックグラウンドで回す

orchestrator（例: エージェントループ）が他のエージェントをバックグラウンド
で起動して監視する、という使い方に向いている:

```sh
# チケット名などで名付けて起動。ログを清潔に保つため --no-tty。
babysit run -d --no-tty --id ENG-123 --timeout 30m -- my-agent --task ENG-123

babysit ls --json                 # 何が動いてて、それぞれの状態
babysit log -s ENG-123 --tail 50  # 今何をしているか
babysit wait -s ENG-123           # 終了までブロック。終了コード＝エージェントのもの
```

- **`--no-tty`** … PTY でなくパイプで起動。非 tty を検出するツールは
  行指向のクリーンな出力を出すので、ログから拾いやすい（フルスクリーン
  再描画のノイズがない）。
- **`--timeout <dur>`** … ハングした run を自動終了（`30s`/`10m`/`2h`/`1d`/
  素の秒）。無人ループの安全弁。
- **`babysit wait`** … 終了までブロックして終了コードを返す。orchestrator が
  ポーリングせずタスクを join できる。`--timeout` で見切り、その場合は
  `124` で終了（セッションは走り続ける）。
- **`babysit log --since <offset> --json`** … 生ログのバイト offset 以降の
  差分だけを読み、`{text, offset, done}` を返す。ポーラーは `offset` から
  再開し、`done` で止められる。**`--follow`**（`-f`）は終了までライブを
  ストリーム（`tail -f` 相当）。

```sh
# 1 タスクの差分ポーリングループ:
off=0
while :; do
  r=$(babysit log -s ENG-123 --since "$off" --json)
  printf '%s' "$(jq -r .text <<<"$r")"
  off=$(jq .offset <<<"$r"); [ "$(jq .done <<<"$r")" = true ] && break
  sleep 1
done
```

ラップされたコマンドは `$BABYSIT_SESSION_ID` を参照できるので、エージェントが
自分のセッションを扱える。タスクごとの分離（git worktree 等）が欲しければ
ワークスペース管理ツールと併用するとよい。

## ディスク上のセッション状態

各セッションは `~/.babysit/sessions/<id>/` に書き出される:

```
meta.json       # 静的な情報（cmd, started_at, …）
status.json     # ライブの状態（running / exited / killed, exit_code）
output.log      # ラップ対象コマンドの生の出力
control.sock    # サブコマンドが通信する Unix ソケット
```

`babysit list` は、所有していた babysit プロセスが死んでいるセッション
（クラッシュ、kill -9、クリーンに終了状態を書き出す前のリブートなど）を
`dead` としてマークする。動いていないものを掃除するには
`babysit prune` を実行する。

## ソースからビルド

```
cargo build --release
# バイナリは target/release/babysit
```
