Skip to content

Commit 442cb95

Browse files
DeusDataDavid34920sonicvizchitralvermanoelkurian
committed
Add KiloCode support, fix Zed JSONC parsing (#24, #53)
- KiloCode: detect globalStorage dir, upsert mcp_settings.json, install global rules at ~/.kilocode/rules/codebase-memory-mcp.md - Zed: parse JSONC (comments + trailing commas) in existing settings.json — fixes "Invalid JSON" error on install with existing Zed config - 2 new tests: KiloCode detection, Zed JSONC settings parsing - Now supports 8 agents: Claude Code, Codex, Gemini, Zed, OpenCode, Antigravity, Aider, KiloCode Co-Authored-By: David34920 <179368816+David34920@users.noreply.github.com> Co-Authored-By: Paul Cohen <200370+sonicviz@users.noreply.github.com> Co-Authored-By: Chitral Verma <11135032+chitralverma@users.noreply.github.com> Co-Authored-By: Noel Kurian <1851449+noelkurian@users.noreply.github.com> Co-Authored-By: José Almeida <57680069+zeval@users.noreply.github.com> Co-Authored-By: Vasani Harshil <147682127+harshil480@users.noreply.github.com>
1 parent 159378a commit 442cb95

File tree

4 files changed

+113
-5
lines changed

4 files changed

+113
-5
lines changed

src/cli/cli.c

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,9 @@ static yyjson_doc *read_json_file(const char *path) {
537537
// NOLINTNEXTLINE(clang-analyzer-security.ArrayBound)
538538
buf[nread] = '\0';
539539

540-
yyjson_doc *doc = yyjson_read(buf, nread, 0);
540+
/* Allow JSONC (comments + trailing commas) — Zed settings.json uses this format */
541+
yyjson_read_flag flags = YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS;
542+
yyjson_doc *doc = yyjson_read(buf, nread, flags);
541543
free(buf);
542544
return doc;
543545
}
@@ -811,7 +813,7 @@ int cbm_remove_zed_mcp(const char *config_path) {
811813
/* ── Agent detection ──────────────────────────────────────────── */
812814

813815
cbm_detected_agents_t cbm_detect_agents(const char *home_dir) {
814-
cbm_detected_agents_t agents = {false, false, false, false, false, false, false};
816+
cbm_detected_agents_t agents = {false, false, false, false, false, false, false, false};
815817
if (!home_dir || !home_dir[0]) {
816818
return agents;
817819
}
@@ -866,6 +868,12 @@ cbm_detected_agents_t cbm_detect_agents(const char *home_dir) {
866868
agents.aider = true;
867869
}
868870

871+
/* KiloCode: globalStorage dir */
872+
snprintf(path, sizeof(path), "%s/.config/Code/User/globalStorage/kilocode.kilo-code", home_dir);
873+
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
874+
agents.kilocode = true;
875+
}
876+
869877
return agents;
870878
}
871879

@@ -1808,8 +1816,11 @@ int cbm_cmd_install(int argc, char **argv) {
18081816
if (agents.aider) {
18091817
printf(" Aider");
18101818
}
1819+
if (agents.kilocode) {
1820+
printf(" KiloCode");
1821+
}
18111822
if (!agents.claude_code && !agents.codex && !agents.gemini && !agents.zed && !agents.opencode &&
1812-
!agents.antigravity && !agents.aider) {
1823+
!agents.antigravity && !agents.aider && !agents.kilocode) {
18131824
printf(" (none)");
18141825
}
18151826
printf("\n\n");
@@ -1949,7 +1960,28 @@ int cbm_cmd_install(int argc, char **argv) {
19491960
printf(" instructions: %s\n", instr_path);
19501961
}
19511962

1952-
/* Step 11: Ensure PATH */
1963+
/* Step 11: Install KiloCode */
1964+
if (agents.kilocode) {
1965+
printf("KiloCode:\n");
1966+
char config_path[1024];
1967+
snprintf(config_path, sizeof(config_path),
1968+
"%s/.config/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json",
1969+
home);
1970+
if (!dry_run) {
1971+
cbm_install_editor_mcp(self_path, config_path);
1972+
}
1973+
printf(" mcp: %s\n", config_path);
1974+
1975+
/* KiloCode uses ~/.kilocode/rules/ for global instructions */
1976+
char instr_path[1024];
1977+
snprintf(instr_path, sizeof(instr_path), "%s/.kilocode/rules/codebase-memory-mcp.md", home);
1978+
if (!dry_run) {
1979+
cbm_upsert_instructions(instr_path, agent_instructions_content);
1980+
}
1981+
printf(" instructions: %s\n", instr_path);
1982+
}
1983+
1984+
/* Step 12: Ensure PATH */
19531985
char bin_dir[1024];
19541986
snprintf(bin_dir, sizeof(bin_dir), "%s/.local/bin", home);
19551987
const char *rc = cbm_detect_shell_rc(home);
@@ -2105,6 +2137,24 @@ int cbm_cmd_uninstall(int argc, char **argv) {
21052137
printf("Aider: removed instructions\n");
21062138
}
21072139

2140+
if (agents.kilocode) {
2141+
char config_path[1024];
2142+
snprintf(config_path, sizeof(config_path),
2143+
"%s/.config/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json",
2144+
home);
2145+
if (!dry_run) {
2146+
cbm_remove_editor_mcp(config_path);
2147+
}
2148+
printf("KiloCode: removed MCP config entry\n");
2149+
2150+
char instr_path[1024];
2151+
snprintf(instr_path, sizeof(instr_path), "%s/.kilocode/rules/codebase-memory-mcp.md", home);
2152+
if (!dry_run) {
2153+
cbm_remove_instructions(instr_path);
2154+
}
2155+
printf(" removed instructions\n");
2156+
}
2157+
21082158
/* Step 2: Remove indexes */
21092159
int index_count = 0;
21102160
const char *cache_dir = get_cache_dir(home);

src/cli/cli.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ typedef struct {
112112
bool opencode; /* opencode on PATH or config exists */
113113
bool antigravity; /* ~/.gemini/antigravity/ exists */
114114
bool aider; /* aider on PATH */
115+
bool kilocode; /* KiloCode globalStorage dir exists */
115116
} cbm_detected_agents_t;
116117

117118
/* Detect which coding agents are installed.

src/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ static void print_help(void) {
134134
printf(" --ui=false Disable HTTP graph visualization (persisted)\n");
135135
printf(" --port=N Set UI port (default 9749, persisted)\n");
136136
printf("\nSupported agents (auto-detected):\n");
137-
printf(" Claude Code, Codex CLI, Gemini CLI, Zed, OpenCode, Antigravity, Aider\n");
137+
printf(" Claude Code, Codex CLI, Gemini CLI, Zed, OpenCode, Antigravity, Aider, KiloCode\n");
138138
printf("\nTools: index_repository, search_graph, query_graph, trace_call_path,\n");
139139
printf(" get_code_snippet, get_graph_schema, get_architecture, search_code,\n");
140140
printf(" list_projects, delete_project, index_status, detect_changes,\n");

tests/test_cli.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,44 @@ TEST(cli_zed_mcp_uninstall) {
833833
PASS();
834834
}
835835

836+
TEST(cli_zed_mcp_jsonc_comments) {
837+
/* Issue #24: Zed settings.json uses JSONC (comments + trailing commas) */
838+
char tmpdir[256]; snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-mcp-XXXXXX");
839+
if (!cbm_mkdtemp(tmpdir))
840+
SKIP("cbm_mkdtemp failed");
841+
842+
char configpath[512];
843+
snprintf(configpath, sizeof(configpath), "%s/.config/zed/settings.json", tmpdir);
844+
char dir[512];
845+
snprintf(dir, sizeof(dir), "%s/.config/zed", tmpdir);
846+
test_mkdirp(dir);
847+
848+
/* JSONC with comments and trailing commas — must not fail */
849+
write_test_file(configpath,
850+
"// Zed settings\n"
851+
"{\n"
852+
" \"theme\": \"One Dark\",\n"
853+
" /* multi-line\n"
854+
" comment */\n"
855+
" \"vim_mode\": true,\n" /* trailing comma */
856+
"}\n");
857+
858+
int rc = cbm_install_zed_mcp("/usr/local/bin/codebase-memory-mcp", configpath);
859+
ASSERT_EQ(rc, 0);
860+
861+
const char *data = read_test_file(configpath);
862+
ASSERT_NOT_NULL(data);
863+
/* Original settings preserved */
864+
ASSERT(strstr(data, "One Dark") != NULL);
865+
ASSERT(strstr(data, "vim_mode") != NULL);
866+
/* MCP server added */
867+
ASSERT(strstr(data, "codebase-memory-mcp") != NULL);
868+
ASSERT(strstr(data, "context_servers") != NULL);
869+
870+
test_rmdir_r(tmpdir);
871+
PASS();
872+
}
873+
836874
/* ═══════════════════════════════════════════════════════════════════
837875
* PATH management tests (port of TestCLI_InstallPATHAppend)
838876
* ═══════════════════════════════════════════════════════════════════ */
@@ -1261,6 +1299,22 @@ TEST(cli_detect_agents_finds_antigravity) {
12611299
PASS();
12621300
}
12631301

1302+
TEST(cli_detect_agents_finds_kilocode) {
1303+
char tmpdir[256]; snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-detect-XXXXXX");
1304+
if (!cbm_mkdtemp(tmpdir)) SKIP("cbm_mkdtemp failed");
1305+
1306+
char dir[512];
1307+
snprintf(dir, sizeof(dir),
1308+
"%s/.config/Code/User/globalStorage/kilocode.kilo-code", tmpdir);
1309+
test_mkdirp(dir);
1310+
1311+
cbm_detected_agents_t agents = cbm_detect_agents(tmpdir);
1312+
ASSERT_TRUE(agents.kilocode);
1313+
1314+
test_rmdir_r(tmpdir);
1315+
PASS();
1316+
}
1317+
12641318
TEST(cli_detect_agents_none_found) {
12651319
char tmpdir[256]; snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-detect-XXXXXX");
12661320
if (!cbm_mkdtemp(tmpdir)) SKIP("cbm_mkdtemp failed");
@@ -1274,6 +1328,7 @@ TEST(cli_detect_agents_none_found) {
12741328
ASSERT_FALSE(agents.gemini);
12751329
ASSERT_FALSE(agents.zed);
12761330
ASSERT_FALSE(agents.antigravity);
1331+
ASSERT_FALSE(agents.kilocode);
12771332

12781333
rmdir(tmpdir);
12791334
PASS();
@@ -1871,6 +1926,7 @@ SUITE(cli) {
18711926
RUN_TEST(cli_zed_mcp_install);
18721927
RUN_TEST(cli_zed_mcp_preserves_settings);
18731928
RUN_TEST(cli_zed_mcp_uninstall);
1929+
RUN_TEST(cli_zed_mcp_jsonc_comments);
18741930

18751931
/* PATH management (3 tests) */
18761932
RUN_TEST(cli_ensure_path_append);
@@ -1908,6 +1964,7 @@ SUITE(cli) {
19081964
RUN_TEST(cli_detect_agents_finds_gemini);
19091965
RUN_TEST(cli_detect_agents_finds_zed);
19101966
RUN_TEST(cli_detect_agents_finds_antigravity);
1967+
RUN_TEST(cli_detect_agents_finds_kilocode);
19111968
RUN_TEST(cli_detect_agents_none_found);
19121969

19131970
/* Codex MCP config upsert (3 tests — group B) */

0 commit comments

Comments
 (0)