Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,34 @@ follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Changed
- **tmux runs now start attached** — `sleeperagent run` (and `attach-existing`)
from a real terminal puts your terminal inside the tmux session immediately
(via a supervised `tmux attach`), so Linux/macOS now match the Windows/ConPTY
experience: prompt and use the agent exactly as if you'd launched it
directly, while the watchdog monitors from the same process and auto-resumes
after a limit reset. Details:
- The supervisor's own attach client doesn't trigger auto-detach, but a
*second* client attaching during your view — or a manual `tmux attach`
after you detach it — is still treated as a takeover and steps the
watchdog aside.
- Detaching your view (tmux prefix + `d`) keeps the watchdog running and
returns you to its console log with the `d`/`q`/`k` hotkeys active; the
hotkeys also engage if the initial attach fails (e.g. running from inside
an existing tmux session skips the auto-attach entirely).
- Stopping the watchdog while you're inside the view (`sleeperagent
detach`/`stop`) notifies you via the tmux status line and waits for you to
detach instead of yanking your terminal out of the session.
- Supervisor logs go to the instance log file while the view is attached
(`sleeperagent logs --name N`); after the view detaches, the run logs to
its terminal again.
- Non-TTY runs (scripts, CI) are unchanged.

### Added
- **`--detached`** flag on `run` and `attach-existing` — opt out of the new
attach-on-start behavior and watch from the console with the `d`/`q`/`k`
hotkeys, as before.

## [0.3.0] - 2026-07-04

This release also folds in everything previously listed under
Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,16 @@ Optional extras: a local [Ollama](https://ollama.com) for `--reprompt`; `notify-
sleeperagent run --agent claude --name mytask
```

`--agent` picks the adapter (how to detect the limit and drive the agent); by default it also launches that adapter's own command, so the `claude` adapter just runs `claude`. You only need a trailing `-- <command…>` to launch something *different* — your own flags, a wrapper, or another binary (see [Examples](#examples)). Press `d` to detach, or just leave it running. Run `sleeperagent` with no arguments for the built-in help.
`--agent` picks the adapter (how to detect the limit and drive the agent); by default it also launches that adapter's own command, so the `claude` adapter just runs `claude`. You only need a trailing `-- <command…>` to launch something *different* — your own flags, a wrapper, or another binary (see [Examples](#examples)). Run `sleeperagent` with no arguments for the built-in help.

On every platform, `run` from a real terminal drops you **straight into the live agent**: prompt it and use it exactly as if you'd launched it directly, while SleeperAgent watches in the background and auto-resumes after a limit reset. On the tmux backend, detach your view with the tmux prefix + `d` (watchdog keeps running), or pass `--detached` to skip attaching entirely.

---

## Ways to use it

- **`sleeperagent run`** — the default. Launches the agent, watches it in your terminal, detaches with a hotkey, and takes over the moment you're back. Check on it from any other shell with `sleeperagent status`.
- **`sleeperagent attach-existing`** — you already started the agent yourself in tmux and want SleeperAgent to pick up watching it without restarting anything.
- **`sleeperagent attach-existing`** — you already started the agent yourself in tmux and want SleeperAgent to pick up watching it without restarting anything. From a real terminal this also drops you into the live session, same as `run`.

---

Expand Down Expand Up @@ -130,6 +132,7 @@ sleeperagent run --agent claude --name mytask
| `--prompt` | Static resume prompt to inject on reset. |
| `--reprompt` | Local-LLM reprompt, e.g. `ollama:llama3.1` (falls back to static). |
| `--backend` | `tmux` or `pty`. Unix defaults to tmux when it is available, otherwise pty; Windows defaults to pty. |
| `--detached` | tmux backend: don't attach this terminal to the session; watch from the console with the `d`/`q`/`k` hotkeys instead. |
| `--yolo` | Append the agent's skip-permissions flag (**DANGEROUS** — unattended, no prompts). |
| `--auto-answer-prompts` | Answer interactive prompts with the first/default option so the run doesn't stall while you're away (**default: on**; pass `=false` to disable). |
| `--webhook` | POST notifications to this URL as JSON. |
Expand Down Expand Up @@ -169,11 +172,11 @@ SleeperAgent is built to get out of your way. How handoff works depends on the b

**tmux backend (Linux/macOS):** the agent lives in a tmux session that **outlives the supervisor**, so nothing is lost when you take over. Install tmux (`brew install tmux` on macOS) or pass `--backend tmux` if you specifically need this behavior.

- **Hotkeys** (foreground run): `d`/`q` detach, `k` kills the session (with a `y` confirm).
- **`sleeperagent detach --name X`** from any other shell.
- **Ctrl-C** detaches — it never kills the session.
- **Auto-detach:** the moment you `tmux attach`, SleeperAgent notices and steps aside so you don't both type.
- **You start attached:** `run` from a terminal puts you inside the session immediately — prompt the agent as usual while the watchdog monitors. Detach the *view* with the tmux prefix + `d` (default `Ctrl-b d`); the watchdog keeps running and you get its console log back **with the `d`/`q`/`k` hotkeys active**. (Runs from inside an existing tmux session skip the auto-attach, since tmux refuses nested attaches.)
- **`sleeperagent detach --name X`** from any other shell stops watching (the session keeps running). If you're still inside the view, SleeperAgent tells you via the tmux status line and waits for you to detach rather than yanking your terminal.
- **Auto-detach:** a `tmux attach` *after* you detach your initial view — or a **second** client attaching while your view is up — is treated as a takeover, and SleeperAgent steps aside so you don't both type.
- Reattach anytime with `tmux attach -t <name>`.
- **`--detached` mode:** the pre-0.4 console view — supervisor logs in your terminal with hotkeys `d`/`q` detach, `k` kill (with a `y` confirm); Ctrl-C detaches, never kills.

**pty / ConPTY backend (default on Windows, automatic Unix fallback when tmux is missing):** the agent is a child of the supervisor, so it **can't be handed back interactively**. `detach` gives the terminal back to you until the agent exits; `stop --kill` ends the agent. Use the tmux backend if you need full handoff.

Expand Down
12 changes: 7 additions & 5 deletions cmd/sleeperagent/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import (
"github.com/amanjaiman/sleeperagent/internal/statefile"
)

// instanceLogPath is where a pty-backend instance's supervisor log is written.
// instanceLogPath is where an instance's supervisor log is written: pty-backend
// runs always, and tmux runs while the interactive view is attached — the two
// modes where supervisor output is kept off the terminal so it doesn't corrupt
// the agent's TUI.
func instanceLogPath(instance string) string {
return filepath.Join(statefile.Dir(), instance+".log")
}

// logsCmd prints (and optionally follows) an instance's supervisor log. The log
// exists for pty-backend runs, where supervisor output is kept off the terminal
// so it doesn't corrupt the agent's TUI.
// logsCmd prints (and optionally follows) an instance's supervisor log.
func logsCmd(args []string) error {
fs := flag.NewFlagSet("logs", flag.ContinueOnError)
name := fs.String("name", "", "instance name (required)")
Expand All @@ -39,7 +40,8 @@ func logsCmd(args []string) error {
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("no log file for instance %q at %s\n"+
"(logs are written by the pty backend; a tmux foreground run logs to its own terminal)",
"(the log is written by pty-backend runs, and by tmux runs while the interactive "+
"view is attached; after the view detaches, the run logs to its own terminal)",
*name, path)
}
return err
Expand Down
Loading
Loading