Get Notified When a Long Terminal Command Finishes

Stop babysitting a 40-minute build. One line after any command sends the result to your phone — including when you already walked away.

Updated

Table of Contents

You start a training run, a full test suite, a docker build, or an AI coding agent working through a long task. Then you have two bad options: sit and watch a progress bar, or walk away and come back in twenty minutes to find it failed after ninety seconds.

There is a third option that takes one line.

The one-liner

Create a channel in Echobell, copy its webhook URL, and append this to whatever you are running:

pnpm build; curl -sS -X POST https://hook.echobell.one/t/YOUR_TOKEN \
  -H 'content-type: application/json' \
  -d '{"title":"build finished","body":"echobell-web"}'

Note the ; and not &&. With && the notification only fires when the command succeeds, which is exactly backwards — a failure is the case you most want to hear about.

Send the exit code too

A notification that says "finished" is only half the story. Capture the status:

pnpm build; s=$?; curl -sS -X POST https://hook.echobell.one/t/YOUR_TOKEN \
  -H 'content-type: application/json' \
  -d "{\"title\":\"build $([ $s -eq 0 ] && echo ok || echo FAILED)\",\"status\":\"$s\"}"

s=$? has to come immediately after the command — anything in between, including the [ test itself, overwrites $?.

Make it a shell function

Typing that every time defeats the purpose. Put this in ~/.zshrc or ~/.bashrc:

notify() {
  "$@"
  local status=$?
  curl -sS -X POST "$ECHOBELL_HOOK" \
    -H 'content-type: application/json' \
    -d "{\"command\":\"$*\",\"status\":\"$status\",\"host\":\"$(hostname -s)\"}" \
    >/dev/null
  return $status
}

Set ECHOBELL_HOOK in your shell profile — or better, keep it out of your dotfiles repo entirely and export it from a file you do not commit. Then:

notify pnpm test
notify cargo build --release
notify python train.py

return $status at the end matters: it keeps notify transparent, so notify make && ./deploy.sh still behaves the way you expect.

With that payload, the channel templates can be:

Title

{{command}} · {{status}}

Body

on {{host}}

Only ring me when it fails

Most runs succeed and you do not need to hear about them. Two channels handle this cleanly: a normal one for everything, and a calling one with a condition:

status != "0"

Point notify at the second channel for anything you would get out of bed for, and the successful runs stay silent.

Surviving a closed laptop and a dropped SSH session

If the command is running over SSH, closing your laptop kills the shell and the notification never fires. Two fixes:

tmux — start the command inside a session and detach:

tmux new -d -s build 'notify pnpm build'

nohup — for a one-off:

nohup bash -c 'notify pnpm build' >/dev/null 2>&1 &

Either way the process outlives your connection, and the notification arrives whether or not you are still attached.

AI coding agents

The same pattern covers a CLI agent working through a long task:

notify codex exec "refactor the payments module and run the tests"

For agents that pause mid-run and wait for a human — an approval gate, a permission prompt — a completion notification is the wrong tool, because the run has not completed. That case needs the agent's own hook or callback, and it is worth a phone call rather than a push. Turning agent approval gates into phone calls covers that, including Claude Code's Notification hook and its agent_needs_input matcher.

Use both: the hook for "I am stuck", the shell function for "I am done".

What not to put in the notification

Not the command output. It is tempting to pipe the last few lines of a failing build into the body. Resist it — build logs contain tokens, connection strings and customer data more often than people expect, and a notification body ends up on a lock screen. Send the exit code and go look at the terminal.

Not the channel URL, into a public dotfiles repo. Anyone with the URL can post to your channel. Keep it in an untracked file, and turn on POST Only so a stray link preview cannot fire it.

Frequently asked questions

Why not just use terminal-notifier or notify-send?

Those show a notification on the machine running the command. That works while you are sitting at it. The point of this setup is the case where you are not — a remote box, a closed laptop, a different room.

Does this work on Windows?

The pattern does; the syntax differs. In PowerShell, Invoke-RestMethod -Method Post -Uri $env:ECHOBELL_HOOK -ContentType application/json -Body $json is the equivalent, with $LASTEXITCODE in place of $?.

Can I get the notification on my watch?

Yes. Subscriptions deliver to a paired Apple Watch, which is genuinely the right form factor for "the build is done."

What if the command takes 12 hours?

Nothing in this setup times out — the curl runs whenever the command returns. Run it under tmux so a dropped connection does not take the process with it.

Can my teammates get the same notifications?

Yes. Share the channel and each subscriber picks their own notification type. Handy for a shared training box where more than one person cares that the GPU is free again.

Wrap-up

One shell function, one channel, and a condition if you only want the failures. It costs about two minutes to set up and it gives you back the twenty you spend watching progress bars.

Download Echobell for iPhone or get it on Google Play, then try it on something that takes long enough to walk away from.