一键导入
ai-cli
Drive AI CLIs (opencode, pi, codex, claude-code, …) — auth, streaming, tool use, sessions
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Drive AI CLIs (opencode, pi, codex, claude-code, …) — auth, streaming, tool use, sessions
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | ai-cli |
| description | Drive AI CLIs (opencode, pi, codex, claude-code, …) — auth, streaming, tool use, sessions |
| allowed-tools | Bash(agent-tui:*) |
agent-tui can drive other AI coding agents as the PTY app under it — useful when you want a meta-agent orchestrating sub-agents, when you want to record/replay an AI session for audit, or when you want the snapshot+ref interaction model on top of another AI CLI's UI.
The basic loop is the same as core: spawn the CLI, wait for its prompt, snapshot, interact. What changes is what you're waiting for — AI CLIs have characteristic UI shapes that make detection robust.
Claude Code, Codex, Pi, Aider, and OpenCode have provider-specific terminal manifests. They share a prompt/response shape, but their refs are intentionally not collapsed into one namespace:
@claude role=root
├── @claude.response role=response
├── @claude.input role=input focused=true when the cursor is there
├── @claude.approval role=approval-request
├── @claude.tool role=tool-call
├── @claude.file-change role=file-change
└── @claude.done role=done
@codex role=root
└── same child refs under @codex
@pi role=root
└── same child refs under @pi
@aider role=root
└── same child refs under @aider
@opencode role=root
└── same child refs under @opencode
The signal refs are regex-derived from the visible terminal screen. They are enough for supervising a live session, but they are not the same thing as SDK/harness event streams with structured permission or tool-result objects.
Batch modes are intentionally different. pi --print, codex exec,
and Claude print/help/version invocations are stdout-producing commands,
not long-lived live agent screens, so the bundled provider manifests do
not claim them. Capture those with the generic shell/stdout path.
Read agent-tui skills get addressing for selector syntax. Common
patterns:
| Goal | Selector |
|---|---|
| Wait for Codex to be ready for input | @codex.input[focused] |
| Snapshot just the Codex reply | @codex.response |
| Wait for approval | @codex.approval |
| Wait for a file edit | @codex.file-change |
| Wait for completion | @codex.done |
| Get the input prompt's current line | @codex.input (read .name) |
The useful case is a live session over time: prompt, wait for a plan, approve, verify a file change, run checks, and wait for done.
agent-tui spawn -- codex
agent-tui wait --ref '@codex.input[focused]'
agent-tui type --to '@codex.input' 'plan the release note and wait for approval'
agent-tui press --to '@codex.input' '<cr>'
agent-tui wait --ref '@codex.approval'
agent-tui type --to '@codex.input' 'approve'
agent-tui press --to '@codex.input' '<cr>'
agent-tui wait --ref '@codex.file-change'
agent-tui type --to '@codex.input' 'run checks'
agent-tui press --to '@codex.input' '<cr>'
agent-tui wait --ref '@codex.done'
This skill assumes the core loop. It covers AI-CLI-specific patterns: auth, streaming detection, session DBs, tool use.
Pi is a single-turn-friendly OpenAI-compatible CLI. Its --print
mode runs non-interactively, prints the reply to stdout, exits.
agent-tui spawn -- pi --provider openai --model gpt-4o-mini --print "say hello"
agent-tui wait --text "\$" # back to shell prompt after pi exits
agent-tui snapshot # captures pi's printed reply
Pi reads provider config from $PI_CODING_AGENT_DIR/models.json.
For multi-provider setups, configure providers there ahead of time.
OpenCode is interactive by default but has a run subcommand for
non-interactive use:
agent-tui spawn -- bash -c "cd /work && exec opencode run -m openai/o3-mini 'refactor this'"
agent-tui wait --text "build · o3-mini" # session header
# OpenCode's `run --format default` doesn't write the body to stdout
# — see "Reading the assistant's reply" below.
Critical flags for non-interactive automation:
--dangerously-skip-permissions — auto-approve tool calls;
without it, opencode prompts on the TTY and deadlocks under a
programmatic PTY.--title 'fixed string' — opencode normally fires a hidden
title-generation request to the model before the real prompt;
pass a fixed title to skip it.--pure — skip plugins (deterministic).Run cd /work && exec opencode … so the per-project opencode.json
gets picked up (opencode looks for it in the current directory).
Two strategies, depending on the CLI:
1. CLI writes the reply to stdout (Pi, claude-code, --format json):
agent-tui snapshot # body is on the screen
2. CLI writes only a header, persists the body to a session DB
(OpenCode --format default):
OpenCode persists each session at
~/.local/share/opencode/opencode.db (SQLite, WAL mode). After the
process exits, read the part table:
sqlite3 ~/.local/share/opencode/opencode.db \
"SELECT data FROM part WHERE message_id IN (
SELECT id FROM message ORDER BY time_created DESC LIMIT 1)"
Or, in WAL mode immediately after a fresh run (data may not yet be checkpointed), grep both files:
grep -a "MARKER_TEXT" ~/.local/share/opencode/opencode.db \
~/.local/share/opencode/opencode.db-wal
For visible-progress streaming, prefer a CLI mode that writes events to stdout as they arrive:
agent-tui spawn -- bash -c "cd /work && exec opencode run --format json 'long task'"
# OpenCode emits one JSON event per line as the model streams.
# agent-tui's wait --text matches against each line as it lands.
agent-tui wait --text "\"type\":\"text\"" # first text delta
This is much more reliable than mid-render screenshots — JSON deltas are deterministic, screen contents during a stream are not.
When the model calls a tool (bash, edit, glob, …), the CLI executes it locally then sends the result back in a follow-up request. The full loop:
function_call(bash, {"command":"…","description":"…"}).--dangerously-skip-permissions no
prompt blocks; otherwise the CLI shows a confirm dialog.function_call_output containing
the stdout.agent-tui spawn -- bash -c "cd /work && exec opencode run \
--dangerously-skip-permissions \
-m openai/o3-mini 'find files matching pattern X'"
agent-tui wait --text "build · "
# The bash tool runs inside the same /work dir agent-tui spawned in.
# After exit, the DB has the full call → output → reply chain.
OpenCode bash tool schema requires BOTH command AND
description keys in the arguments JSON. The model usually emits
both correctly; if you're hand-crafting the args (test scenarios),
omitting either yields a SchemaError instead of running the command.
Most AI CLIs persist a session ID that lets you resume later:
# OpenCode
agent-tui spawn -- bash -c "cd /work && exec opencode run --continue 'follow-up question'"
agent-tui spawn -- bash -c "cd /work && exec opencode run --session <ID> 'specific session'"
# Pi
agent-tui spawn -- pi --resume <session-id> --print "follow-up"
The session ID surfaces in stdout on creation. For OpenCode it also
appears in the DB's session table.
Detection options, in order of robustness:
agent-tui wait --exit (P2) — best, no ambiguity.agent-tui wait --text "\$ "
— works when you spawned bash -c "agent …" rather than the
agent directly.\n flush, claude-code's > prompt — fragile to version
changes.Don't use --idle as the primary "done" signal — long-running
generations have legitimate quiet periods.
Today, set credentials before spawn:
agent-tui spawn --env OPENAI_API_KEY=sk-… --env ANTHROPIC_API_KEY=… -- opencode run …
Or write the CLI's own credentials file under $HOME before
spawning. Per-CLI:
| CLI | Credentials path |
|---|---|
| OpenCode | ~/.config/opencode/auth.json |
| Pi | $PI_CODING_AGENT_DIR/models.json |
| Claude Code | ~/.claude.json |
| Codex | ~/.config/codex/credentials.json |
The P3 auth vault will provide a single agent-tui auth save <name>
API across CLIs.
Driving an AI CLI in a TEST should not hit a real model. agent-tui
ships an in-process fake-inference server (under
crates/agent-tui-integration/src/fake_inference.rs) that speaks
the OpenAI Chat Completions API and the Responses API. Point the
CLI's baseURL at it and script canned replies:
// In an integration test:
let server = FakeServer::start(
Script::new()
.stream(["Hello ", "from ", "the fake"])
.tool_call("bash", r#"{"command":"ls /","description":"list"}"#)
.say("Done.")
).await?;
// CLI under test points at server.url(), runs, asserts against
// the session DB or stdout.
See crates/agent-tui-integration/tests/opencode_fake_inference.rs
and pi_fake_inference.rs for working scenarios. Lessons learned:
latest.~/.cache/opencode/bin/rg).$HOME when you need to inspect the session DB
post-run..db-wal too for content written but not yet
checkpointed.agent-tui usage — spawn, snapshot, wait, interact with PTY apps via @eN refs
Selectors, refs, and targeted writes — the DOM-lite addressing model for nodes inside a pane
Drive common TUI apps — htop, less, lazygit, tig, fzf, nano — for engine-correctness coverage
Verbs grouped by what you're trying to do (ask/edit/watch/run), not by capability surface
Open files, edit, save, search in vim/nvim with adapter-aware state
Drive bash/zsh shell sessions; detect prompt vs. running command via OSC 133