Extensions: skills, plugins, MCP, sub-agents; configurable keys
ci / linux (clang) (push) Canceled after 0s
ci / linux (gcc) (push) Canceled after 0s
ci / sanitizers (push) Canceled after 0s
ci / musl (push) Canceled after 0s
ci / macos (push) Canceled after 0s

- 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
+55 -4
View File
@@ -167,6 +167,45 @@ int main(void) {
"missing required arg");
reset(&r);
/* compact read_file (default profile) */
ctx.compact = 1;
EXPECT(run(&ctx, mk("read_file", "path", "src/notes.txt", NULL), &r) == 0 &&
strcmp(r.out, "src/notes.txt · 1 line\n1|TODO: x\n") == 0,
"compact read: header + numbers");
reset(&r);
EXPECT(run(&ctx, mk("read_file", "path", "lines.txt", "offset", "10",
"limit", "3", NULL),
&r) == 0 &&
strstr(r.out, "lines.txt · lines 10-12 of 100\n10|line 10\n") ==
r.out &&
strstr(r.out, "… 88 more lines; continue with offset=13"),
"compact slice header and continuation");
reset(&r);
EXPECT(run(&ctx, mk("read_file", "path", "src/notes.txt", "line_numbers",
"false", NULL),
&r) == 0 &&
strstr(r.out, "\nTODO: x\n") && !strstr(r.out, "1|"),
"compact read without numbers");
reset(&r);
{
char big[5000];
memset(big, 'a', sizeof(big) - 2);
big[sizeof(big) - 2] = '\n';
big[sizeof(big) - 1] = 0;
wfile("long.txt", big);
EXPECT(run(&ctx, mk("read_file", "path", "long.txt", NULL), &r) == 0 &&
strstr(r.out, "[line cut, +2998 bytes]") &&
strstr(r.out, "1 long line cut"),
"compact read cuts long lines");
reset(&r);
}
wfile("empty.txt", "");
EXPECT(run(&ctx, mk("read_file", "path", "empty.txt", NULL), &r) == 0 &&
strstr(r.out, "empty file"),
"compact read of an empty file");
reset(&r);
ctx.compact = 0;
/* list_dir */
EXPECT(run(&ctx, mk("list_dir", NULL), &r) == 0, "list_dir ok");
EXPECT(r.out && strstr(r.out, "src/") && strstr(r.out, " main.c"),
@@ -180,6 +219,8 @@ int main(void) {
EXPECT(run(&ctx, mk("list_dir", "path", "src", "depth", "1", NULL), &r) == 0 &&
strstr(r.out, "deep/") && !strstr(r.out, "util.c"),
"list_dir depth");
EXPECT(r.out && strstr(r.out, "deep/ (1 entry)"),
"list_dir counts entries at the depth limit");
reset(&r);
/* glob */
@@ -205,20 +246,22 @@ int main(void) {
/* grep */
EXPECT(run(&ctx, mk("grep", "pattern", "int add", NULL), &r) == 0, "grep ok");
EXPECT(r.out && strstr(r.out, "src/deep/util.c:1:int add"), "grep hit");
EXPECT(r.out && strstr(r.out, "src/deep/util.c\n1:int add"), "grep hit grouped");
EXPECT(r.out && strncmp(r.out, "1 match in 1 file\n", 18) == 0,
"grep summary first");
EXPECT(r.out && !strstr(r.out, "junk.c") && !strstr(r.out, "run.log"),
"grep skips node_modules and ignored files");
reset(&r);
EXPECT(run(&ctx, mk("grep", "pattern", "x = \\d+", "glob", "*.c", NULL),
&r) == 0 &&
strstr(r.out, "src/main.c:3:"),
strstr(r.out, "src/main.c\n3:"),
"grep translates \\d");
reset(&r);
EXPECT(run(&ctx, mk("grep", "pattern", "RETURN", "ignore_case", "true",
"path", "src/main.c", "context", "1", NULL),
&r) == 0 &&
strstr(r.out, "src/main.c:5: return 0;") &&
strstr(r.out, "src/main.c-4-") && strstr(r.out, "src/main.c-6-"),
strstr(r.out, "\n5: return 0;") && strstr(r.out, "\n4-") &&
strstr(r.out, "\n6-"),
"grep single file, icase, context");
reset(&r);
EXPECT(run(&ctx, mk("grep", "pattern", "(", NULL), &r) == 1 &&
@@ -241,11 +284,19 @@ int main(void) {
strstr(r.out, "not found"),
"edit not found");
reset(&r);
EXPECT(run(&ctx, mk("edit_file", "path", "src/main.c", "old_string",
"int main(void) {\n int x = 7;", "new_string", "y",
NULL),
&r) == 1 &&
strstr(r.out, "appears (whitespace aside) at line 2"),
"edit not found points at the near line");
reset(&r);
EXPECT(run(&ctx, mk("edit_file", "path", "src/deep/util.c", "old_string",
"int a, int b", "new_string", "long a, long b", NULL),
&r) == 1 &&
strstr(r.out, "matches 2 places"),
"edit ambiguous");
EXPECT(r.out && strstr(r.out, "(lines 1, 2)"), "edit ambiguous lines");
reset(&r);
EXPECT(run(&ctx, mk("edit_file", "path", "src/deep/util.c", "old_string",
"int a, int b", "new_string", "long a, long b",