Extensions: skills, plugins, MCP, sub-agents; configurable keys

- 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
This commit is contained in:
WHC
2026-10-05 14:55:31 +08:00
parent c81df42f0b
commit 7d3ec9fcb9
38 changed files with 8356 additions and 399 deletions
+69 -1
View File
@@ -27,7 +27,7 @@ int cst_settings_load(cst_settings_t *s) {
FILE *fp = fopen(path, "rb");
if (!fp)
return -1;
char buf[16384];
static _Thread_local char buf[65536];
size_t n = fread(buf, 1, sizeof(buf) - 1, fp);
fclose(fp);
buf[n] = 0;
@@ -42,6 +42,14 @@ int cst_settings_load(cst_settings_t *s) {
cJSON_GetObjectItemCaseSensitive(root, "effort"));
copy_str(s->approval, sizeof(s->approval),
cJSON_GetObjectItemCaseSensitive(root, "approval"));
s->show_thinking =
cJSON_IsTrue(cJSON_GetObjectItemCaseSensitive(root, "show_thinking"));
const cJSON *tr = cJSON_GetObjectItemCaseSensitive(root, "trusted_workspaces");
const cJSON *w;
cJSON_ArrayForEach(w, tr) {
if (s->ntrusted < CST_SETTINGS_MAX_TRUSTED && cJSON_IsString(w))
copy_str(s->trusted[s->ntrusted++], sizeof(s->trusted[0]), w);
}
const cJSON *models = cJSON_GetObjectItemCaseSensitive(root, "models");
const cJSON *m;
cJSON_ArrayForEach(m, models) {
@@ -73,6 +81,13 @@ int cst_settings_save(const cst_settings_t *s) {
cJSON_AddStringToObject(root, "effort", s->effort);
if (s->approval[0])
cJSON_AddStringToObject(root, "approval", s->approval);
if (s->show_thinking)
cJSON_AddTrueToObject(root, "show_thinking");
if (s->ntrusted) {
cJSON *tr = cJSON_AddArrayToObject(root, "trusted_workspaces");
for (int i = 0; i < s->ntrusted; i++)
cJSON_AddItemToArray(tr, cJSON_CreateString(s->trusted[i]));
}
cJSON *models = cJSON_AddObjectToObject(root, "models");
for (int i = 0; i < s->nmodels; i++)
cJSON_AddStringToObject(models, s->models[i].provider, s->models[i].model);
@@ -146,3 +161,56 @@ int cst_settings_remember_approval(int approval) {
snprintf(s.approval, sizeof(s.approval), "%s", cst_approval_name(approval));
return cst_settings_save(&s);
}
int cst_settings_remember_thinking(int show_thinking) {
cst_settings_t s;
cst_settings_load(&s);
s.show_thinking = show_thinking != 0;
return cst_settings_save(&s);
}
static void real_ws(const char *ws, char *out, size_t n) {
if (!ws || os_realpath(ws, out, n) != 0)
snprintf(out, n, "%s", ws ? ws : "");
}
int cst_settings_trusted(const char *workspace) {
char real[OS_PATH_MAX];
real_ws(workspace, real, sizeof(real));
cst_settings_t s;
cst_settings_load(&s);
for (int i = 0; i < s.ntrusted; i++)
if (strcmp(s.trusted[i], real) == 0)
return 1;
return 0;
}
int cst_settings_trust(const char *workspace, int on) {
char real[OS_PATH_MAX];
real_ws(workspace, real, sizeof(real));
if (!real[0] || strlen(real) >= sizeof(((cst_settings_t *)0)->trusted[0]))
return -1;
cst_settings_t s;
cst_settings_load(&s);
int k = 0, found = 0;
for (int i = 0; i < s.ntrusted; i++) {
if (strcmp(s.trusted[i], real) == 0) {
found = 1;
if (!on)
continue;
}
if (k != i)
memcpy(s.trusted[k], s.trusted[i], sizeof(s.trusted[0]));
k++;
}
s.ntrusted = k;
if (on && !found) {
if (s.ntrusted >= CST_SETTINGS_MAX_TRUSTED) {
memmove(s.trusted[0], s.trusted[1],
sizeof(s.trusted[0]) * (CST_SETTINGS_MAX_TRUSTED - 1));
s.ntrusted--;
}
snprintf(s.trusted[s.ntrusted++], sizeof(s.trusted[0]), "%.511s", real);
}
return cst_settings_save(&s);
}