picord is a Discord integration extension for pi / pi-mono.
npm package: @venthezone/picord
It lets you run pi from Discord while keeping pi’s native sessions, models, skills, and extensions.
In a guild:
- a project channel maps to a local workspace
- a thread inside that channel maps to a pi session
- the thread title becomes the session name
In DMs:
- the DM acts like a direct personal session
- Default runtime:
discord-port - Legacy fallback:
PICORD_RUNTIME_ARCH=legacy
discord-port is the main runtime now.
- Discord bot integration as a pi extension
- project channel → workspace mapping
- thread → pi session binding
- pi skills exposed as slash commands
- model and thinking controls from Discord
- provider login and API key update flow from Discord
- native pi session resume support
- workspace-only file access by default
- approval flow for blocked or out-of-workspace access
- managed project channels backed by local workspace folders
cd picord
npm installSee .env.example.
export PICORD_DISCORD_TOKEN=your_discord_bot_tokenDISCORD_BOT_TOKEN also works as a fallback.
cp picord.config.example.json picord.config.jsonpicord.config.json is local-only and gitignored.
Use these scopes:
botapplications.commands
Recommended permissions:
View ChannelsSend MessagesSend Messages in ThreadsCreate Public ThreadsManage ThreadsRead Message HistoryUse Application CommandsManage ChannelsManage Roles(optional, only needed if you want picord to auto-create the configured access role)
In the Discord Developer Portal:
- open your application
- go to Bot
- enable Message Content Intent
That intent is required for the full message-driven guild flow.
For normal use, install picord as a persistent pi extension.
The published package name is:
@venthezone/picordYou can install it through pi with:
pi install npm:@venthezone/picordYou can also use the local repo directly by either:
- adding this package path to your pi extension settings, or
- symlinking or copying the repo into
~/.pi/agent/extensions/picord/
This repo already declares its extension entrypoint in package.json:
{
"pi": {
"extensions": ["./src/index.ts"]
}
}If you just want to try picord without installing it persistently, run:
pi -e ./src/index.ts --no-sessionFor a stable VPS/runtime setup, run picord in its own tmux session from the local repo source.
Start it:
cd /path/to/picord
set -a && source .env && set +a
npm run sync:discord-commands
tmux new-session -d -s picord 'cd /path/to/picord && set -a && source .env && set +a && exec pi -e ./src/index.ts --no-session'Useful tmux commands:
# attach to the running session
tmux attach -t picord
# stop picord
tmux kill-session -t picord
# restart picord after code changes
tmux kill-session -t picord 2>/dev/null || true
npm run build
npm run sync:discord-commands
tmux new-session -d -s picord 'cd /path/to/picord && set -a && source .env && set +a && exec pi -e ./src/index.ts --no-session'This is the recommended way to keep picord running on a remote machine while still using the live local source tree.
npm run check
npm run doctor
npm run smoke:discordIf slash commands drift after adding commands or skills, run:
npm run sync:discord-commandsexport PICORD_DISCORD_TOKEN=your_discord_bot_tokenexport PICORD_DISCORD_APPLICATION_ID=your_application_id
export PICORD_CONFIG=/absolute/or/relative/path/to/picord.config.json
export PICORD_RUNTIME_ARCH=legacy{
"allowDm": true,
"cwd": ".",
"statePath": "./picord.state.json",
"workspaceBasePath": "~/.picord/workspace",
"workspaceRoots": {},
"toolMode": "coding",
"allowedGuildIds": ["123"],
"allowedChannelIds": [],
"allowedRoleIds": [],
"allowedRoleNames": ["picord"],
"allowedUserIds": [],
"ownerUserId": "222",
"blockedPathPatterns": [".env", ".env.*", "*.pem", "*.key", "id_rsa", "id_ed25519"],
"hostChannelId": "123456789012345678",
"hostChannelName": "host",
"registerCommands": true,
"thinkingLevel": "off",
"critiqueAutoShare": false,
"systemPromptAppend": "Prefer concise Discord-friendly replies."
}cwd: default workspace root for DMs or unmapped channelsstatePath: persistent state for managed project channels and thread/session bindingsworkspaceBasePath: base directory used by/project-create; defaults to~/.picord/workspaceworkspaceRoots: optional static mapping of project channel ID → local workspace path; leave it empty if you want/project-createto manage channels and workspace folders for youallowedChannelIds: static channel allowlist; bot-managed project channels are added from stateallowedRoleIds/allowedRoleNames: required guild roles for normal bot usageallowedUserIds: explicit user allowlistownerUserId: owner who can approve access requests and run owner-only commandshostChannelId: exact control channel ID for owner/admin commandshostChannelName: fallback control channel name; defaults tohostblockedPathPatterns: sensitive files that stay blocked or approval-gatedcritiqueAutoShare: when true, picord appends a critique.work diff link after Discord runs that change the git working tree
- create or map a project channel to a workspace
- send a message in that project channel to start a thread
- keep working inside the thread
- that thread stays bound to the same pi session
Use the host control channel for owner and admin actions like:
/project-create/add-project/add-project-path/project-list/project-list-available/session/login/reload/access-requests/access-allow/access-deny
/add-project opens a picker for direct subfolders under workspaceBasePath and creates or reuses a project channel automatically.
For advanced manual binding, use /add-project-path. In current-channel mode, run it as the owner in the channel you want to bind.
/session works in the host channel and lets you pick an existing pi session. Picord will create or reuse the matching project channel for that session's workspace, create a thread, and resume the selected session there.
DMs work as a direct session using the configured default workspace.
/ask prompt:<text>/abort/refresh-session/resume session:<session-file-or-id>/sessions/reset/status/scope-models provider:<provider> query:<optional filter>/use-model model:<provider/model>/model model:<provider/model>/think level:<none|low|medium|high|xhigh>/diff/review
/diff uploads the current git diff to critique.work and returns the shareable URL.
/review asks critique to generate a review for the current diff and returns the review URL.
These must be run in the configured host control channel.
/reload/login/project-create name:<project-name>
/login opens a provider picker. API-key providers prompt for a replacement key in Discord. openai-codex prefers the headless device flow: picord shows the OpenAI verification URL plus the one-time code and then polls for completion automatically.
/add-project/add-project-path path:<path> mode:<new-channel|current-channel> name:<optional>/project-list/project-list-available/session/access-requests/access-allow request_id:<id> mode:once|always/access-deny request_id:<id>
Each discovered pi skill with a Discord-safe name is also registered as a slash command.
Examples from this environment include:
/brainstorming/humanizer/security-review/tdd-workflow/verification-loop
Each skill command accepts an optional prompt argument and runs through pi as /skill:<name>.
- Discord token is read from environment variables, not from JSON config
- guild access is restricted by allowlists and role checks
- file access is limited to the workspace root by default
- sensitive paths like
.env,.env.*,*.pem, and*.keyare blocked or approval-gated - blocked or out-of-workspace access requests are sent back to Discord for owner approval
Useful commands:
npm run check
npm run doctor
npm run doctor:discord-port
npm run doctor:legacy
npm run smoke:discord
npm run smoke:discord:port
npm run smoke:discord:legacy
npm run sync:discord-commandsIf you are running picord from the local source tree in tmux on a VPS, the usual update cycle is:
cd /path/to/picord
npm run build
npm run sync:discord-commands
tmux kill-session -t picord 2>/dev/null || true
tmux new-session -d -s picord 'cd /path/to/picord && set -a && source .env && set +a && exec pi -e ./src/index.ts --no-session'- Full guild message flow depends on Message Content Intent being enabled on the Discord app
- DMs and unmapped channels still fall back to the configured default
cwd - OpenAI Codex login prefers the native headless device flow and polls automatically after you enter the one-time code on OpenAI's verification page; the manual paste flow remains only as a fallback if OpenAI falls back to browser completion
- Bash safety is path-based and conservative; direct tool access is guarded more strictly than arbitrary shell behavior