# Shellhorn
**Get phone notifications when your long-running shell commands finish.** Perfect for ML training, builds, or any command you don't want to babysit.
[](https://github.com/mitchins/shellhorn/actions/workflows/ci.yml)
[](https://codecov.io/github/mitchins/shellhorn)
[](https://badge.fury.io/py/shellhorn)
[](https://ghcr.io/mitchins/shellhorn/monitor)
[](https://www.python.org/downloads/)
[](https://opensource.org/licenses/MIT)
[](https://github.com/psf/black)
```bash
# Install
pip install shellhorn
# Just prepend any command
shellhorn python3 my-training-script.py # Get notified when done
```
## Quick Setup
**Pushover (phone notifications):**
```bash
shellhorn config set notifications.pushover.app_token YOUR_TOKEN
shellhorn config set notifications.pushover.user_key YOUR_USER
shellhorn config set notifications.pushover.enabled true
```
**MQTT (centralized monitoring):**
```bash
shellhorn config set notifications.mqtt.broker_host mqtt.example.com
shellhorn config set notifications.mqtt.enabled true
```
**Test it:**
```bash
shellhorn config test # Sends test notification
```
## Usage
```bash
# Any command works - just prepend with shellhorn
shellhorn make build && make test
shellhorn ./deploy-script.sh
shellhorn python3 -m pytest --long-running-tests
shellhorn npm run build
```
**What you get:**
- Success notifications with duration
- Failure alerts with exit codes
- Orphaned process detection (via MQTT monitor)
- Works with pipes, redirects, and complex commands
- **Start notifications disabled by default** (you know when you started it!)
## More Options
<details>
<summary><b>Environment Variables</b> (alternative to config commands)</summary>
```bash
export SHELLHORN_PUSHOVER_TOKEN=your_app_token
export SHELLHORN_PUSHOVER_USER=your_user_key
export SHELLHORN_MQTT_BROKER=mqtt.example.com
```
</details>
<details>
<summary><b>CLI Override</b> (one-time config)</summary>
```bash
shellhorn --pushover-token=xxx --pushover-user=yyy python3 script.py
shellhorn --mqtt-broker=localhost python3 script.py
```
</details>
<details>
<summary><b>Config Commands</b></summary>
```bash
shellhorn config show # View current config
shellhorn config test # Test notifications
shellhorn --version # Show version
# Notification preferences (start notifications off by default)
shellhorn config set preferences.notify_start true # Enable start notifications
shellhorn config set preferences.notify_success false # Disable success notifications
```
</details>
<details>
<summary><b>MQTT Details</b></summary>
**Topics:**
- `shellhorn/start` - Command started
- `shellhorn/complete` - Command finished
- `shellhorn/error` - Unexpected errors
- `shellhorn/interrupt` - Interrupted (Ctrl+C)
**Message format:**
```json
{
"command": "python3 script.py",
"status": "success",
"duration": 123.45,
"client_id": "shellhorn_123456789"
}
```
</details>
<details>
<summary><b>Monitor (Centralized Alerts)</b></summary>
Deploy the monitor to get alerts when hosts disconnect unexpectedly:
```bash
# Quick start with Docker (from GitHub Container Registry)
docker run -d --name shellhorn-monitor \
-e MQTT_BROKER=192.168.1.100 \
-e PUSHOVER_TOKEN=xxx -e PUSHOVER_USER=yyy \
ghcr.io/mitchins/shellhorn/monitor:latest
# Or with config file (YAML)
docker run -d -v ./monitor.yaml:/config/monitor.yaml \
ghcr.io/mitchins/shellhorn/monitor:latest
```
**Perfect for detecting lost commands** when machines shut down or disconnect. See `monitor/` directory for full setup.
</details>
---
*Perfect for ML training, CI/CD pipelines, data processing, or any command you don't want to babysit. The name "Shellhorn" comes from **shell** (command wrapper) + **horn** (notification alerts) 🐚📯*
Raw data
{
"_id": null,
"home_page": null,
"name": "shellhorn",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "shell, command, wrapper, notification, monitoring, mqtt, pushover",
"author": "Mitchell Currie",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/11/e2/3e1177f166c362e131ff704349148438cb784cd8a5861f9cb4f9bb49731b/shellhorn-1.0.0.tar.gz",
"platform": null,
"description": "# Shellhorn\n\n**Get phone notifications when your long-running shell commands finish.** Perfect for ML training, builds, or any command you don't want to babysit.\n\n[](https://github.com/mitchins/shellhorn/actions/workflows/ci.yml)\n[](https://codecov.io/github/mitchins/shellhorn)\n[](https://badge.fury.io/py/shellhorn)\n[](https://ghcr.io/mitchins/shellhorn/monitor)\n[](https://www.python.org/downloads/)\n[](https://opensource.org/licenses/MIT)\n[](https://github.com/psf/black)\n\n```bash\n# Install\npip install shellhorn\n\n# Just prepend any command\nshellhorn python3 my-training-script.py # Get notified when done\n```\n\n## Quick Setup\n\n**Pushover (phone notifications):**\n```bash\nshellhorn config set notifications.pushover.app_token YOUR_TOKEN\nshellhorn config set notifications.pushover.user_key YOUR_USER \nshellhorn config set notifications.pushover.enabled true\n```\n\n**MQTT (centralized monitoring):**\n```bash\nshellhorn config set notifications.mqtt.broker_host mqtt.example.com\nshellhorn config set notifications.mqtt.enabled true\n```\n\n**Test it:**\n```bash\nshellhorn config test # Sends test notification\n```\n\n## Usage\n\n```bash\n# Any command works - just prepend with shellhorn\nshellhorn make build && make test\nshellhorn ./deploy-script.sh\nshellhorn python3 -m pytest --long-running-tests\nshellhorn npm run build\n\n```\n\n**What you get:**\n- Success notifications with duration\n- Failure alerts with exit codes \n- Orphaned process detection (via MQTT monitor)\n- Works with pipes, redirects, and complex commands\n- **Start notifications disabled by default** (you know when you started it!)\n\n## More Options\n\n<details>\n<summary><b>Environment Variables</b> (alternative to config commands)</summary>\n\n```bash\nexport SHELLHORN_PUSHOVER_TOKEN=your_app_token\nexport SHELLHORN_PUSHOVER_USER=your_user_key\nexport SHELLHORN_MQTT_BROKER=mqtt.example.com\n```\n</details>\n\n<details>\n<summary><b>CLI Override</b> (one-time config)</summary>\n\n```bash\nshellhorn --pushover-token=xxx --pushover-user=yyy python3 script.py\nshellhorn --mqtt-broker=localhost python3 script.py\n```\n</details>\n\n<details>\n<summary><b>Config Commands</b></summary>\n\n```bash\nshellhorn config show # View current config\nshellhorn config test # Test notifications\nshellhorn --version # Show version\n\n# Notification preferences (start notifications off by default)\nshellhorn config set preferences.notify_start true # Enable start notifications\nshellhorn config set preferences.notify_success false # Disable success notifications\n```\n</details>\n\n<details>\n<summary><b>MQTT Details</b></summary>\n\n**Topics:**\n- `shellhorn/start` - Command started\n- `shellhorn/complete` - Command finished\n- `shellhorn/error` - Unexpected errors \n- `shellhorn/interrupt` - Interrupted (Ctrl+C)\n\n**Message format:**\n```json\n{\n \"command\": \"python3 script.py\",\n \"status\": \"success\", \n \"duration\": 123.45,\n \"client_id\": \"shellhorn_123456789\"\n}\n```\n</details>\n\n<details>\n<summary><b>Monitor (Centralized Alerts)</b></summary>\n\nDeploy the monitor to get alerts when hosts disconnect unexpectedly:\n\n```bash\n# Quick start with Docker (from GitHub Container Registry)\ndocker run -d --name shellhorn-monitor \\\n -e MQTT_BROKER=192.168.1.100 \\\n -e PUSHOVER_TOKEN=xxx -e PUSHOVER_USER=yyy \\\n ghcr.io/mitchins/shellhorn/monitor:latest\n\n# Or with config file (YAML)\ndocker run -d -v ./monitor.yaml:/config/monitor.yaml \\\n ghcr.io/mitchins/shellhorn/monitor:latest\n```\n\n**Perfect for detecting lost commands** when machines shut down or disconnect. See `monitor/` directory for full setup.\n</details>\n\n---\n\n*Perfect for ML training, CI/CD pipelines, data processing, or any command you don't want to babysit. The name \"Shellhorn\" comes from **shell** (command wrapper) + **horn** (notification alerts) \ud83d\udc1a\ud83d\udcef*\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "\ud83d\udc1a\ud83d\udcef A lightweight command wrapper that sounds the horn when your shell commands complete, fail, or die unexpectedly",
"version": "1.0.0",
"project_urls": {
"Homepage": "https://github.com/mitchins/shellhorn",
"Issues": "https://github.com/mitchins/shellhorn/issues",
"Repository": "https://github.com/mitchins/shellhorn"
},
"split_keywords": [
"shell",
" command",
" wrapper",
" notification",
" monitoring",
" mqtt",
" pushover"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "5093ebe4157ad0555468db9f7c04f5fb6a1b63874b9999fe2c1341d30b04c214",
"md5": "9b3ecd6c96e7b6344cad15d57ac1cac6",
"sha256": "aad7137be363ce17a7be9c4cc7fd8b656ca220ec5003ca68646ef3e2f2c0ec4a"
},
"downloads": -1,
"filename": "shellhorn-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "9b3ecd6c96e7b6344cad15d57ac1cac6",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 26564,
"upload_time": "2025-08-26T09:33:08",
"upload_time_iso_8601": "2025-08-26T09:33:08.329399Z",
"url": "https://files.pythonhosted.org/packages/50/93/ebe4157ad0555468db9f7c04f5fb6a1b63874b9999fe2c1341d30b04c214/shellhorn-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "11e23e1177f166c362e131ff704349148438cb784cd8a5861f9cb4f9bb49731b",
"md5": "58495429fb9dc6e3d0a7750c6b9a3b20",
"sha256": "c4ba8bb1c8a16de44c4fc6a135dac01fd1e16f8fc7c10de3d3a95a22e1bd5bb8"
},
"downloads": -1,
"filename": "shellhorn-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "58495429fb9dc6e3d0a7750c6b9a3b20",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 26191,
"upload_time": "2025-08-26T09:33:09",
"upload_time_iso_8601": "2025-08-26T09:33:09.323728Z",
"url": "https://files.pythonhosted.org/packages/11/e2/3e1177f166c362e131ff704349148438cb784cd8a5861f9cb4f9bb49731b/shellhorn-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-26 09:33:09",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "mitchins",
"github_project": "shellhorn",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "shellhorn"
}