@qcts33/pi-notify0.5.0
Send notifications through Bark or Nextcloud when pi completes agent turns
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-notify ๐ฑ
Send notifications through Bark or Nextcloud when pi completes agent turns. Get notified on your phone โ or in your Nextcloud clients โ when long-running tasks finish, errors occur, or after each individual turn, without watching the terminal.
Notifications are off until you run /notify on in the session you want to hear from. Every other session โ including the pi child process of a subagent โ stays silent. See Activation.
Package: @qcts33/pi-notify
Repository: forgejo.horse-carat.ts.net/qcts33/pi-extensions (tailnet-only), package packages/pi-notify
How it works
pi (your computer)
โ
โ agent completes a turn โ extension fires
โ
โผ
pi-notify extension
โ
โโโ Bark channel โโโโโโโบ POST https://api.day.app/push
โ { title, body, sound, group }
โ Bark server โ Apple Push Notification service โ iPhone
โ
โโโ Nextcloud channel โโบ POST /ocs/v2.php/apps/notifications/api/v3/
admin_notifications/{recipient}
{ subject, message }
Nextcloud โ your phone, desktop and web clients
Every configured channel sends; a channel with no credentials is skipped. Configure both and one event produces one notification on each (see Two channels at once).
Prerequisites
- A computer running pi (macOS, Linux, or Windows)
- Pi 1.x (floor 1.0.0, tested on 1.2.0) โ the extension uses native
fetchandExtensionAPI. Below the floor a session shows one warning at session start; the rest of the extension still runs. - One channel:
- Bark โ the Bark iOS app, free on the App Store, plus the device key it shows.
- Nextcloud โ Nextcloud 30 or newer with the Notifications app enabled, an account that is an administrator, and an app password. The recipient's Nextcloud mobile clients must be signed in and registered for push; otherwise the notification still appears in the web UI's notification list, but no phone wakes.
Installation
via npm
pi install npm:@qcts33/pi-notify
from a local checkout
pi install /path/to/pi-extensions/packages/pi-notify
Quick test (no install)
pi -e /path/to/pi-extensions/packages/pi-notify
The upstream package @aslamplr/pi-ios-notify still exists on npm. Installing it gets upstream's 0.1.1,
without the changes listed under Fork provenance.
Setup
Bark channel
- Install Bark on your iPhone and open it. It shows a device key URL like
https://api.day.app/YOUR_DEVICE_KEY_HERE. - In pi, run
/notify bark setupand paste theYOUR_DEVICE_KEY_HEREpart. - Run
/notify test barkโ a "๐ Test" notification should arrive within a second or two.
Nextcloud channel
- In Nextcloud, open Settings โ Security โ Devices & sessions and create an app password. Name it for this extension; revoke it there when you stop using it.
- In pi, run
/notify nextcloud setupand answer four prompts:- URL โ the Nextcloud base URL, including any subdirectory, e.g.
https://cloud.example.comorhttps://host/nextcloud. - User โ the account that signs the request. The v3 endpoint requires an administrator.
- App password โ the password from step 1. It is visible while you type it; the prompt says so.
- Recipient โ the user ID that receives the notification. Leave it empty to notify the authenticated account.
- URL โ the Nextcloud base URL, including any subdirectory, e.g.
- Run
/notify test nextcloud. The output names the failure when one occurs: a 401 means the user or app password is wrong, a 403 means the account is not an administrator, a 404 means the instance is older than Nextcloud 30 or the Notifications app is disabled.
An empty answer at any prompt keeps the value already configured. /notify nextcloud remove clears the whole channel.
Activation
Notifications are off by default and every session starts off. Only a session you activate sends:
/notify on
The state lives in the running pi process and is never written to disk, so each of these starts you inactive again: /reload, --resume, /new, a fork, a session switch, a restart. Run /notify on again after any of them. While the session is active the footer shows ๐ฑ, and /notify off clears it.
/notify on needs at least one configured channel; it covers both channels at once. /notify test ignores the gate: it sends whenever you ask it to.
The events toggles below choose which events an active session may send. They never make an inactive session send.
Commands
| Command | Description |
|---|---|
/notify on |
Activate notifications for the current session |
/notify off |
Deactivate notifications for the current session |
/notify test [bark|nextcloud] |
Send a test through every configured channel, or through the one named |
/notify status |
Show the channels, events and settings |
/notify events <type> <true|false> |
Enable/disable notification types (agent-end, agent-settled, prompt-tools, turn-end, error) |
/notify prompt-tools list |
List configured prompt tool names and labels |
/notify prompt-tools add <name> [label] |
Add a tool name to watch with optional label |
/notify prompt-tools remove <name> |
Remove a tool from the watch list |
/notify prompt-tools clear |
Remove all prompt tools |
/notify bark setup |
Enter or replace the Bark device key |
/notify bark remove |
Clear the Bark device key |
/notify bark sound <name> |
Set the Bark notification sound |
/notify bark icon <url|pi|default> |
Set a custom Bark notification icon |
/notify bark url <value> |
Set the URL Bark opens when the notification is tapped |
/notify nextcloud setup |
Enter or replace the Nextcloud URL, user, app password and recipient |
/notify nextcloud remove |
Clear the Nextcloud channel |
/notify debug <on|off> |
Enable/disable debug logging |
/notify hostname <true|false> |
Append the hostname to notification bodies |
/notify body-content <prompt|output> |
Set whether the Complete notification shows the agent's last output or your prompt |
Notification events
An active session sends the events switched on here. These toggles do not make an inactive session send; see Activation.
| Event | Default | Description |
|---|---|---|
agent-end |
โ on | Notify when agent finishes a response |
agent-settled |
โ off | Notify only when the agent is truly idle (no auto retry/compaction/continuation). More accurate than agent-end; recommended with agent-end disabled |
prompt-tools |
โ on | Notify mid-turn when a configured tool calls for user input |
turn-end |
โ off | Notify after each individual LLM turn (can be noisy!) |
error |
โ on | Notify when a tool execution fails |
Note: Enabling both
agent-endandagent-settledsends duplicate completion notifications โ one peragent_endrun plus one when the agent fully settles. For a single, accurate completion signal, useagent-end=false+agent-settled=true.
Toggle with:
/notify events agent-end false
/notify events agent-settled true
/notify events prompt-tools false
/notify events turn-end true
/notify events error false
Notification body content
The "โ Complete" notification body can show either the agent's last output or your original prompt:
output(default) โ the agent's final natural-language reply, truncated to 120 characters. The notification itself tells you what the agent concluded.promptโ the prompt you typed, with the turn count (legacy behavior).
If the agent's final turn produced no text (e.g. it ended on pure tool calls or thinking blocks), output mode falls back to showing your prompt so the notification body is never empty. Thinking content is never captured.
Toggle with:
/notify body-content output
/notify body-content prompt
Check the current value with /notify status.
Prompt tools
Prompt tools are tools registered by extensions that may ask for user input mid-turn, such as an approval gate or a question tool. The extension watches none of them until you list it: a tool name in this list is the only reason its mid-turn call sends a notification. When the LLM calls a listed tool, every configured channel is notified immediately.
/notify prompt-tools list
/notify prompt-tools add my_approval_tool "Approval"
/notify prompt-tools remove my_approval_tool
The label is shown as the notification title suffix; omitting it uses the tool name. The list is stored as promptTools in the config file, and removing a tool there (or with remove / clear) is permanent โ it is not re-added on the next start.
Cross-extension notifications
Any extension can send a notification by emitting an event on pi's shared event bus:
pi.events.emit("pi-notify:notify", {
title: "๐ Approval needed",
body: "bash: rm -rf node_modules",
source: "my-extension", // optional
});
pi-notify listens for pi-notify:notify and forwards the title and body to every configured channel. The emitter controls the notification content entirely. Requires an active session (see Activation) and the prompt-tools event toggle to be enabled.
The event was called pi-ios-notify:notify before 0.5.0; the old name is no longer heard.
Bark-specific settings
/notify bark sound birdsong
/notify bark icon https://example.com/my-icon.png
/notify bark url ssh://user@myserver
- Sound โ any sound the Bark app offers. Common values:
default,minuet,alarm,anticipate,birdsong,bloom,calypso,chime,descant,electronic,fanfare,horn,ladder,mazurka,nightingale,piano,rocket,sirius,strums,suspense,telegraph,tiptoes,tritone,tuberose,twinkle. - Icon โ
defaultresets to Bark's icon,piuses the pi logo, anything else is a URL. - URL โ any URL scheme; opens when the notification is tapped. Clear it with
/notify bark url "".
Nextcloud's endpoint accepts none of these: the v3 request carries a subject and a message only, and the client decides the rest.
Two channels at once
Configured channels are independent. With both Bark and Nextcloud set up, every notification goes to both, so a completion wakes the phone twice. To stop that, remove one channel (/notify bark remove or /notify nextcloud remove); there is no per-channel event toggle.
Configuration file
The extension stores its config at ~/.pi/notify.json. You can edit it directly:
{
"title": "pi",
"bodyContent": "output",
"showHostname": false,
"debug": false,
"events": {
"agentEnd": true,
"agentSettled": false,
"promptTools": true,
"turnEnd": false,
"error": true
},
"promptTools": {},
"bark": {
"key": "YOUR_DEVICE_KEY",
"server": "https://api.day.app",
"sound": "default",
"icon": "",
"url": ""
},
"nextcloud": {
"server": "https://cloud.example.com",
"user": "admin",
"appPassword": "YOUR_APP_PASSWORD",
"recipient": "alice"
}
}
An empty bark.key leaves the Bark channel off; an empty nextcloud.server, nextcloud.user or nextcloud.appPassword leaves Nextcloud off. An empty nextcloud.recipient sends to nextcloud.user. promptTools maps each watched tool name to its notification label; an empty object watches none, which is the default.
Upgrading from 0.4.0 reads the old ~/.pi/ios-notify.json once and writes its settings into the new file; the old file is left untouched and no longer read afterwards.
Custom Bark server
If you self-host Bark, set the server:
{
"bark": {
"server": "https://bark.yourdomain.com",
"key": "YOUR_KEY"
}
}
What notifications look like
| Event | Notification |
|---|---|
| Agent completes response | โ
pi Complete โ "refactor the auth module to use JWT" (3 turns) |
| Prompt tool called mid-turn | ๐ฌ pi Approval โ my_approval_tool |
| Prompt tool called mid-turn | ๐ฌ pi Wait โ my_question_tool |
| Cross-extension event emit | ๐ฌ pi {emitter's title} โ {emitter's body} |
| Each turn | ๐ pi Turn โ Turn 3 finished |
| Tool error | โ ๏ธ pi Error โ Error in bash: command not found: foobar |
On Nextcloud these arrive as a notification from the admin_notifications app: the title line is the subject and the body is the message.
Privacy
- The Bark device key and the Nextcloud app password are stored in plaintext in
~/.pi/notify.json. The Nextcloud password is an app password, so it can be revoked in Nextcloud without touching the account's main password; create one for this extension alone. - Notification content goes to the servers you configured: the Bark server and your Nextcloud instance. The body โ your prompt, or the agent's last text truncated to 120 characters โ is the only conversation content that leaves the machine.
- A Nextcloud notification is stored by your Nextcloud server until it is dismissed, which is why it also appears in the web UI's notification list.
- Bark stores notification content encrypted on its server; you can additionally set up end-to-end encryption in the Bark app.
Project structure
pi-notify/
โโโ index.ts # Extension entry point: events, activation, /notify command
โโโ channels.ts # The Bark and Nextcloud request builders and delivery
โโโ config.ts # Config file, defaults and the 0.4.0 migration
โโโ utils.ts # Pure text helpers
โโโ test/ # Vitest suite for config, channels and the helpers
โโโ defaults.json # Default Bark icon
โโโ package.json # Package manifest with pi.extensions entry
โโโ README.md # This file
Standard npm package layout โ the pi.extensions field in package.json tells pi where to find entry points.
Development
This package is a workspace member of the pi-extensions monorepo. Everything runs from the repo root through npm workspaces:
npm install # once, at the repo root
npm test # vitest for this package, node --test for the others
npm run verify # lint + typecheck + tests + pack check
Load for testing:
pi -e /path/to/pi-extensions/packages/pi-notify
Reload changes:
/reload
Tests
npm test
Runs vitest on everything that does not need a live pi session:
test/config.test.tsโ defaults, the flat 0.4.0 shape, the nested shape, trimming and invalid valuestest/channels.test.tsโ configured-channel selection, UTF-8 byte truncation, both request formats, per-channel failure lines and the timeouttest/utils.test.tsโ prompt truncation, pluralization, boolean parsing and custom-message text
index.ts binds the handlers and the command; no test imports it, so check it in a live pi session.
โ๏ธ No Warranty
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. The authors make no representations that this extension will deliver notifications reliably or in a timely manner. You are responsible for testing notification delivery and configuring appropriate settings.
License
MIT โ see LICENSE, which carries upstream's copyright notice as well as this fork's.
Fork provenance
This package is qcts33's fork of aslamplr/pi-ios-notify,
taken from upstream 0.1.1 and published under the @qcts33 scope. From 0.5.0 it publishes as
@qcts33/pi-notify with the command /notify; the old names were dropped rather than aliased
(ADR-0007). On top of upstream's 0.1.1 it adds:
- an
agent-settledevent, driven by pi'sagent_settledevent, as a more accurate completion signal thanagent-end - a
body-contentsetting that puts the agent's last output in the notification body instead of your prompt - bounded fetch timeouts per channel (5 seconds for Bark, 20 for Nextcloud) and fire-and-forget delivery, so a slow server cannot stall the agent loop
- notifications only in a session the user activated with
/notify on; every other session, including the childpiprocess of a subagent, starts silent - a Nextcloud channel that creates notifications through the Notifications v3 API, alongside Bark
Upstream is kept as a read-only remote: upstream fixes are cherry-picked into this fork, and this fork's changes are not upstreamed automatically.
Credits
- Bark โ the wonderful free iOS push notification app
- Nextcloud โ the Notifications app and its v3 API
- aslamplr/pi-ios-notify โ the upstream extension this fork descends from
- Inspired by doublezz10/opencode_ios_notifications, Stephanvs/opencode-ntfy, and blanboom/bark-notification-for-claude-codex-opencode