Skip to content

Commit 1a0f85f

Browse files
authored
Merge pull request #17 from ngallodev/feat/legacy-mcp-block-warning
feat(cli): warn about legacy codebase-memory-mcp instruction blocks
2 parents 71e1712 + 8ae1000 commit 1a0f85f

3 files changed

Lines changed: 138 additions & 1 deletion

File tree

‎docs/CONFIGURATION.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ not name is permitted.
195195

196196
## 5. Agent and Editor Integration Files
197197

198-
The CLI-first `install` path can write Codebase Memory skills and durable CLI instructions for detected agents. Hooks are a separate explicit opt-in via `codebase-memory-cli install-hooks`. Neither lifecycle surface creates, migrates, adopts, or removes MCP registrations or MCP-owned assets; existing MCP state is treated as foreign for side-by-side safety.
198+
The CLI-first `install` path can write Codebase Memory skills and durable CLI instructions for detected agents. Hooks are a separate explicit opt-in via `codebase-memory-cli install-hooks`. Neither lifecycle surface creates, migrates, adopts, or removes MCP registrations or MCP-owned assets; existing MCP state is treated as foreign for side-by-side safety. Both `install` and `uninstall` warn when an instruction file still contains a legacy `codebase-memory-mcp` managed block from the upstream MCP tool, but they leave that block in place for you to remove by hand.
199199

200200
Those target paths vary by tool and platform, so the easiest way to inspect the exact files for your machine is:
201201

‎src/cli/cli.c‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3229,6 +3229,40 @@ int cbm_remove_instructions(const char *path) {
32293229
: CLI_ERR;
32303230
}
32313231

3232+
/* Upstream codebase-memory-mcp wrote its own managed instruction blocks. The
3233+
* fork's CP78 side-by-side policy treats that MCP state as foreign, so install
3234+
* and uninstall never rewrite or remove it — but the block still points at MCP
3235+
* tools this CLI-only fork does not provide, so warn once per file. Read-only:
3236+
* this never modifies the file. */
3237+
#define LEGACY_MCP_MARKER_START "<!-- codebase-memory-mcp:start -->"
3238+
#define LEGACY_MCP_MARKER_END "<!-- codebase-memory-mcp:end -->"
3239+
3240+
static bool cbm_file_has_legacy_mcp_block(const char *path) {
3241+
if (!path || !path[0]) {
3242+
return false;
3243+
}
3244+
char *content = read_file_str(path, NULL);
3245+
if (!content) {
3246+
return false;
3247+
}
3248+
const char *begin = strstr(content, LEGACY_MCP_MARKER_START);
3249+
bool complete = begin != NULL &&
3250+
strstr(begin + strlen(LEGACY_MCP_MARKER_START), LEGACY_MCP_MARKER_END) != NULL;
3251+
free(content);
3252+
return complete;
3253+
}
3254+
3255+
static void cbm_warn_legacy_mcp_block(const char *path) {
3256+
if (!cbm_file_has_legacy_mcp_block(path)) {
3257+
return;
3258+
}
3259+
(void)fprintf(stderr,
3260+
" warning: %s still contains a legacy codebase-memory-mcp instruction block; "
3261+
"it refers to MCP tools that codebase-memory-cli does not provide. Remove it "
3262+
"manually if it is no longer wanted.\n",
3263+
path);
3264+
}
3265+
32323266
/* ── Codex MCP config (TOML) ─────────────────────────────────── */
32333267

32343268
#define CODEX_CMM_TABLE "mcp_servers.codebase-memory-mcp"
@@ -7816,13 +7850,17 @@ static bool install_generic_agent_config(const char *label, const char *instr_pa
78167850
if (g_install_plan) {
78177851
if (instr_path) {
78187852
plan_record(label, "instructions", instr_path);
7853+
if (cbm_file_has_legacy_mcp_block(instr_path)) {
7854+
plan_record(label, "warning", instr_path);
7855+
}
78197856
}
78207857
return true;
78217858
}
78227859
printf("%s:\n", label);
78237860
printf(" integration: CLI instructions only; hooks require `codebase-memory-cli "
78247861
"install-hooks`\n");
78257862
if (instr_path) {
7863+
cbm_warn_legacy_mcp_block(instr_path);
78267864
if (!dry_run) {
78277865
if (!prepare_config_parent(instr_path) ||
78287866
cbm_upsert_instructions(instr_path, agent_instructions_content) != CLI_OK) {
@@ -9518,6 +9556,7 @@ static char *cbm_build_install_plan_json_options(const char *home, const char *b
95189556
yyjson_mut_val *agent_files = yyjson_mut_arr(doc);
95199557
yyjson_mut_val *prompt_files = yyjson_mut_arr(doc);
95209558
yyjson_mut_val *hooks = yyjson_mut_arr(doc);
9559+
yyjson_mut_val *warnings = yyjson_mut_arr(doc);
95219560
for (int i = 0; i < plan.count; i++) {
95229561
cbm_plan_entry_t *e = &plan.items[i];
95239562
if (strcmp(e->kind, "config") == 0 || strcmp(e->kind, "mcp_config") == 0) {
@@ -9536,6 +9575,8 @@ static char *cbm_build_install_plan_json_options(const char *home, const char *b
95369575
} else if (strcmp(e->kind, "prompt") == 0) {
95379576
yyjson_mut_arr_add_strcpy(doc, prompt_files, e->path);
95389577
yyjson_mut_arr_add_strcpy(doc, instrs, e->path);
9578+
} else if (strcmp(e->kind, "warning") == 0) {
9579+
yyjson_mut_arr_add_strcpy(doc, warnings, e->path);
95399580
} else {
95409581
yyjson_mut_arr_add_strcpy(doc, instrs, e->path);
95419582
}
@@ -9546,6 +9587,7 @@ static char *cbm_build_install_plan_json_options(const char *home, const char *b
95469587
yyjson_mut_obj_add_val(doc, root, "agent_files_planned", agent_files);
95479588
yyjson_mut_obj_add_val(doc, root, "prompt_files_planned", prompt_files);
95489589
yyjson_mut_obj_add_val(doc, root, "hooks_planned", hooks);
9590+
yyjson_mut_obj_add_val(doc, root, "warnings", warnings);
95499591
yyjson_mut_obj_add_bool(doc, root, "writes_started", false);
95509592
yyjson_mut_obj_add_bool(doc, root, "network_after_install", false);
95519593
yyjson_mut_obj_add_str(doc, root, "next_safe_command",
@@ -10258,6 +10300,7 @@ static void uninstall_agent_mcp_instr(mcp_uninstall_args_t paths, bool dry_run,
1025810300
const char *instr_path = paths.instr_path;
1025910301
printf("%s: preserved MCP config by CP78 side-by-side policy\n", name);
1026010302
if (instr_path) {
10303+
cbm_warn_legacy_mcp_block(instr_path);
1026110304
if (!dry_run && cbm_remove_instructions(instr_path) != CLI_OK) {
1026210305
record_agent_config_error(true, name, "instructions_uninstall", instr_path);
1026310306
}

‎tests/test_cli.c‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7349,6 +7349,99 @@ TEST(cli_codex_respects_codex_home) {
73497349
PASS();
73507350
}
73517351

7352+
TEST(cli_install_warns_about_legacy_mcp_instruction_block) {
7353+
char tmpdir[256];
7354+
snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-codex-legacy-mcp-XXXXXX");
7355+
if (!cbm_mkdtemp(tmpdir))
7356+
FAIL("cbm_mkdtemp failed");
7357+
7358+
char codex_home[512];
7359+
char agents_path[640];
7360+
char binary_path[768];
7361+
snprintf(codex_home, sizeof(codex_home), "%s/.codex", tmpdir);
7362+
snprintf(agents_path, sizeof(agents_path), "%s/AGENTS.md", codex_home);
7363+
#ifdef _WIN32
7364+
snprintf(binary_path, sizeof(binary_path), "%s/.local/bin/codebase-memory-cli.exe", tmpdir);
7365+
#else
7366+
snprintf(binary_path, sizeof(binary_path), "%s/.local/bin/codebase-memory-cli", tmpdir);
7367+
#endif
7368+
ASSERT_EQ(test_mkdirp(codex_home), 0);
7369+
7370+
const char *user_guidance = "# Personal Codex guidance\nKeep this byte-for-byte.\n";
7371+
const char *legacy_block = "<!-- codebase-memory-mcp:start -->\n"
7372+
"Call search_graph and trace_path.\n"
7373+
"<!-- codebase-memory-mcp:end -->\n";
7374+
char seeded[2048];
7375+
snprintf(seeded, sizeof(seeded), "%s%s", user_guidance, legacy_block);
7376+
ASSERT_EQ(write_test_file(agents_path, seeded), 0);
7377+
7378+
char *saved_home = save_test_env("HOME");
7379+
char *saved_path = save_test_env("PATH");
7380+
char *saved_codex = save_test_env("CODEX_HOME");
7381+
cbm_setenv("HOME", tmpdir, 1);
7382+
cbm_setenv("PATH", tmpdir, 1);
7383+
cbm_setenv("CODEX_HOME", codex_home, 1);
7384+
7385+
/* A dry run warns and writes nothing. */
7386+
#ifndef _WIN32
7387+
cli_fd_capture_t dry_capture;
7388+
cli_fd_capture_begin(&dry_capture, stderr, STDERR_FILENO);
7389+
#endif
7390+
int dry_rc = cbm_install_agent_configs(tmpdir, binary_path, false, true);
7391+
#ifndef _WIN32
7392+
char *dry_err = cli_fd_capture_end(&dry_capture);
7393+
#else
7394+
char *dry_err = NULL;
7395+
#endif
7396+
char *after_dry = read_test_file_alloc(agents_path);
7397+
bool dry_preserved = after_dry && strcmp(after_dry, seeded) == 0;
7398+
bool dry_warned = true;
7399+
#ifndef _WIN32
7400+
dry_warned =
7401+
dry_err && strstr(dry_err, agents_path) != NULL &&
7402+
strstr(dry_err, "still contains a legacy codebase-memory-mcp instruction block") != NULL;
7403+
#endif
7404+
free(after_dry);
7405+
free(dry_err);
7406+
7407+
/* A real install warns, leaves the legacy bytes intact, and adds our block once. */
7408+
#ifndef _WIN32
7409+
cli_fd_capture_t install_capture;
7410+
cli_fd_capture_begin(&install_capture, stderr, STDERR_FILENO);
7411+
#endif
7412+
int install_rc = cbm_install_agent_configs(tmpdir, binary_path, false, false);
7413+
#ifndef _WIN32
7414+
char *install_err = cli_fd_capture_end(&install_capture);
7415+
#else
7416+
char *install_err = NULL;
7417+
#endif
7418+
char *after = read_test_file_alloc(agents_path);
7419+
bool legacy_preserved = after && strstr(after, legacy_block) != NULL;
7420+
bool user_preserved = after && strstr(after, user_guidance) != NULL;
7421+
bool cli_block_added_once =
7422+
after && test_count_substring(after, test_cli_managed_block_start) == 1U;
7423+
bool install_warned = true;
7424+
#ifndef _WIN32
7425+
install_warned =
7426+
install_err && strstr(install_err, agents_path) != NULL &&
7427+
strstr(install_err, "still contains a legacy codebase-memory-mcp instruction block") !=
7428+
NULL;
7429+
#endif
7430+
7431+
free(after);
7432+
free(install_err);
7433+
restore_test_env("HOME", saved_home);
7434+
restore_test_env("PATH", saved_path);
7435+
restore_test_env("CODEX_HOME", saved_codex);
7436+
test_rmdir_r(tmpdir);
7437+
7438+
if (dry_rc != 0 || install_rc != 0 || !dry_preserved || !dry_warned || !legacy_preserved ||
7439+
!user_preserved || !cli_block_added_once || !install_warned)
7440+
FAIL("Codex install must warn about a legacy codebase-memory-mcp block in --dry-run and "
7441+
"on install, keep its bytes and the user's, and add one CLI managed block");
7442+
PASS();
7443+
}
7444+
73527445
TEST(cli_codex_install_manages_global_instruction_block_issue1689) {
73537446
char tmpdir[256];
73547447
snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-codex-agents-pointer-XXXXXX");
@@ -13533,6 +13626,7 @@ SUITE(cli) {
1353313626
RUN_TEST(cli_registry_installs_codebuddy_bob_and_pochi_durable_context);
1353413627
RUN_TEST(cli_openclaw_resolves_active_json5_workspace);
1353513628
RUN_TEST(cli_codex_respects_codex_home);
13629+
RUN_TEST(cli_install_warns_about_legacy_mcp_instruction_block);
1353613630
RUN_TEST(cli_grok_respects_grok_home);
1353713631
RUN_TEST(cli_codex_install_manages_global_instruction_block_issue1689);
1353813632
RUN_TEST(cli_gemini_session_hook_uses_json_for_all_sources);

0 commit comments

Comments
 (0)