A consumer cannot express dense argument syntax, and there is no prefix-command surface
The contract expresses slash commands only. A consumer that wants dense, expert-shaped input — repeated values, open-ended arity, flags interleaved with positionals — cannot express it, and there is no concept of a command arriving as ordinary message text.
The two mechanisms, and why one does not cover the other
Slash commands, which the contract has: registered ahead of time through RegisterCommands, the platform renders the UI, validates types before dispatch, and delivers named options. Discoverable, validated, and needs no privileged intent.
Prefix commands, which it does not: the bot reads ordinary messages and treats anything starting with a sigil as a command. !attack goblin -t goblin2 -t goblin3 -d 1d6 -rr 3. The bot receives raw text and does all the parsing itself.
They are not substitutes. Discord slash options cap at 25 per command, are fixed-arity, and permit exactly two levels of nesting. A command wanting eleven targets, or an arbitrary number of modifiers, cannot be expressed as options at all.
avrae is the reference for how far this can be taken: repeated flags accumulating into lists, ephemeral flags with usage counts (-d1 applies once, -d3 three times), quoted values with escaping, and positional words interleaved freely with flags. Its utils/argparser.py is the artefact worth reading.
What it would cost, stated up front
Prefix commands require the Message Content privileged intent. Discord restricted it in 2022, and a bot in more than 100 guilds needs approval to hold it. Slash commands need nothing.
That is not a detail to discover later. This module already declares intents from Needs, and NeedMessages exists — so the cost is expressible, but it is a real one and it lands on every consumer that opts in.
The estate has also been caught once already making a comfortable claim about what a server owner can see of a bot's intents (see AGENTS.md). Anything written about the privacy posture of message-content access should be checked against a real consent screen rather than reasoned from documentation.
Include a spike on pflag
Whether to hand-roll the grammar or reuse an existing parser is genuinely open, and three things are already established:
- Stdlib
flagis disqualified. It stops parsing at the first non-flag argument, soattack goblin -t xnever sees-t. Chat grammars interleave constantly. It also has no repeated-flag accumulation without a customValue, and wants flags pre-declared against pointer targets, which fights a declarative command set. pflagis functionally viable — interspersed parsing by default, slice types for repeats. Its cost is a second dependency, against a module whose stated point is thatgo.modrequires exactly one. Worth notingdepfootprint_test.gowould not catch it: the guard names platform SDKs and websockets, not general dependencies, so the property is documented rather than enforced.- Neither matches chat conventions anyway.
--flag=value,-abcbundling and--terminators are POSIX shapes. Quoted values, repeated accumulation and ephemeral counts are what people actually type into a chat client.
The spike should establish whether pflag earns the dependency once wrapped, or whether a small purpose-built parser is less code than the wrapper would be.
A question the spike must answer, not assume
Does the grammar parse message text, a slash string option, or both? They are different entry points with different trust properties: a slash option arrives through a platform that validated everything around it, while message text is wholly untrusted and the contract already withholds rendering for that reason. Deciding this changes whether the parser takes a string or an Interaction.
Where it might live, not a proposal
Options considered and deliberately not chosen yet: a subpackage such as chat-platform/args, keeping the root package a pure contract and go.mod unchanged; its own module, usable outside the chat family; or the consumer's own problem.
Provenance
Derived rather than reported. It came out of designing the slash-command trio (#16 (closed), #17 (closed), #18 (closed)) against scoutdm's spec 0027, whose surface is roughly twenty-five commands. avrae's parser was read on 2026-08-31; the Discord limits above are from #18 (closed)'s measurements against the real API and are not re-verified here.
Nothing is blocked on this. The trio is what scoutdm needs today, and this is deliberately deferred until typed options exist and the genuine gap can be seen rather than anticipated.