Skip to main content
Every button has a button.json spec in its button folder. It is the source of truth for runtime, arguments, HTTP settings, metadata, concurrency, and packaging fields.
Project-local buttons use the same shape under .buttons/buttons/<name>/. App buttons (created with --app) live under apps/<name>/ instead — see App buttons. The agent prompt file is AGENTS.md; an older AGENT.md is still read as a fallback but never written.

Example

Real files always carry runtime, env, timeout_seconds, and mcp_enabled even when empty or default; the example omits the zero-value ones for brevity.

Core fields

Code buttons

Code buttons use runtime: shell, bash, python, or node. The script lives next to button.json as main.sh, main.py, or main.js. Both shell runtimes use main.sh; shell executes it with /bin/sh, while bash resolves and executes Bash from PATH. Arguments arrive as environment variables:
Battery secrets arrive as BUTTONS_BAT_<KEY>.

HTTP buttons

HTTP buttons are created with --url. They have no main.* file and carry an empty runtime; the url field identifies them.

Prompt buttons

Prompt-only buttons use runtime: prompt and do not have a main.* file. Their instruction lives in AGENTS.md and is returned in the prompt field when pressed. Code and HTTP buttons can also carry AGENTS.md instructions; in that case the command runs first and the prompt is attached to the result.

App buttons

App buttons set kind: "app" and live under apps/<name>/ instead of buttons/<name>/. They are created by cloning a git URL or copying a local path:
They have no main.* file. Instead they carry a serve block describing how the app builds and runs: CreateApp defaults serve to { "type": "static", "output": "dist" }.

Arguments

Args are declared at create time:
Supported arg types are string, int, bool, and enum.

Output schema

output_schema is optional JSON Schema for the button’s JSON stdout. Drawers use it to type-check refs and auto-connect compatible fields.
When a button prints valid JSON to stdout, a drawer exposes it as ${step_id.output.<field>}.

Queues

queue limits concurrent presses.
Buttons sharing a queue name share the same slot pool. A key scopes that pool per press, so one queue can enforce a per-user or per-account limit.

Packaging fields

Buttons published to or installed from a registry can include package metadata. The root dependency manifest is .buttons/buttons.json; the exact resolved versions and hashes live in .buttons/buttons-lock.json. button.json remains the runtime/package spec for one button. Example package fields:
If @your-desk/hello is later published as 2, buttons status can report it by reading .buttons/buttons.json and .buttons/buttons-lock.json. buttons update moves it only when the manifest requested "latest".

Triggers

A button can carry a triggers array so it fires automatically on cron, file-watch, or webhook events. Triggers run under buttons serve and are managed with buttons trigger add|list|rm.

Board metadata

pinned: true surfaces a button as a large card at the top of buttons board.