Design and build agent-first CLIs with HATEOAS JSON responses, context-protecting output, and self-documenting command trees. Use when creating new CLI tools, adding commands to existing CLIs, or reviewing CLI design for agent-friendliness. Triggers on 'build a CLI', 'add a command', 'CLI design', 'agent-friendly output', or any task involving command-line tool creation.
How this skill is triggered — by the user, by Claude, or both
Slash command
/everything-claude-code:cli-designThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
CLIs in this system are **agent-first, human-distant-second**. Every command returns structured JSON that an agent can parse, act on, and follow. Humans are welcome to pipe through `jq`.
CLIs in this system are agent-first, human-distant-second. Every command returns structured JSON that an agent can parse, act on, and follow. Humans are welcome to pipe through jq.
Every command returns JSON. No plain text. No tables. No color codes. Agents parse JSON; they don't parse prose.
# This is the ONLY output format
mycli status
# → { "ok": true, "command": "mycli status", "result": {...}, "next_actions": [...] }
No --json flag. No --human flag. JSON is the default and only format.
Every response includes next_actions — an array of commands the agent can run next, with descriptions. The agent never has to guess what's available.
{
"ok": true,
"command": "mycli send pipeline/video.download",
"result": {
"event_id": "01KHF98SKZ7RE6HC2BH8PW2HB2",
"status": "accepted"
},
"next_actions": [
{
"command": "mycli run 01KHF98SKZ7RE6HC2BH8PW2HB2",
"description": "Check run status for this event"
},
{
"command": "mycli logs --follow",
"description": "Watch worker logs in real-time"
},
{
"command": "mycli health",
"description": "Check system health"
}
]
}
next_actions are contextual — they change based on what just happened. A failed command suggests different next steps than a successful one.
The root command (no args) returns the full command tree so an agent can discover everything in one call:
{
"ok": true,
"command": "mycli",
"result": {
"description": "CLI description",
"health": { "server": {...}, "worker": {...} },
"commands": [
{ "name": "send", "description": "Send event", "usage": "mycli send <event> -d '<json>'" },
{ "name": "status", "description": "System status", "usage": "mycli status" },
{ "name": "health", "description": "Health check all services", "usage": "mycli health" }
]
},
"next_actions": [...]
}
Agents have finite context windows. CLI output must not blow them up.
Rules:
{
"ok": true,
"command": "mycli logs",
"result": {
"lines": 20,
"total": 4582,
"truncated": true,
"full_output": "/var/folders/.../mycli-logs-abc123.log",
"entries": ["...last 20 lines..."]
},
"next_actions": [
{ "command": "mycli logs --tail 100", "description": "Show more log lines" }
]
}
When something fails, the response includes a fix field — plain language telling the agent what to do about it.
{
"ok": false,
"command": "mycli send pipeline/video.download",
"error": {
"message": "Server not responding",
"code": "SERVER_UNREACHABLE"
},
"fix": "Start the server: docker compose up -d",
"next_actions": [
{ "command": "mycli health", "description": "Re-check system health after fix" },
{ "command": "docker ps", "description": "Check if Docker containers are running" }
]
}
Every command uses this exact shape:
{
ok: true,
command: string, // the command that was run
result: object, // command-specific payload
next_actions: Array<{
command: string, // exact command to copy-paste/run
description: string // what it does
}>
}
{
ok: false,
command: string,
error: {
message: string, // what went wrong
code: string // machine-readable error code
},
fix: string, // plain-language suggested fix
next_actions: Array<{
command: string,
description: string
}>
}
All CLIs use @effect/cli with Bun. Consistency across the system matters more than framework preference.
import { Command, Options } from "@effect/cli"
import { NodeContext, NodeRuntime } from "@effect/platform-node"
const send = Command.make("send", {
event: Options.text("event"),
data: Options.optional(Options.text("data").pipe(Options.withAlias("d"))),
}, ({ event, data }) => {
// ... execute, return JSON envelope
})
const root = Command.make("mycli", {}, () => {
// Root: return health + command tree
}).pipe(Command.withSubcommands([send, status, health]))
Build with Bun, install to ~/.bun/bin/:
bun build src/cli.ts --compile --outfile mycli
cp mycli ~/.bun/bin/
Command.makenext_actions — what makes sense AFTER this specific commandcommands array in the self-documenting output| Don't | Do |
|---|---|
| Plain text output | JSON envelope |
| Tables with ANSI colors | JSON arrays |
--json flag to opt into JSON | JSON is the only format |
| Dump 10,000 lines | Truncate + file pointer |
Error: something went wrong | { ok: false, error: {...}, fix: "..." } |
| Undiscoverable commands | Root returns full command tree |
| Static help text | HATEOAS next_actions |
console.log("Success!") | { ok: true, result: {...} } |
| Exit code as the only error signal | Error in JSON + exit code |
| Require the agent to read --help | Root command self-documents |
send, status, health, logsmycli memory search, mycli loop start--kebab-case: --max-quality, --follow-d for --data, -f for --followdomain/action: pipeline/video.download, content/summarizenpx claudepluginhub austinatneuko/everything-claude-codeCreates bite-sized, testable implementation plans from specs or requirements, with file structure and task decomposition. Activates before coding multi-step tasks.