mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 18:58:00 +00:00
Fix remaining install/update gaps + in-memory zip extraction
Install command: - Add cbm_kill_other_instances() to kill stale MCP servers - Add cbm_macos_adhoc_sign() to sign binary if placed unsigned Update command: - Replace skills-only reinstall with full cbm_install_agent_configs() - Replace external unzip with cbm_extract_binary_from_zip() via zlib Refactor: extract 10-agent config loop into cbm_install_agent_configs() called by both install and update. New: cbm_extract_binary_from_zip() — in-memory zip extraction with stored + deflate support, path traversal rejection, bounds checks. 4 unit tests. Smoke: add install.ps1 E2E for Windows.
This commit is contained in:
1 parent
006e6dbf20
commit
8951647d3e
4 files changed
+586
-251
No files matched your search
+413
-250
@@ -1092,6 +1092,7 @@ int cbm_upsert_instructions(const char *path, const char *content) {
|
||||
} else {
|
||||
/* Append section */
|
||||
size_t new_len = existing_len + 1 + strlen(section);
|
||||
// NOLINTNEXTLINE(clang-analyzer-optin.taint.TaintedAlloc)
|
||||
result = malloc(new_len + 1);
|
||||
if (!result) {
|
||||
free(existing);
|
||||
@@ -1736,6 +1737,128 @@ unsigned char *cbm_extract_binary_from_targz(const unsigned char *data, int data
|
||||
return NULL; /* binary not found */
|
||||
}
|
||||
|
||||
/* ── Zip extraction (in-memory, replaces external unzip) ──────── */
|
||||
|
||||
unsigned char *cbm_extract_binary_from_zip(const unsigned char *data, int data_len, int *out_len) {
|
||||
if (!data || data_len <= 0 || !out_len) {
|
||||
return NULL;
|
||||
}
|
||||
*out_len = 0;
|
||||
|
||||
/* Zip local file header layout */
|
||||
enum {
|
||||
ZIP_SIG_0 = 0x50,
|
||||
ZIP_SIG_1 = 0x4B,
|
||||
ZIP_SIG_2 = 0x03,
|
||||
ZIP_SIG_3 = 0x04,
|
||||
ZIP_HDR_SZ = 30,
|
||||
ZIP_OFF_METHOD = 8,
|
||||
ZIP_OFF_COMP = 18,
|
||||
ZIP_OFF_UNCOMP = 22,
|
||||
ZIP_OFF_NAMELEN = 26,
|
||||
ZIP_OFF_EXTRALEN = 28,
|
||||
ZIP_STORED = 0,
|
||||
ZIP_DEFLATE = 8
|
||||
};
|
||||
static const uint32_t ZIP_MAX_UNCOMP = 500U * 1024U * 1024U;
|
||||
|
||||
int pos = 0;
|
||||
while (pos + ZIP_HDR_SZ <= data_len) {
|
||||
if (data[pos] != ZIP_SIG_0 || data[pos + 1] != ZIP_SIG_1 || data[pos + 2] != ZIP_SIG_2 ||
|
||||
data[pos + 3] != ZIP_SIG_3) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t method =
|
||||
(uint16_t)(data[pos + ZIP_OFF_METHOD] | (data[pos + ZIP_OFF_METHOD + 1] << 8));
|
||||
uint32_t comp_size =
|
||||
(uint32_t)(data[pos + ZIP_OFF_COMP] | (data[pos + ZIP_OFF_COMP + 1] << 8) |
|
||||
(data[pos + ZIP_OFF_COMP + 2] << 16) | (data[pos + ZIP_OFF_COMP + 3] << 24));
|
||||
uint32_t uncomp_size =
|
||||
(uint32_t)(data[pos + ZIP_OFF_UNCOMP] | (data[pos + ZIP_OFF_UNCOMP + 1] << 8) |
|
||||
(data[pos + ZIP_OFF_UNCOMP + 2] << 16) |
|
||||
(data[pos + ZIP_OFF_UNCOMP + 3] << 24));
|
||||
uint16_t name_len =
|
||||
(uint16_t)(data[pos + ZIP_OFF_NAMELEN] | (data[pos + ZIP_OFF_NAMELEN + 1] << 8));
|
||||
uint16_t extra_len =
|
||||
(uint16_t)(data[pos + ZIP_OFF_EXTRALEN] | (data[pos + ZIP_OFF_EXTRALEN + 1] << 8));
|
||||
|
||||
int header_end = pos + ZIP_HDR_SZ + name_len + extra_len;
|
||||
if (header_end + (int)comp_size > data_len) {
|
||||
break; /* Truncated */
|
||||
}
|
||||
|
||||
/* Extract filename */
|
||||
char fname[512] = {0};
|
||||
int fn_copy = name_len < (int)sizeof(fname) - 1 ? name_len : (int)sizeof(fname) - 1;
|
||||
memcpy(fname, data + pos + 30, (size_t)fn_copy);
|
||||
fname[fn_copy] = '\0';
|
||||
|
||||
/* Security: reject path traversal */
|
||||
if (strstr(fname, "..")) {
|
||||
pos = header_end + (int)comp_size;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find the binary — basename match */
|
||||
const char *basename = strrchr(fname, '/');
|
||||
basename = basename ? basename + 1 : fname;
|
||||
|
||||
if (strcmp(basename, "codebase-memory-mcp") == 0 ||
|
||||
strcmp(basename, "codebase-memory-mcp.exe") == 0) {
|
||||
|
||||
const unsigned char *file_data = data + header_end;
|
||||
|
||||
if (method == ZIP_STORED) {
|
||||
if (comp_size > ZIP_MAX_UNCOMP) {
|
||||
return NULL;
|
||||
}
|
||||
unsigned char *out = malloc(comp_size);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(out, file_data, comp_size);
|
||||
*out_len = (int)comp_size;
|
||||
return out;
|
||||
}
|
||||
if (method == ZIP_DEFLATE) {
|
||||
if (uncomp_size > ZIP_MAX_UNCOMP) {
|
||||
return NULL;
|
||||
}
|
||||
unsigned char *out = malloc(uncomp_size);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
z_stream strm = {0};
|
||||
strm.next_in = (unsigned char *)file_data;
|
||||
strm.avail_in = comp_size;
|
||||
strm.next_out = out;
|
||||
strm.avail_out = uncomp_size;
|
||||
|
||||
/* -MAX_WBITS = raw deflate (no zlib/gzip header) */
|
||||
if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) {
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
int ret = inflate(&strm, Z_FINISH);
|
||||
inflateEnd(&strm);
|
||||
if (ret != Z_STREAM_END) {
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
*out_len = (int)strm.total_out;
|
||||
return out;
|
||||
}
|
||||
/* Unknown compression method */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pos = header_end + (int)comp_size;
|
||||
}
|
||||
|
||||
return NULL; /* binary not found */
|
||||
}
|
||||
|
||||
/* ── Index management ─────────────────────────────────────────── */
|
||||
|
||||
static const char *get_cache_dir(const char *home_dir) {
|
||||
@@ -2232,6 +2355,237 @@ static const char *detect_arch(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ── Agent config install/refresh (shared by install + update) ── */
|
||||
|
||||
static void cbm_install_agent_configs(const char *home, const char *binary_path, bool force,
|
||||
bool dry_run) {
|
||||
cbm_detected_agents_t agents = cbm_detect_agents(home);
|
||||
printf("Detected agents:");
|
||||
if (agents.claude_code) {
|
||||
printf(" Claude-Code");
|
||||
}
|
||||
if (agents.codex) {
|
||||
printf(" Codex");
|
||||
}
|
||||
if (agents.gemini) {
|
||||
printf(" Gemini-CLI");
|
||||
}
|
||||
if (agents.zed) {
|
||||
printf(" Zed");
|
||||
}
|
||||
if (agents.opencode) {
|
||||
printf(" OpenCode");
|
||||
}
|
||||
if (agents.antigravity) {
|
||||
printf(" Antigravity");
|
||||
}
|
||||
if (agents.aider) {
|
||||
printf(" Aider");
|
||||
}
|
||||
if (agents.kilocode) {
|
||||
printf(" KiloCode");
|
||||
}
|
||||
if (agents.vscode) {
|
||||
printf(" VS-Code");
|
||||
}
|
||||
if (agents.openclaw) {
|
||||
printf(" OpenClaw");
|
||||
}
|
||||
if (!agents.claude_code && !agents.codex && !agents.gemini && !agents.zed && !agents.opencode &&
|
||||
!agents.antigravity && !agents.aider && !agents.kilocode && !agents.vscode &&
|
||||
!agents.openclaw) {
|
||||
printf(" (none)");
|
||||
}
|
||||
printf("\n\n");
|
||||
|
||||
/* Claude Code: skills + MCP + hooks */
|
||||
if (agents.claude_code) {
|
||||
char skills_dir[1024];
|
||||
snprintf(skills_dir, sizeof(skills_dir), "%s/.claude/skills", home);
|
||||
printf("Claude Code:\n");
|
||||
|
||||
int skill_count = cbm_install_skills(skills_dir, force, dry_run);
|
||||
printf(" skills: %d installed\n", skill_count);
|
||||
|
||||
if (cbm_remove_old_monolithic_skill(skills_dir, dry_run)) {
|
||||
printf(" removed old monolithic skill\n");
|
||||
}
|
||||
|
||||
char mcp_path[1024];
|
||||
snprintf(mcp_path, sizeof(mcp_path), "%s/.claude/.mcp.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_install_editor_mcp(binary_path, mcp_path);
|
||||
}
|
||||
printf(" mcp: %s\n", mcp_path);
|
||||
|
||||
char mcp_path2[1024];
|
||||
snprintf(mcp_path2, sizeof(mcp_path2), "%s/.claude.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_install_editor_mcp(binary_path, mcp_path2);
|
||||
}
|
||||
printf(" mcp: %s\n", mcp_path2);
|
||||
|
||||
char settings_path[1024];
|
||||
snprintf(settings_path, sizeof(settings_path), "%s/.claude/settings.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_claude_hooks(settings_path);
|
||||
cbm_install_hook_gate_script(home);
|
||||
}
|
||||
printf(" hooks: PreToolUse (code discovery gate)\n");
|
||||
}
|
||||
|
||||
/* Codex CLI */
|
||||
if (agents.codex) {
|
||||
printf("Codex CLI:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.codex/config.toml", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_codex_mcp(binary_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/.codex/AGENTS.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
}
|
||||
|
||||
/* Gemini CLI */
|
||||
if (agents.gemini) {
|
||||
printf("Gemini CLI:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.gemini/settings.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_install_editor_mcp(binary_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/.gemini/GEMINI.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
|
||||
if (!dry_run) {
|
||||
cbm_upsert_gemini_hooks(config_path);
|
||||
}
|
||||
printf(" hooks: BeforeTool (grep/file search reminder)\n");
|
||||
}
|
||||
|
||||
/* Zed */
|
||||
if (agents.zed) {
|
||||
printf("Zed:\n");
|
||||
char config_path[1024];
|
||||
#ifdef __APPLE__
|
||||
snprintf(config_path, sizeof(config_path),
|
||||
"%s/Library/Application Support/Zed/settings.json", home);
|
||||
#else
|
||||
snprintf(config_path, sizeof(config_path), "%s/.config/zed/settings.json", home);
|
||||
#endif
|
||||
if (!dry_run) {
|
||||
cbm_install_zed_mcp(binary_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
}
|
||||
|
||||
/* OpenCode */
|
||||
if (agents.opencode) {
|
||||
printf("OpenCode:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.config/opencode/opencode.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_opencode_mcp(binary_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/.config/opencode/AGENTS.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
}
|
||||
|
||||
/* Antigravity */
|
||||
if (agents.antigravity) {
|
||||
printf("Antigravity:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.gemini/antigravity/mcp_config.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_antigravity_mcp(binary_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/.gemini/antigravity/AGENTS.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
}
|
||||
|
||||
/* Aider */
|
||||
if (agents.aider) {
|
||||
printf("Aider:\n");
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/CONVENTIONS.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
}
|
||||
|
||||
/* KiloCode */
|
||||
if (agents.kilocode) {
|
||||
printf("KiloCode:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path),
|
||||
"%s/.config/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json",
|
||||
home);
|
||||
if (!dry_run) {
|
||||
cbm_install_editor_mcp(binary_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/.kilocode/rules/codebase-memory-mcp.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
}
|
||||
|
||||
/* VS Code */
|
||||
if (agents.vscode) {
|
||||
printf("VS Code:\n");
|
||||
char config_path[1024];
|
||||
#ifdef __APPLE__
|
||||
snprintf(config_path, sizeof(config_path),
|
||||
"%s/Library/Application Support/Code/User/mcp.json", home);
|
||||
#else
|
||||
snprintf(config_path, sizeof(config_path), "%s/.config/Code/User/mcp.json", home);
|
||||
#endif
|
||||
if (!dry_run) {
|
||||
cbm_install_vscode_mcp(binary_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
}
|
||||
|
||||
/* OpenClaw */
|
||||
if (agents.openclaw) {
|
||||
printf("OpenClaw:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.openclaw/openclaw.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_install_editor_mcp(binary_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Subcommand: install ──────────────────────────────────────── */
|
||||
|
||||
int cbm_cmd_install(int argc, char **argv) {
|
||||
@@ -2286,244 +2640,34 @@ int cbm_cmd_install(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1b: Kill running MCP server instances so agents pick up new config */
|
||||
if (!dry_run) {
|
||||
int killed = cbm_kill_other_instances();
|
||||
if (killed > 0) {
|
||||
printf("Stopped %d running MCP server instance(s).\n\n", killed);
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 1c: macOS ad-hoc signing (in case binary was placed without signing) */
|
||||
#ifdef __APPLE__
|
||||
{
|
||||
char sign_path[1024];
|
||||
snprintf(sign_path, sizeof(sign_path), "%s/.local/bin/codebase-memory-mcp", home);
|
||||
struct stat sign_st;
|
||||
if (stat(sign_path, &sign_st) == 0) {
|
||||
(void)cbm_macos_adhoc_sign(sign_path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Step 2: Binary path */
|
||||
char self_path[1024];
|
||||
snprintf(self_path, sizeof(self_path), "%s/.local/bin/codebase-memory-mcp", home);
|
||||
|
||||
/* Step 3: Detect agents */
|
||||
cbm_detected_agents_t agents = cbm_detect_agents(home);
|
||||
printf("Detected agents:");
|
||||
if (agents.claude_code) {
|
||||
printf(" Claude-Code");
|
||||
}
|
||||
if (agents.codex) {
|
||||
printf(" Codex");
|
||||
}
|
||||
if (agents.gemini) {
|
||||
printf(" Gemini-CLI");
|
||||
}
|
||||
if (agents.zed) {
|
||||
printf(" Zed");
|
||||
}
|
||||
if (agents.opencode) {
|
||||
printf(" OpenCode");
|
||||
}
|
||||
if (agents.antigravity) {
|
||||
printf(" Antigravity");
|
||||
}
|
||||
if (agents.aider) {
|
||||
printf(" Aider");
|
||||
}
|
||||
if (agents.kilocode) {
|
||||
printf(" KiloCode");
|
||||
}
|
||||
if (agents.vscode) {
|
||||
printf(" VS-Code");
|
||||
}
|
||||
if (agents.openclaw) {
|
||||
printf(" OpenClaw");
|
||||
}
|
||||
if (!agents.claude_code && !agents.codex && !agents.gemini && !agents.zed && !agents.opencode &&
|
||||
!agents.antigravity && !agents.aider && !agents.kilocode && !agents.vscode &&
|
||||
!agents.openclaw) {
|
||||
printf(" (none)");
|
||||
}
|
||||
printf("\n\n");
|
||||
/* Step 3: Install/refresh all agent configs */
|
||||
cbm_install_agent_configs(home, self_path, force, dry_run);
|
||||
|
||||
/* Step 4: Install Claude Code skills + hooks */
|
||||
if (agents.claude_code) {
|
||||
char skills_dir[1024];
|
||||
snprintf(skills_dir, sizeof(skills_dir), "%s/.claude/skills", home);
|
||||
printf("Claude Code:\n");
|
||||
|
||||
int skill_count = cbm_install_skills(skills_dir, force, dry_run);
|
||||
printf(" skills: %d installed\n", skill_count);
|
||||
|
||||
if (cbm_remove_old_monolithic_skill(skills_dir, dry_run)) {
|
||||
printf(" removed old monolithic skill\n");
|
||||
}
|
||||
|
||||
/* MCP config — write to both locations for compatibility.
|
||||
* Claude Code <=2.1.x reads ~/.claude/.mcp.json
|
||||
* Claude Code >=2.1.80 reads ~/.claude.json */
|
||||
char mcp_path[1024];
|
||||
snprintf(mcp_path, sizeof(mcp_path), "%s/.claude/.mcp.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_install_editor_mcp(self_path, mcp_path);
|
||||
}
|
||||
printf(" mcp: %s\n", mcp_path);
|
||||
|
||||
char mcp_path2[1024];
|
||||
snprintf(mcp_path2, sizeof(mcp_path2), "%s/.claude.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_install_editor_mcp(self_path, mcp_path2);
|
||||
}
|
||||
printf(" mcp: %s\n", mcp_path2);
|
||||
|
||||
/* PreToolUse hook + gate script */
|
||||
char settings_path[1024];
|
||||
snprintf(settings_path, sizeof(settings_path), "%s/.claude/settings.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_claude_hooks(settings_path);
|
||||
cbm_install_hook_gate_script(home);
|
||||
}
|
||||
printf(" hooks: PreToolUse (code discovery gate)\n");
|
||||
}
|
||||
|
||||
/* Step 5: Install Codex CLI */
|
||||
if (agents.codex) {
|
||||
printf("Codex CLI:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.codex/config.toml", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_codex_mcp(self_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/.codex/AGENTS.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
}
|
||||
|
||||
/* Step 6: Install Gemini CLI */
|
||||
if (agents.gemini) {
|
||||
printf("Gemini CLI:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.gemini/settings.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_install_editor_mcp(self_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/.gemini/GEMINI.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
|
||||
/* BeforeTool hook (shared with Antigravity) */
|
||||
if (!dry_run) {
|
||||
cbm_upsert_gemini_hooks(config_path);
|
||||
}
|
||||
printf(" hooks: BeforeTool (grep/file search reminder)\n");
|
||||
}
|
||||
|
||||
/* Step 7: Install Zed */
|
||||
if (agents.zed) {
|
||||
printf("Zed:\n");
|
||||
char config_path[1024];
|
||||
#ifdef __APPLE__
|
||||
snprintf(config_path, sizeof(config_path),
|
||||
"%s/Library/Application Support/Zed/settings.json", home);
|
||||
#else
|
||||
snprintf(config_path, sizeof(config_path), "%s/.config/zed/settings.json", home);
|
||||
#endif
|
||||
if (!dry_run) {
|
||||
cbm_install_zed_mcp(self_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
}
|
||||
|
||||
/* Step 8: Install OpenCode */
|
||||
if (agents.opencode) {
|
||||
printf("OpenCode:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.config/opencode/opencode.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_opencode_mcp(self_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/.config/opencode/AGENTS.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
}
|
||||
|
||||
/* Step 9: Install Antigravity */
|
||||
if (agents.antigravity) {
|
||||
printf("Antigravity:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.gemini/antigravity/mcp_config.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_antigravity_mcp(self_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/.gemini/antigravity/AGENTS.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
}
|
||||
|
||||
/* Step 10: Install Aider */
|
||||
if (agents.aider) {
|
||||
printf("Aider:\n");
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/CONVENTIONS.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
}
|
||||
|
||||
/* Step 11: Install KiloCode */
|
||||
if (agents.kilocode) {
|
||||
printf("KiloCode:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path),
|
||||
"%s/.config/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json",
|
||||
home);
|
||||
if (!dry_run) {
|
||||
cbm_install_editor_mcp(self_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
|
||||
/* KiloCode uses ~/.kilocode/rules/ for global instructions */
|
||||
char instr_path[1024];
|
||||
snprintf(instr_path, sizeof(instr_path), "%s/.kilocode/rules/codebase-memory-mcp.md", home);
|
||||
if (!dry_run) {
|
||||
cbm_upsert_instructions(instr_path, agent_instructions_content);
|
||||
}
|
||||
printf(" instructions: %s\n", instr_path);
|
||||
}
|
||||
|
||||
/* Step 12: Install VS Code */
|
||||
if (agents.vscode) {
|
||||
printf("VS Code:\n");
|
||||
char config_path[1024];
|
||||
#ifdef __APPLE__
|
||||
snprintf(config_path, sizeof(config_path),
|
||||
"%s/Library/Application Support/Code/User/mcp.json", home);
|
||||
#else
|
||||
snprintf(config_path, sizeof(config_path), "%s/.config/Code/User/mcp.json", home);
|
||||
#endif
|
||||
if (!dry_run) {
|
||||
cbm_install_vscode_mcp(self_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
}
|
||||
|
||||
/* Step 13: Install OpenClaw */
|
||||
if (agents.openclaw) {
|
||||
printf("OpenClaw:\n");
|
||||
char config_path[1024];
|
||||
snprintf(config_path, sizeof(config_path), "%s/.openclaw/openclaw.json", home);
|
||||
if (!dry_run) {
|
||||
cbm_install_editor_mcp(self_path, config_path);
|
||||
}
|
||||
printf(" mcp: %s\n", config_path);
|
||||
}
|
||||
|
||||
/* Step 14: Ensure PATH */
|
||||
/* Step 4: Ensure PATH */
|
||||
char bin_dir[1024];
|
||||
snprintf(bin_dir, sizeof(bin_dir), "%s/.local/bin", home);
|
||||
const char *rc = cbm_detect_shell_rc(home);
|
||||
@@ -2979,21 +3123,42 @@ int cbm_cmd_update(int argc, char **argv) {
|
||||
}
|
||||
free(bin_data);
|
||||
} else {
|
||||
/* Zip extraction: exec unzip directly without shell interpretation */
|
||||
const char *unzip_argv[] = {"unzip", "-o", "-d", bin_dir, tmp_archive, NULL};
|
||||
rc = cbm_exec_no_shell(unzip_argv);
|
||||
cbm_unlink(tmp_archive);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "error: extraction failed\n");
|
||||
/* Zip extraction: in-memory (no external unzip dependency) */
|
||||
FILE *f = fopen(tmp_archive, "rb");
|
||||
if (!f) {
|
||||
fprintf(stderr, "error: cannot open %s\n", tmp_archive);
|
||||
return 1;
|
||||
}
|
||||
/* Rename variant binary if needed */
|
||||
if (want_ui) {
|
||||
char ui_bin[1024];
|
||||
snprintf(ui_bin, sizeof(ui_bin), "%s/codebase-memory-mcp-ui.exe", bin_dir);
|
||||
snprintf(bin_dest, sizeof(bin_dest), "%s/codebase-memory-mcp.exe", bin_dir);
|
||||
rename(ui_bin, bin_dest);
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
unsigned char *data = malloc((size_t)fsize);
|
||||
if (!data) {
|
||||
fclose(f);
|
||||
cbm_unlink(tmp_archive);
|
||||
return 1;
|
||||
}
|
||||
fread(data, 1, (size_t)fsize, f);
|
||||
fclose(f);
|
||||
|
||||
int bin_len = 0;
|
||||
unsigned char *bin_data = cbm_extract_binary_from_zip(data, (int)fsize, &bin_len);
|
||||
free(data);
|
||||
cbm_unlink(tmp_archive);
|
||||
|
||||
if (!bin_data || bin_len <= 0) {
|
||||
fprintf(stderr, "error: binary not found in zip archive\n");
|
||||
free(bin_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (cbm_replace_binary(bin_dest, bin_data, bin_len, 0755) != 0) {
|
||||
fprintf(stderr, "error: cannot write to %s\n", bin_dest);
|
||||
free(bin_data);
|
||||
return 1;
|
||||
}
|
||||
free(bin_data);
|
||||
}
|
||||
|
||||
/* Step 5b: macOS ad-hoc signing (required for arm64, harmless for x86_64) */
|
||||
@@ -3003,11 +3168,9 @@ int cbm_cmd_update(int argc, char **argv) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Step 6: Reinstall skills (force to pick up new content) */
|
||||
char skills_dir[1024];
|
||||
snprintf(skills_dir, sizeof(skills_dir), "%s/.claude/skills", home);
|
||||
int skill_count = cbm_install_skills(skills_dir, true, false);
|
||||
printf("Updated %d skill(s).\n", skill_count);
|
||||
/* Step 6: Refresh all agent configs (skills, MCP entries, hooks) */
|
||||
printf("Refreshing agent configurations...\n");
|
||||
cbm_install_agent_configs(home, bin_dest, true, false);
|
||||
|
||||
/* Step 7: Verify new version (exec directly, no shell interpretation) */
|
||||
printf("\nUpdate complete. Verifying:\n");
|
||||
|
||||
Reference in new issue
Block a user