# cursor-notifier
Discord webhook notifier for cursor-agent activity in tmux panes.
It polls tmux panes, identifies panes running cursor agent (by foreground TTY process names like `cursor-agent` or `node`), and sends a Discord message when activity transitions from active to idle (token counter disappears).
## Features
- Foreground process detection via pane TTY (robust to shells/multiplexers)
- Token counter detection with flexible regex (e.g. `12 tokens`, `12.3k tokens`)
- Discord webhook integration with error details
- Includes pane working directory and git branch in notifications
- `.env` support for configuration
- Binary entrypoint `cursor-notifier` when installed
## Install (from source)
```bash
# Clone and enter
git clone https://github.com/yourname/cursor-notifier.git
cd cursor-notifier
# Optional: use the provided runner
./run_notifier.sh --verbose --dry-run
# Or install locally in editable mode
python3 -m venv .venv
. .venv/bin/activate
pip install --upgrade pip
pip install -e .
```
## Configuration
You can configure via environment variables or a `.env` file in the project directory (loaded automatically if `python-dotenv` is installed).
- `CURSOR_NOTIFIER_WEBHOOK`: Discord webhook URL (required unless `--dry-run`)
- `CURSOR_NOTIFIER_INTERVAL`: Poll interval seconds (default: 4)
- `CURSOR_NOTIFIER_LINES`: Number of lines to scan from tmux buffer (default: 120)
- `CURSOR_NOTIFIER_PROCESSES`: Comma-separated executable names to treat as cursor agent (default: `cursor-agent,node`)
- `CURSOR_NOTIFIER_MISSES`: Consecutive misses before idling (default: 2)
Example `.env`:
```env
CURSOR_NOTIFIER_WEBHOOK=https://discord.com/api/webhooks/123/abc
CURSOR_NOTIFIER_PROCESSES=cursor-agent,node
CURSOR_NOTIFIER_INTERVAL=4
CURSOR_NOTIFIER_LINES=120
CURSOR_NOTIFIER_MISSES=2
```
## Usage
After installation, you can run the console script:
```bash
notifier-cursor --verbose
```
Or from source within this repo:
```bash
./run_notifier.sh --verbose
```
Useful flags:
- `--process-names`: Comma-separated executable names to monitor (e.g., `cursor-agent,node`)
- `--interval`: Polling interval seconds (default 4)
- `--lines`: How many recent lines to scan in each pane
- `--debug`: Log diagnostics for all panes
- `--verbose`: Print logs
- `--dry-run`: Do not send Discord messages
- `--test [message]`: Send a test message to the webhook and exit
Send a quick test:
```bash
notifier-cursor --test "hello from cursor-notifier"
```
## How it works
- Enumerates tmux panes with `tmux list-panes -a`
- For each pane, gets its TTY and uses `ps -t <tty> -o comm=` to list executable names
- If any of those names match your configured target names, the pane is considered a cursor agent pane
- It captures recent pane output and checks for a token counter; when it disappears, it sends a Discord message with the pane path and git branch
## Packaging and publishing to PyPI
This project is set up with `pyproject.toml` using setuptools and exposes an entrypoint `cursor-notifier`.
### Build
```bash
python3 -m pip install --upgrade build twine
python3 -m build
```
Artifacts will be in `dist/` (e.g., `cursor_notifier-0.1.0-py3-none-any.whl` and `.tar.gz`).
### Test upload to TestPyPI (recommended)
```bash
python3 -m twine upload --repository testpypi dist/*
# Then install to verify
python3 -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple cursor-notifier
```
### Upload to PyPI
```bash
python3 -m twine upload dist/*
```
You will need a PyPI account and API token. Store credentials securely, e.g., in `~/.pypirc`:
```ini
[distutils]
index-servers = pypi testpypi
[pypi]
repository = https://upload.pypi.org/legacy/
username = __token__
password = pypi-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```
## Troubleshooting
- 403 on webhook: ensure the webhook URL is correct, the webhook has channel access, and you are not using the Slack-compatible URL. Use `--test` to get detailed error info.
- No panes detected: confirm tmux is running and that `tmux list-panes -a` works. Ensure the target process names include the executable you run (e.g., add `node`).
- Git branch missing: the pane path must be inside a git repo.
## License
MIT
Raw data
{
"_id": null,
"home_page": null,
"name": "cursor-notifier",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "tmux, discord, webhook, cursor-agent, notifier",
"author": null,
"author_email": "Daniel Bowen <dnielbowen@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/9f/59/02b7ded9b36053481b10b1bbc0d13e65c7d64eca459f316274b7a04b9520/cursor_notifier-0.1.2.tar.gz",
"platform": null,
"description": "# cursor-notifier\n\nDiscord webhook notifier for cursor-agent activity in tmux panes.\n\nIt polls tmux panes, identifies panes running cursor agent (by foreground TTY process names like `cursor-agent` or `node`), and sends a Discord message when activity transitions from active to idle (token counter disappears).\n\n## Features\n\n- Foreground process detection via pane TTY (robust to shells/multiplexers)\n- Token counter detection with flexible regex (e.g. `12 tokens`, `12.3k tokens`)\n- Discord webhook integration with error details\n- Includes pane working directory and git branch in notifications\n- `.env` support for configuration\n- Binary entrypoint `cursor-notifier` when installed\n\n## Install (from source)\n\n```bash\n# Clone and enter\ngit clone https://github.com/yourname/cursor-notifier.git\ncd cursor-notifier\n\n# Optional: use the provided runner\n./run_notifier.sh --verbose --dry-run\n\n# Or install locally in editable mode\npython3 -m venv .venv\n. .venv/bin/activate\npip install --upgrade pip\npip install -e .\n```\n\n## Configuration\n\nYou can configure via environment variables or a `.env` file in the project directory (loaded automatically if `python-dotenv` is installed).\n\n- `CURSOR_NOTIFIER_WEBHOOK`: Discord webhook URL (required unless `--dry-run`)\n- `CURSOR_NOTIFIER_INTERVAL`: Poll interval seconds (default: 4)\n- `CURSOR_NOTIFIER_LINES`: Number of lines to scan from tmux buffer (default: 120)\n- `CURSOR_NOTIFIER_PROCESSES`: Comma-separated executable names to treat as cursor agent (default: `cursor-agent,node`)\n- `CURSOR_NOTIFIER_MISSES`: Consecutive misses before idling (default: 2)\n\nExample `.env`:\n\n```env\nCURSOR_NOTIFIER_WEBHOOK=https://discord.com/api/webhooks/123/abc\nCURSOR_NOTIFIER_PROCESSES=cursor-agent,node\nCURSOR_NOTIFIER_INTERVAL=4\nCURSOR_NOTIFIER_LINES=120\nCURSOR_NOTIFIER_MISSES=2\n```\n\n## Usage\n\nAfter installation, you can run the console script:\n\n```bash\nnotifier-cursor --verbose\n```\n\nOr from source within this repo:\n\n```bash\n./run_notifier.sh --verbose\n```\n\nUseful flags:\n\n- `--process-names`: Comma-separated executable names to monitor (e.g., `cursor-agent,node`)\n- `--interval`: Polling interval seconds (default 4)\n- `--lines`: How many recent lines to scan in each pane\n- `--debug`: Log diagnostics for all panes\n- `--verbose`: Print logs\n- `--dry-run`: Do not send Discord messages\n- `--test [message]`: Send a test message to the webhook and exit\n\nSend a quick test:\n\n```bash\nnotifier-cursor --test \"hello from cursor-notifier\"\n```\n\n## How it works\n\n- Enumerates tmux panes with `tmux list-panes -a`\n- For each pane, gets its TTY and uses `ps -t <tty> -o comm=` to list executable names\n- If any of those names match your configured target names, the pane is considered a cursor agent pane\n- It captures recent pane output and checks for a token counter; when it disappears, it sends a Discord message with the pane path and git branch\n\n## Packaging and publishing to PyPI\n\nThis project is set up with `pyproject.toml` using setuptools and exposes an entrypoint `cursor-notifier`.\n\n### Build\n\n```bash\npython3 -m pip install --upgrade build twine\npython3 -m build\n```\n\nArtifacts will be in `dist/` (e.g., `cursor_notifier-0.1.0-py3-none-any.whl` and `.tar.gz`).\n\n### Test upload to TestPyPI (recommended)\n\n```bash\npython3 -m twine upload --repository testpypi dist/*\n# Then install to verify\npython3 -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple cursor-notifier\n```\n\n### Upload to PyPI\n\n```bash\npython3 -m twine upload dist/*\n```\n\nYou will need a PyPI account and API token. Store credentials securely, e.g., in `~/.pypirc`:\n\n```ini\n[distutils]\nindex-servers = pypi testpypi\n\n[pypi]\n repository = https://upload.pypi.org/legacy/\n username = __token__\n password = pypi-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\n[testpypi]\n repository = https://test.pypi.org/legacy/\n username = __token__\n password = pypi-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n```\n\n## Troubleshooting\n\n- 403 on webhook: ensure the webhook URL is correct, the webhook has channel access, and you are not using the Slack-compatible URL. Use `--test` to get detailed error info.\n- No panes detected: confirm tmux is running and that `tmux list-panes -a` works. Ensure the target process names include the executable you run (e.g., add `node`).\n- Git branch missing: the pane path must be inside a git repo.\n\n## License\n\nMIT\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Discord webhook notifier for cursor-agent activity in tmux panes",
"version": "0.1.2",
"project_urls": {
"Homepage": "https://github.com/dnielbowen/cursor-notifier",
"Issues": "https://github.com/dnielbowen/cursor-notifier/issues"
},
"split_keywords": [
"tmux",
" discord",
" webhook",
" cursor-agent",
" notifier"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "e4be4248c3c49f7ce714a4eba215992cb722a045415437f37fbbc80f06c3d395",
"md5": "a8205a4b6088e219e71e8a94e7061e94",
"sha256": "c54d142404ae43c940ae76195d0e1210489216abfd36a74b39d69894e121f97c"
},
"downloads": -1,
"filename": "cursor_notifier-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a8205a4b6088e219e71e8a94e7061e94",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 9854,
"upload_time": "2025-08-24T01:45:26",
"upload_time_iso_8601": "2025-08-24T01:45:26.850842Z",
"url": "https://files.pythonhosted.org/packages/e4/be/4248c3c49f7ce714a4eba215992cb722a045415437f37fbbc80f06c3d395/cursor_notifier-0.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "9f5902b7ded9b36053481b10b1bbc0d13e65c7d64eca459f316274b7a04b9520",
"md5": "8b77e4825853ccfd50ac10712aa4184b",
"sha256": "e3fbfaa5eda3d74645289ef7fb7c1e51d84ed78a4447ba04f989f35b5f8cafbb"
},
"downloads": -1,
"filename": "cursor_notifier-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "8b77e4825853ccfd50ac10712aa4184b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 9309,
"upload_time": "2025-08-24T01:45:27",
"upload_time_iso_8601": "2025-08-24T01:45:27.700961Z",
"url": "https://files.pythonhosted.org/packages/9f/59/02b7ded9b36053481b10b1bbc0d13e65c7d64eca459f316274b7a04b9520/cursor_notifier-0.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-24 01:45:27",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "dnielbowen",
"github_project": "cursor-notifier",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "python-dotenv",
"specs": [
[
">=",
"1.0"
]
]
}
],
"lcname": "cursor-notifier"
}