StaticallEvery loaded CommandDefinition — the whole filename-keyed cache,
not the per-giver affordance set. Populated by preloadAll at
boot (which runs before HelpCatalogue warms), so the help index's
commands projector sees the complete verb roster. Returns a snapshot
array; ordering follows insertion.
StaticapplyApply the cardinality / onExcess / onShortage policy to a resolved MQL candidate list.
Decision matrix:
type=object (implicit { exactly: 1 }):
0 matches → pass through (resolveModel lands stuff=null)
1 match → pass through
>1 match → onExcess decides:
'top' → first match wins (default)
'error' → controller-rejected:ambiguous, null
'prompt' → await PromptApi.mqlObject; player
picks; cancel propagates as
PromptCancelledError (caught in
CommandGiver). No Interactive on
the context degrades to ambiguous.
type=objects (default { min: undefined, max: undefined }):
under min → onShortage='error', controller-rejected:insufficient
over max → onExcess decides:
'truncate' → cut to max
'error' → controller-rejected:too-many, null
'prompt' → await PromptApi.mqlMany; player
picks within bounds; cancel
propagates as PromptCancelledError.
in-range → pass through
The async prompt paths only fire when an Interactive is attached to the context (Avatar dispatch). Programmatic / scripted dispatch paths fall back to the degrade-to-error branch, since there's nobody to ask.
Returns the filtered stuff list, or null when the policy
dictates failure (an error note has been added to the
context). Throws PromptCancelledError when the player
cancels — the caller (CommandGiver) catches and emits a
cancelled-shape controller-rejected note.
StaticapplyRecency-stack delta for a successful containment move. Source- side pops, dest-side pushes; if the moving item is itself a CommandGiver, its env+peers slice is rebuilt from the new neighborhood.
Called by ContainmentApi.move after setContainer succeeds,
before notification hooks fire.
StaticapplyRecency-stack delta for a hosted-update host/unhost (the aether
hosting relation). A hosted update contributes its
self-bucket command definitions to its host's stack with the
update Stuff as the recency source, so getAffordances() resolves
commandSource to the update (the "verb dispatch routes through
the augment/update" pattern). Hosting surfaces the verbs;
unhosting retires them — gain/lose-post-spawn live.
Called by AetherMixin.hostUpdate / unhostUpdate.
StaticapplyApply a bar's input-mode prefix to a raw command line — the load-bearing pre-tokenize step on the command-entry hot path (server-authoritative input mode, per-bar).
Pure and total: given the raw text and the resolved prefix for the submitting bar, return the text the interpreter should actually dispatch. Three rules, in order:
/-escape → strip the leading slash and run the rest raw
(a one-off un-moded command; /look lexes as look).mode-management → the mode verb itself is exempt, so
mode off / mode chat x always reach the interpreter
un-prefixed regardless of the active mode.Otherwise the prefix is prepended: chat-mode + hello →
chat hello. Kept here (not in msh) so the tokenizer stays
Stuff-unaware; the per-bar lookup happens at the call site.
StaticapplyRecency-stack delta for a shadow attach/detach. A shadow whose
class declares commandContributions lands on the host's stack
(and on reachable peers' stacks per bucket) on attach; detach
pops the shadow from every giver.
Called by ShadowApi.attach and ShadowApi.detach around the
atomic install/remove.
StaticassembleBind a ParsedCommand to a CommandDefinition.
Two-tier option scope: tokens before the subcommand are bound
against the verb-scoped options; tokens after are bound against
the active subcommand's options. Positional fields fill the
fields: block in insertion order. A greedy: true field grabs
the slice of parsed.source from the first unconsumed positional
through end-of-input, preserving whitespace and quotes-as-literal
(per spec §2.4).
Returns { error: 'shape', summary } for cases the chain should
fall through on (pattern doesn't fit, unknown subcommand,
leftover positionals, missing required positional). Returns
{ error: 'bind', summary } for cases that stop the chain
(unknown option, malformed option value, repeated non-multi
option, boolean given a value, value-bearing option missing its
value).
StaticassembleBuild a CommandModel from a structured-form payload (widget
input). The structured path skips parse/match: the client has
already chosen verb/subcommand and field keys. The matcher
still validates field-name legality and runs type coercion;
type: object fields go through MQL in resolveAndValidate
just like the text path.
StaticclearClear the filename cache. Used by tests; production should
prefer invalidate(filename) to drop a single entry when a
YAML changes on disk.
StaticcollectCollect each hosted update's self-bucket command definitions for
a host, paired with the update as the affording source. Used by
CommandGiverMixin's self-seeding (both postRegister and the
lazy _ensureSelfEntry safety net) so a host that gained updates
outside a delta (e.g. a test that hosts then reads affordances)
still surfaces their verbs. Returns [] for a non-host.
StaticcollectCollect the self-bucket command contributions from a class
chain. The concrete class wins over its mixins; mixins later in
the prototype chain (closer to Object) lose to earlier ones.
Used at host registration to seed the 'self' recency entry.
StaticcreateConstruct a fresh CommandContext for one dispatch attempt.
The dispatcher mints a per-_executeOne context so the
accumulator captures exactly the claiming attempt's notes —
see CommandGiverMixin._runChain. Tests use this to
drive controllers directly with a synthetic ctx.
StaticemitEmit a system.commands.{added,removed,reset} frame to a
recipient. Stamps commandId / causingCommandId from the
ambient ExecutionContext when present (so a recency-stack
mutation triggered inside a command is auto-attributed). Skips
silently when the recipient isn't a Sensor — schema delivery is
best-effort.
StaticforceProgrammatic command invocation — fire text on giver exactly
as if the player had typed it, but stamp forced: true on the
resulting Command frame so hooks can tell the two apart.
Used by:
look after a successful
traversal), so the dispatcher's normal updates_focus path
re-anchors the focus chain for the new room.Player-typed commands continue to flow through executeCommand
directly with forced defaulting to false. Hooks that need to
distinguish (e.g., a cinematic-locked NPC blocking auto-look)
walk the stack via ExecutionContextApi.getCommandStack
and look for forced ancestors.
StaticgetGet a command definition by filename, loading it if not cached. Returns the cached instance on repeat calls so the recency-push pipeline never re-parses the same YAML.
StaticgetProject a CommandDefinition to a wire-safe schema payload for
client-side widget rendering. Used by system.commands.{added, reset}.
StaticinvalidateDrop the cached CommandDefinition for one YAML so the next
getCommand(filename) re-reads from disk. The escape hatch
for dev edits — command YAMLs don't auto-reload (the cache
outlives file edits), so the workflow is: edit YAML → call
CommandApi.invalidate('foo.yaml') → next push reloads.
Note: live recency-stack entries that already hold a reference
to the old CommandDefinition keep using it until they pop.
The next applyContainmentDelta / applyShadowDelta push will
pick up the reloaded definition.
Returns true if the entry existed and was removed.
StaticoverlayOverlay a structured body side-channel (fields) onto an
already-bound model, restricted to the command's payload:-block
fields + the designated body field (a greedy string positional
arg). Options/flags and object/MQL selectors are unreachable — the
structural narrowness that keeps fields from filling a selector.
Called by CommandGiver.executeCommand after the model is bound from
the parsed string and BEFORE resolveModel, so the same resolve →
validate → controller → envelope chain runs. The command string is
always parsed first; this is purely additive, never a string-less
dispatch path. Tiebreak: fields wins when both inline-greedy and
the side-channel supply the body.
StaticpreloadEager boot-time load: walk every YAML under mud/cmd/, parse
it, and resolve every validator reference into a live function.
After preload, every cached CommandDefinition has its
FieldDefinition._resolvedValidators populated.
Returns the count of YAMLs that loaded successfully and the list of files that failed (parse error, validator-resolve error, etc).
StaticpreloadWalk every validator attached to command (verb-level + field +
verb-option + payload + per-subcommand) and await any preload
hooks they declare. Idempotent — StuffApi.singleton(path) is
a no-op if the singleton already exists.
The dispatcher calls this AFTER resolveModel (so field
validators get the bound MQL result, not the raw query string)
and BEFORE runValidators (so sync validators see a populated
singleton cache). Validators without a preload are skipped.
Preload signatures mirror the validators' sync bodies — verb-
level preloads receive (context), field-level preloads receive
(value, field, context). Field preloads inspect the bound
value to compute per-target deps (e.g. a
requiresAnimateTarget preload reads the bound Stuff's
_speciesPath).
MQL path-literal preloading (e.g. ensuring /lib/species/...
referenced in a :race(...) filter is live) is NOT covered
here; it lands when a verb actually needs it. Today the only
preload consumer is requiresAnimate.
Optionalsubcommand: stringStaticresolveBack-compat wrapper: resolve MQL then run validators in a single
sync call. NOT used by the dispatcher (which interleaves an
async validator-preload phase between MQL resolve and the sync
validator phase via preloadValidatorDeps). Kept for tests and
one-off callers that want the combined sync surface.
StaticresolveResolve a verb-level validator spec to a live CommandValidator.
Same path-resolution as resolveValidator, but the runtime
signature is (context) => string | undefined rather than the
field-level (value, field, context) => ….
StaticresolveRun MQL resolution on type: object / type: objects fields and
options. Returns the bound model with MqlOneResult /
MqlManyResult wrappers where strings used to be; the rest of
the fields pass through.
Does NOT run validators — that's runValidators. The split
exists so the dispatcher can insert an async preload phase between
MQL resolution and validation: field validator preloads need the
resolved value to compute their deps (e.g. requiresAnimateTarget
reads the bound target's _speciesPath).
Async since applyCardinalityPolicy (called per resolved field)
can push a PromptApi prompt and await the player's pick. The
await propagates PromptCancelledError to the caller.
Reads command from context; the active subcommand (if any)
is read from model.subcommand, which the matcher stamped at
bind time.
StaticresolveResolve a parser spec to a Parser instance.
Spec conventions:
/) → <src>/mud/lib/command/parsers/<name>.ts./X) → <src>/mud/X.ts.The default framework parser is 'msh' (Mud SHell — the
tokenizer-driven shell). Custom parsers can live anywhere under
mud/; reference them by absolute path from the
shell.parser setting.
StaticresolveResolve a YAML-declared validator reference to its
FieldValidator function.
Path conventions:
/X → <src>/mud/X.ts (mud-rooted absolute)../X, ../X → relative to fromYaml's directory.Bare names and package specifiers are rejected — the path tells you exactly where the validator lives, no implicit search paths.
The JS module cache handles repeat loads; no bespoke registry.
StaticrunRun every sync validator attached to command against the
already-resolved model in context. Order matches the bind
pipeline: verb-level first, then field, option, payload, and
per-subcommand option validators. First failure short-circuits
with a structured validator-failed note on context.
Companion to resolveModel — call this after MQL resolution (and after the dispatcher's async preload phase) so field validator sync bodies see bound values.
Optionalpreloads: ValidatorPreloadsStaticvalidateValidate value against a JSON Schema fragment. Returns a
friendly error string on failure, null on success. Compiled
validators are cached by JSON-stringified schema so repeated
calls against the same fragment skip recompilation.
Used by the matcher's struct path and by WriteController, which
reads a class's static dataSchema after the (async) class load
and validates with the same machinery.
CommandApi - Static command definition cache
Production dispatch never queries the cache directly; it walks each giver's recency stack (
CommandGiverMixin.getAffordances()) and filters by verb, keeping each match paired with its affording source. The filename-keyed map is just a 'load once, reuse' sharing layer between the recency-push helpers and the YAML preload pass.This Api is a thin, security-gated forwarding shell: the logic lives in the hot-reloadable CommandLogic singleton at
/obj/api/command, reached synchronously viaStuffApi.singletonSync.dest /obj/api/commandreloads it.