scoot 0.9.0
A reliability release, from an outside review of 0.8.1; the R-numbers are the review's finding ids.
- A worktree never loses work (R05).
/worktree keepand/worktree mergeused to read a failed commit (a rejected hook, a missing identity) as "no changes" and force-remove the worktree with the files in it.finishnow tells a clean tree, a committed one, and a failed commit apart, retains the worktree and the branch on any Git failure, and keeps the session in it until you fix it ordiscard. Work the agent already committed on the branch is merged too, measured against the recorded base commit. - Entering a worktree moves the scope (R06). The file scope and the hooks now follow the root, so a worktree nested under the original checkout no longer leaves the original in scope; path grants for the old root are dropped.
- Writes that cannot destroy a file (R08).
write_filewithout a stringcontentis an error, not an emptied file (an explicit""still empties it).edit_filerefuses a file that is not UTF-8 instead of silently replacing bytes, keeps CRLF files CRLF while letting the model quote them with plain newlines, and refuses to overwrite a file that changed after it was read. Both tools write through a temp file and an atomic rename that preserves the mode, so a failed write leaves the old file intact. - Failed commands explain themselves (R09). A failing test or build used to reach the model as
ERROR: exit 1; the captured output now follows the error line, so the model fixes the code instead of rerunning the command. - Resume keeps your approval mode (R04). Resuming a session that once ran in
yolono longer turns--approval alwaysintoyolo; the conversation is restored, the permission policy is this run's. - Every tool call gets a result (R11). Aborting or interrupting a batch answers every remaining call with a "not executed" result, and loading a saved session fills in any missing one, so the next request is never rejected for an unanswered tool call. Arguments that are not a JSON object never run a tool with empty arguments.
- A cut-off reply is not an answer (R12). A reply that hit the model's output limit, or a stream that closed before its terminal event, ends the turn as
incomplete: the partial text is shown with a warning, one-shot exits 1, headless emitsstatus: incomplete. Tool calls in such a reply are not executed and the retry is bounded. A 200 stream with no events, or with unreadable events, is an error rather than an empty success. - Stop means stop (R10). Shell commands and hooks run in their own process group, which ESC and the timeout terminate as a whole, with bounded waits and a monotonic deadline; a background grandchild no longer keeps the call blocked. The ESC watcher shuts the socket down before closing it and closes the response, so a stalled read wakes on every platform. Hooks honour the cancel event they were always handed.
- Streaming (R14). Short SSE events are delivered as they arrive instead of waiting for a 1 KB buffer, and a character split across two reads is decoded whole.
- Zipapp (R19).
scoot.pyznow exits withmain()'s status (a handled error used to exit 0) and contains the package only; CI checks both. - A blueprint.
docs/blueprint.mdis the minimal, self-contained spec for building a CLI agent like scoot: components and their contracts, the turn, the tools, the permission rules, the build order, and the acceptance checks.
Install: pipx install scootcli && pipx ensurepath, or curl -fsSL https://raw.githubusercontent.com/sergenes/scootcli/main/install.sh | bash, or download scoot.pyz below. Upgrade: pipx upgrade scootcli.