A lightweight Herdr plugin that sends Telegram notifications when an OpenCode agent changes state.
The plugin is written in Go and supports macOS and Linux.
- Detects OpenCode agent status changes through Herdr.
- Sends Telegram notifications when an agent:
- finishes (
done) - becomes blocked (
blocked)
- finishes (
- Includes:
- project
- workspace
- agent
- execution duration
- latest assistant response
- Retrieves only the latest assistant text from the OpenCode session.
- Excludes MCP activity, tool calls, tool results, reasoning and other OpenCode metadata.
- Resolves the OpenCode session from the Herdr pane working directory.
- Selects the most recently updated session for that directory.
- Uses
opencode exportto retrieve the session. - Writes the export directly to a temporary file to avoid stdout pipe truncation.
- Stores lightweight pane state using Herdr's plugin state directory.
- Uses Telegram HTML formatting.
- Has no external Go dependencies.
- Herdr
0.8.0or newer - OpenCode CLI
- A Telegram bot
- A Telegram chat ID
- macOS or Linux
Go 1.22+ is required when installing or building the plugin from source.
The plugin itself uses only the Go standard library.
The recommended installation method is Herdr's GitHub plugin installer:
herdr plugin install gpando/herdr-telegramHerdr will:
- Clone the repository.
- Read
herdr-plugin.toml. - Build the Go plugin.
- Install and enable the plugin.
Verify the installation:
herdr plugin listYou should see:
gpando.telegram-notifier (Telegram Notifier) enabled
Find the plugin configuration directory using Herdr:
herdr plugin config-dir gpando.telegram-notifierCreate telegram.json in that directory:
{
"telegram_token": "YOUR_TELEGRAM_BOT_TOKEN",
"telegram_chat_id": "YOUR_TELEGRAM_CHAT_ID",
"include_last_message": true,
"max_message_length": 1800,
"session_search_limit": 100
}Protect the configuration file:
chmod 600 "$(herdr plugin config-dir gpando.telegram-notifier)/telegram.json"| Option | Description | Default |
|---|---|---|
telegram_token |
Telegram Bot API token | required |
telegram_chat_id |
Destination Telegram chat ID | required |
include_last_message |
Include the latest OpenCode assistant response | true |
max_message_length |
Maximum response length | 1800 |
session_search_limit |
Number of OpenCode sessions inspected | 100 |
Create a Telegram bot using BotFather and obtain its bot token.
The bot token is a credential and must not be committed to Git. Keep it only in the local telegram.json configuration.
If a bot token is accidentally committed, revoke/regenerate it immediately.
The repository contains a platform-independent Herdr manifest:
id = "gpando.telegram-notifier"
name = "Telegram Notifier"
version = "0.1.0"
description = "Send Herdr OpenCode agent status changes to Telegram"
min_herdr_version = "0.8.0"
platforms = ["linux", "macos"]
[[build]]
command = ["go", "build", "-o", "herdr-telegram", "."]The Go binary is built natively on the machine where the plugin is installed.
No platform-specific binary needs to be committed to the repository.
Clone the repository:
git clone https://github.com/gpando/herdr-telegram.git
cd herdr-telegramLink it locally into Herdr:
herdr plugin link .After changing the Go source:
gofmt -w main.go
go build -o herdr-telegram .The plugin is invoked by Herdr for each event; it does not run as a persistent daemon.
OpenCode
│
│ agent status change
▼
Herdr
│
│ pane.agent_status_changed
▼
herdr-telegram
│
├── HERDR_PLUGIN_CONTEXT_JSON
│ │
│ └── focused_pane_cwd
│
├── opencode session list
│ │
│ └── latest session for project directory
│
├── opencode export
│ │
│ └── temporary JSON file
│
├── last assistant message
│
▼
Telegram Bot API
│
▼
Telegram client
The plugin does not rely on Herdr's native agent session information.
It reads:
HERDR_PLUGIN_CONTEXT_JSON
and specifically:
{
"focused_pane_cwd": "/path/to/project"
}The plugin then runs:
cd /path/to/project
opencode session list -n 100 --format jsonSessions are filtered using an exact directory match:
session.directory == focused_pane_cwd
The session with the newest updated timestamp is selected.
This avoids relying on:
- session titles
- pane titles
- native Herdr agent session IDs
- global session ordering
After resolving the session, the plugin executes:
opencode export <session-id>The export is written directly to a temporary file.
This is intentional.
Capturing a large opencode export through a stdout pipe can result in truncated output and errors such as:
unexpected end of JSON input
The plugin therefore uses:
opencode export
│
└── stdout → temporary file
│
▼
os.ReadFile()
│
▼
JSON parsing
Once parsed, the plugin searches the messages backwards and selects the last message where:
role == "assistant"
Only parts where:
type == "text"
are included.
This deliberately excludes:
- MCP activity
- tool calls
- tool results
- reasoning
- token information
- model information
- context statistics
- other UI metadata
The resulting text is sent as the Latest response section of the Telegram notification.
A completed execution produces a notification similar to:
✅ OpenCode finished
Project: hello
Workspace: Uno
Agent: opencode
Duration: 54s
Latest response:
The agent's latest assistant response...
A blocked agent produces:
⚠️ OpenCode needs your attention
Project: hello
Workspace: Uno
Agent: opencode
Latest response:
The agent's latest assistant response...
Herdr provides:
HERDR_PLUGIN_STATE_DIR
The plugin stores:
$HERDR_PLUGIN_STATE_DIR/state.json
The state tracks the last known status and the start time of working events.
This allows the plugin to calculate the current:
working → done
interval.
Inspect plugin executions with:
herdr plugin log list \
--plugin gpando.telegram-notifier \
--limit 20Useful diagnostic messages include:
herdr-telegram: resolved OpenCode session ses_...
herdr-telegram: OpenCode export returned ... bytes
A successful invocation should report:
status: succeeded
exit_code: 0
The repository contains test.sh for manually testing the plugin environment.
Run it from an OpenCode project directory:
./test.shYou can also specify the project explicitly:
PROJECT_DIR="$HOME/projects/my-project" ./test.shThe test uses a synthetic Herdr pane ID so that it does not interfere with the state of real panes.
A valid Telegram configuration is required.
Check:
herdr plugin listFor local development:
herdr plugin unlink gpando.telegram-notifier
herdr plugin link .Inspect the plugin logs:
herdr plugin log list \
--plugin gpando.telegram-notifier \
--limit 20Verify the configuration directory:
herdr plugin config-dir gpando.telegram-notifierThen check telegram.json.
The plugin requires the opencode executable to be available in the environment used by Herdr.
Check:
which opencode
opencode --versionAlso verify from the project directory:
cd /path/to/project
opencode session list -n 10 --format jsonThe plugin uses an exact match between:
HERDR_PLUGIN_CONTEXT_JSON.focused_pane_cwd
and:
OpenCode session.directory
Check manually:
cd /path/to/project
opencode session list -n 100 --format jsonThis indicates that the exported JSON was truncated.
The plugin avoids the stdout-pipe problem by writing opencode export directly to a temporary file before parsing it.
Inspect:
herdr plugin log list \
--plugin gpando.telegram-notifier \
--limit 20The Telegram bot token is a credential. Treat it like a password.
If a token is accidentally committed:
- Revoke or regenerate it through BotFather.
- Remove it from the repository.
- Remove it from Git history if necessary.
- Store the replacement only in local configuration or a secret manager.
The plugin communicates with:
- the local Herdr plugin environment
- the local OpenCode CLI
- the Telegram Bot API
| Platform | Architecture | Status |
|---|---|---|
| macOS | Apple Silicon | Supported |
| macOS | Intel | Supported |
| Linux | x86_64 | Supported |
| Linux | ARM64 | Supported |
The Go build is performed natively by Herdr during plugin installation.
Source code:
https://github.com/gpando/herdr-telegram
MIT License.
See LICENSE.