Skip to content
Public template

About

Template for harnsy Actions: a Go and a bash example, the contract, agent skills

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

harnsy action template

Write harnsy actions here: scripts an agent proposes and you run from the harnsy dashboard with one button, with a status, a summary and figures for every run.

harnsy runs an action in its project's directory. Press Use this template and either make your copy the project, or copy actions/, cmd/, internal/, Makefile and the agent files into the project the actions are for. Then start from one of the three examples.

Example Kind What it shows
actions/disk bash script the smallest action: a parameter, ::step, ::fail, a result with figures
actions/todo-count Go, one file a Go script harnsy runs with go run: standard library only
actions/go-stats command (Go binary) a program built from this module (cmd/ + internal/), with tests, using the contract helper internal/action

Quick start

make check                 # gofmt, go vet, tests, build, every action run once
make try A=disk            # run an action the way harnsy runs it
make try A=disk P='-p max_used=95'
go run ./cmd/propose -dry-run actions/disk            # the proposal, printed
go run ./cmd/propose -from <agent> -project <project> actions/disk

make try gives the action what harnsy gives it (the working directory, a trimmed environment, ACTION_RESULT, ACTION_DIR, the time limit), prints its output and its last ::step and ::fail, and checks the result against the contract. A proposal waits on the dashboard until you enable it; nothing runs before that.

An action

Each action is a directory in actions/:

actions/<name>/
  action.json      the definition (contract/action-definition.schema.json), without script.body
  run.sh           the script, by lang: run.sh (bash, sh), run.py, run.js, main.go

cmd/propose puts the script into script.body when it sends the proposal. A command action has no script file: its command.argv[0] is a file in the project.

{
  "name": "disk",
  "title": "Disk",
  "script": {"lang": "bash"},
  "params": [{"name": "max_used", "type": "int", "min": 1, "max": 100, "default": 90}],
  "timeout": "1m",
  "numbers": [{"key": "free_gb", "label": "Free, GB"}, {"key": "used_gb", "of": "size_gb"}]
}

The contract, in short

The full text is contract/contract.md (version 1.1.0, as harnsy serves it at /docs/actions/contract.md), with JSON Schemas and an OpenAPI description next to it.

  • Arguments. One per parameter, in the declared order ($1, $2, …); in a command, an argument that is exactly {name} becomes that value (--level={level} stays as written: use --level {level}). Never a shell string. Values are trimmed: enum the value, int and item digits, bool true or false (1, t, TRUE are accepted), commit the full 40-character hash a branch, tag or commit resolves to in the action's directory.
  • Where. On a Linux node, for now.
  • Environment. Only PATH, HOME, LANG, USER, TMPDIR, HARNSY_HOME (so the harnsy CLI reaches this node), the names the action lists in env, and ACTION_CONTRACT (1), ACTION_RUN_ID, ACTION_RESULT (write the result there), ACTION_DIR (a directory of the run's own).
  • Markers. A whole line of output (standard output or error): ::step <text> (what runs now) and ::fail <text> (why it failed, one line).
  • Exit. 0 is ok, anything else failed; past timeout (1m to 2h, default 30m) the run is stopped. When the script exits, what it left running is stopped too.
  • Result. JSON in $ACTION_RESULT, up to 64 KiB, every field optional:
{
  "summary": "5.1 GB free of 456 GB (99% used)",
  "numbers": {"free_gb": 5.1, "used_pct": 99},
  "artifacts": [{"path": "dist/app.tar.gz", "label": "build"}],
  "links": [{"url": "https://example.com/report/7", "label": "report"}]
}

At most 8 numbers (a number or a text of up to 40 characters), 20 artifacts inside the project or ACTION_DIR, 10 http(s) links.

What the human approves

You enable one version of an action; harnsy pins it by sha256 and every change is a new version that waits for you again.

  • A script (bash, sh, python, node, go): the whole body is pinned and shown to you before you enable it.
  • A command: harnsy pins the file argv[0] only, not what it calls. Propose a built binary (bin/go-stats): a build of changed code has another sha256, so harnsy turns the action off and waits for you to enable it again. Do not propose a wrapper such as run.sh that calls go run ./cmd/x: the Go code under it would change without asking you.

Go

  • One file (lang: go): harnsy runs go run <copy>.go with the action's directory as the working directory, so the project's go.mod applies: the script could import the project's packages, and a toolchain line may download a Go toolchain. Keep it to the standard library: harnsy pins the script, not what it imports (make try fails on any other import). Copy the few helper lines from actions/todo-count/main.go.
  • A program (command): cmd/<name>/main.go with its logic in internal/<name> and tests next to it, the contract through internal/action (FromEnv, Step, Fail, Result.Check, Env.Write). make build puts it in bin/.

Checks

make check is what CI runs (.github/workflows/ci.yml), plus golangci-lint (.golangci.yml) and shellcheck on the bash scripts. internal/def tests that every action in actions/ loads, passes the checks harnsy makes and runs with its defaults.

For agents

AGENTS.md is the guide for coding agents in this repository. Skills to write, test and propose an action are in .agents/skills/ (Codex, OpenCode) and .claude/skills/ (Claude Code).

Contract versions

The contract has a semantic version. ACTION_CONTRACT holds its major number; a script of major 1 keeps working on every 1.x harnsy. When the contract adds something (a minor version), update contract/ from your node's /docs/actions/ files.

License

MIT.

About

Template for harnsy Actions: a Go and a bash example, the contract, agent skills

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages