Skip to main content
Buttons stores data under ~/.buttons/ by default. In a project initialized with buttons init, the active data directory is the closest .buttons/ folder in the current directory tree. Each button gets its own subdirectory with a spec file, optional code file, agent context, and run history. Drawers get the same pattern for workflow specs and workflow history.

Directory layout

~/.buttons/ or .buttons/
├── buttons.json              # project manifest; desired buttons and version policy
├── buttons-lock.json         # resolved exact versions, hashes, and sources
├── history.json              # local create/edit/install/update history; gitignored
├── buttons/
│   ├── deploy/
│   │   ├── button.json       # button spec
│   │   ├── main.sh           # script (code and file buttons)
│   │   ├── AGENT.md          # agent instructions (agent buttons)
│   │   └── pressed/
│   │       ├── 2026-04-10T12-00-00Z.json
│   │       └── 2026-04-10T14-32-11Z.json
│   ├── get-user/
│   │   ├── button.json
│   │   └── pressed/
│   └── summarize-pr/
│       ├── button.json
│       ├── AGENT.md
│       └── pressed/
├── drawers/
│   └── release-flow/
│       ├── drawer.json
│       └── pressed/
├── batteries.json            # secret/env values; gitignored for project-local dirs
├── webhook.json              # Cloudflare tunnel config; gitignored for project-local dirs
├── idempotency/              # cached successful press results
└── queues/                   # file-lock semaphore state

button.json

The spec file for a button. It describes the button type, arguments, runtime, and all other configuration. Newly created specs use "schema_version": 2.
{
  "schema_version": 2,
  "name": "deploy",
  "description": "Deploy a tagged release to an environment",
  "runtime": "shell",
  "args": [
    { "name": "env", "type": "string", "required": true },
    { "name": "version", "type": "string", "required": true }
  ],
  "created_at": "2026-04-10T11:00:00Z"
}
See button.json for every field, including HTTP host locks, queues, output schemas, package metadata, and required batteries.

drawer.json

The workflow spec for a drawer. It stores inputs, steps, refs, triggers, return values, and error handling.
{
  "schema_version": 1,
  "name": "release-flow",
  "steps": [
    { "id": "build", "kind": "button", "button": "build" },
    { "id": "publish", "kind": "button", "button": "publish" }
  ]
}
See drawer.json for the full workflow schema.

buttons.json and buttons-lock.json

buttons.json at the root of .buttons/ declares the desired project buttons and version policy. buttons-lock.json records the exact resolved versions, sources, and content hashes. Commit it with buttons.json so teammates can recreate the same installed buttons.

history.json

history.json records local create, edit, install, update, delete, rollback, and failed resolution events. Commands write explicit events; file hooks can record manual edits to tracked button and drawer files. In a team repo, commit buttons.json, buttons-lock.json, and authored button/drawer files. Do not commit history.json; it is a local runtime artifact. When installed buttons are generated entirely from a registry, the repo can recreate them from the manifest and lock file. When a button is authored directly in the project, commit its folder under buttons/.

main.*

The script file for code buttons. The extension matches the runtime: main.sh for shell, main.py for Python, main.js for Node. HTTP and prompt-only buttons do not have a main.* file.

AGENT.md

Present on every button. For prompt buttons, contains the raw instruction text stored at create time via --prompt; when pressed, the contents are returned in the prompt field of the JSON output. For other button types, AGENT.md holds notes and context an agent can read to understand when and how to press the button.

pressed/

One JSON file per run, named by timestamp. Each file records the input arguments, exit code, stdout, stderr, duration, and whether the run succeeded.
{
  "button": "deploy",
  "pressed_at": "2026-04-10T14:32:11Z",
  "args": { "env": "staging", "version": "v1.4.2" },
  "exit_code": 0,
  "stdout": "Deploying v1.4.2 to staging...\nDone.",
  "stderr": "",
  "duration_ms": 3241
}

batteries.json

Batteries are key/value pairs injected into button presses as BUTTONS_BAT_<KEY>. Project-local batteries live at .buttons/batteries.json. Global batteries live at ~/.buttons/batteries.json. Local values override global values on key collision.

webhook.json

Named webhook setup stores Cloudflare tunnel configuration in webhook.json. It is machine-specific and should not be committed.

idempotency/

Successful presses with --idempotency-key are cached here until their TTL expires.

queues/

Buttons with a queue declaration use file locks here to enforce concurrency limits.

Overriding the home directory

Set BUTTONS_HOME to use a different base directory:
BUTTONS_HOME=/tmp/test-buttons buttons create test-btn --code 'echo ok' --runtime shell
This is useful in CI, tests, or container environments where you want an isolated state directory.
The ~/.buttons/ directory and all files inside it are created with mode 0700 / 0600. Other users on the same system cannot read button specs or run history.