A five-minute build runs. The agent waits. You wait. Nothing else happens — and you are billed for all of it twice: your hour, and the tokens burning in a session doing nothing.
No single wait is worth stopping for. It is their sum that costs, and the sum is invisible until something counts it.
$ jbx gain
jbx gain — since the beginning
commands wrapped 142
of those, detached 11 (7.7% of them)
they took 3h04m
you stood still 35m12s
given back 2h29m ███████████████████░░░░░ 81.0%
project calls detached elapsed waited saved impact
acme 142 11 3h04m 35m12s 2h29m (81%) ██████████
api 96 7 2h11m 18m03s 1h53m (86%) ████████░░
front 34 4 39m 3m17s 36m (92%) ██░░░░░░░░
last hour 6 calls · 1 detached · 8m12s saved (73%)
last day 38 calls · 4 detached · 40m05s saved (77%)
all 142 calls · 11 detached · 2h29m saved (81%)
That is one week. Put your own rate on it.
JobBox wraps every command your agent runs. The quick ones come back untouched — output as written, exit code unchanged, as though nothing were there. The slow ones detach themselves, say so, and tell whoever needs to know when they end.
Nobody judges in advance which is which. That judgement is the thing everybody gets wrong, so JobBox does not make it: it runs the line and finds out.
One binary. Rust, serde_json, nothing else. Linux, macOS, Windows.
$ jbx hook --list
claude Bash PreToolUse ~/.claude/settings.json
gemini run_shell_command BeforeTool ~/.gemini/settings.json
droid Execute PreToolUse declare by hand
cursor Shell preToolUse declare by hand (no unasked endings)
copilot bash preToolUse declare by hand
They agree on almost nothing — not the name of the shell tool, not the
event, not the shape of the answer, not the file it is declared in.
jbx init handles the first two; the rest take one block of JSON, and
CLI-AI.md has the exact block for each, along with the
two it deliberately does not support and why.
As a Claude Code plugin — the hooks, the binary and a background watch in one thing:
$ claude plugin marketplace add quazardous/jobbox
$ claude plugin install jbx@jobbox
Or as a command, anywhere:
$ curl -fsSL https://raw.githubusercontent.com/quazardous/jobbox/main/install.sh | sh
It checks the download against the sums published with the release, puts
it on your PATH, and asks before declaring its hooks — they go in a
settings file other tools share. On Windows, irm https://raw.githubusercontent.com/quazardous/jobbox/main/install.ps1 | iex, and WINDOWS.md has the rest.
Run jbx init as well if you have rtk.
A plugin declares hooks; it cannot displace somebody else's, and two
hooks rewriting one field is a race no harness documents. init settles
that by calling rtk itself.

Nothing changes — until something is slow:
$ npm run build
> building…
jbx: this passed 30s, so it is in the BACKGROUND as j7f3a91c — nothing lost.
DO NOT WAIT FOR IT, DO SOMETHING ELSE. With nothing else: Monitor
`jbx wait j7f3a91c`, which ends when the job does.
jbx help j7f3a91c
The build output arrived as it was written, not replayed at the end.
The ending reaches you two ways, and one is better. Left alone, it is
announced on the next turn — free, and it costs that delay. Monitored, it
arrives the moment it happens: jbx wait <id> ends exactly when the job
does, so anything watching it is woken then. jbx watch does that for
every job at once, one line each.
That is the difference between waiting and being told. Polling is neither — it is waiting with extra steps.
The old answer to "when should this go to the background?" was a document telling an agent to estimate how long a command would take. Agents get that wrong, and so do people. jbx removes the question and leaves a smaller one:
Do you need this result before you can do anything else?
Almost always, no. When the answer is yes, say so — jbx fg -- '<line>'
runs without ever letting go, and jbx gain counts what that cost.
saved is
counted and why it is a ceiling, and what was deliberately left out.Made with simai-cli, from demo/detach.txt.
MIT — see LICENSE.
127 commits
Hacker News (1)
Rust
93.3%
PowerShell
3.6%
Shell
3.2%
A five-minute build runs. The agent waits. You wait. Nothing else happens — and you are billed for all of it twice: your hour, and the tokens burning in a session doing nothing.
No single wait is worth stopping for. It is their sum that costs, and the sum is invisible until something counts it.
$ jbx gain
jbx gain — since the beginning
commands wrapped 142
of those, detached 11 (7.7% of them)
they took 3h04m
you stood still 35m12s
given back 2h29m ███████████████████░░░░░ 81.0%
project calls detached elapsed waited saved impact
acme 142 11 3h04m 35m12s 2h29m (81%) ██████████
api 96 7 2h11m 18m03s 1h53m (86%) ████████░░
front 34 4 39m 3m17s 36m (92%) ██░░░░░░░░
last hour 6 calls · 1 detached · 8m12s saved (73%)
last day 38 calls · 4 detached · 40m05s saved (77%)
all 142 calls · 11 detached · 2h29m saved (81%)
That is one week. Put your own rate on it.
JobBox wraps every command your agent runs. The quick ones come back untouched — output as written, exit code unchanged, as though nothing were there. The slow ones detach themselves, say so, and tell whoever needs to know when they end.
Nobody judges in advance which is which. That judgement is the thing everybody gets wrong, so JobBox does not make it: it runs the line and finds out.
One binary. Rust, serde_json, nothing else. Linux, macOS, Windows.
$ jbx hook --list
claude Bash PreToolUse ~/.claude/settings.json
gemini run_shell_command BeforeTool ~/.gemini/settings.json
droid Execute PreToolUse declare by hand
cursor Shell preToolUse declare by hand (no unasked endings)
copilot bash preToolUse declare by hand
They agree on almost nothing — not the name of the shell tool, not the
event, not the shape of the answer, not the file it is declared in.
jbx init handles the first two; the rest take one block of JSON, and
CLI-AI.md has the exact block for each, along with the
two it deliberately does not support and why.
As a Claude Code plugin — the hooks, the binary and a background watch in one thing:
$ claude plugin marketplace add quazardous/jobbox
$ claude plugin install jbx@jobbox
Or as a command, anywhere:
$ curl -fsSL https://raw.githubusercontent.com/quazardous/jobbox/main/install.sh | sh
It checks the download against the sums published with the release, puts
it on your PATH, and asks before declaring its hooks — they go in a
settings file other tools share. On Windows, irm https://raw.githubusercontent.com/quazardous/jobbox/main/install.ps1 | iex, and WINDOWS.md has the rest.
Run jbx init as well if you have rtk.
A plugin declares hooks; it cannot displace somebody else's, and two
hooks rewriting one field is a race no harness documents. init settles
that by calling rtk itself.

Nothing changes — until something is slow:
$ npm run build
> building…
jbx: this passed 30s, so it is in the BACKGROUND as j7f3a91c — nothing lost.
DO NOT WAIT FOR IT, DO SOMETHING ELSE. With nothing else: Monitor
`jbx wait j7f3a91c`, which ends when the job does.
jbx help j7f3a91c
The build output arrived as it was written, not replayed at the end.
The ending reaches you two ways, and one is better. Left alone, it is
announced on the next turn — free, and it costs that delay. Monitored, it
arrives the moment it happens: jbx wait <id> ends exactly when the job
does, so anything watching it is woken then. jbx watch does that for
every job at once, one line each.
That is the difference between waiting and being told. Polling is neither — it is waiting with extra steps.
The old answer to "when should this go to the background?" was a document telling an agent to estimate how long a command would take. Agents get that wrong, and so do people. jbx removes the question and leaves a smaller one:
Do you need this result before you can do anything else?
Almost always, no. When the answer is yes, say so — jbx fg -- '<line>'
runs without ever letting go, and jbx gain counts what that cost.
saved is
counted and why it is a ceiling, and what was deliberately left out.Made with simai-cli, from demo/detach.txt.
MIT — see LICENSE.
Hacker News (1)
127 commits
Rust
93.3%
PowerShell
3.6%
Shell
3.2%