n8n for agents. A CLI workflow engine where AI agents build and run their own automations.
Each button is a self-contained, reusable action. Create it once, press it forever. Buttons wraps code, APIs, and agent instructions into a single interface with typed args and structured output.
Buttons ships as a single static binary for macOS and Linux (amd64 + arm64). Windows support is tracked in autonoco/autono#350.
curl -fsSL https://raw.githubusercontent.com/autonoco/buttons/main/install.sh | shInstalls to /usr/local/bin by default (uses sudo if needed). The script verifies the SHA256 checksum of every download. Override with env vars:
curl -fsSL https://raw.githubusercontent.com/autonoco/buttons/main/install.sh | BUTTONS_VERSION=v0.70.0 sh
curl -fsSL https://raw.githubusercontent.com/autonoco/buttons/main/install.sh | BUTTONS_INSTALL_DIR=$HOME/.local/bin shbrew install autonoco/tap/buttonsTap is auto-updated by goreleaser on every release. brew upgrade buttons to update.
go install github.com/autonoco/buttons@latestInstalls to $(go env GOPATH)/bin (usually ~/go/bin).
npm install -g @autono/buttonsThin JS shim that resolves the platform binary via optionalDependencies.
docker pull ghcr.io/autonoco/buttons:latest
docker run --rm -v $PWD/.buttons:/home/buttons/.buttons \
ghcr.io/autonoco/buttons:latest press weather --arg city=MiamiMulti-arch Alpine image, ~5 MB, published to GHCR. Mount a volume to persist state between invocations.
buttons update # download and install the latest release
buttons update --check # check only
buttons update --json # structured outputThe command downloads the latest release, verifies the SHA256, and atomically replaces the running binary. Homebrew installs are auto-detected — you'll be told to brew upgrade buttons instead. Docker users re-pull the image.
buttons version
buttons --version# Create a button from an API
buttons create weather --url 'https://wttr.in/{{city}}?format=j1' --arg city:string:required -d "get weather"
# See what it does
buttons weather
# Press it
buttons press weather --arg city=Miami
# See the history
buttons history weatherThree button types. Use --prompt as a modifier on any of them to attach an instruction for the consuming agent.
# Scaffold + edit (default). Creates main.sh with a placeholder, tells you where to edit it.
buttons create deploy --arg env:string:required
# Then edit ~/.buttons/buttons/deploy/main.sh and press:
buttons press deploy --arg env=staging
# Or skip the scaffold with --code for one-liners
buttons create greet --code 'echo "Hello, $BUTTONS_ARG_NAME!"' --arg name:string:required
# Python scaffold
buttons create transform --runtime python --arg input:string:required
# Node scaffold
buttons create parse --runtime node
# Or import an existing script file
buttons create etl --file ./scripts/etl.sh --arg source:string:required# GET with URL templates
buttons create weather --url 'https://wttr.in/{{city}}?format=j1' --arg city:string:required
# POST with JSON body
buttons create notify --url https://hooks.slack.com/services/xxx \
--method POST \
--header "Content-Type: application/json" \
--body '{"text": "{{message}}"}' \
--arg message:string:required
# GraphQL
buttons create repos --url https://api.github.com/graphql \
--method POST \
--header "Authorization: Bearer {{token}}" \
--header "Content-Type: application/json" \
--body '{"query": "{ repository(owner: \"{{owner}}\", name: \"{{repo}}\") { stargazerCount } }"}' \
--arg token:string:required --arg owner:string:required --arg repo:string:requiredImport an existing script. The file is copied into the button folder.
buttons create deploy -f ./scripts/deploy.sh --arg env:string:requiredThe --prompt flag attaches an instruction for the consuming agent. Use it standalone or combine it with code/API buttons.
# Standalone: just an instruction
buttons create deploy-checklist \
--prompt "Before deploying, verify: 1) All tests pass 2) Staging is green 3) Team notified"
# Code + prompt: run the command, agent knows what to do with the output
buttons create check-logs \
--code 'northflank logs --service api --env production --tail 100' \
--prompt "Summarize any errors or warnings from these logs"
# API + prompt: call the API, agent interprets the result
buttons create analyze-weather \
--url 'https://wttr.in/{{city}}?format=j1' \
--arg city:string:required \
--prompt "Extract temperature and conditions as a one-line summary"When pressed, the result includes both the output and the prompt:
{
"ok": true,
"data": {
"status": "ok",
"stdout": "ERROR 2026-04-18 connection timeout to db-primary...",
"prompt": "Summarize any errors or warnings from these logs",
"button": "check-logs"
}
}buttons press weather --arg city=Miami
buttons press weather --arg city=Miami --json
buttons press weather --arg city=Miami --dry-run # validate args + render command, don't execute
buttons press slow-task --timeout 120# Define at creation
buttons create deploy --code '...' --arg env:string:required --arg verbose:bool:optional
# Pass at press time
buttons press deploy --arg env=production --arg verbose=trueTypes: string, int, bool, enum. Enum declares a fixed set of valid values — the CLI rejects anything outside the set and the board renders a picker:
buttons create deploy --code '...' --arg env:enum:required:staging|prod|canaryCode buttons get args as BUTTONS_ARG_<NAME> env vars. API buttons use {{arg}} template substitution.
buttons # interactive card-grid board in a TTY, plain table when piped
buttons <name> # full-screen detail page (args, last run, script); press `e` to edit
buttons logs <name> # live log viewer — follow mode with `f`, jump with `g`/`G`, `Esc` to exit
buttons list --json # machine-readable listOn the board, pressing a button with required arguments opens an inline form instead of erroring out. Fill it in, hit Enter, the press fires.
Personal defaults live in ~/.buttons/settings.json. Manage them with buttons config:
buttons config # show current values
buttons config set default-timeout 600 # default for future `buttons create`
buttons config set theme amber # board TUI theme: default | paper | phosphor | amber
buttons config unset theme # revert to built-in defaultEnv var overrides: BUTTONS_HOME (relocate the data directory), BUTTONS_THEME (one-shot theme override — useful for A/B).
Batteries are key/value pairs injected into every button press as BUTTONS_BAT_<KEY>=<value>. Use them to keep API tokens out of button scripts.
buttons batteries set APIFY_TOKEN apify_api_xxx # local by default inside a project, global otherwise
buttons batteries set OPENAI_KEY sk-... --global # ~/.buttons/batteries.json
buttons batteries list
buttons batteries get APIFY_TOKEN
buttons batteries rm OLD_KEYLocal (.buttons/batteries.json) overrides global on key collisions. Keys must match [A-Z][A-Z0-9_]*.
Every press is recorded as a JSON file in the button's pressed/ folder.
buttons history
buttons history weather
buttons history --last 5
buttons history weather --jsonbuttons delete deploy
buttons delete deploy -F # skip confirmation
buttons delete deploy --json # JSON mode implies force
buttons rm deploy # rm is an alias for deleteEach button is a self-contained folder:
~/.buttons/buttons/weather/
button.json # spec (args, runtime, timeout)
main.sh # code file (.sh, .py, .js based on runtime)
AGENT.md # agent instruction/context
pressed/ # run history
2026-04-18T09-53-45.json
Override storage location with BUTTONS_HOME.
Every command supports --json. Piped output auto-detects non-TTY and outputs JSON automatically.
{"ok": true, "data": {"status": "ok", "stdout": "...", "prompt": "...", "button": "weather"}}
{"ok": false, "error": {"code": "MISSING_ARG", "message": "...", "hint": "...", "spec": [...]}}Error codes: NOT_FOUND, MISSING_ARG, VALIDATION_ERROR, TIMEOUT, SCRIPT_ERROR, RUNTIME_MISSING, STORAGE_ERROR, NOT_APPLICABLE, INTERNAL_ERROR, NOT_IMPLEMENTED.
By default, buttons create <name> scaffolds a shell button with a placeholder main.sh the agent can edit. Shortcut flags below let you skip the placeholder.
| Flag | Short | Description |
|---|---|---|
--runtime |
Scaffold runtime: shell, python, node (default: shell) | |
--code |
Inline script body (shortcut for one-liners) | |
--file |
-f |
Copy an existing script file into the button folder |
--url |
HTTP API endpoint (supports {{arg}} templates) |
|
--method |
HTTP method (default: GET) | |
--header |
HTTP header as Key: Value (repeatable) |
|
--body |
HTTP request body (supports {{arg}} templates) |
|
--prompt |
Prompt instruction written to AGENT.md (standalone or modifier on any source) | |
--arg |
Argument: name:type:required|optional (enums: name:enum:required:a|b|c) |
|
--description |
-d |
Button description |
--timeout |
Execution timeout in seconds (default: 300) | |
--max-response-size |
Max HTTP response body for --url buttons, e.g. 10M, 1G (default: 10M) |
|
--allow-private-networks |
Allow --url buttons to reach private network addresses (localhost, 10/8, 172.16/12, 192.168/16, 169.254/16). Required for local dev targets. |
- Every execution runs under
context.WithTimeout(default 300s) - Process group kill: SIGTERM then SIGKILL after 5s grace
- Args passed as env vars, never interpolated into shell
- HTTP buttons block private networks by default and cap response bodies
See SECURITY.md for the full threat model and how to report vulnerabilities.
Full CLI reference, concepts, and changelog live in the docs/ directory (published via Mintlify).
Buttons is licensed under the Apache License, Version 2.0.
Copyright 2026 Darley Ventures LLC dba Autono.