Skip to content

Commit 601fff5

Browse files
Enterprise-grade framework overhaul: config, tests, CI, enriched schemas
Addresses every gap between documented patterns and actual example code. Changes verified by 40 integration tests and Codex GPT-5.4 review. Tier 1 — Credibility fixes: - Add 40 integration tests across 4 test files verifying exit code contracts (0-4), JSON envelope structure, agent-info manifest accuracy, and robustness against malformed config - Add GitHub Actions CI (macOS + Linux) - Add --quiet global flag (suppresses human output, JSON always emits) - Replace all hardcoded strings with env!("CARGO_PKG_NAME") / config - Fix: TTY parse errors now exit 3 (framework code), not 2 (clap code) - Fix: agent-info/config path work even with malformed config.toml Tier 2 — Significant improvements: - Add config loading via figment (3-tier: defaults → TOML → env vars) - Add config show/path subcommands - Enrich agent-info with full argument schemas (types, required, defaults, possible values) so agents can construct calls without guessing - Add "Getting Started: Build Your Own" section to README - Enforce --style values via clap value_parser to match agent-info Tier 3 — Polish: - Add CI status and MSRV badges to README - Update AGENTS.md with Ctx pattern, lazy config, standard commands - Update CONTRIBUTING.md with test suite and project structure - Clean clippy warnings (zero warnings with -D warnings) - Hidden contract command for deterministic exit-code testing - Hermetic tests (temp HOME for skill install)
1 parent 7a7c22a commit 601fff5

23 files changed

Lines changed: 1308 additions & 99 deletions

.github/workflows/ci.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
build-and-test:
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
os: [ubuntu-latest, macos-latest]
18+
runs-on: ${{ matrix.os }}
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- uses: dtolnay/rust-toolchain@stable
23+
24+
- uses: Swatinem/rust-cache@v2
25+
with:
26+
workspaces: example
27+
28+
- name: Build
29+
working-directory: example
30+
run: cargo build --locked
31+
32+
- name: Test
33+
working-directory: example
34+
run: cargo test --locked

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@ target/
22
.claude/
33
.agents/
44
.cursor/
5-
.github/
65
.windsurf/

AGENTS.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ Split your CLI into focused modules. Never write a monolithic main.rs.
1414
src/
1515
main.rs # Entry point only: parse, detect format, dispatch, exit
1616
cli.rs # Clap derive: Cli struct + Commands enum + Args structs
17+
config.rs # AppConfig + load() via figment (3-tier precedence)
1718
error.rs # Error enum with exit_code(), error_code(), suggestion()
18-
output.rs # Format enum + print_success_or() + print_error()
19+
output.rs # Format enum, Ctx struct, print_success_or(), print_error()
1920
commands/
2021
mod.rs # Re-exports
2122
<command>.rs # One file per domain command
22-
agent_info.rs # Capability manifest (always present)
23+
agent_info.rs # Capability manifest with arg schemas (always present)
2324
skill.rs # Skill install + status (always present)
25+
config.rs # config show/path (always present)
2426
update.rs # Self-update (optional)
27+
tests/ # Integration tests verifying contracts
2528
Cargo.toml
2629
```
2730

@@ -38,7 +41,7 @@ src/
3841

3942
## Output Format
4043

41-
Detect automatically:
44+
Detect automatically. Bundle format + quiet into an output context:
4245

4346
```rust
4447
pub enum Format { Json, Human }
@@ -49,8 +52,18 @@ impl Format {
4952
else { Format::Human }
5053
}
5154
}
55+
56+
pub struct Ctx { pub format: Format, pub quiet: bool }
57+
58+
impl Ctx {
59+
pub fn new(json_flag: bool, quiet: bool) -> Self {
60+
Self { format: Format::detect(json_flag), quiet }
61+
}
62+
}
5263
```
5364

65+
Pass `Ctx` to all commands. `--quiet` suppresses human output; JSON always emits.
66+
5467
Success envelope (stdout):
5568
```json
5669
{"version": "1", "status": "success", "data": { ... }}
@@ -107,9 +120,13 @@ fn main() {
107120
std::process::exit(3);
108121
}
109122
};
110-
let format = Format::detect(cli.json);
111-
if let Err(e) = run(cli, format) {
112-
print_error(format, &e);
123+
let ctx = Ctx::new(cli.json, cli.quiet);
124+
let config = config::load().unwrap_or_else(|e| {
125+
print_error(ctx.format, &e);
126+
std::process::exit(e.exit_code());
127+
});
128+
if let Err(e) = run(cli, ctx, &config) {
129+
print_error(ctx.format, &e);
113130
std::process::exit(e.exit_code());
114131
}
115132
}
@@ -147,16 +164,18 @@ Every CLI has these built-in commands:
147164
- `skill install` -- write SKILL.md to `~/.claude/skills/<name>/`, `~/.codex/skills/<name>/`, `~/.gemini/skills/<name>/`
148165
- `skill status` -- check installation status
149166

167+
Standard:
168+
- `config show` -- display effective merged config (secrets masked)
169+
- `config path` -- print config file path
170+
150171
Optional:
151172
- `update [--check]` -- self-update from GitHub Releases
152-
- `config show` -- display current config (secrets masked)
153-
- `config set <key> <value>` -- update config
154173

155174
## Global Flags
156175

157176
Always at the top-level `Cli` struct:
158-
- `--json` -- force JSON output even in terminal (required)
159-
- `--quiet` -- suppress non-essential output (add if your CLI has verbose default output)
177+
- `--json` -- force JSON output even in terminal (required, always present)
178+
- `--quiet` -- suppress informational human output; JSON always emits (required, always present)
160179

161180
## Dependencies
162181

CONTRIBUTING.md

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ Thanks for your interest in contributing.
77
1. Fork the repo and create a branch from `main`.
88
2. Make your changes. Keep them focused -- one concern per PR.
99
3. If you add a pattern, include it in both the README and the `example/` CLI.
10-
4. Test that the example builds and all behaviors work:
10+
4. Run the full test suite:
1111
```bash
12-
cd example && cargo build --release
13-
./target/release/greeter hello Boris --style pirate
14-
./target/release/greeter hello Boris | jq # JSON envelope
15-
./target/release/greeter hello "" 2>&1; echo $? # exit code 3
16-
./target/release/greeter --help > /dev/null; echo $? # exit code 0
17-
./target/release/greeter agent-info | jq # valid manifest
12+
cd example && cargo test --locked
1813
```
14+
All integration tests must pass. They verify:
15+
- Exit code contracts (0-4)
16+
- JSON envelope structure (success + error)
17+
- `--help`/`--version` exit 0
18+
- `agent-info` manifest matches actual commands
19+
- Piped output auto-switches to JSON
1920
5. Open a pull request with a clear description of what you changed and why.
2021

2122
## Project structure
@@ -24,18 +25,26 @@ Thanks for your interest in contributing.
2425
README.md # Full framework documentation: philosophy, patterns, reusable modules
2526
AGENTS.md # Condensed build instructions for AI coding agents
2627
CONTRIBUTING.md # This file
28+
.github/workflows/ # CI: builds and tests on macOS + Linux
2729
example/
2830
src/
2931
main.rs # Entry point: parse, detect format, dispatch, exit
30-
cli.rs # Clap derive definitions
32+
cli.rs # Clap derive definitions (--json, --quiet, all commands)
33+
config.rs # Config loading via figment (defaults -> TOML -> env vars)
3134
error.rs # Error enum with exit_code(), error_code(), suggestion()
32-
output.rs # Format detection + JSON envelope helpers
35+
output.rs # Format detection, Ctx struct, JSON envelope helpers
3336
commands/
3437
mod.rs # Re-exports
3538
hello.rs # Domain command example
36-
agent_info.rs # Capability manifest
37-
skill.rs # Skill install + status
38-
update.rs # Self-update
39+
agent_info.rs # Enriched capability manifest with arg schemas
40+
config.rs # config show/path
41+
contract.rs # Hidden deterministic exit-code trigger for tests
42+
skill.rs # Skill install + status (uses CARGO_PKG_NAME)
43+
update.rs # Self-update (repo configurable via config)
44+
tests/
45+
exit_code_contracts.rs # All 5 exit codes verified
46+
output_contracts.rs # JSON envelope shape, quiet flag, help wrapping
47+
agent_info_contract.rs # Manifest fields, routable commands, arg schemas
3948
Cargo.toml
4049
```
4150

@@ -44,14 +53,15 @@ example/
4453
- New patterns or refinements to existing ones, backed by real-world agent usage.
4554
- Bug fixes or improvements to the example CLI.
4655
- Documentation improvements that make the patterns clearer or more precise.
47-
- Integration tests that verify framework invariants (help exits 0, piped output is valid JSON, etc.).
56+
- Additional integration tests verifying framework invariants.
4857
- Links to additional CLIs built with this architecture.
4958

5059
## Guidelines
5160

5261
- The example demonstrates the five core patterns plus the entry point, error type, and output helpers. Reusable modules like config loading, secret handling, and HTTP retry are documented as code patterns in the README -- they don't need to be in the example.
5362
- Keep the example minimal -- it demonstrates patterns, not a real product.
5463
- Ensure the README, AGENTS.md, and example stay consistent with each other.
64+
- All new patterns must have corresponding integration tests.
5565

5666
## Style
5767

README.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@
1212

1313
<br />
1414

15+
[![CI](https://github.com/199-biotechnologies/agent-cli-framework/actions/workflows/ci.yml/badge.svg)](https://github.com/199-biotechnologies/agent-cli-framework/actions/workflows/ci.yml)
1516
[![Rust](https://img.shields.io/badge/Rust-000000?style=for-the-badge&logo=rust&logoColor=white)](https://www.rust-lang.org/)
17+
[![MSRV 1.85+](https://img.shields.io/badge/MSRV-1.85%2B-orange?style=for-the-badge)](https://www.rust-lang.org/)
1618
[![MIT License](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge)](LICENSE)
1719
[![PRs Welcome](https://img.shields.io/badge/PRs-Welcome-brightgreen?style=for-the-badge)](CONTRIBUTING.md)
1820

1921
---
2022

2123
Five patterns turn any Rust CLI into a tool AI agents can pick up and use without documentation, MCP servers, or skill files. The binary describes itself, returns structured output, and uses semantic exit codes. Your CLI becomes the tool, the documentation, and the API -- all in one binary.
2224

23-
[Philosophy](#philosophy) | [Why This Exists](#why-this-exists) | [Patterns](#patterns) | [Reusable Modules](#reusable-modules) | [Example](#example) | [Invariants](#invariants)
25+
[Philosophy](#philosophy) | [Why This Exists](#why-this-exists) | [Patterns](#patterns) | [Reusable Modules](#reusable-modules) | [Getting Started](#getting-started-build-your-own) | [Example](#example) | [Invariants](#invariants)
2426

2527
</div>
2628

@@ -619,6 +621,67 @@ pub fn should_retry(err: &reqwest::Error) -> bool {
619621

620622
---
621623

624+
## Getting Started: Build Your Own
625+
626+
**1. Copy the scaffold:**
627+
628+
```bash
629+
cp -r example/ my-cli/
630+
cd my-cli/
631+
```
632+
633+
**2. Rename the binary** in `Cargo.toml`:
634+
635+
```toml
636+
[package]
637+
name = "my-cli" # Your binary name
638+
version = "0.1.0"
639+
edition = "2024"
640+
rust-version = "1.85"
641+
```
642+
643+
Update the `[[bin]]` section and the `#[command(name = "...")]` in `cli.rs`.
644+
645+
**3. Replace the `hello` command** with your domain logic. Keep the same structure:
646+
647+
```
648+
src/
649+
main.rs # Entry point (barely changes between CLIs)
650+
cli.rs # clap derive definitions
651+
config.rs # 3-tier config loading
652+
error.rs # AppError with exit_code(), error_code(), suggestion()
653+
output.rs # Format detection + envelope helpers
654+
commands/
655+
mod.rs
656+
agent_info.rs # Update: list YOUR commands
657+
your_command.rs # Your domain logic
658+
skill.rs # Skill content auto-derived from CARGO_PKG_NAME
659+
config.rs # config show/path (works out of the box)
660+
update.rs # Self-update (just change repo owner/name in config)
661+
```
662+
663+
**4. Update `agent-info`** to list your actual commands with argument schemas. This is the contract agents bootstrap from.
664+
665+
**5. Write tests and run them:**
666+
667+
```bash
668+
cargo test # All integration tests
669+
cargo run -- agent-info # Verify manifest
670+
cargo run -- config show # Verify config loading
671+
echo '{}' | cargo run -- hello Test # Verify JSON envelope in pipe
672+
```
673+
674+
**6. Ship it:**
675+
676+
```bash
677+
cargo build --release # Single binary, sub-10ms cold start
678+
./target/release/my-cli skill install # Deploy to Claude/Codex/Gemini
679+
```
680+
681+
The framework conventions (`env!("CARGO_PKG_NAME")`, config loading, skill install) adapt automatically when you rename the package. No find-and-replace needed.
682+
683+
---
684+
622685
## Example
623686

624687
The `example/` directory contains a modular `greeter` CLI demonstrating the five core patterns (agent-info, JSON envelope, semantic exit codes, skill self-install, self-update) and the reusable entry point, error type, and output helpers. Config loading, secret handling, XDG paths, and HTTP retry are documented as code patterns in the Reusable Modules section above -- copy them into your CLI when you need them.

0 commit comments

Comments
 (0)