Pipe it to the agent: the Tempr CLI in scripts and CI

  • CLI

The quickest way to give an agent context is often the one you already use for everything else on the command line: a pipe. The output you're looking at, a diff, a build log, a list of commits, goes straight into the agent along with what you want done with it.

bash
git diff | tempr "Review this change for bugs and missing tests"

The Tempr CLI is built to fit into that kind of workflow. It reads stdin, writes to stdout, reports in JSON when you ask it to, and exits with a code a script can act on. Here are the patterns we use most, and what had to be right underneath for them to work.

The prompt says what, the pipe says what with

With a prompt argument, piped text follows the prompt as context. With no prompt, the piped text is the whole prompt, so you can write the instruction into the stream yourself:

bash
# Explain a failure from a build log
cat build.log | tempr "Why did this fail? Cite the relevant lines."

# Put the instruction in the stream
{ echo "Summarize these commits for the release notes:"; git log --oneline v1.2..; } | tempr

# A prompt too long or too quote-heavy for the command line
tempr --prompt-file task.md

A piped run takes one turn and exits, so it works in a script. For a single file, you don't need a pipe at all: @path in the prompt attaches it, as in tempr "add doc comments to @src/Payment/PaymentRequest.cs".

What we fixed in 0.9.11

Two things got in the way of this until CLI 0.9.11, released on October 5.

Piped input was ignored whenever there was a prompt. git diff | tempr "Review this" sent the prompt and dropped the diff, so the model reviewed nothing. Now the prompt goes first and the piped input after it, as it does in Claude Code and Codex. The CLI waits up to 3 seconds for piped input to start, so a slow git diff on a large repository still makes it. If nothing arrives, which happens when a parent process leaves stdin open without writing to it, the prompt runs on its own and the CLI notes that on stderr. Redirect stdin from /dev/null (NUL on Windows) to skip the wait.

Whitespace was being collapsed. Blank lines, indentation, and lines holding only spaces were squeezed into single spaces before the prompt reached the model. For prose that hardly matters. For a diff it matters a lot: lines ran together, and a review could report formatting problems that weren't in the code. Prompts now reach the model exactly as written, blank lines and indentation included, and that fix applies to tempr acp in editors too.

Asking it to act, not just answer

Piping is for context. When the agent should change something, give it the goal and let it work:

bash
tempr -y "fix the failing test in CartServiceTests and run the suite"

-y lets file edits and allow-listed commands run without stopping to ask. Commands that aren't on the allow-list, and anything that deletes, publishes or force-pushes, still need approval. When there's no terminal to approve them on, because input is piped, output is JSON, or the run is in CI, they're refused, and the model is told why so it can find another way or stop. You add the commands a job needs with tempr config allow-command <prefix>, or an .agentcommands.json file at the workspace root.

On Linux, -y also runs the agent's commands in a sandbox by default, built on the kernel's Landlock, with no install and no special privileges. Commands can change the checkout, temporary folders and package caches, and nothing else. --sandbox cuts off the network as well. If you ask for the sandbox and the machine can't provide it, nothing runs.

Output a script can read

--json turns the run into newline-delimited JSON: a first line describing the session, one line per event as the agent works, and a final result line.

bash
tempr --json -y "list the public types in this project" \
  | tail -n 1 \
  | jq '{status, summary, files: .filesChanged}'

The result line carries the final answer as summary, the files the agent changed, token usage, and a status. The exit code makes the same distinctions, so a script can branch on it without parsing anything:

  • 0, ok: the turn finished.
  • 1, error: something went wrong: the server, the network, a tool.
  • 2: the command line didn't make sense.
  • 3: not signed in, or the license has no plan.
  • 4, step-limit: the run hit its step limit with work left. --continue picks it up.
  • 5, tool-refused: a tool call was refused, usually because nothing could approve it.
  • 6, cost-limit: the run reached its price ceiling.
  • 7: you asked for the sandbox and it can't run on this machine, so nothing ran.

In CI

Put those together and an agent becomes a pipeline step. A pull-request review in GitHub Actions:

yaml
- env:
    TEMPR_LICENSE_KEY: ${{ secrets.TEMPR_LICENSE_KEY }}
  run: |
    { echo "Review this PR:"; git diff origin/main...; } \
      | tempr --json --max-cost 1.00 -m anthropic/claude-sonnet-4-6

A few habits make this reliable:

  • Keep the key in a secret. The CLI signs in from TEMPR_LICENSE_KEY, so the key never appears on a command line or in a log.
  • Name the model. -m keeps the run from depending on a default set on someone's machine.
  • Cap the spend. --max-cost puts a ceiling on the run, and exit code 6 tells the job it was reached.
  • Keep tasks read-only unless the workspace is disposable. Review, summarize and triage are safe anywhere; edits belong on a runner you can throw away.

CI systems set the CI variable, and the CLI notices: a tool call that would have asked for approval is refused instead of waiting forever, and there's no logo, update notice or crash report in your logs.

The full guide, with a complete workflow file, is Scripts, pipes and CI in the docs, and every flag is in the CLI reference. If you haven't installed the CLI yet, start on the CLI page.