10 KiB
10 KiB
name, description
| name | description |
|---|---|
| command-system | Use when writing, modifying, or debugging CProof command functions (cmd_funcs.c), command definitions (cmd_defs.c), or autocomplete (cmd_ac.c). Documents the command dispatch architecture, critical argument indexing rules (args[0] contains subcommand name), and common patterns. |
CProof Command System
When to Use This Skill
Use this skill when:
- Writing new command handler functions in
cmd_funcs.c - Adding/modifying command definitions in
cmd_defs.c - Implementing autocomplete for commands in
cmd_ac.c - Debugging command argument parsing issues
Core Architecture
Command Structure (Command)
typedef struct cmd_t {
gchar* cmd; // e.g., "/ai"
gchar** (*parser)(const char*, int, int, gboolean*); // parse_args, parse_args_with_freetext, parse_args_as_one
int min_args; int max_args;
void (*setting_func)(void); // Shows current settings in console (or NULL)
struct { const char* cmd; gboolean (*func)(ProfWin*, const char*, gchar**); } sub_funcs[50];
gboolean (*func)(ProfWin*, const char*, gchar**); // Main handler (or NULL)
CommandHelp help; // Tags, synopsis, desc, args, examples
} Command;
Help Structure (CommandHelp)
typedef struct cmd_help_t {
gchar* tags[20]; // Command categories (NULL-terminated)
gchar* synopsis[50]; // Usage examples (NULL-terminated)
gchar* desc; // Description text
gchar* args[128][2]; // Argument names and descriptions ({NULL,NULL}-terminated)
gchar* examples[20]; // Example commands (NULL-terminated)
} CommandHelp;
Available Tags (cmd_defs.c:82-89)
| Tag | Description |
|---|---|
CMD_TAG_CHAT |
Chat commands |
CMD_TAG_GROUPCHAT |
Group chat commands |
CMD_TAG_ROSTER |
Roster management |
CMD_TAG_PRESENCE |
Presence commands |
CMD_TAG_CONNECTION |
Connection management |
CMD_TAG_DISCOVERY |
Discovery commands |
CMD_TAG_UI |
UI display commands |
CMD_TAG_PLUGINS |
Plugin commands |
Dispatch Flow (_cmd_execute())
User: /ai set provider openai https://api.openai.com
|
v
parse_args("set provider openai https://...", 0, 5) → {"set","provider","openai","https://...",NULL}
|
v
args[0]="set" matches cmd->sub_funcs[0].cmd="set"
|
v
cmd_ai_set(window, "/ai", args) ← args[0] is "set", NOT "provider"!
CRITICAL: Subcommand functions receive the FULL args array. args[0] contains the subcommand name itself. All parameter accesses must be shifted +1.
Argument Indexing Reference
| User Input | args[0] | args[1] | args[2] | args[3] |
|---|---|---|---|---|
/ai |
NULL | - | - | - |
/ai set provider openai url |
"set" |
"provider" |
"openai" |
"url" |
/ai set token openai sk-xxx |
"set" |
"token" |
"openai" |
"sk-xxx" |
/ai start openai/gpt-4o |
"start" |
"openai/gpt-4o" |
- | - |
/ai remove provider foo |
"remove" |
"provider" |
"foo" |
- |
/tls trust |
"trust" |
- | - | - |
/connect account server foo |
"account" |
"server" |
"foo" |
- |
Correct Subcommand Function Pattern
gboolean cmd_ai_set(ProfWin* window, const char* const command, gchar** args)
{
// args[0] = "set" (subcommand name)
// args[1] = next subcommand (provider/token/org)
if (args[1] == NULL) { cons_bad_cmd_usage(command); return TRUE; }
if (g_strcmp0(args[1], "provider") == 0) {
// args[2] = name, args[3] = url
if (g_strv_length(args) < 4) { cons_bad_cmd_usage(command); return TRUE; }
ai_add_provider(args[2], args[3], NULL);
cons_show("Provider '%s' configured with URL: %s", args[2], args[3]);
} else if (g_strcmp0(args[1], "token") == 0) {
// args[2] = provider, args[3] = token
if (g_strv_length(args) < 4) { cons_bad_cmd_usage(command); return TRUE; }
ai_set_provider_key(args[2], args[3]);
} else if (g_strcmp0(args[1], "org") == 0) {
// args[2] = provider, args[3] = org_id
if (g_strv_length(args) < 4) { cons_bad_cmd_usage(command); return TRUE; }
AIProvider* p = ai_get_provider(args[2]);
if (p) { g_free(p->org_id); p->org_id = g_strdup(args[3]); }
} else {
cons_bad_cmd_usage(command); return TRUE;
}
cons_show("");
return TRUE;
}
Common Bug (WRONG)
// BUG: args[0] is "set", not "provider"
if (g_strcmp0(args[0], "provider") == 0) { ... }
// BUG: args[1] is "provider", not the name
ai_add_provider(args[1], args[2], NULL);
Command Definition Pattern (cmd_defs.c)
Macros (cmd_defs.c:91-98)
#define CMD_PREAMBLE(c, p, min, max, set) .cmd = c, .parser = p, .min_args = min, .max_args = max, .setting_func = set,
#define CMD_MAINFUNC(f) .func = f,
#define CMD_SUBFUNCS(...) .sub_funcs = { __VA_ARGS__, { NULL, NULL } },
#define CMD_TAGS(...) .help.tags = { __VA_ARGS__, NULL },
#define CMD_SYN(...) .help.synopsis = { __VA_ARGS__, NULL },
#define CMD_DESC(d) .help.desc = d,
#define CMD_ARGS(...) .help.args = { __VA_ARGS__, { NULL, NULL } },
#define CMD_EXAMPLES(...) .help.examples = { __VA_ARGS__, NULL }
Example Definition
{ CMD_PREAMBLE("/ai", parse_args, 0, 5, NULL)
CMD_SUBFUNCS({ "set", cmd_ai_set }, { "remove", cmd_ai_remove },
{ "start", cmd_ai_start }, { "clear", cmd_ai_clear },
{ "correct", cmd_ai_correct }, { "providers", cmd_ai_providers })
CMD_MAINFUNC(cmd_ai)
CMD_TAGS(CMD_TAG_CHAT)
CMD_SYN("/ai", "/ai set provider <name> <url>", "/ai set token <provider> <token>")
CMD_DESC("Interact with AI models via OpenAI-compatible APIs.")
CMD_ARGS({ "", "Display current AI settings" },
{ "set provider <name> <url>", "Add or update a provider" })
CMD_EXAMPLES("/ai", "/ai set token openai sk-xxx", "/ai start openai/gpt-4o")
},
Autocomplete Pattern (cmd_ac.c)
Initialization
// In cmd_ac_init():
autocomplete_add(ai_subcommands_ac, "set");
autocomplete_add(ai_subcommands_ac, "remove");
autocomplete_add(ai_set_subcommands_ac, "provider");
autocomplete_add(ai_set_subcommands_ac, "token");
autocomplete_add(ai_set_subcommands_ac, "org");
g_hash_table_insert(ac_funcs, "/ai", _ai_autocomplete);
Callback
static char* _ai_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{
char* result = NULL;
result = autocomplete_param_with_ac(input, "/ai", ai_subcommands_ac, TRUE, previous);
if (result) return result;
result = autocomplete_param_with_ac(input, "/ai set", ai_set_subcommands_ac, TRUE, previous);
if (result) return result;
// Dynamic: provider names from ai_list_providers()
return NULL;
}
Common Patterns
Window Type Access
ProfAiWin* aiwin = (window && window->type == WIN_AI) ? (ProfAiWin*)window : wins_get_ai();
// Or switch:
switch (window->type) {
case WIN_CHAT: { ProfChatWin* w = (ProfChatWin*)window; /* w->barejid */ break; }
case WIN_MUC: { ProfMucWin* w = (ProfMucWin*)window; /* w->roomjid */ break; }
}
Connection Check
if (connection_get_status() != JABBER_CONNECTED) {
cons_show("You are not currently connected.");
return TRUE;
}
Options Parsing
gchar* opt_keys[] = { "server", "port", "tls", NULL };
GHashTable* opts = parse_options(&args[args[0] ? 1 : 0], opt_keys, &parsed);
// &args[args[0] ? 1 : 0] skips args[0] if present
Memory Management
auto_gcharv gchar** args = cmd->parser(inp, min, max, &result); // Auto-freed
auto_gchar gchar* path = prefs_get_string(PREF_SOME_PREF); // Auto-freed
Argument Parsers (parser.c)
| Function | Description |
|---|---|
parse_args() |
Standard argument parsing, splits on spaces |
parse_args_with_freetext() |
Last argument captures everything after max tokens |
parse_args_as_one() |
Everything after first space becomes single argument |
Console Output Functions (ui.h)
| Function | Description |
|---|---|
cons_show(const char* msg, ...) |
Print formatted message to console |
cons_show_error(const char* cmd, ...) |
Print error message |
cons_bad_cmd_usage(const char* cmd) |
Print usage error |
Adding a New Command: Step-by-Step
Step 1: Declare Function in cmd_funcs.h
gboolean cmd_mycommand(ProfWin* window, const char* const command, gchar** args);
Step 2: Implement Function in cmd_funcs.c
gboolean
cmd_mycommand(ProfWin* window, const char* const command, gchar** args)
{
// args[0] = first argument (or NULL if none)
if (args[0] == NULL) {
cons_show("No argument provided.");
return TRUE;
}
cons_show("Got: %s", args[0]);
return TRUE;
}
Step 3: Add Definition to command_defs[] in cmd_defs.c
Find the end of the array (look for }; after the last entry) and add:
{ CMD_PREAMBLE("/mycommand",
parse_args, 0, 1, NULL)
CMD_MAINFUNC(cmd_mycommand)
CMD_TAGS(CMD_TAG_CHAT)
CMD_SYN("/mycommand [<arg>]")
CMD_DESC("My new command description.")
CMD_ARGS({ "<arg>", "An argument" })
CMD_EXAMPLES("/mycommand hello")
},
Step 4: Add Autocomplete
In cmd_ac.c:
- Add static function declaration at top:
static char* _mycommand_autocomplete(ProfWin* window, const char* const input, gboolean previous);
- Register in
cmd_ac_init():
g_hash_table_insert(ac_funcs, "/mycommand", _mycommand_autocomplete);
- Implement the function:
static char*
_mycommand_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{
return NULL; // No autocomplete
}
Key Files
src/command/cmd_defs.c- Command definitionssrc/command/cmd_defs.h- Type definitionssrc/command/cmd_funcs.c- Handler implementationssrc/command/cmd_funcs.h- Function declarationssrc/command/cmd_ac.c- Autocompletesrc/tools/parser.c- Argument parsingsrc/ui/ui.h- Console output functions