Read this before writing any code.
acp-cli is a headless CLI client for the Agent Client Protocol (ACP). It spawns coding agents (Claude, Codex, Gemini, etc.) as child processes and communicates over structured JSON-RPC via stdio pipes.
- Version: 0.2.1
- Language: Rust (edition 2024)
- Published to: crates.io (
acp-cli) - GitHub: https://github.com/motosan-dev/acp-cli
acp-cli/
├── Cargo.toml
├── src/
│ ├── main.rs # CLI entry point
│ ├── lib.rs # Library re-exports
│ ├── config.rs # Config loading + merging
│ ├── error.rs # Error types (thiserror)
│ ├── agent/registry.rs # Agent name → command mapping
│ ├── bridge/ # AcpBridge (spawn_blocking + LocalSet)
│ ├── cli/ # Commands: init, prompt, exec, sessions
│ ├── client/ # BridgedAcpClient + permission resolution
│ ├── output/ # Renderers: text, json, quiet
│ ├── queue/ # Queue owner/client, IPC, lease
│ └── session/ # Session scoping, persistence, history
├── skills/acp-cli/ # AI skill card
├── README.md
├── llms.txt # LLM-consumable API reference
├── CLAUDE.md # Claude Code project instructions
├── CHANGELOG.md
└── LICENSE
- !Send bridge pattern — ACP SDK uses
Rc/spawn_local; bridge runs inspawn_blocking+LocalSet, communicates via mpsc channels - Queue system — first process becomes queue owner (holds agent connection), subsequent processes connect as clients via Unix socket
- Session scoping — key is
SHA-256(agent + "\0" + git_root + "\0" + name), ensuring isolation per project/agent/name - Auth token chain — env var → config file →
~/.claude.json→ macOS Keychain (never guess, always explicit) - Permission modes — three modes (approve-all, approve-reads, deny-all) applied at BridgedAcpClient level
- Agent registry — hardcoded defaults + config overrides + raw command fallback
cargo fmtbefore every commitcargo clippy -- -D warnings— zero warnings policy- Errors: use
thiserrorviaAcpCliError; neverunwrap()in library code - Async:
tokioruntime;async-traitfor trait objects - Serialization:
serdewith#[serde(default)]for backward-compatible config fields - Tests:
#[tokio::test]for async tests
# Development
cargo fmt
cargo clippy -- -D warnings
cargo check
# Testing (macOS needs libiconv)
LIBRARY_PATH="/opt/homebrew/opt/libiconv/lib" cargo test
# Run locally
LIBRARY_PATH="/opt/homebrew/opt/libiconv/lib" cargo run -- claude "hello"- Add entry to
default_registry()insrc/agent/registry.rs - Update agent table in
README.mdandllms.txt - No code changes needed — registry maps name → command + args
- Add variant to
Commandsenum insrc/cli/mod.rs - Add match arm in
src/main.rsrun()function - Implement in
src/cli/<command>.rs - Update
Commandssection inREADME.md,llms.txt
- Do not use
unwrap()orexpect()in library code (only in main.rs after error handling) - Do not add agent-specific logic outside
agent/registry.rsandbridge/mod.rs - Do not store secrets in config without the user's explicit consent (
initasks) - Do not break the
AcpBridgepublic API — it's used as a library bymotosan-workflow-core - Do not use
git add -A— always add specific files
cargo fmt
cargo clippy -- -D warnings
LIBRARY_PATH="/opt/homebrew/opt/libiconv/lib" cargo check# 1. Update CHANGELOG.md (move Unreleased → version)
# 2. Bump version in Cargo.toml
# 3. Update version in: llms.txt, AGENTS.md
# 4. Commit + tag + push
git commit -m "chore: release v0.2.0"
git tag -a v0.2.0 -m "v0.2.0 — summary"
git push origin main v0.2.0Tag push triggers publish.yml → crates.io.