Build Your Own Headless Agent
Looking to build an interactive terminal agent with a customizable UI instead? See the Build Your Own Agent TUI guide.
The create-headless-agent skill scaffolds a headless agent in TypeScript + Bun — no terminal UI, just structured input and output. It’s designed for CLI tools, API servers, queue workers, and automation pipelines where you need an agent that runs programmatically.
Under the hood, the generated project uses @openrouter/agent for the inner loop (model calls, tool execution, stop conditions) — the same SDK that powers the Agent TUI, with a non-interactive outer layer.
When to build your own
Building a headless agent makes sense when:
- You need headless automation — batch processing, CI pipelines, queue workers, or structured output validation
- You need custom tools — your agent interacts with your own APIs, databases, or domain-specific systems that generic agents can’t reach
- You want control over the loop — you need custom stop conditions, cost limits, or model selection logic that hosted agents don’t expose
- You’re shipping a product — the agent is part of your application, not a developer tool, and you need to own the entry point (CLI, API server, embedded)
- You want structured output — NDJSON event streams, exit codes, or schema-validated responses for programmatic consumption
- You want to learn — understanding how agents work at the tool-execution level makes you better at using and debugging them
If you need an interactive terminal experience, use the Agent TUI skill instead.
Install the skill
The create-headless-agent skill is part of the OpenRouter Skills collection. Install it with your AI coding agent of choice:
GitHub CLI
Claude Code
Cursor
Requires GitHub CLI v2.90.0+. Works with Claude Code, Cursor, OpenCode, Codex, Gemini CLI, Windsurf, and many more agents:
Once installed, ask your agent something like “scaffold a headless agent” or “build me a CLI agent” and the skill activates automatically.
Prerequisites
How it works
Like the TUI skill, the headless skill presents your coding agent with an interactive checklist. You pick tools and modules, and it generates a complete project — but instead of a REPL, the entry point accepts prompts via --prompt, positional arguments, or piped stdin and outputs plain text, NDJSON event streams, or just an exit code.
What @openrouter/agent handles
Output modes
The generated CLI supports three output modes:
Generated project structure
Run it with:
Customization options
The skill presents a checklist when invoked. Items marked on are pre-selected defaults.
Server tools
Executed by OpenRouter server-side — zero client code needed.
Client-side tools
Generated into src/tools/ with Bun-native implementations.
Modules
Optional architectural components for the headless agent.
Highlighted features
Safe retry on 429/5xx
The generated runAgentWithRetry wrapper retries transient API errors (rate limits, server errors) with exponential backoff — but only if no tool calls have executed yet. Once a mutating tool like file_write or shell has run, replaying the agent from the initial prompt would double-execute side effects. In that case, retries throw immediately instead of risking repeated mutations.
For mid-run resilience (crash-resume, cross-process approval flows), pair with the optional Session Persistence module, which writes every message to a JSONL file so the agent can pick up where it left off.
Structured output with --output-schema
Constrain the agent’s final response to match a JSON Schema using Ajv. The scaffold is tolerant of markdown fences, so schemas work even when the model wraps JSON in code blocks:
Exit codes:
0— agent succeeded and output matched schema1— agent or API error2— output failed schema validation (Ajv error message on stderr, or emitted as avalidation_errorevent in--jsonmode)
Entry points
The skill generates a CLI entry point by default, but you can also ask for:
- HTTP server —
Bun.serve()with SSE streaming for building web-accessible agents - MCP server — expose the agent as an MCP tool for other agents to call