6
0
Fork
You've already forked withdone
0
a process wrapper that let's an LLM agent exit itself
  • Shell 100%
Hugo O'Connor 3c1ce951a1
feat(recipes): codex-exec-sentinel for OpenAI Codex CLI
Composes withdone with 'codex exec' (Codex's non-interactive
subcommand) using --dangerously-bypass-approvals-and-sandbox so the
agent can invoke its bash tool to write the sentinel.
Authenticated via 'codex login' (ChatGPT/Codex subscription billing).
Verified live: rc=7 propagates cleanly, transcript clean (no /dev/tty
bypass issues like opencode), no terminal-mode corruption.
Added recipes/codex-exec-sentinel.sh as a turnkey script and a new
section 2 in recipes/README.md showing the inline pattern. Renumbered
sections 3-7 → 4-8.
2026年05月13日 21:09:45 +10:00
recipes feat(recipes): codex-exec-sentinel for OpenAI Codex CLI 2026年05月13日 21:09:45 +10:00
tests BREAKING: v3.0.0 — drop -m marker-line mode, sentinel-file only 2026年05月13日 21:04:31 +10:00
.gitignore revert: drop Rust v3 port, restore shell withdone v2.1.1 2026年05月13日 19:16:46 +10:00
LICENSE Initial commit: withdone v1.0.0 2026年05月13日 16:21:33 +10:00
README.md BREAKING: v3.0.0 — drop -m marker-line mode, sentinel-file only 2026年05月13日 21:04:31 +10:00
SPEC-001-withdone.md BREAKING: v3.0.0 — drop -m marker-line mode, sentinel-file only 2026年05月13日 21:04:31 +10:00
withdone BREAKING: v3.0.0 — drop -m marker-line mode, sentinel-file only 2026年05月13日 21:04:31 +10:00

withdone

A tiny POSIX sh process wrapper that lets a coding-agent CLI declare its own logical completion and exit code by writing it to a sentinel file. withdone watches the path, kills the child on detection, and exits with the agent-supplied code.

withdone -s PATH -- COMMAND [ARGS...]

Caller mints PATH, embeds it in the agent's prompt as the completion instruction ("when done: echo N > PATH"), passes the same path to withdone via -s. Agent's bash tool writes the code; withdone reads it and propagates as its own exit code.

See SPEC-001-withdone.md for the contract, recipes/ for compositions with expect(1), script(1), timeout(1), etc.

Why

Coding agents have no built-in way to tell an orchestrator "I finished task X with exit code N" — the host process keeps running. Existing workarounds (parse output for a marker, detect idle, watch for prompt re-appearance) all guess at completion and can't communicate semantic exit codes. withdone gives the agent a direct channel: write the code to a path you control via your bash tool. The wrapper exits with that code, no guessing.

withdone deliberately does not allocate PTYs, inject prompts, detect readiness, parse output, or do log rotation. Those are existing Unix tools (script, expect, unbuffer, timeout, tee, sed, ts); compose them. See ADR-002 for why.

Install

curl -fsSL https://codeberg.org/anuna/withdone/raw/branch/main/withdone \
 -o ~/.local/bin/withdone && chmod +x ~/.local/bin/withdone

Or, if you've cloned the repo and want to track edits:

ln -s "$(pwd)/withdone" ~/.local/bin/withdone

Verify with withdone --versionwithdone 3.0.0. No dependencies beyond mkfifo, mktemp, sleep, cat, tr — all POSIX-baseline.

Quick example

s=$(mktemp -u "${TMPDIR:-/tmp}/withdone-XXXXXX")
prompt="Respond with the single word 'hello'.
When done, run this command via your bash tool — no other output:
 echo 0 > $s(use a non-zero number if you couldn't complete the task; do not run
 this command until the task is genuinely done.)"
withdone -s "$s" -- opencode run --dangerously-skip-permissions "$prompt" </dev/null
echo $? # 0

The completion instruction (canonical)

Every prompt you send through withdone MUST teach the agent how to signal completion. The canonical wording is:

When you have completed the task, signal completion via your bash tool by running exactly: echo <CODE> > <SENTINEL_PATH>

Where <CODE> is a single non-negative integer:

  • 0 on success
  • 1 if you could not complete the task
  • 2+ for other non-zero codes describing specific failures

Do not run this command until the task is genuinely done.

Paste it into your prompt verbatim with the sentinel path substituted. If you find a phrasing that gives more reliable agent compliance, open a PR against this README.

Usage

withdone -s PATH [OPTIONS] -- COMMAND [ARGS...]
Options:
 -s, --sentinel PATH path the agent writes its exit code to (required)
 MUST NOT exist at invocation time
 --grace SECS SIGTERM-to-SIGKILL grace period (default: 5)
 -h, --help
 -V, --version

Exit codes:

Code Meaning
0 Agent wrote 0, or child exited 0
1–63, 65–123 Forwarded child or agent-supplied code
64 Usage error or invalid sentinel content
127 Exec failure

For wall-clock bounds, wrap with timeout(1): timeout 600 withdone ....

Pipeline composition

The whole point: exit codes propagate cleanly through shell composition.

# Watch loop — the canonical use case
while task=$(get_next_task); do
 s=$(mktemp -u "${TMPDIR:-/tmp}/withdone-XXXXXX")
 prompt="$taskWhen done: echo 0 > $s (success) or echo N > $s (failure code N)."
 timeout 600 withdone -s "$s" -- opencode run \
 --dangerously-skip-permissions "$prompt" \
 </dev/null > "logs/${task}.log" 2>&1
 case $? in
 0) record_success "$task" ;;
 124) record_wallclock_exceeded "$task" ;;
 *) record_failure "$task" "$?" ;;
 esac
done

See recipes/README.md for more patterns — including Claude TUI subscription mode via expect.

Testing

tests/live-opencode.sh # live LLM integration; costs a small LLM round-trip

Status

v3.0.0 — approved and implemented.

v3 removes the v2 marker-line mode (-m MARKER watching stdout). The sentinel-file mode covers every case marker-line did, with strictly better reliability across TUI agents, /dev/tty-bypass agents, and multilingual model paraphrasing. Single mode, one obvious way to use the tool.

The script and its spec are intended to remain stable; cat -v Considered Harmful is the explicit standing rebuke against feature additions.

License

Apache 2.0 — see LICENSE.