behave
CLIツールの動作(stdout、stderr、終了コード)をゴールデンテストとして記録し、RustやGoで書き直した別の実装に対して再生します。CLIおよびMCPサーバーとして動作します。
ドキュメント
behave
If you find this useful, you can support me by getting a SkinToScan pack, my other project. It's a one-time purchase that directly funds my open-source work.
behave records how a command-line tool actually behaves, then replays those cases against another implementation. I built it for CLI compatibility testing: checking a Rust or Go port against the original, or catching a refactor that changed visible behaviour.
It treats the tool as a black box. It reads --help and the man page, generates cases (flags, flag pairs, listed values, odd stdin, missing files, bad values), records stdout, stderr and the exit code, and diffs a second binary against that recording.

I have used it on the Rust rewrites of GNU coreutils, findutils, sed and procps. So far that has led to 41 upstream bug reports and 48 fix PRs, 33 of them merged (as of 13 Nov 2026). The full list is in upstream findings.
Install
Homebrew (macOS and Linux), a binary from Releases, or go install:
brew install emirmrkaljevic/tap/behave
# or: go install github.com/emirmrkaljevic/behave/cmd/behave@latest
Example
Record GNU sort, check that the recording is stable, then compare a rewrite:
behave probe sort
behave run behave-sort
behave diff behave-sort --impl ./target/release/sort --skip-tag meta
--skip-tag meta drops --help and --version, which a rewrite is allowed to word differently.
A failing case looks like this (abridged):
✗ flag --output-delimiter, value -1, arg -f1 0135-flag-output-delimiter-value-1-arg-f1.yaml
$ cut --output-delimiter -1 -f1
exit:
exit: want 0, got 1
stdout:
- hello
flags with differences:
--output-delimiter 2/16 cases differ
flags fully compatible: 9 of 10
cut vs ./uu/cut: 172/175 passed, 3 failed, 0 skipped (differs in: exit 2, stdout 3) [0.1s]
The repo also ships ready-made specs under examples/. behave run examples/cut is a small GNU cut suite; behave diff examples/jq --impl gojq checks gojq against jq 1.7. The full list, with case counts, is in docs/examples.md.
For tools that need a positional argument, pass samples with --arg:
behave probe jq --arg . --arg '.a' --arg keys
Add a hand-written case with behave add behave-cut --stdin 'a,b' -- -d, -f2. The commands and spec format page covers the rest of the CLI, including coverage and JUnit output for CI.
MCP (optional)
behave mcp exposes the same probe, record and diff flow over stdio, if you want to drive it from an editor. Setup and tools are in docs/mcp.md.
Limitations
behave only runs the binaries you point it at, in a fresh temp directory. Do not probe tools that change things outside their working directory (package managers, rm -rf style tools) without --skip-flag and a sandbox.
Recordings are only as good as the original you pointed at. Record from the release the rewrite actually targets. GNU coreutils 9.12 and 9.7 already disagree on some tail -v headers. Cases that change between runs, such as sort --random-sort, are marked flaky and skipped. Some differences are deliberate design choices, not bugs.
Status: early (v0.1). The CLI works; feedback is welcome.
Docs
- Examples
- What it found upstream
- Commands, spec format and CI notes
- MCP server
- Guides: testing a rewrite, golden tests
MCP Registry name: io.github.emirmrkaljevic/behave
License
MIT