Adds an effect= annotation for classifying command side effects, and fixes four latent SpecCommand bugs uncovered while auditing the code paths a new field has to touch.
Added
-
effect=on commands (#739 by @jdx). Declare what running a command does to the world so docs, wrapper scripts, and AI agent allowlists can consume the same annotation instead of hand-maintaining their own lists:cmd "ls" effect="read" help="List installed tools" cmd "use" effect="write" help="Install a tool and add it to the config" cmd "uninstall" effect="destructive" help="Remove a tool"
Effect Meaning readOnly inspects state. Idempotent. writeCreates or modifies state, but removes nothing the user can't recreate. destructiveMay delete or irreversibly overwrite. Deserves a confirmation prompt. Accepted as either a prop (
effect="read") or a child node (effect "read"); unknown values are a parse error. The field is not inherited by subcommands (git remoteandgit remote removedo different things), and unset means unknown, not safe — consumers should treat missing values as "ask". Round-trips through KDL and renders an- **Effect**:line in generated markdown. Exported asusage::SpecCommandEffect.
Fixed
Four silent gaps in SpecCommand handling, all found by auditing every field against merge, the KDL serializer, and the docs model (#740 by @jdx):
deprecatedwas dropped bymerge. An included spec could silently un-deprecate a command.exampleswere never written by the KDL serializer.spec.to_string()dropped every example.help_md/before_help_md/after_help_mdwere accepted only as props but serialized as child nodes. Any spec with markdown help failed to reparse withError: unsupported cmd key help_md. They are now accepted as child nodes as well.restart_tokennever reached the docs model, so no template could render it.
Changed
- Missing
SpecCommandfields are now a compile error (#740).mergeand bothFromimpls destructure their source with no.., so adding a field produces three compile errors pointing at exactly the places that owe it a decision. Runtime-derived fields are bound with aNOTEcomment. A new round-trip test parses a spec exercising every field, serializes, reparses, and compares serde representations — catching the class of bug where the serializer writes something the parser rejects (which is how thehelp_mdregression survived).
Full Changelog: v3.5.7...v3.6.0
💚 Sponsor usage
usage is maintained by @jdx, an open source developer for entire.io, the title sponsor of the jdx.dev open source tools including mise, aube, hk, and more. Work on usage is funded by sponsorships.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider sponsoring at jdx.dev. Every sponsorship helps the project stay independent and moving.