A CLI for Humans and Agents, Spaces
Key point
Turning an interactive CLI into flags and structured data lets agents automate it end to end.
Details
Building a CLI for an internal platform, Spaces found a design that works for both humans and agents. With just spaces init my-project and spaces dev, the team built a flow spanning project creation, dev environment startup, config generation, and staging deployment—and this CLI ended up being a tool not just for humans, but for coding agents as well.
The criteria for a good CLI for humans were clear. Scaffolding should build structure and show choices, Development should run immediately, and Operations should be handled carefully. TUI-based selectors looked great, but agents got stuck because they couldn't handle ANSI codes and keyboard input.
The key solution was to reduce every interaction to flags. Instead of thinking about the input method first, the team thought about what information was needed, then made that information suppliable via flags, defaults, and config files. -y became not just an option to skip confirmations, but a contract to resolve all necessary values programmatically without blocking on stdin.
Concretely, changes moved in the following direction:
- Provide non-interactive alternatives to interactive questions, like
--components - Design so that in
-ymode, every prompt resolves via a flag value or a smart default - Separate execution logic from the input path so it can be tested once
The second pillar was structured data. Instead of hardcoding module types, the team built a plugin system where each component declares its own properties and behavior. Humans pick from a TUI; agents query the registry and get JSON back. Same data, just different rendering.
This change also simplified maintenance. Previously, adding a new module meant fixing the picker, the Dockerfile generator, the env file writer, and the compose template all at once. Now, all it takes is adding a single plugin class. The registry became the single source of truth.
To help agents better understand a project, context.json and AGENTS.md are also generated during init. The former is a structured snapshot containing modules, ports, run commands, and env vars; the latter contains execution rules for LLMs. Since agents read these before doing any work, mistakes like guessing ports, running the wrong test command, or reinstalling already-managed dependencies are greatly reduced.
The last problem was implicit state. Looking for config.yaml in the current working directory feels natural to a human, but it's a trap for an agent running from the workspace root. So the team reduced reliance on CWD and switched to a combination of explicit paths and parent-directory traversal.
In summary, the principles that made the biggest difference were:
- Every interactive input must have a flag equivalent
- Every flag must have a smart default for headless mode
- Hidden state like CWD, env vars, and dotfiles must be exposed as input
- Plugins should be data models, not code fragments
- Context files like
context.jsonhelp agents, CI, and scripts alike
As a result, the CLI got better for humans too. While keeping the TUI, spinners, and confirmation dialogs intact, the team opened a second door through which agents could enter. The constraints of humans and agents ultimately point to the same problem, and a design that satisfies those constraints is exactly what makes a tool more composable, scriptable, and testable.
This summary was generated automatically by AI. Check the original for the author's claims and context. Copyright belongs to the original author.
Our guide explains how the AI works. Report summary errors, attribution issues, or removal requests via Contact.