- 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
117 lines
3.3 KiB
C
117 lines
3.3 KiB
C
/* cst_shell_compact: ANSI stripping, \r progress folding, repeated-line
|
|
* collapse and head/tail cutting of run_shell output. */
|
|
#include "cst_tools.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static int nfail;
|
|
|
|
#define CHECK(cond, msg) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \
|
|
nfail++; \
|
|
} \
|
|
} while (0)
|
|
|
|
static char *compact(const char *s) {
|
|
size_t n = 0;
|
|
return cst_shell_compact(s, strlen(s), &n);
|
|
}
|
|
|
|
int main(void) {
|
|
char *o;
|
|
|
|
o = compact("plain\noutput\n");
|
|
CHECK(o && strcmp(o, "plain\noutput\n") == 0, "clean output unchanged");
|
|
free(o);
|
|
|
|
o = compact("no newline");
|
|
CHECK(o && strcmp(o, "no newline") == 0, "missing final newline kept");
|
|
free(o);
|
|
|
|
o = compact("");
|
|
CHECK(o && strcmp(o, "") == 0, "empty");
|
|
free(o);
|
|
|
|
o = compact("a\n\nb\n");
|
|
CHECK(o && strcmp(o, "a\n\nb\n") == 0, "blank lines kept");
|
|
free(o);
|
|
|
|
o = compact("\x1b[1;31merror\x1b[0m: bad\n\x1b]0;title\x07ok\n");
|
|
CHECK(o && strcmp(o, "error: bad\nok\n") == 0, "ANSI CSI and OSC removed");
|
|
free(o);
|
|
|
|
o = compact(" 0%\r 50%\r100%\ndone\r\n");
|
|
CHECK(o && strcmp(o, "100%\ndone\n") == 0, "\\r progress folded, CRLF ok");
|
|
free(o);
|
|
|
|
o = compact("ab\bc\n");
|
|
CHECK(o && strcmp(o, "ac\n") == 0, "backspace applied");
|
|
free(o);
|
|
|
|
o = compact("x\nx\ny\n");
|
|
CHECK(o && strcmp(o, "x\nx\ny\n") == 0, "a pair is kept as is");
|
|
free(o);
|
|
|
|
o = compact("start\nwarn\nwarn\nwarn\nwarn\nend\n");
|
|
CHECK(o && strcmp(o, "start\nwarn\n… (previous line repeated 3 more "
|
|
"times)\nend\n") == 0,
|
|
"runs collapsed");
|
|
free(o);
|
|
|
|
{
|
|
char *big = malloc(1000 * 16);
|
|
size_t used = 0;
|
|
for (int i = 1; i <= 1000; i++)
|
|
used += (size_t)sprintf(big + used, "line %d\n", i);
|
|
o = compact(big);
|
|
CHECK(o && strstr(o, "line 1\n") && strstr(o, "line 80\n") &&
|
|
!strstr(o, "line 81\n") && strstr(o, "… 800 lines omitted …\n") &&
|
|
strstr(o, "line 881\n") && strstr(o, "line 1000\n"),
|
|
"head + tail with omitted count");
|
|
free(o);
|
|
free(big);
|
|
}
|
|
|
|
{
|
|
char *wide = malloc(5000 + 2);
|
|
memset(wide, 'w', 5000);
|
|
wide[5000] = '\n';
|
|
wide[5001] = 0;
|
|
o = compact(wide);
|
|
CHECK(o && strstr(o, " … [+3000 bytes]\n") && strlen(o) < 2100,
|
|
"long line cut");
|
|
free(o);
|
|
free(wide);
|
|
}
|
|
|
|
{
|
|
/* 200 lines of 1 KB: over the byte budget, the tail wins */
|
|
char *fat = malloc(200 * 1030);
|
|
size_t used = 0;
|
|
for (int i = 1; i <= 200; i++) {
|
|
used += (size_t)sprintf(fat + used, "%04d ", i);
|
|
memset(fat + used, 'f', 1000);
|
|
used += 1000;
|
|
fat[used++] = '\n';
|
|
}
|
|
fat[used] = 0;
|
|
o = compact(fat);
|
|
CHECK(o && strlen(o) <= 42 * 1024 && strstr(o, "0200 ") &&
|
|
strstr(o, "lines omitted"),
|
|
"byte budget keeps the tail");
|
|
free(o);
|
|
free(fat);
|
|
}
|
|
|
|
if (nfail) {
|
|
fprintf(stderr, "test_shell_compact: %d failure(s)\n", nfail);
|
|
return 1;
|
|
}
|
|
printf("test_shell_compact: OK\n");
|
|
return 0;
|
|
}
|