Agent-First Config v0.1.0
The first release of Agent-First Config — dot-path get/set/add/remove for JSON, TOML, and YAML, usable as a Rust library or as the afconfig CLI binary.
Every tool that reads and writes config ends up writing the same code: load the file, reach into a nested map, coerce a string to the right type, write it back. The path logic, the coercion table, the keyed-list routing — it’s the same every time. So we extracted it.
Agent-First Config v0.1.0 is that extraction: a Rust library and a CLI binary that share one implementation of dot-path traversal and coercion across JSON, TOML, and YAML.
Lib usage
use agent_first_config::{Value, get_path, set_path, add_keyed, KeyedList};
let mut config = Value::Object(Default::default());
// "993" → integer (type inferred by shape)
set_path(&mut config, "imap.port", &["993".to_string()], &[])?;
// keyed-list: route "identities.me" to the array element where identity == "me"
let keyed = [KeyedList { prefix: "identities", slug_field: "identity" }];
add_keyed(&mut config, "identities", "me", &keyed, None,
&[("email".to_string(), "me@example.com".to_string())])?;
let email = get_path(&config, "identities.me.email", &keyed)?;
The library has no format deps by default — enable only what you use:
| Feature | What it adds |
|---|---|
json | JSON backend (on by default) |
toml | TOML, format-preserving via toml_edit |
yaml | YAML re-serializing |
schema | CliSchema trait + markdown / annotated-config rendering |
cli | afconfig binary (pulls in clap; enables all formats + schema) |
CLI usage
afconfig get config.json imap.host
afconfig set config.json imap.port 993
afconfig add config.json identities support email=support@example.com name="Support"
afconfig remove config.json identities support
afconfig show config.json
Format is detected from the file extension. The same coercion rules apply: "true" becomes a bool, "993" an integer. Force the type with a prefix: s:true stays a string, b:1 becomes a bool.
Traversal rules
- Objects: standard dot-path (
imap.host) - Keyed lists: slug as a path segment (
identities.me) — register viaKeyedList { prefix, slug_field }; numeric index takes priority (steps.0) - Scalar arrays: multi-value set replaces the whole array (
set tags dev staging prod→["dev","staging","prod"]) - Greedy key matching: if the config has a key
"case.add", pathactions.case.add.stepsresolves it before treating the dots as separators
Install
Rust library:
cargo add agent-first-config
CLI (all backends):
cargo install agent-first-config --features cli