Coding

acpx-faq

Try it

Run coding agents (codex, claude, agy/Antigravity) through the acpx ACP CLI - the headless lane outside a herdr pane (no HERDR_ENV). Use before launching or prompting a subagent, and when a command fails, a session is not found, or a prompt is lost.

What it does

Drive coding agents headlessly through acpx (>= 0.15.1). Command syntax: - the binary ships its own reference and is the tiebreaker on syntax. That doc is strong on command shape and silent on failure semantics: it has no exit-code table, no state table, and does not mention at all. This file is…

The skill document

acpx FAQ

Drive coding agents headlessly through acpx (>= 0.15.1). Command syntax: acpx --skill show acpx - the binary ships its own reference and is the tiebreaker on syntax. That doc is strong on command shape and silent on failure semantics: it has no exit-code table, no status state table, and does not mention --mcp-config at all. This file is the other half.

Read the Invariants, then your agent's section in Per agent - each is a complete launch-to-result recipe. After that: Sessions, Completion, MCP, Limits, Failures.

Invariants

  1. Global flags precede the agent subcommand. --cwd, --model, --approve-all, --agent, --format, --timeout, --ttl, --mcp-config are all global. The subcommand accepts only -s, --no-wait, -f (and exec also takes --config-option). Putting a global flag after the agent exits 2 with error: unknown option '--cwd'.
  2. -s never creates a session. It resolves one, walking from --cwd up to the git root. No match exits 4. Create first with sessions ensure --name .
  3. status is not a turn signal. It reports the queue-owner process, not the turn, and keeps saying running after the turn has ended. Never gate automation on it.
  4. Permission flags only gate requests the adapter chooses to raise. Neither the codex nor the claude adapter asks before writing files, so --deny-all does not stop writes. Isolation comes from the --cwd you hand it, never from a flag.
  5. [done] end_turn and exit 0 are not proof of success. A content-filter kill, an MCP load failure, or a truncated turn all end that way. Read the stream, or require the agent to write a result file you can check.

Per agent

Each section is self-contained: flags, launch, and the quirks that bite while it runs.

agy / Antigravity - the --agent escape hatch

acpx has no Antigravity adapter and never will get one. Issue #362 was closed as externally blocked: Antigravity ships no supported ACP stdio mode. The built-in gemini agent is the public Gemini CLI, a different product, and it is dead for Code Assist:

[error] RUNTIME: This client is no longer supported for Gemini Code Assist for individuals.
To continue using Gemini, please migrate to the Antigravity suite of products

The only route is Google's own signed ACP server through the raw-command escape hatch:

D=/abs/real/dir                              # must exist: roots resolve through realpath
acpx --agent ~/.local/lib/antigravity-acp/agy_acp_server.par \
     --cwd "$D" --model gemini-3.7-flash-medium --timeout 1800 \
     exec 'Carry out $D/brief.md. Write your report to $D/report.md.'
  • Effort lives in the model id - gemini-3.7-flash-{low,medium,high}, gemini-3.8-flash-high. This server exposes no separate effort option.
  • First run only: [error] RUNTIME: Authentication required. Re-run once with ACPX_AUTH_OAUTH_PERSONAL=1 and complete the browser sign-in; credentials persist in ~/.gemini and every later run needs nothing.
  • Never pass a positional agent with --agent - exit 2, Do not combine positional agent with --agent override.
  • Check the tier before sending anything private. The oauth-personal path can report currentTier: {'id': 'free-tier'}, whose terms permit human review of submitted prompts and code for up to 18 months. Run with --verbose and grep loadCodeAssist response to see it.
  • agy hides MCP failures inside a successful-looking turn. A failed server appears as inline text - MCP load failed for pond: ... expect initialized request, but received: ... "server/discover" - in a stream that still ends [done] end_turn with exit 0. Grep the output for MCP load failed, do not trust the exit code.
  • The CLI (agy) and this ACP server keep separate state under the same home: two onboardings, and authenticating one does not authenticate the other.

codex

D=/abs/dir
acpx --cwd "$D" codex sessions ensure --name work            # idempotent; -s cannot create
acpx --cwd "$D" codex set model gpt-5.6-sol -s work          # -> model set: gpt-5.6-sol
acpx --cwd "$D" codex set reasoning_effort high -s work      # -> config set: ... (5 options)
acpx --cwd "$D" --approve-all --timeout 5400 --ttl 0 --format quiet --suppress-reads \
     codex -s work 'Carry out ./brief.md. Write your report to ./report.md.' > run.log 2>&1 &

For a one-shot, skip the session entirely - exec --config-option sets model and effort inline (0.14.0+), applied after --model and before the prompt:

acpx --cwd "$D" --timeout 1800 codex exec --config-option reasoning_effort=low 'Summarize ./DESIGN.md'
  • The adapter floats. @agentclientprotocol/codex-acp@^1.1.5 is a caret on a 1.x, so it tracks the latest 1.x on every npx resolution. A codex-side change can land without you upgrading acpx.
  • OpenAI's content filter kills benign turns and the turn ends looking clean ([done] end_turn, exit 0). Vocabulary like race / sweep / exploit / attack in a filename, comment or prompt triggers it. Read the transcript tail for the flag line before believing completion. Recover by re-prompting the same -s (context survives): rename the artifact neutrally and list the remaining steps explicitly.
  • Queue a follow-up onto a live session by prompting the same name again - it runs after the current turn rather than interrupting. This is how you course-correct a running executor without relaunching it.
  • Headful takeover: sessions show prints a sessionId that is the ordinary codex rollout id, so codex resume opens the same thread in a TUI with model and effort intact. Close first - sessions close - or you get already has an active writer (code -32600).
  • codex queue --thread does not reach an acpx-driven session (see What acpx cannot do).

claude

D=/abs/dir
acpx --cwd "$D" --model claude-opus-5 claude sessions ensure -s work
acpx --cwd "$D" --model claude-opus-5 --approve-all --suppress-reads --timeout 2400 \
     claude -s work -f /abs/brief.md >> run.log 2>&1 &
  • No effort knob. set reasoning_effort returns Internal error. Only the model id.
  • --model and set model validate differently, and this surprises people. The adapter advertises ["default","opus[1m]","claude-fable-5[1m]","sonnet","haiku"], and set model refuses anything outside that list: Invalid value for config option model: claude-opus-5 (ACP -32603, adapter reported "Internal error"). The global --model at session creation passes the id through to the harness instead, so --model claude-opus-5 works fine. A genuinely unknown id still fails loudly: RUNTIME: Internal error: There's an issue with the selected model ().
  • Fable runs: --model 'claude-fable-5[1m]' (quote it - the brackets are shell globs).
  • User-scope skills are excluded on purpose. The adapter loads project and local settings but not user settings, so a user-level skill or slash command comes back Unknown command: /polish. Fix with ACPX_CLAUDE_INCLUDE_USER_SETTINGS=1 - and settings bind at session creation, so an existing session must be recreated, not re-prompted.
  • Second account: export CLAUDE_CONFIG_DIR and CLAUDE_SECURESTORAGE_CONFIG_DIR before acpx; the child inherits them. That is the whole mechanism for driving another lane.
  • The adapter is pinned far behind your CLI. claude-agent-acp@^0.60.0 resolves to exactly 0.60.0, which pins @anthropic-ai/claude-agent-sdk@0.3.215, which bundles claude 2.1.215
    • regardless of the version on your PATH. Consequences: the bundled binary predates the cross-session messaging socket, so these sessions are unreachable by peer messaging, and any newer harness feature is simply absent. Override with --agent pointed at a newer adapter build if you need one.
  • A session-creation stall is a known adapter combination bug; acpx's own error text recommends --approve-all with nonInteractivePermissions=deny, upgrading both sides, or falling back to claude exec as a one-shot.

Sessions

A session is keyed on (agent command, absolute cwd, optional name). Because cwd is part of the key, parallel executors each need their own directory - give every one its own git worktree and their per-session model and effort settings cannot race.

verbbehavior
sessions ensure --name returns the existing session or creates one - idempotent, safe before every prompt
sessions new --name soft-closes any current session and creates a fresh one ((replaced ))
a bare prompt with -s never auto-creates; exits 4 with Create one: ...
sessions show lastActivity, lastPrompt, historyEntries, sessionId, closed
sessions history --limit Nthe actual turn content
sessions close releases the agent; required before codex resume and before sessions export
sessions list --locallocal records including closed ones
sessions prunedeletes closed records - they persist indefinitely otherwise

Resolution without -s walks from --cwd up to the git root. A session whose cwd you have since deleted becomes an unreachable registry row: sessions close cannot target it without its cwd, so prune it.

Completion

Never poll status. It is a local kill(pid,0)-style check on the queue owner and never touches the agent. Its states are running, idle, dead, no-session, where dead means the owner is gone or the last exit was abnormal. A turn that finished seconds ago still reads running, because the owner survives for its idle TTL (default 300s, --ttl , --ttl 0 to keep it forever). A loop waiting for idle will spin past real completion and time out.

What is actually correct:

  • the foreground stream's terminating [done] end_turn, plus the process exit code;
  • sessions show (lastActivity, historyEntries) and sessions history ;
  • best of all, a result file the brief required the agent to write - poll for the file.

--no-wait returns as soon as the queue owner acknowledges the submission - [queued] in well under a second, even on an idle session. It is not delivery and not completion: if a turn is already running, the prompt waits for the turn boundary. Background a normal blocking prompt when you want the transcript; use --no-wait only to enqueue.

Supervising a backgrounded run: count tool lines and check mtime (rg -c '^\[tool\]' run.log, stat -f '%Sm' run.log) rather than tailing the whole log, and check liveness with pgrep -fl 'acpx|codex-acp|claude-agent-acp'.

Ctrl+C (and the cancel subcommand) sends ACP session/cancel first and force-kills only if the agent does not stop in time.

MCP

--mcp-config replaces the project/global mcpServers for that invocation; relative paths inside resolve from --cwd.

The file shape is not the one every other tool uses. acpx wants a JSON array of named server objects. Handing it the standard object-keyed map throws an uncaught Node exception with a stack trace, not a clean CLI error:

Error: Invalid mcpServers in /path/to/config.json: expected array
    at parseMcpServers (.../acpx/dist/cli.js:1393)
// WRONG - the Claude Code / standard shape
{ "mcpServers": { "pond": { "command": "pond", "args": ["mcp"] } } }

// RIGHT - an array, each entry carrying its own name
{ "mcpServers": [ { "name": "pond", "type": "http", "url": "http://127.0.0.1:9797/mcp" } ] }

Two more traps:

  • A live queue owner refuses a config change. The owner carries the config path and a SHA-256 fingerprint; switching MCP config on a persistent session requires sessions close first.
  • stdio servers can fail against a strict client. Antigravity's MCP client sends server/discover before initialized, which a compliant stdio server rejects (expect initialized request). Serve over HTTP instead when the consumer is agy.

${...} expansion inside an MCP config is evaluated in the launcher's environment, because the acpx child inherits it - so a variable like a session id resolves to your id, not the child's. Never let an identity flow in through the environment.

Limits

  • Incoming ACP messages are capped at 64 MiB since 0.15.1 (previously unlimited). Raise with ACPX_MAX_ACP_MESSAGE_BYTES, or 0 to disable. A previously-working large-payload script can start failing here.
  • Terminal output retention is 64 KiB per call; ACPX_TERMINAL_MAX_OUTPUT_BYTES adjusts it.
  • --timeout is the wall clock for the whole prompt. Long unattended work wants --timeout 5400 --ttl 0; a wrapping shell timeout is a reasonable belt-and-braces.
  • Output: --format quiet for many backgrounded executors, text for one you are watching, json (with --json-strict) when a script parses it. --suppress-reads keeps read-file contents out of the log and is worth setting on every long run.
  • Pass long briefs with -f or a path in the prompt, not inlined text: it keeps the content out of the shell command, where a driving harness's classifier may block on it.

What acpx cannot do

Structural, not bugs. Do not design around them.

  • An ACP session does not wake. An externally injected message lands in the transcript but starts no turn, because under ACP the turn loop belongs to the ACP client. The only inbound channel to an acpx session is acpx itself (-s , optionally --no-wait).
  • codex queue --thread does not reach an acpx-driven codex session, though it does wake a plain interactive codex. The ACP wrapper is not in the app-server's context.
  • acpx-spawned Claude sessions bind no IPC socket (entrypoint: sdk-cli, no key file), so ListAgents / SendMessage cannot see or reach them.
  • acpx cannot attach to a session it did not start. It owns the process it drives; a human-started headful session is out of reach.
  • Permission flags are not a sandbox. See invariant 4: --deny-all does not prevent file writes, because the adapters do not ask. --no-terminal genuinely removes the terminal capability (an agent that calls it gets a hard error), but for filesystem safety the only real control is which --cwd you hand it.

Failures

Exit codes, which the shipped doc does not list:

codemeaning
0success (also cancel with nothing to cancel)
1agent / protocol / runtime error - the catch-all
2usage error: bad or conflicting flags, malformed --agent
3--timeout exceeded
4no session found by the directory walk
5every permission request denied or cancelled, none approved
130interrupted (cooperative cancel first)

⚠ No acpx session found (searched up to ). (exit 4) - invariant 2. Emitted by set, by -s prompts, by anything that resolves a session. Fix: sessions ensure --name first. Confirm --cwd is the directory you think it is.

error: unknown option '--cwd' (exit 2) - a global flag placed after the agent subcommand. Move it before.

Do not combine positional agent with --agent override (exit 2) - drop the positional claude/codex when --agent supplies the adapter.

Invalid mcpServers in : expected array (uncaught, stack trace) - the MCP config is the standard object map; convert it to an array. See MCP.

Invalid value for config option model: (ACP -32603, adapter reported "Internal error")

  • set model with an id outside the adapter's advertised list. Use --model at session creation, or pick an advertised id from --format json ... status.

RUNTIME: Internal error: There's an issue with the selected model () - the harness itself rejects the id. This is a real typo or an entitlement problem, not the previous case.

Internal error from claude set reasoning_effort - there is no effort knob on the claude adapter. Expected; ignore.

[error] RUNTIME: Authentication required - the agy ACP server has never been authenticated. One run with ACPX_AUTH_OAUTH_PERSONAL=1.

RUNTIME: This client is no longer supported for Gemini Code Assist for individuals - you used the built-in gemini agent. It is the public Gemini CLI, not Antigravity; use --agent with the .par server.

already has an active writer (code -32600) - codex resume while acpx still owns the thread. sessions close first.

Missing --skill action. (exit 1) - --skill needs show, list, install, export or help.

error: unknown option '--one-shot' (exit 2, usage on stderr) - not a verb; exec is the one-shot form.

Timeouts (exit 3) mean the wall clock ran out, not that the agent is stuck. Check sessions show for lastActivity before assuming failure, and remember an adapter-side timeout surfaces as exit 1 instead.

Silent failures worth an explicit check: a content-filter kill and an MCP load failure both end [done] end_turn with exit 0; a --no-wait prompt that "sent" may still be queued behind a running turn; status reporting running long after completion; and a session record whose cwd no longer exists lingering until pruned.

Related skills

Join a video meeting as an AI bot with voice, avatar, and screenshare across four operating modes.

by johnpatternai21 installs8 stars

Stores durable facts in a categorized, plain-markdown vault on disk, alongside your agent's built-in memory.

by Iván555 installs18 stars

Generate and edit Draw.io, Mermaid, and Excalidraw diagrams from natural language using a structured JSON spec.

by nssa.io1.0k installs47 stars

Trade crypto, manage a multi-chain wallet, and query an AI analyst from one CLI.

by lowesyang162 installs109 stars

Fetch raw ad creative, app, ranking, and revenue data from AdMapix as structured JSON.

by fly0pants4.3k installs296 stars

Query Twitter/X profiles, tweets, follower events, and KOL data through the 6551 REST API.

by infra403840 installs27 stars

More from tenequm

Browse all skills

Reference patterns for TanStack Query, Router, and Start in full-stack React apps.

by tenequm22 installs1 stars

Build HTTP 402 machine-to-machine payments with the MPP protocol across TypeScript, Python, and Rust SDKs.

by tenequm20 installs1 stars

Opinionated guide to building Agent Skills for any agent, covering layout, frontmatter, descriptions, and Claude Code specifics.

by tenequm24 installs

Pinned reference for Lance v11.0.0-beta.6 and the Rust/Python engine, with v10.0.0 as the stable line.

by tenequm22 installs

Build and deploy JavaScript, TypeScript, Python, or Rust code on Cloudflare's global edge network using the Wrangler CLI.

by tenequm20 installs

Build native macOS apps with Swift 6.3, SwiftUI, SwiftData, concurrency, and on-device AI.

by tenequm19 installs