When people say they want a CLI for WordPress, they often mean a thin wrapper around the REST API. That was not what I wanted here. I wanted a CLI that a coding agent could actually use without guessing. That means fewer choices, stricter inputs, and output that is easy to hand to the next step.
The structure of this CLI came from that constraint. I did not start with one big command that could “write a post.” I started with smaller pieces that were easy to trust, then built higher-level workflows on top of them once the lower layers were solid.
Start with the boring foundation
The first layer is not glamorous. It is configuration, connectivity, and identity. Commands like config, health, inspect, and whoami exist so a caller can answer simple questions before it mutates anything: are the credentials present, is the site reachable, what does the API expose, and which user am I acting as?
That matters for agents because hidden state is where they get into trouble. If the environment is wrong, I want the failure to happen early and clearly. A good agent-facing CLI should make it cheap to verify the world before doing work.
The other part of this foundation is output. Every command returns compact JSON with a stable shape. Sensitive values are masked, undefined fields are dropped, and keys are sorted. That sounds small until you start chaining commands together. Then you realize the output format is part of the product, not an afterthought.
Keep the middle layer small and predictable
Once the foundation was in place, the next step was not automation. The next step was a clean set of resource-level commands for post, page, media, and taxonomy. Each group does a small set of understandable things: get, list, search, create, update, delete, restore, schedule.
I like that shape because it gives agents good building blocks. If an agent needs to inspect a taxonomy term, upload a file, or update a post title, it can do that directly. Those commands are narrow enough to test well and boring enough to trust. That is a feature, not a limitation.
This is also where I keep the WordPress-specific details. The CLI handles route selection, parameter normalization, and response formatting. The caller does not need to know which REST endpoint to hit or which fields WordPress expects for every operation.
Build higher-level workflows on top of proven pieces
Only after the lower layers were useful on their own did it make sense to add compose <file>. That command is intentionally higher-level. It takes a local JSON file and turns it into a multi-step workflow: resolve the author, validate the payload, resolve terms, upload media, create the post, update the content, and optionally publish or schedule it.
The important part is that compose is not magic. It is orchestration. It is a higher-level function built on top of smaller functions that already existed and were already useful on their own. That makes the code easier to reason about, and it makes failures easier to explain.
It also means the hard rules live close to the workflow. The compose input has a strict schema. Scheduling requires a timezone-aware timestamp. Term matches must be exact. Media paths are checked locally before upload. Ambiguity stops the run. For coding agents, those rules are not annoying details. They are the safety rails.
What other builders can learn from this
- Do not start with the fanciest command. Start with the commands that make state visible and failures obvious.
- Keep low-level operations available even after you add higher-level workflows. You will need them for debugging, inspection, and recovery.
- Validate locally whenever you can. It is cheaper to reject a bad input file before making remote changes.
- Fail hard on ambiguous lookups. If a category or tag match is unclear, stop instead of guessing.
- Treat output shape as part of the API. If humans can read it and agents can chain it, you are on the right track.
Why this structure held up
This CLI feels reliable because each layer has a clear job. The foundation checks the environment and identity. The middle layer exposes small content operations. The top layer orchestrates real editorial work. That separation let the tool grow without turning into a pile of ad hoc commands.
If I were building another CLI for coding agents, I would use the same progression again: start with boring building blocks, make them predictable, then compose them into higher-level workflows only after the smaller parts have earned their place.