picord is a Discord integration for pi / pi-mono.
npm package: @venthezone/picord
It lets you use 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
If you remember one thing, remember this: channel = workspace, thread = session.
discord-port is the only runtime.
- a Discord bot that runs pi as an 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 flows 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
Setting up with a coding agent (pi)? Point it at INSTALL.md — it's written as an agent-executable guide: it lists exactly what to ask the user for (bot token, user ID, guild ID), how to create the Discord app, and how to verify the setup. The steps below are the same flow, summarized for manual setup.
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 or remote setup, run picord in its own tmux session from the local repo source.
Recommended workflow:
cd /path/to/picord
npm run picord:startUseful runtime commands:
# show tmux + systemd state
npm run picord:status
# show recent tmux output
npm run picord:logs
# restart after code changes
npm run picord:restart
# stop picord
npm run picord:stopThe wrapper script keeps startup predictable by:
- requiring
.env - running
npm run sync:discord-commands - stopping the
picordsystemd service first if it is active - recreating the tmux session cleanly
If you want to inspect the live tmux session directly:
tmux attach -t picordThis is the recommended way to keep picord running on a remote machine while still using the live 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_EXA_API_KEY=your_exa_api_key{
"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 foldersallowedChannelIds: 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 treeexaApiKey: Optional Exa API key to skip OAuth flow; Exa works without it via MCP OAuth
Picord includes built-in MCP servers that are automatically enabled. These load in addition to any servers configured in ~/.pi/mcp.json.
Exa provides AI-powered web search, code search, and company research tools. Exa is always enabled — no API key required. On first use, Exa's hosted MCP server initiates an OAuth flow so you can authenticate in-browser for free access.
Configuration (optional):
If you have an Exa API key and want to skip the OAuth flow, set it via environment variable:
export PICORD_EXA_API_KEY=your_exa_api_keyOr in picord.config.json:
{
"exaApiKey": "your_exa_api_key"
}Get your API key at dashboard.exa.ai/api-keys.
Requires @modelcontextprotocol/sdk with streamable HTTP transport support (v1.9.0+). If the SDK version doesn't support streamable HTTP, the Exa server is silently skipped.
- 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
That is the main flow for normal project use.
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 workspace, create a thread, and resume the selected session there.
DMs work as a direct session using the configured default workspace. This is useful for quick personal tasks when you do not need a guild project channel.
/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 commands 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 smoke:discord
npm run sync:discord-commandsIf you are running picord from the local source tree in tmux on a VPS, use the runtime wrapper script for a consistent setup:
cd /path/to/picord
npm run picord:start
npm run picord:status
npm run picord:logs
npm run picord:restart
npm run picord:stopThe wrapper script:
- requires
.env - runs
npm run sync:discord-commandson start - stops the
picordsystemd service if it is active so you do not run duplicate bot processes - recreates the tmux session cleanly
- 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