| name | Discord |
| description | Post messages, read channels, and manage a Discord guild from the terminal — roles, channels, permission overwrites, threads, scheduled events, webhooks, guild-level settings, and a declarative snapshot/diff/apply loop. USE WHEN discord, post to discord, send message, notify channel, read discord, check discord, update discord, discord thread, discord channel, announce, discord role, channel permissions, guild layout, snapshot guild, apply layout. |
Discord Skill
Discord CLI for posting updates, reading channels, and managing a whole guild —
like gh for GitHub.
"From PAI to Discord. Discord to PAI is done through Grove."
CLI Tool
Discord uses a CLI at ~/bin/discord. All commands are bash commands:
discord post "Your message here"
discord post --channel tasks "PR merged"
discord read
discord channels
discord threads
Run discord --help for full command list.
Multi-server (posting to a guild other than the default)
The bot can be in several guilds with the same token. Target another guild
either by a one-off --guild <id> (overrides the guild used for channel/thread
name resolution) or by a saved --server <name> profile:
discord post --guild 123456789012345678 --channel general "Deployed to staging"
discord config set-server myserver 123456789012345678 general
discord post --server myserver "Deployed to staging"
discord read --server myserver
Precedence: explicit --guild/--channel flags > --server profile >
top-level config. With neither flag, behaviour is identical to single-guild.
Cached channels.<name>.id entries are used only when their owning guild
matches the selected --guild/--server context; otherwise the CLI resolves
the name inside the selected guild or fails loudly.
Guild management
Beyond post/read, the CLI manages a guild's whole structure: roles, channels,
permission overwrites, threads, scheduled events, webhooks, and
guild-level settings — plus a declarative snapshot → diff → apply loop for
authoring a guild from a YAML layout. Run discord guild --help for the group
list; the command map below is the full surface.
Safety defaults — read before you mutate a guild
These are shipped behaviours, not conventions. They cannot be turned off:
guild apply is a dry run by default. Without --execute it prints the
ordered plan and mutates nothing. You must add --execute to touch the
guild. A dry run always ends with Dry run (no --execute): nothing was changed.
- Deletion is opt-in twice over.
guild apply never deletes a live
resource just because it is missing from the layout — that resource is reported
unmanaged and left alone. Removing something requires both a prune:
block in the layout naming it and the --prune flag. One without the other
deletes nothing.
- An unresolved layout placeholder refuses the run. A layout may carry
__NAME__ placeholders for ids it must not commit (a bot mention in a channel
topic). They resolve from the environment, or from --var NAME=VALUE (which
wins), before the diff. If any are unresolved — including set-but-empty —
guild diff and guild apply both exit 1 naming them, before any Discord
call. A literal __NAME__ is never written into a guild.
- Destructive single commands require confirmation.
role delete,
channel delete, and event delete refuse to run without -y/--yes;
webhook delete needs -y/--yes too. Managed roles (bot / integration /
booster) are refused outright — Discord owns their lifecycle.
- Credentials never appear in output.
webhook create prints the webhook URL
once (it is a posting credential — store it yourself; the CLI never saves
it); webhook list never shows token values; guild snapshot never serializes
webhook tokens. The bot token lives only in cli.yaml, never in output.
Command map
Every group takes -g/--guild <id> and -s/--server <name> to target a guild
other than the config default (same precedence as post, see above).
role — assign/remove on members, and manage the role list:
discord role list
discord role add -m <userId> -r Builder
discord role remove -m <userId> -r Builder
discord role create -n Builder -c '#E67E22' --hoist --mentionable
discord role edit -r Builder -n Maker
discord role reorder -r Builder -p 5
discord role delete -r Builder -y
channel — create/edit/delete channels and categories, list, and manage forum tags:
discord channel create -n workshop-floor -t text -p Workshop --topic "Members' floor"
discord channel create -n Workshop -t category
discord channel edit -c workshop-floor --slowmode 10
discord channel list
discord channel tags set -c build-logs -t wip -t shipped -t help-wanted
discord channel tags list -c build-logs
discord channel delete -c old-channel -y
perms — channel permission overwrites (deny @everyone, allow a role):
discord perms set -c strategy -r '@everyone' --deny VIEW_CHANNEL
discord perms set -c strategy -r Builder --allow VIEW_CHANNEL,SEND_MESSAGES
discord perms show -c strategy
discord perms sync -c strategy
discord perms clear -c strategy -r Builder
The overwrite is replaced (allow/deny are the complete masks for that target),
and a bot can only grant permissions it itself holds.
thread — create and manage guild threads (active listing is discord threads):
discord thread create -c workshop-floor -n "Build log: v0.6.0"
discord thread add -t <threadId> -m <userId>
discord thread remove -t <threadId> -m <userId>
discord thread archive -t <threadId>
discord thread unarchive -t <threadId>
discord thread list -c workshop-floor --archived
forum — board-native forum posts (a forum post IS a thread + starter message):
discord forum tags -c quest-board
discord forum post -c quest-board --title "Quest: fix the boiler" \
--tags quest:open,help-wanted "Reward: 3 favor. Claim below."
discord forum posts -c quest-board
discord forum posts -c quest-board --tag quest:open
discord forum retag -t <threadId> --add in-progress --remove quest:open
discord forum retag -t "Quest: fix the boiler" --set done
Tags are given by NAME (case-insensitive against the forum's tag set; manage the
set with channel tags set). Follow-ups need no new verbs — the printed id is a
thread id, so discord post --thread <id> and discord read --thread <id> work.
retag is the quest-board state machine (--set replaces; --add/--remove
modify incrementally and are mutually exclusive with --set); a --thread NAME
matches active threads only — retag archived posts by id. Moderated tags need
the bot to have Manage Threads (Discord enforces it; a 403 says so).
event — guild scheduled events (RSVP-able events):
discord event create -n "Team meetup" --start 2026-08-01T19:00:00+12:00 --voice general
discord event create -n "Meetup" --start 2026-08-01T19:00:00+12:00 \
--location "Wellington" --end 2026-08-01T21:00:00+12:00
discord event list
discord event rsvps --event <eventId>
discord event edit --event <eventId> -n "New name"
discord event delete --event <eventId> --yes
webhook — per-message identity; the URL is a credential shown once:
discord webhook create -c announcements -n "Release Bot"
discord webhook list
discord webhook exec -u <webhookUrl> -m "Shipped v0.6.0" -a "Release Bot"
discord webhook delete -w <webhookId> -y
guild — guild-level settings and the declarative layout loop:
discord guild show
discord guild edit --verification low --rules-channel welcome
discord guild community-enable --rules-channel welcome --updates-channel mod-log
discord guild welcome show
discord guild welcome set --enabled --channel 'welcome::Start here'
discord guild onboarding show
discord guild onboarding set --file onboarding.json
discord guild snapshot -o guild.snapshot.yaml
discord guild diff --layout guild-layout.yaml
discord guild apply --layout guild-layout.yaml
discord guild apply --layout guild-layout.yaml --execute
discord guild apply --layout guild-layout.yaml --var ESCORT_BOT_ID=<id> --execute
For the end-to-end guild-authoring walkthrough, read
GuildSetup.
Workflow Routing
When executing operations:
-> READ: The workflow file first
-> EXECUTE: Follow the workflow steps
| Action | Workflow | Trigger Examples |
|---|
| Post | Post | "post to discord", "send message to discord", "notify channel", "announce" |
| Read | Read | "read discord", "check discord messages", "what's happening on discord" |
| GuildSetup | GuildSetup | "set up a guild", "author a guild layout", "snapshot the guild", "apply a layout", "diff the guild" |
Setup
If discord config show returns empty, guide the user:
discord config set botToken <token>
discord config set guildId <id>
discord config set defaultChannel <name>
For a second guild the bot has joined (e.g. a staging server), register a profile:
discord config set-server myserver <guildId> [defaultChannel]
Or hand-edit the servers: block in the config file:
servers:
myserver:
guildId: "123456789012345678"
defaultChannel: general
Config stored at ~/.config/metafactory/cortex/cli.yaml (read canonical-first with ~/.config/cortex/cli.yaml then ~/.config/grove/cli.yaml as legacy fallbacks during the XDG transition; first write migrates the legacy copy canonical-side).
Attachments
discord post --file <path> attaches a local file (repeatable); the message text is optional when a file is present. Files are read + existence-checked before the post, so a bad path posts nothing.