Run builds and tests
greppy bash-smart runs a build, test or lint command unchanged and keeps its exit code. You get back a one-line verdict, the error and warning blocks and a log whose long stretches are folded instead of printed in full. Your agent sees what matters first, and nothing is lost: the raw output stays retrievable.
greppy bash-smart -- cargo test
greppy bash-smart -- npm run build
greppy bash-smart -e 'panicked at' -- cargo test
The agent prompt tells agents to run every build, test or lint command this way. See Agent setup.
A test run with one failure
The example is a small Python project with 380 tests, one of which fails: slugify("Rust & Python") returns rust---python instead of rust-python.
greppy bash-smart -- python3 -m unittest -v
FAILED — exit 1: 4 errors, 0 warnings
383 FAIL: test_slug_07 (tests.test_textkit.SlugifyTest.test_slug_07)
…
392 AssertionError: 'rust---python' != 'rust-python'
401 FAILED (failures=1)
… partial output — raw log "/var/folders/hf/x8jpryjx6xs8k0kwmkj5spkw0000gn/T/greppy-bash-smart-501/5460-1791407870089721000.stdout"; read with greppy read-file
test_clamp_00 (tests.test_textkit.ClampTest.test_clamp_00) ... ok
…
test_clamp_109 (tests.test_textkit.ClampTest.test_clamp_109) ... ok
… lines 22-320 (299 collapsed lines) — raw log "/var/folders/hf/x8jpryjx6xs8k0kwmkj5spkw0000gn/T/greppy-bash-smart-501/5460-1791407870089721000.stderr"; read with greppy read-file
test_slug_00 (tests.test_textkit.SlugifyTest.test_slug_00) ... ok
… lines 322-327 (6 collapsed lines) — raw log "/var/folders/hf/x8jpryjx6xs8k0kwmkj5spkw0000gn/T/greppy-bash-smart-501/5460-1791407870089721000.stderr"; read with greppy read-file
328:test_slug_07 (tests.test_textkit.SlugifyTest.test_slug_07) ... FAIL
test_slug_08 (tests.test_textkit.SlugifyTest.test_slug_08) ... ok
… lines 330-341 (12 collapsed lines) — raw log "/var/folders/hf/x8jpryjx6xs8k0kwmkj5spkw0000gn/T/greppy-bash-smart-501/5460-1791407870089721000.stderr"; read with greppy read-file
…
Ran 380 tests in 0.006s
FAILED (failures=1)
The verdict and the error block come first, each line with its number in the raw output. Then comes the log: the start, the folded stretches, the highlighted line 328 with the failing test and the full tail with traceback and summary. The exit code is the one from unittest, here 1.
Get folded lines back
Every fold tells you how to get to its lines. With expansion storage, it ends with — greppy expand ID, and greppy expand ID prints the stored raw lines page by page, targeted with --offset and --limit. In the run above, the expansion storage was busy; the fold then names the raw log file, and greppy read-file fetches the lines.
greppy read-file /var/folders/hf/x8jpryjx6xs8k0kwmkj5spkw0000gn/T/greppy-bash-smart-501/5460-1791407870089721000.stderr --lines 320:329
/private/var/folders/hf/x8jpryjx6xs8k0kwmkj5spkw0000gn/T/greppy-bash-smart-501/5460-1791407870089721000.stderr:320-329
test_clamp_99 (tests.test_textkit.ClampTest.test_clamp_99) ... ok
test_slug_00 (tests.test_textkit.SlugifyTest.test_slug_00) ... ok
test_slug_01 (tests.test_textkit.SlugifyTest.test_slug_01) ... ok
test_slug_02 (tests.test_textkit.SlugifyTest.test_slug_02) ... ok
test_slug_03 (tests.test_textkit.SlugifyTest.test_slug_03) ... ok
test_slug_04 (tests.test_textkit.SlugifyTest.test_slug_04) ... ok
test_slug_05 (tests.test_textkit.SlugifyTest.test_slug_05) ... ok
test_slug_06 (tests.test_textkit.SlugifyTest.test_slug_06) ... ok
test_slug_07 (tests.test_textkit.SlugifyTest.test_slug_07) ... FAIL
test_slug_08 (tests.test_textkit.SlugifyTest.test_slug_08) ... ok
Raw stdout and stderr stay byte-exact: the preview never replaces the capture, and a failing command stays a failure. The full rules are in the display contract.
The verdict
Line 1 is the verdict, with the exit code and the counted error and warning blocks:
ok — exit 0
FAILED — exit 1: 1 error, 0 warnings
FAILED — exit 1: 4 errors, 0 warnings
Then come the error and warning blocks and every line that matches an -e REGEX. The rest becomes compact and retrievable. The exit code of bash-smart is the exit code of the command, so scripts and CI treat it exactly like the original.
What you see
| Output | Display |
|---|---|
| Short output (up to 80 lines in total) | once after the verdict, on its original stdout or stderr stream; the diagnostics are not repeated before it, and their count is in the verdict |
| Longer output | error and warning blocks first, then the log with folded stretches; every fold names greppy expand ID or the raw log file |
| A single line over 4,096 bytes | a preview of the first 2,048 and the last 1,024 bytes plus a notice about the omitted bytes and the raw log file |
| Timeouts and interrupted output | keep their diagnostic prefix and their recovery pack |
Highlight lines
-e REGEX lifts every matching line of a long log above the folded output, with its line number. For several patterns, you repeat it:
greppy bash-smart -e 'test_clamp_150' -e '^Ran ' -- python3 -m unittest -v
FAILED — exit 1: 4 errors, 0 warnings
383 FAIL: test_slug_07 (tests.test_textkit.SlugifyTest.test_slug_07)
…
401 FAILED (failures=1)
67 test_clamp_150 (tests.test_textkit.ClampTest.test_clamp_150) ... ok
399 Ran 380 tests in 0.005s
…
Commands, environment and pipelines
bash-smart runs the argv after -- directly.
| Input | Behavior |
|---|---|
leading VAR=value tokens |
become environment variables of the child process |
| shell builtins and operators | you pass them as one quoted expression; greppy refuses unquoted shell syntax with exit 64 and shows the quoted form |
| quoted expressions | run in Bash with pipefail, so a successful tail or grep does not mask a failing producer |
$ greppy bash-smart -- GREETING=hello python3 -c 'import os; print(os.environ["GREETING"])'
ok — exit 0
hello
$ greppy bash-smart -- cd tests
greppy: invalid request: bash-smart received unquoted shell syntax; pass the complete shell expression as one quoted argument, for example `greppy bash-smart -- "cd DIR && COMMAND"`
$ greppy bash-smart -- 'python3 -m unittest 2>&1 | tail -n 3'
FAILED — exit 1: 1 error, 0 warnings
Ran 380 tests in 0.009s
FAILED (failures=1)
The second command exits with code 64, the third with code 1, although tail itself succeeded.
Long-running commands
If a command prints nothing for 15 seconds, bash-smart writes a sign of life to stderr, and after that one per minute. It names the PID of the direct child process and the elapsed time. This way nobody mistakes an active cargo or test run for a dead wrapper while its output is being captured.
greppy bash-smart -- 'sleep 80; echo done'
bash-smart: command still running — pid=46907, elapsed=15s; child output is being captured and will be summarized on exit
bash-smart: command still running — pid=46907, elapsed=75s; child output is being captured and will be summarized on exit
ok — exit 0
done
When the index is busy
bash-smart does not wait for indexing. If an indexer is holding the repository's store, the command starts immediately, and the one thing skipped is the optional expansion storage. greppy reports this with bash-smart: index writer active; command execution continues without expansion storage; long output stays compact with raw-log paths instead of expand IDs. Folding keeps working and names the raw log file instead of an expand ID.
In the built-in agent
The built-in agent has exactly one tool, greppy, and runs its commands through it as bash-smart -- CMD. This way build output reaches the model in compact form.