piscord
Lightweight Discord gateway for pi coding agent
Package details
Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
A lightweight Discord gateway for pi coding agent. SQLite-backed queue, per-channel session isolation, crash recovery, abort support. One command to set up, runs as a daemon, and keeps task and delivery state across restarts.
Latest release: 2.0.0. Requires Node.js ≥22.19.0 and pi ≥0.83.0 <0.86.0. See upgrade and recovery before updating from 1.x, and Changelog for details.
npm install -g piscord
piscord setup # interactive wizard -- walks you through everything
That's it. The setup wizard checks prerequisites, asks for your Discord bot token, lets you pick a channel policy, and optionally installs + starts a background service. Your bot is live in under a minute.
Prerequisites
- Node.js ≥ 22.19.0
- Linux, macOS, or Windows
- pi ≥ 0.83.0 and < 0.86.0 installed and on
PATH(recommended: 0.85.1), with a configured provider - Discord bot token — create one here
- Enable Message Content Intent under Privileged Gateway Intents
- Bot permissions:
Send Messages,Read Message History,View Channels,Attach Files
Features
- Bridges to your existing
pi— shells out to thepibinary and reuses your login + model access - Optional conversation threads — enable per parent channel; each question gets an isolated conversation with inherited settings
- Per-channel sessions — each Discord channel gets its own persistent conversation history
- Per-channel working directories — optionally override
PI_CWDfor specific channels without changing the global default - Channel access policy —
open(all channels),open-trigger(all channels, @mention required), orallowlist(manual registration only) - SQLite message queue — resumes pending work and saved answers; reports interrupted execution without replaying it
- Concurrency control — per-channel serial processing + configurable global limit
- DM auto-registration — direct messages work out of the box
- Discord slash commands —
/pi status,/pi model,/pi thinking,/pi new,/pi stop - Abort command —
/pi stopterminates the running task and clears queued messages - Attachment relay — Discord file uploads are downloaded and passed to
piby local path so agents can inspect or convert any supported file type without flooding context - Message and file sending —
piscord sendlets pi send plain text, files, or both to any Discord channel - Scheduled tasks — cron or one-time tasks that trigger pi sessions on schedule
- Archive auto-cleanup — archived sessions are cleaned up after a configurable retention period
- Cross-platform — runs on Linux, macOS, and Windows with platform-aware defaults
- Typing indicators — shows "bot is typing" while
piprocesses - Message splitting — handles Discord's 2000-character limit automatically
- Daemon management — systemd on Linux, launchd on macOS
- Platform-aware paths — XDG on Linux,
~/Library/Application Supporton macOS,%LOCALAPPDATA%on Windows
How It Works
Discord ──discord.js──→ Gateway ──pi subprocess──→ Pi Agent
│ │
SQLite Session dirs
(message queue) (per channel)
The gateway does not embed or replace pi. It finds and runs your installed pi:
- Binary discovery — uses
PI_BINconfig or findspiinPATH - Auth reuse —
pireads its own~/.pi/agent/auth.jsonwhen invoked - Model catalog — cached results from asynchronous CLI discovery; a bounded, short-lived SDK probe supplies metadata and fallback models
- Invocation — each message is processed as
pi --session-dir <dir> --continue -p <message>; a short-lived supervisor terminates owned processes if the gateway exits
Channel Policy
During setup you pick one of three policies. This controls how the bot interacts with server channels:
| Policy | Behavior |
|---|---|
open |
All guild channels auto-register on first message. No @mention needed. |
open-trigger |
All guild channels auto-register, but only respond when @mentioned. |
allowlist |
Only manually registered channels are active. |
- DMs always auto-register when
AUTO_REGISTER_DMS=true(the default). - Use
EXCLUDED_CHANNELSto block specific channels from auto-registration inopen/open-triggermode.
If you chose allowlist, register channels manually:
piscord register 123456789012345678 "my-server #general" --no-trigger
piscord register 123456789012345678 "my-server #general" --cwd /srv/repos/app
Re-running piscord register with --cwd updates that channel's working directory override. If no override is set, the gateway uses the global PI_CWD.
Slash Commands
The gateway registers a global /pi command on Discord:
| Subcommand | Description |
|---|---|
/pi status |
Show model, thinking, working directory, session info, token usage |
/pi model |
Set the channel's model (autocomplete from pi's available models) |
/pi reset-model |
Clear the channel's model override |
/pi reset-thinking |
Clear the thinking override and inherit the default again |
/pi threads enabled:true |
Enable automatic conversation threads in this parent channel |
/pi thinking |
Set thinking level: off / minimal / low / medium / high / xhigh |
/pi new |
Start a fresh session for this channel |
/pi stop |
Abort the current task and clear queued messages |
/pi model reads the catalog from the configured PI_BIN, so it stays in sync when pi adds or removes models. It also honors pi's enabledModels setting (configured through /scoped-models), including model order and glob patterns. If no scope is configured, it shows all available models.
Conversation Threads
Automatic thread creation is off by default, including after an upgrade. Enable it in a registered server text channel with /pi threads enabled:true (requires Manage Channels), or locally:
piscord threads 123456789012345678 on
piscord threads 123456789012345678 off
The bot needs Create Public Threads and Send Messages in Threads. A question that meets the parent's normal trigger rules opens a thread on that user message. The question and its attachments enter a new pi session; continue inside the thread without mentioning the bot again. A new question in the parent starts another conversation. The parent conversation's history is not copied.
Threads inherit model, thinking and working-directory settings from their parent, unless individually overridden. Parent changes apply to subsequent tasks. /pi reset-model and /pi reset-thinking restore inheritance; /pi new and /pi stop affect only the current thread. Disabling automatic threads preserves existing conversations.
Manually created threads remain usable under the channel policy. In allowlist mode, register a manual thread separately. Their settings inherit from a registered parent once the thread is recognized. Automatically created threads are registered by the gateway.
Scheduled prompts directed to an enabled parent channel create a brief starter message and use the same thread routing. Schedules directed to an existing thread continue there. Creation failures are reported before pi runs. Archived threads keep their sessions and can continue when Discord permits; locked or inaccessible threads may fail delivery. Deleted threads cancel pending work and disable their schedules. Their session folders are archived after active work drains, then cleaned using ARCHIVE_RETENTION_DAYS. Reconciliation also checks for deletion missed while the gateway was offline.
Task Recovery and Delivery
Stop the running gateway before upgrading. The database migration preserves existing channels, settings, sessions and pending messages. pi 0.74 is no longer supported; upgrade Node and pi together before starting this version. Setup and startup verify both the installed SDK peers and the executable selected by PI_BIN (or PATH); a missing, unsupported or unresponsive executable fails preflight.
| State at restart | Behavior |
|---|---|
| Waiting to execute | Continues in queue |
| Thread routing, before execution | Resumes routing using the saved starter message |
| Execution started, result unknown | Marked interrupted; a notice asks you to check completed operations before submitting again |
| Answer saved, delivery incomplete | Continues sending the saved answer, without rerunning pi |
| Explicitly stopped or timed out | Remains stopped |
Answers are saved before sending. Temporary delivery failures have a bounded retry budget; confirmed chunks are skipped. Recent ambiguous sends use Discord's nonce deduplication and message lookup. If an older send cannot be confirmed, automatic delivery stops to avoid duplicates. Permission failures also stop retrying. You can read the saved answer locally with piscord result <task-id>; task notices include the ID. Already transmitted messages are not removed by /pi stop.
Only one gateway may own a database. A second instance exits with a diagnostic. After a crash, the lock can take 30 seconds to expire; an owner that is still alive is never displaced merely because its heartbeat is old. Use a local filesystem for the SQLite database and lock files.
AGENT_TIMEOUT_MS sets a total pi invocation limit, with 0 (the default) preserving unlimited long tasks. Timeout and cancellation terminate the invocation and its owned subprocesses. Quiet stdout does not trigger a timeout. During shutdown, the gateway first allows SHUTDOWN_TIMEOUT_MS for active tasks, then aborts them and drains bounded process/delivery operations before closing the database.
Model discovery runs in the background and never delays a normal message for a catalog refresh. Autocomplete can briefly show an old list or no choices while loading. /pi status distinguishes loading, unavailable and stale catalogs. Explicit model changes wait for discovery and report unavailable discovery separately from a missing model.
Tools for Pi
The gateway exposes two capabilities through its CLI that pi itself can invoke. You don't type these commands in your terminal — you just tell pi in Discord, and it handles the rest.
For example, you can say to pi:
"Create a daily task at 9am UTC that generates a summary report" "Send me report.pdf with a message saying here you go" "Set a one-time reminder for the 2pm meeting today"
pi will run the appropriate piscord task or piscord send command behind the scenes.
Scheduled tasks
pi can schedule cron-based or one-time prompts through the gateway's scheduler. Tasks are injected into the normal message queue, so they use the channel's configured model, thinking level, and working directory.
Under the hood, pi runs commands like:
piscord task add \
--name "daily-report" \
--schedule "0 9 * * *" \
--channel dc:123456789 \
--prompt "Generate today's summary report"
piscord task add \
--name "meeting-reminder" \
--schedule "2026-04-05T14:00:00Z" \
--channel dc:123456789 \
--prompt "Remind Colin about the 2pm meeting" \
--once
The --schedule value uses standard 5-field cron syntax (minute hour day month weekday). For one-time tasks, add --once and pass an ISO 8601 datetime.
Task management — also available via pi:
piscord task list # List all tasks
piscord task disable <id> # Pause
piscord task enable <id> # Resume
piscord task remove <id> # Delete
Sending messages and files to Discord
pi can send plain text messages, files, or both to any Discord channel using the gateway's built-in relay.
When you ask pi to send something, it runs commands like:
piscord send --channel dc:123456789 --text "hello"
piscord send --channel dc:123456789 --file /path/to/report.pdf --text "Here's the report"
piscord send --channel dc:123456789 --file chart.png --file data.csv
--textworks on its own- Up to 10 files per message (Discord limit)
- Respects
MAX_ATTACHMENT_BYTESper file - Works independently — no running gateway daemon required
Daemon Management
The setup wizard offers to install a background service automatically. You can also manage it manually:
piscord daemon install # Generate + enable service
piscord daemon start # Start
piscord daemon status # Check status
piscord daemon logs # Tail log output
piscord daemon stop # Stop
piscord daemon uninstall # Remove the service
- Linux — uses a systemd user service
- macOS — uses a launchd user agent
- Windows — daemon management is not yet supported; run
piscord startin a terminal or use Task Scheduler manually
Headless Linux servers: enable user lingering so the service runs without an active login session:
sudo loginctl enable-linger $USER
Configuration Reference
Config file location depends on your OS (see Data Locations). On Linux: ~/.config/pi-discord-gateway/config.env
Most users won't need to edit this file directly — piscord setup generates it for you. If you do want to tweak advanced settings, you can edit the file manually, or ask your pi to configure it for you. Run piscord status to see the config path on your system.
| Variable | Default | Description |
|---|---|---|
DISCORD_BOT_TOKEN |
(required) | Discord bot token |
PI_BIN |
pi |
Path to pi binary |
PI_MODEL |
(none) | Default model override |
PI_THINKING |
(none) | Default thinking level |
PI_CWD |
$HOME |
Default working directory for pi; can be overridden per registered channel |
PI_EXTRA_FLAGS |
(none) | Extra flags passed to pi |
TRIGGER_NAME |
pi |
Bot trigger name for @mentions |
CHANNEL_POLICY |
allowlist |
Channel access: open, open-trigger, or allowlist |
EXCLUDED_CHANNELS |
(none) | Comma-separated channel IDs to exclude from auto-registration |
MAX_CONCURRENCY |
3 |
Max parallel pi invocations |
MAX_SCHEDULED_CONCURRENCY |
1 |
Max scheduled tasks enqueued per tick |
POLL_INTERVAL_MS |
1000 |
Queue poll interval (ms) |
AGENT_TIMEOUT_MS |
0 |
Total pi invocation limit in ms (0 = unlimited) |
SHUTDOWN_TIMEOUT_MS |
15000 |
Graceful shutdown timeout (ms) |
AUTO_REGISTER_DMS |
true |
Auto-register DM channels |
ARCHIVE_RETENTION_DAYS |
30 |
Days to keep archived sessions (0 = never clean) |
MAX_ATTACHMENT_BYTES |
26214400 |
Max size per attachment (0 = no limit) |
MAX_TOTAL_ATTACHMENT_BYTES |
52428800 |
Max combined attachment size (0 = no limit) |
MEDIA_RETENTION_HOURS |
168 |
Hours to keep downloaded attachment files for path-based agent access |
SESSIONS_DIR |
(platform default)/sessions | Session storage directory (see Data Locations) |
DB_PATH |
(platform default)/gateway.db | SQLite database path (see Data Locations) |
LOG_LEVEL |
info |
Log level: debug/info/warn/error |
After changing config, restart the service: piscord daemon stop && piscord daemon start
CLI Reference
piscord setup [token] Interactive setup wizard
piscord start Start gateway (foreground)
piscord status Show diagnostics
piscord channels List registered channels
piscord register <id> <name> [options] Register a channel
piscord unregister <id> Unregister a channel
piscord threads <channel-id> <on|off> Configure automatic conversation threads
piscord result <task-id> Read a saved answer without rerunning pi
piscord send --channel <jid> [--text <msg>] [--file <path> ...]
piscord task add --name <n> --schedule <cron|iso> --channel <jid> --prompt <text> [--once]
piscord task list | remove <id> | enable <id> | disable <id>
piscord archive list List archived sessions
piscord archive cleanup [--dry-run] Clean up expired archived sessions
piscord daemon install | uninstall | start | stop | status | logs
piscord help Show help
Register options
| Flag | Effect |
|---|---|
--no-trigger |
Respond to all messages (not just @mentions) |
--main |
Mark as main channel (implies --no-trigger) |
--folder <name> |
Custom session folder name |
--cwd <path> |
Override PI_CWD for this channel only |
Data Locations
Paths are platform-aware. Defaults by OS:
| Item | Linux | macOS | Windows |
|---|---|---|---|
| Config | ~/.config/pi-discord-gateway/config.env |
~/Library/Application Support/piscord-gateway/config.env |
%APPDATA%\piscord-gateway\config.env |
| Database | ~/.local/share/piscord-gateway/gateway.db |
~/Library/Application Support/piscord-gateway/gateway.db |
%LOCALAPPDATA%\piscord-gateway\gateway.db |
| Sessions | ~/.local/share/piscord-gateway/sessions/ |
~/Library/Application Support/piscord-gateway/sessions/ |
%LOCALAPPDATA%\piscord-gateway\sessions\ |
| pi auth | ~/.pi/agent/auth.json |
~/.pi/agent/auth.json |
~/.pi/agent/auth.json |
Alternative Installation
npx (quick trial, no global install)
npx piscord@latest setup
From source
git clone https://github.com/Crokily/pi-discord-gateway.git
cd pi-discord-gateway
npm install && npm run build
node dist/cli/index.js setup
Troubleshooting
piscord status shows "Pi binary: not found".
- Check
pi --versionworks in the same shell - Set
PI_BIN=/full/path/to/piin config.env - Restart:
piscord daemon stop && piscord daemon start
piscord status shows "Pi auth: missing".
- Run
piand complete the login flow - Confirm
~/.pi/agent/auth.jsonexists for the same user running the gateway
piscord daemon status— check for errorspiscord daemon logs— see log output- Linux: for headless servers, run
sudo loginctl enable-linger $USER - macOS: check
~/Library/Logs/piscord-gateway/for launchd output
openpolicy: checkEXCLUDED_CHANNELSdoesn't include your channelallowlistpolicy: runpiscord channels— at least one channel must be registered- For trigger-only channels: mention the bot by name or use
@TriggerName - DMs auto-register when
AUTO_REGISTER_DMS=true
Development
npm install
npm run dev # Start with tsx (no build needed)
npm run build # Compile TypeScript
npm test # Run Vitest suite
npm run typecheck # Check source and test types
npm run test:compat # Exercise installed pi with an isolated local model fixture
See maintenance validation for the current regression coverage and release instructions for the npm publishing process. Merging a PR does not publish a package; publishing is triggered by a version tag.
Security
- Protect
config.env— it contains your Discord bot token - Anyone who can message a registered channel can spend your pi usage
- Review attachment size limits before exposing the bot
- Run the service as a normal user, not root
License
MIT
Version History
| Version | Date | Changes |
|---|---|---|
| 2.0.0 | 2026-09-14 | Modern pi compatibility, optional threads and durable task/delivery recovery |
| 1.7.0 | 2026-07-17 | /pi model syncs with pi's catalog and enabledModels |
| 1.6.1 | 2026-06-15 | Fixed README version metadata |
| 1.6.0 | 2026-06-15 | Path-based attachment relay and clearer empty-output errors |
| 1.5.3 | 2026-05-19 | Fix ESM peer-dep check, cross-platform test fixes |
| 1.5.1 | 2026-05-15 | Startup check for legacy @mariozechner/pi-ai package |
| 1.5.0 | 2026-05-15 | Cross-platform support (macOS, Windows), launchd, new deps |
| 1.4.3 | 2026-05-03 | Compatibility with older pi-ai thinking APIs |
| 1.4.2 | 2026-04-06 | Fixed default XDG data directory mismatch |
| 1.4.1 | 2026-04-06 | Fixed text-only sends via piscord send |
| 1.4.0 | 2026-04-06 | Added per-channel working directories |
| 1.3.0 | 2026-04-04 | Improved setup UX, faster install |
| 1.2.0 | 2026-04-04 | Added channel policy, abort, scheduler, send-file |
| 1.1.0 | 2026-03-31 | Renamed package to piscord |
| 1.0.0 | 2026-03-28 | Initial release |
See Changelog for full details.