Files
cproof-context/patterns/commands.md
jabber.developer2 22977846a3 docs: split context into layered, agent-oriented files
Replace the single file-structure.md with a stratified layout designed
for AI/agent skill consumption: tables and concrete identifiers over
prose, files loaded on demand, content separated by churn rate.

Layers:
- architecture/  stable structural reference (overview, source-map,
                 test-map, data-flow)
- patterns/      memory, commands, autocomplete, events, xmpp,
                 encryption, ui, plugins
- testing/       unit-tests, stubs, functional-tests, bench
- build/         local, docker, ci
- playbooks/     add-command, add-test, add-autocomplete,
                 add-event-handler, add-encryption
- gotchas.md     append-only dated entries (seven seed entries)
- wip/           branch-specific notes; deleted on merge to master

Stable layers describe cproof on master only. In-flight feature
branches (currently feat/ai) get a single file under wip/.

INDEX.md is the entry map with churn labels; SKILL.md is the
always-loaded skill hint pointing to it.
2026-04-30 20:52:06 +03:00

3.8 KiB

Commands

Three files, three concerns

File Holds
src/command/cmd_defs.c (cmd_defs.h) The big static array of Command records — every /foo is one entry.
src/command/cmd_funcs.c (cmd_funcs.h) Handler implementations: gboolean cmd_<name>(ProfWin*, const char* const, gchar**).
src/command/cmd_ac.c (cmd_ac.h) Per-command autocomplete logic and registration.

Anatomy of a command record

Defined inline in cmd_defs.c using uppercase macros (declared in cmd_defs.c):

{ CMD_PREAMBLE("/caps",
               parse_args, 0, 1, NULL)
  CMD_MAINFUNC(cmd_caps)
  CMD_TAGS(
          CMD_TAG_DISCOVERY,
          CMD_TAG_CHAT,
          CMD_TAG_GROUPCHAT)
  CMD_SYN(
          "/caps",
          "/caps <fulljid>|<nick>")
  CMD_DESC("...")
  CMD_ARGS(
          { "<fulljid>", "..." },
          { "<nick>",    "..." })
  CMD_EXAMPLES(
          "/caps user@host/res")
}
Macro Role
CMD_PREAMBLE(name, parse_fn, min, max, pre_hook) Command name, parser, arg count bounds, optional pre-hook.
CMD_MAINFUNC(fn) Single handler for the whole command.
CMD_SUBFUNCS({"sub", fn}, ...) Dispatch by first argument. Use instead of CMD_MAINFUNC when the command has subcommands (e.g. /status get, /status set).
CMD_TAGS(...) One or more CMD_TAG_* (UI grouping, search).
CMD_SYN(...) Synopsis lines for /help <cmd>.
CMD_DESC(...) Long description.
CMD_ARGS({"name", "desc"}, ...) Argument table for help.
CMD_EXAMPLES(...) Examples shown in help.

The full set of CMD_* macro definitions is at the top of cmd_defs.c — read them when authoring a new command.

Argument parsing

parse_args() lives in src/tools/parser.c:

gchar** parse_args(const char* const inp, int min, int max, gboolean* result);
  • Splits on whitespace, honours quoted segments.
  • Returns NULL on parse failure (sets *result = FALSE).
  • The handler receives the result as gchar** args — free is owned by the caller in cmd_defs.c, the handler does not free.

Parser variants used in CMD_PREAMBLE:

  • parse_args — standard quoted-aware split.
  • parse_args_with_freetext — last argument is the rest of the line, no splitting (used by /msg, /me, etc.).
  • parse_args_as_one — concatenates remaining tokens into a single string.

Handler signature

gboolean cmd_foo(ProfWin* window, const char* const command, gchar** args);

Return TRUE on success, FALSE to print bad-usage. command is the literal /foo typed (useful when the same handler is registered under multiple names).

Connection-state gates

Most commands that touch XMPP must verify connection state first:

if (connection_get_status() != JABBER_CONNECTED) {
    cons_show("You are not currently connected.");
    return TRUE;
}

cons_bad_cmd_usage(cmd) is the canonical "wrong arguments" path; pass the literal command name.

Adding a new command

See playbooks/add-command.md for the full walkthrough. Short version:

  1. New entry in cmd_defs.c with CMD_PREAMBLE/CMD_MAINFUNC/...
  2. Implement cmd_<name>() in cmd_funcs.c (declare in cmd_funcs.h).
  3. (If autocompletion needed) implement _<name>_autocomplete() in cmd_ac.c and register it via g_hash_table_insert(ac_funcs, "/name", ...).
  4. Add a unit test pair tests/unittests/test_cmd_<name>.{c,h} and register tests in tests/unittests/unittests.c.

Conventions

  • cmd_<name> for handlers; _<name>_autocomplete (file-static) for the autocomplete callback.
  • Subcommand dispatch via CMD_SUBFUNCS is preferred over if/else chains inside cmd_<name> when possible.
  • Keep handlers in cmd_funcs.c thin: parse & validate args, then delegate to a domain module (xmpp/, config/, ui/).