https://github.com/autonoco/buttons
CLI workflow engine for agents.
https://github.com/autonoco/buttons
agent-cli agent-tools agent-workflows
Last synced: about 1 month ago
JSON representation
CLI workflow engine for agents.
- Host: GitHub
- URL: https://github.com/autonoco/buttons
- Owner: autonoco
- License: apache-2.0
- Created: 2026-04-11T20:49:41.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-04-12T18:31:27.000Z (about 1 month ago)
- Last Synced: 2026-04-12T19:31:53.376Z (about 1 month ago)
- Topics: agent-cli, agent-tools, agent-workflows
- Language: Go
- Homepage: https://docs.buttons.sh
- Size: 221 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
# Buttons
[](https://github.com/autonoco/buttons/actions/workflows/ci.yml)
[](LICENSE)
[](https://github.com/autonoco/buttons/releases)
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.
## Install
Buttons ships as a single static binary for **macOS** and **Linux** (amd64 + arm64). Windows support is tracked in [autonoco/autono#350](https://github.com/autonoco/autono/issues/350).
### npm / pnpm / bun
Easiest path if you already have a JavaScript toolchain:
```bash
npm install -g @autono/buttons
pnpm add -g @autono/buttons
bun add -g @autono/buttons
```
The npm package is a thin JS shim that resolves the matching platform binary via `optionalDependencies`. After install, `buttons` is on your `$PATH` just like any other install method.
### curl (macOS / Linux)
The fastest path for a single machine:
```bash
curl -fsSL https://raw.githubusercontent.com/autonoco/buttons/main/install.sh | sh
```
Defaults install to `/usr/local/bin` and use `sudo` if that directory isn't writable. Pin a specific version or change the install location with env vars:
```bash
# Pin a version
curl -fsSL https://raw.githubusercontent.com/autonoco/buttons/main/install.sh | BUTTONS_VERSION=v0.1.0 sh
# Install to a user-owned directory (no sudo)
curl -fsSL https://raw.githubusercontent.com/autonoco/buttons/main/install.sh | BUTTONS_INSTALL_DIR=$HOME/.local/bin sh
```
The script verifies the SHA256 checksum of every download against the release's `checksums.txt` before installing.
### Docker (for deployed agents)
Buttons is published as a multi-arch image on GitHub Container Registry:
```bash
docker pull ghcr.io/autonoco/buttons:latest
docker run --rm ghcr.io/autonoco/buttons:latest version
```
Most agent deployments want the binary baked into their own image. Use a multi-stage copy:
```dockerfile
FROM python:3.12-slim AS agent
# ... your agent setup ...
# Buttons binary, ~5 MB, no runtime dependencies
COPY --from=ghcr.io/autonoco/buttons:v0.1.0 /usr/local/bin/buttons /usr/local/bin/buttons
```
The image is Alpine-based with `/bin/sh` so shell buttons work out of the box. Derived images can `apk add python3 nodejs` when those runtimes are needed for other button types.
For a one-off invocation with state persisted to the host:
```bash
docker run --rm -v $PWD/.buttons:/home/buttons/.buttons \
ghcr.io/autonoco/buttons:latest press weather --arg city=Miami
```
### Go
If you already have a Go toolchain:
```bash
go install github.com/autonoco/buttons@latest
```
Installs to `$(go env GOPATH)/bin` (usually `~/go/bin`). Make sure that's on your `$PATH`.
### Homebrew
```bash
brew install autonoco/tap/buttons
```
Or in two steps if you prefer:
```bash
brew tap autonoco/tap
brew install buttons
```
The formula is auto-generated by [goreleaser](https://goreleaser.com/) on every release and pushed to [autonoco/homebrew-tap](https://github.com/autonoco/homebrew-tap). Updates land via:
```bash
brew upgrade buttons
```
`buttons update` knows about Homebrew installs and will tell you to use `brew upgrade` instead of trying to overwrite a brew-managed binary.
## Updating
```bash
buttons update # download and install the latest version
buttons update --check # just check, don't install
buttons update --json # structured output
```
The update command downloads the latest release from GitHub, verifies the SHA256 checksum, and atomically replaces the running binary. If the binary was installed via Homebrew, it tells you to use `brew upgrade` instead.
For containerized and CI environments where `buttons update` isn't practical:
| Installed via | Update with |
|---|---|
| `curl \| sh` | Re-run the same `curl \| sh` command |
| Docker | `docker pull ghcr.io/autonoco/buttons:latest` |
| `go install` | `go install github.com/autonoco/buttons@latest` |
| Homebrew (once available) | `brew upgrade buttons` |
## Verify the installation
```bash
buttons version
buttons version --json
buttons --version # shorter Cobra-built-in form
```
## Quick Start
```bash
# 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 weather
```
## Creating Buttons
Three button types. Use `--prompt` as a modifier on any of them to attach an instruction for the consuming agent.
### Code
```bash
# 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
```
### API
```bash
# 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:required
```
### File
Import an existing script. The file is copied into the button folder.
```bash
buttons create deploy -f ./scripts/deploy.sh --arg env:string:required
```
### Agent (standalone or modifier)
The `--agent` flag attaches an instruction for the consuming agent. Use it standalone or combine it with code/API buttons.
```bash
# Standalone: just an instruction
buttons create deploy-checklist \
--agent "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`:
```json
{
"ok": true,
"data": {
"status": "ok",
"stdout": "ERROR 2026-03-31 connection timeout to db-primary...",
"prompt": "Summarize any errors or warnings from these logs",
"button": "check-logs"
}
}
```
## Pressing Buttons
```bash
buttons press weather --arg city=Miami
buttons press weather --arg city=Miami --json
buttons press weather --arg city=Miami --dry-run
buttons press slow-task --timeout 120
```
### Arguments
```bash
# 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=true
```
Types: `string`, `int`, `bool`. Code buttons get args as `BUTTONS_ARG_` env vars. API buttons use `{{arg}}` template substitution.
## Discovering Buttons
```bash
# Board view (all buttons)
buttons
# Detail view (single button with args, usage, last run)
buttons weather
# JSON list
buttons list --json
```
## History
Every press is recorded as a JSON file in the button's `pressed/` folder.
```bash
buttons history
buttons history weather
buttons history --last 5
buttons history weather --json
```
## Deleting Buttons
```bash
buttons delete deploy
buttons delete deploy -F # skip confirmation
buttons delete deploy --json # JSON mode implies force
buttons rm deploy # rm is an alias for delete
```
## Button Folder Structure
Each 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-03-31T09-53-45.json
```
Override storage location with `BUTTONS_HOME` environment variable.
## JSON Output
Every command supports `--json`. Piped output auto-detects non-TTY and outputs JSON automatically.
```json
{"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`, `INTERNAL_ERROR`, `NOT_IMPLEMENTED`.
## Create Flags
By default, `buttons create ` 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` |
| `--description` | `-d` | Button description |
| `--timeout` | | Execution timeout in seconds (default: 60) |
| `--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. |
## Security
- `context.WithTimeout` on every execution (default 60s)
- Process group kill: SIGTERM then SIGKILL after 5s grace
- Args as env vars, never interpolated into shell
- File permissions: 0600 on specs, 0700 on code files
- URL `{{arg}}` values are context-aware encoded (`url.PathEscape` in paths, `url.QueryEscape` in queries, JSON-escaped in bodies)
- HTTP response bodies are capped (default 10 MB, per-button configurable)
- HTTP buttons block private network addresses by default; opt in per-button with `--allow-private-networks`
See [SECURITY.md](SECURITY.md) for the full threat model and how to report vulnerabilities privately.
## License
Buttons is licensed under the [Apache License, Version 2.0](LICENSE).
Copyright 2026 Darley Ventures LLC dba Autono.