- Skills (SKILL.md discovery, skill tool, /skills /skill) - plugin.json exec tools with permission prompts, timeouts and workspace trust (/plugins) - MCP stdio client: JSON-RPC initialize/tools/list/tools/call, mcp__server__tool, /mcp and sidebar status - task tool with general/explore/plan and .agent/agents/*.md roles, parallel read-only runs, cancel propagation, /agents - Keymap with Ctrl+X leader, keybinds.json overrides and /keys panel - Collapsed thinking, throttled streaming Markdown, leaner tool output, parallel read-only tools - Spawn: close-on-exec pipes and a process-wide fork lock - Tests for keymap, skills, plugins, MCP, sub-agents, shell compaction; docs/plugins.md
90 lines
2.8 KiB
C
90 lines
2.8 KiB
C
#include "cst_ext.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "cst_mcp.h"
|
|
#include "cst_plugins.h"
|
|
#include "cst_skills.h"
|
|
#include "cst_subagent.h"
|
|
#include "cst_tools.h"
|
|
#include "cst_util.h"
|
|
|
|
static char g_report[256];
|
|
|
|
const char *cst_extensions_report(void) { return g_report; }
|
|
|
|
void cst_extensions_wait(FILE *log) {
|
|
int ready = 0, total = 0;
|
|
cst_mcp_counts(&ready, &total);
|
|
if (total == 0)
|
|
return;
|
|
cst_mcp_wait_ready(30000);
|
|
cst_mcp_info_t info[CST_MCP_MAX];
|
|
int n = cst_mcp_info(info, CST_MCP_MAX);
|
|
for (int i = 0; log && i < n; i++) {
|
|
if (info[i].state == CST_MCP_READY)
|
|
fprintf(log, "mcp %s: %d tool%s\n", info[i].name, info[i].ntools,
|
|
info[i].ntools == 1 ? "" : "s");
|
|
else if (info[i].state != CST_MCP_OFF)
|
|
fprintf(log, "mcp %s: %s\n", info[i].name,
|
|
info[i].error[0] ? info[i].error : "not ready");
|
|
}
|
|
}
|
|
|
|
void cst_extensions_load(const cst_agent_config_t *cfg, char *report,
|
|
size_t report_sz) {
|
|
if (report && report_sz)
|
|
report[0] = 0;
|
|
g_report[0] = 0;
|
|
if (!cfg || cfg->tool_profile == CST_TOOLS_MINIMAL)
|
|
return;
|
|
const char *ws = cfg->workspace_root;
|
|
int trusted = cst_settings_trusted(ws);
|
|
int mcp_untrusted = 0, plugin_untrusted = 0;
|
|
/* workspace and user mcp.json come before plugin servers: first name wins */
|
|
cst_mcp_load_configs(ws, trusted, &mcp_untrusted);
|
|
int np = cst_plugins_load(ws, trusted, &plugin_untrusted);
|
|
|
|
cst_skill_t *v = calloc(CST_SKILLS_MAX, sizeof(*v));
|
|
int ns = v ? cst_skills_list(ws, v, CST_SKILLS_MAX) : 0;
|
|
free(v);
|
|
if (ns > 0)
|
|
cst_skills_register_tool();
|
|
if (cfg->depth == 0)
|
|
cst_subagent_register_tool(ws);
|
|
cst_mcp_start_all(ws);
|
|
|
|
cst_buf_t b = {0};
|
|
if (ns > 0)
|
|
cst_buf_printf(&b, "%d skill%s", ns, ns == 1 ? "" : "s");
|
|
if (np > 0) {
|
|
cst_plugin_info_t info[CST_PLUGINS_MAX];
|
|
int n = cst_plugins_info(info, CST_PLUGINS_MAX), nt = 0;
|
|
for (int i = 0; i < n; i++)
|
|
nt += info[i].ntools;
|
|
cst_buf_printf(&b, "%s%d plugin%s (%d tool%s)", b.len ? " · " : "", np,
|
|
np == 1 ? "" : "s", nt, nt == 1 ? "" : "s");
|
|
}
|
|
int ready = 0, total = 0;
|
|
cst_mcp_counts(&ready, &total);
|
|
if (total > 0)
|
|
cst_buf_printf(&b, "%s%d MCP server%s", b.len ? " · " : "", total,
|
|
total == 1 ? "" : "s");
|
|
if (mcp_untrusted + plugin_untrusted > 0)
|
|
cst_buf_printf(&b, "%suntrusted workspace: /plugins trust",
|
|
b.len ? " · " : "");
|
|
if (report && report_sz)
|
|
snprintf(report, report_sz, "%s", b.buf ? b.buf : "");
|
|
snprintf(g_report, sizeof(g_report), "%s", b.buf ? b.buf : "");
|
|
cst_buf_free(&b);
|
|
}
|
|
|
|
void cst_extensions_unload(void) {
|
|
cst_mcp_stop_all();
|
|
cst_plugins_unload();
|
|
cst_tool_unregister_owner("builtin:skills");
|
|
cst_tool_unregister_owner("builtin:task");
|
|
}
|