kiri

A local-first, git-based workflow orchestrator for personal automation.

Drop YAML workflows into workflows/, run kiri in the same directory, and trigger them from a feed-first UI on local.kiri.build.

Install

Download the macOS ARM64 binary from the latest release and put it on your $PATH:

chmod +x ~/Downloads/kiri
mv ~/Downloads/kiri /usr/local/bin/kiri
kiri --help

Building from source? See CONTRIBUTING.md.

Quick start

Kiri runs per-directory. Each working directory is its own workspace.

cd ~/projects/some-workspace
kiri init    # scaffold workflows/, scripts/, prompts/
kiri         # boot on :4242

Then open local.kiri.build and click Run on the example workflow.

Workflows

Workflows live in workflows/*.yaml. Each has a name and one or more steps:

name: hello
steps:
  - sh: echo "hello world"

Edits are picked up live — no restart needed. The activity feed on local.kiri.build is where runs surface.

Inline shell — sh:

The simplest step. The string runs under sh -c with a scoped env. Useful for quick scripts that don't warrant a bundle:

name: open-prs
steps:
  - sh: |
      cd "$KIRI_REPO_ROOT"
      gh pr list --state open

Bundle — use:

A reference to scripts/<name>/run.sh in your repo. Bundles are reusable, version-controlled scripts. The env: map sets per-step variables (keys starting with KIRI_ are reserved):

name: self-review
steps:
  - use: claude-code
    env:
      PROMPT_FILE: prompts/self-review.tpl
      MAX_TURNS: "8"

The shipped claude-code bundle reads a prompt template, renders {{VAR}} placeholders from the environment, and spawns the Claude Code CLI.

Piping output between steps

Each step's stdout is piped into the next step's stdin. The first step receives empty stdin. sh: steps can read it with cat; bundles often slurp it into a variable themselves — the shipped claude-code bundle does export KIRI_INPUT="$(cat)" so prompt templates can reference {{KIRI_INPUT}}.

name: greet
steps:
  - sh: echo "Lee"
  - sh: |
      name=$(cat)
      echo "hello, $name"

Step environment

Every step runs in a fresh per-run scratch directory with a scoped env. User env: values apply first; the variables below overwrite on collision, so a workflow can't redirect PATH or shadow kiri's identity vars.

KIRI_RUN_ID
The run's ID.
KIRI_STEP_INDEX
Zero-based index of this step in the workflow.
KIRI_REPO_ROOT
Absolute path of the repo kiri is running against. Steps run in a scratch dir, so use this to reach the repo (cd "$KIRI_REPO_ROOT").
KIRI_META_FILE
Path to a JSON file the step can write to attach meta (cost, tokens, anything) to the run envelope.
KIRI_BUNDLE_DIR
Absolute path to scripts/<name>/. Set only on use: steps. Useful for sourcing sidecar files shipped with the bundle.
PATH, HOME, USER, LOGNAME
Passed through from the kiri process so tools that authenticate as you (Keychain, ssh-agent, gpg) keep working.

Keys starting with KIRI_ in your workflow's env: map are rejected at load time — the prefix is reserved.

Summaries — summarize:

An optional post-run step whose stdout becomes the run's one-or-two-sentence feed-entry summary. Same shape as a step. Kiri ships a claude-code-summarizer bundle with the prompt baked in — drop it in and forget about it:

name: hello
steps:
  - sh: echo "hello world"
summarize:
  use: claude-code-summarizer

Summarize steps additionally receive KIRI_RUN_CONTEXT_FILE — a path to a JSON dump of the run envelope (workflow name, status, duration, per-step kind / status / duration / stdout / stderr / error). The summarizer formats this into its prompt.

Trust model

Kiri runs scripts with your user's permissions. scripts/<name>/run.sh and inline sh: steps are shell scripts you wrote into your own repo — kiri does not sandbox them. Treat them like any shell script you'd run yourself: read it before you use it.

The defences kiri does provide are external: the HTTP API binds to 127.0.0.1 only and requires a custom X-Kiri-Client header on state-changing requests, so other browser tabs and arbitrary LAN clients can't trigger workflow runs.