Hookbell
Notifies Slack — as a Claude Code hook, or as a general "notify me when this finishes" command for any piped output.
Advantage
A Claude Code hook script that only understands its own hook JSON payload
(transcript_path, hook_event_name, ...) can't double as a general-purpose notification command
for anything else you run — and a general-purpose command built without Claude Code in mind knows
nothing about its transcripts, so it can't report what the assistant actually said or which
permission it's waiting on. Maintaining one script per use case means duplicating the Slack-posting
logic each time.
Hookbell covers both with a single command: it inspects stdin and automatically picks the right behavior. A Claude Code hook payload gets reported through its referenced transcript; anything else is treated as free-form piped text and posted as-is.
Quickstart
uv tool install hookbell
Set the webhook URL from a Slack Incoming Webhook:
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/T000/B000/XXXX"
Pipe any text into hookbell to post it to Slack:
echo 'Hello world' | uvx hookbell
That posts this single Slack message:
Finished!
```
Hello world
```
Since any piped input gets wrapped the same way, piping a long-running command's own output into
hookbell delivers that output to Slack the moment the command is done, without having to watch the
terminal for it — redirecting stderr into stdout matters here, since build tools like
docker compose build write their progress there:
docker compose build 2>&1 | uvx hookbell
When the command's output isn't worth forwarding and only knowing it ended matters, chaining with
; instead leaves hookbell's stdin empty, so it just posts Finished! on its own:
docker compose build; uvx hookbell
How do I...
How do I use hookbell as a Claude Code hook?
Point Claude Code's Notification, PermissionRequest, and Stop hooks at hookbell in
settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [{ "type": "command", "command": "uvx hookbell" }]
}
]
}
}
Hookbell recognizes a Claude Code hook payload by its transcript_path field and posts a message
built from that transcript: the assistant's own last message when there is one, or a description of
the pending permission request otherwise. A failed notification here is logged (see slack.log)
rather than raised, so a flaky network never turns into hook-failure noise.
How do I use hookbell in a Docker container?
Hookbell checks /run/secrets/slack_webhook_url before falling back to SLACK_WEBHOOK_URL, so a
Docker secret keeps the webhook URL out of the container's environment and image layers entirely.
With Compose, write the URL to a local file Compose reads at build/run time, and mount it as a
secret named slack_webhook_url so Docker places it at that exact path:
services:
app:
secrets:
- slack_webhook_url
secrets:
slack_webhook_url:
file: ./slack_webhook_url.txt
Credits
This package was created with Cookiecutter and the yukihiko-shinoda/cookiecutter-pypackage project template.
Metadata
Release files for hookbell 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hookbell-0.2.0.tar.gz | 19.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hookbell-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 34.2 kB
Release files / hookbell-0.2.0.tar.gz
| Download URL | hookbell-0.2.0.tar.gz |
|---|---|
| Size | 19.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
da72993772128ee476278fa05da23554e8df8c801dc017da4d51c0aaef8d0689
|
|
BLAKE2b-256 checksum How to use checksums |
b678f315558f78336365ac3f7610d8124b65b36d052802ab8b3f513b23cdca1c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / hookbell-0.2.0-py3-none-any.whl
| Download URL | hookbell-0.2.0-py3-none-any.whl |
|---|---|
| Size | 14.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
993b7dfed107c7802a689b1418198d3aaae6efd7b29e4bc9408af53c63f0e95f
|
|
BLAKE2b-256 checksum How to use checksums |
abc4fcc49df4b045035746456ab083ba7f6980dca96d8761a97369e60c9894be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|