agcli
agcli is a no-bloat Rust crate for building agent-native CLIs.
It is built around the design in design.md:
- JSON-only envelopes
- HATEOAS
next_actions - self-documenting root command tree
- context-safe output truncation
- typed NDJSON streaming with terminal
result/error
Why terminal envelopes and truncation pointers matter
- Terminal
result/errorenvelopes give agents a deterministic finish state, so they can branch on structured outcomes instead of fragile text parsing. - Structured
errorenvelopes support reliable retries, escalation, and fallback actions, whileresultenvelopes make successful completion explicit and machine-verifiable. - Truncation with file pointers lets CLIs cap large outputs safely while preserving continuity: agents can follow the pointer to full logs or artifacts without overflowing context windows.
- This improves reliability and debuggability for long-running automation while reducing token pressure in agent loops.
Install
[]
= "0.8.1"
= "1"
= { = "1", = ["macros", "rt-multi-thread"] }
The crate is 100% async (since v0.8). Handlers, the NDJSON emitter, and
truncation I/O all return Futures. Wire up a tokio runtime in your binary —
the snippet below uses #[tokio::main].
Quick start
use ;
use json;
async
Flag parsing
agcli accepts both --key=value and --key value (space-separated) for
value flags. This matches the HATEOAS [--flag <value>] template form used
throughout the crate's docs.
To disambiguate boolean flags from value flags without a schema layer, the
parser reads each command's .usage(...) string at runtime and treats any
bracketed flag without a <placeholder> (e.g. [--no-git], [--follow])
as a pure boolean. Declared boolean flags will never consume the next token,
so mycli submit --no-git ./plan.html works as expected.
For value flags or undeclared flags, a bare --key followed by a non-flag
token consumes that token as the value (--key value ≡ --key=value). Use
--key=true or -- to force a positional after an undeclared boolean.
Performance
agcli targets macOS and Linux only. The crate ships with optimized release/bench profiles and an optional jemalloc allocator. To maximize runtime performance in a downstream binary:
Recommended Cargo.toml
[]
= { = "0.8.1", = ["jemalloc"] }
[]
= 3
= "thin"
= 1
jemalloc global allocator
In your binary's main.rs:
static GLOBAL: Jemalloc = Jemalloc;
Build-machine-specific codegen
RUSTFLAGS="-C target-cpu=native"
Do not commit this into the repo — it breaks cross-compilation portability.
PGO (Profile-Guided Optimization)
Wokhei-style example
See examples/ops.rs for a full example with:
- command tree responses
- contextual next actions
- log truncation file pointers