o script: audit cleanup

- Merge readloop into read -l (loop mode)
- Remove chatstream command (TUI uses 'o read chat' directly)
- Rework ctl: o ctl <cmd> [session] [agent] — dispatches to
  session ctl for kill/pause/resume, agent ctl for the rest
- Add trap EXIT to kill background statewait loop (fixes zombie)
- Remove -1 compatibility flag from generate
- Remove no-op bracketed paste bind from prompt REPL
- Fix /i help text: 'inject prompt (mid-turn or queued)'
- Update all help text to reflect current commands
This commit is contained in:
Ollie Agent 2026-08-09 02:14:24 +02:00
parent 49c93eab5f
commit d1907adfae
1 changed files with 61 additions and 97 deletions

View File

@ -9,17 +9,14 @@
# - No context set → path used as-is (root-relative)
#
# Subcommands:
# read <path> Read a file
# read <path> Read a file (-l to loop)
# write <path> [data] Write to a file (args or stdin)
# readloop <path> Read in a loop (re-reads after each return)
# ls [path] List namespace entries
# ctl <command> Send a control command (stop, compact, kill, ...)
# prompt Interactive REPL for submitting prompts (requires agent)
# tail Tail chat output (requires agent)
# chatstream Stream chat output (requires agent)
# tui Launch tmux TUI (requires agent)
# env Print resolved session/agent environment
# stop Interrupt running agent (requires agent)
# kill Kill session (requires session)
#
# Environment:
# session Session name (set via 'eval $(o env)')
@ -161,23 +158,29 @@ require_agent() {
echo "$a"
}
agent_path() {
local s a
s=$(require_session) || return 1
a=$(require_agent "$s") || return 1
echo "session/$s/agent/$a"
}
# ── Subcommands ──────────────────────────────────────────────────
cmd_read() {
local loop=false
while [[ "${1:-}" == -* ]]; do
case "$1" in
-l|--loop) loop=true; shift ;;
*) break ;;
esac
done
if [[ $# -lt 1 ]]; then
echo "usage: o read <path>" >&2
echo "usage: o read [-l] <path>" >&2
exit 1
fi
local path
path=$(resolve_path "$1") || exit 1
ollie-9p read "$path"
if $loop; then
while true; do
ollie-9p read "$path" || break
done
else
ollie-9p read "$path"
fi
}
cmd_write() {
@ -219,12 +222,8 @@ cmd_generate() {
# One-shot generation via /generate (no session/agent needed)
# Usage: o generate "prompt"
# echo "prompt" | o generate
# o generate -1 "prompt" (same as above)
local prompt=""
# Handle -1 flag (ignored, for compatibility)
[[ "${1:-}" == "-1" ]] && shift
if [[ $# -gt 0 ]]; then
prompt="$*"
else
@ -270,28 +269,40 @@ cmd_route() {
printf '%s' "$task" | ollie-9p rdwr route
}
cmd_readloop() {
if [[ $# -lt 1 ]]; then
echo "usage: o readloop <path>" >&2
exit 1
fi
local file="$1"; shift
local path
path=$(resolve_path "$file") || exit 1
while true; do
ollie-9p read "$path" || break
done
}
cmd_ctl() {
if [[ $# -lt 1 ]]; then
echo "usage: o ctl <command> [args...]" >&2
echo " commands: stop, compact, kill, clear, model <m>, backend <b>, cwd <d>" >&2
echo "usage: o ctl <command> [session] [agent]" >&2
echo " With agent: stop, compact, clear, model <m>, backend <b>, cwd <d>" >&2
echo " Session-only: kill, pause, resume" >&2
exit 1
fi
local path
path=$(agent_path) || exit 1
echo "$*" | ollie-9p write "$path/ctl"
local cmd="$1"; shift
# Resolve context from remaining args or env
local s="${1:-${session:-}}"
local a="${2:-${agent:-}}"
if [[ -z "$s" ]]; then
echo "error: no session set" >&2
echo " export session=<name> or o ctl <cmd> <session> [agent]" >&2
exit 1
fi
# Session-level commands (no agent needed)
case "$cmd" in
kill|pause|resume)
echo "$cmd" | ollie-9p write "session/$s/ctl"
return
;;
esac
# Agent-level commands
if [[ -z "$a" ]]; then
echo "error: no agent set" >&2
echo " export agent=<name> or o ctl <cmd> <session> <agent>" >&2
exit 1
fi
echo "$cmd" | ollie-9p write "session/$s/agent/$a/ctl"
}
cmd_prompt() {
@ -314,9 +325,6 @@ cmd_prompt() {
local buf="" line
# Enable bracketed paste mode - terminal sends \e[200~ before and \e[201~ after paste
bind 'set enable-bracketed-paste on' 2>/dev/null
while true; do
local prompt_str
if [[ -z "$buf" ]]; then
@ -365,7 +373,7 @@ cmd_prompt() {
echo " /backend X — switch backend"
echo " /cwd X — change working directory"
echo " /quit /q — kill the tmux TUI"
echo " /i — interrupt running agent"
echo " /i <text> — inject prompt mid-turn"
;;
*) echo "unknown command: /$cmd (try /help)" ;;
esac
@ -395,20 +403,6 @@ cmd_prompt() {
done
}
cmd_chatstream() {
local ctx s a
ctx=$(resolve_context "$@") || exit 1
s="${ctx%%|*}"
a="${ctx##*|}"
if [[ -z "$s" || -z "$a" ]]; then
echo "error: session and agent are required" >&2
echo " usage: o chatstream <session> <agent>" >&2
echo " or: export session=<name> agent=<name>" >&2
exit 1
fi
exec ollie-9p read "session/$s/agent/$a/chat"
}
cmd_log() {
local lines=1000
local ctx s a
@ -449,31 +443,6 @@ cmd_env() {
fi
}
cmd_stop() {
local ctx s a
ctx=$(resolve_context "$@") || exit 1
s="${ctx%%|*}"
a="${ctx##*|}"
if [[ -z "$s" || -z "$a" ]]; then
echo "error: session and agent are required" >&2
echo " usage: o stop <session> <agent>" >&2
echo " or: export session=<name> agent=<name>" >&2
exit 1
fi
echo "stop" | ollie-9p write "session/$s/agent/$a/ctl"
}
cmd_kill() {
local s="${1:-${session:-}}"
if [[ -z "$s" ]]; then
echo "error: no session set" >&2
echo " usage: o kill <session>" >&2
echo " or: export session=<name>" >&2
exit 1
fi
echo "kill" | ollie-9p write "session/$s/ctl"
}
cmd_tui() {
local tmux_session="${session:-}"
local s a
@ -523,7 +492,7 @@ cmd_tui() {
# Pane 0: chat (top, 80%) - show last 1000 lines of log, then tail chat
tmux -CC new-session -d -s "$tmux_session" -c "$HOME" \
-e "session=$s" -e "agent=$a"
tmux send-keys -t "$tmux_session" "o log -n 1000 | grep -v '\\[\\[\\[.*\\]\\]\\]' | grep -v '^\`\`\`'; o chatstream" Enter
tmux send-keys -t "$tmux_session" "o log -n 1000 | grep -v '\\[\\[\\[.*\\]\\]\\]' | grep -v '^\`\`\`'; o read chat" Enter
# Pane 1: prompt REPL (bottom, 20% height)
tmux split-window -t "$tmux_session" -v -l 20% \
@ -544,10 +513,12 @@ cmd_tui() {
paused) bg="colour245" ;; # gray
*) bg="default" ;;
esac
tmux set-option -t "$tmux_session" status-right-style "fg=default,bg=$bg"
tmux set-option -t "$tmux_session" status-right "$state | %H:%M"
tmux set-option -t "$tmux_session" status-right-style "fg=default,bg=$bg" 2>/dev/null
tmux set-option -t "$tmux_session" status-right "$state | %H:%M" 2>/dev/null
done
done &
local bg_pid=$!
trap "kill $bg_pid 2>/dev/null" EXIT
# Focus the prompt pane
tmux select-pane -t "$tmux_session:0.1"
@ -561,25 +532,21 @@ usage: o <command> [args...]
Path resolution:
- Root paths (session/*, agents, models, etc.) → used as-is
- Agent files (chat, state, prompt, etc.) → require $session + $agent
- Agent files (chat, prompt, cfg, etc.) → require $session + $agent
- Session files (env, ctl, name, etc.) → require $session
Commands:
read <path> Read a file
read [-l] <path> Read a file (-l: loop, re-read after each return)
write <path> [data] Write to a file (args or stdin)
readloop <path> Read in a loop (re-reads after return)
ls [path] List namespace entries
generate <prompt> One-shot generation (no session needed)
complete <json> Code completion (no session needed)
route <task> Route task to best model (no session needed)
ctl <cmd> Send control command to agent
stop Interrupt running agent
kill Kill active session
ctl <cmd> [s] [a] Send control command (session or agent level)
prompt Interactive multi-line prompt REPL
chatstream Stream chat output
log [-n N] Show last N lines of agent log (default 1000)
tui Launch tmux layout
tui exit Kill tmux TUI session
@ -595,12 +562,13 @@ Examples:
o ls # list at current context level
o ls session # list all sessions
o read models # read root-level file
o read -l statewait # block-loop on state changes
o read chat # stream chat (filtered text)
o read chat.raw # stream chat (full markup)
o generate "explain monads" # one-shot, no session
o complete '{"file":"main.go","prefix":"func "}' # code completion
o route "implement login" # route to best model
o ctl stop # stop agent (uses $session/$agent)
o ctl kill mysession # kill session by name
export session=foo agent=bar # set context
o read chat # read agent's chat
o readloop statewait # blocks until agent state changes
EOF
}
@ -615,16 +583,12 @@ subcmd="$1"; shift
case "$subcmd" in
read) cmd_read "$@" ;;
write) cmd_write "$@" ;;
readloop) cmd_readloop "$@" ;;
ls) cmd_ls "$@" ;;
generate) cmd_generate "$@" ;;
complete) cmd_complete "$@" ;;
route) cmd_route "$@" ;;
ctl) cmd_ctl "$@" ;;
stop) cmd_stop "$@" ;;
kill) cmd_kill "$@" ;;
prompt) cmd_prompt "$@" ;;
chatstream) cmd_chatstream "$@" ;;
log) cmd_log "$@" ;;
tui) cmd_tui "$@" ;;
env) cmd_env "$@" ;;