Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile.cbm
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ GRAPH_BUFFER_SRCS = src/graph_buffer/graph_buffer.c
# Pipeline module (new)
PIPELINE_SRCS = \
src/pipeline/fqn.c \
src/pipeline/project_resolve.c \
src/pipeline/path_alias.c \
src/pipeline/registry.c \
src/pipeline/pipeline.c \
Expand Down
73 changes: 45 additions & 28 deletions src/cli/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -2625,6 +2625,8 @@ int cbm_cmd_config(int argc, char **argv) {
"Enable auto-indexing on MCP session start");
printf(" %-25s default=%-10s %s\n", CBM_CONFIG_AUTO_INDEX_LIMIT, "50000",
"Max files for auto-indexing new projects");
printf(" %-25s default=%-10s %s\n", CBM_CONFIG_AUTO_WATCH, "false",
"Enable git watcher background re-indexing (off by default)");
return 0;
}

Expand All @@ -2650,6 +2652,8 @@ int cbm_cmd_config(int argc, char **argv) {
cbm_config_get(cfg, CBM_CONFIG_AUTO_INDEX, "false"));
printf(" %-25s = %-10s\n", CBM_CONFIG_AUTO_INDEX_LIMIT,
cbm_config_get(cfg, CBM_CONFIG_AUTO_INDEX_LIMIT, "50000"));
printf(" %-25s = %-10s\n", CBM_CONFIG_AUTO_WATCH,
cbm_config_get(cfg, CBM_CONFIG_AUTO_WATCH, "false"));
} else if (strcmp(argv[0], "get") == 0) {
if (argc < MIN_ARGC_GET) {
(void)fprintf(stderr, "Usage: config get <key>\n");
Expand Down Expand Up @@ -2990,7 +2994,7 @@ static void plan_record(const char *agent, const char *kind, const char *path) {
}

static void install_claude_code_config(const char *home, const char *binary_path, bool force,
bool dry_run) {
bool dry_run, bool install_hooks) {
char config_dir[CLI_BUF_1K];
cbm_claude_config_dir(home, config_dir, sizeof(config_dir));
char user_root[CLI_BUF_1K];
Expand All @@ -3010,9 +3014,13 @@ static void install_claude_code_config(const char *home, const char *binary_path
snprintf(p, sizeof(p), "%s/settings.json", config_dir);
plan_record("Claude Code", "mcp_config", p);
snprintf(p, sizeof(p), "%s/hooks/%s", config_dir, CMM_HOOK_GATE_SCRIPT);
plan_record("Claude Code", "hook", p);
if (install_hooks) {
plan_record("Claude Code", "hook", p);
}
snprintf(p, sizeof(p), "%s/hooks/%s", config_dir, CMM_SESSION_REMINDER_SCRIPT);
plan_record("Claude Code", "hook", p);
if (install_hooks) {
plan_record("Claude Code", "hook", p);
}
return;
}

Expand Down Expand Up @@ -3041,14 +3049,16 @@ static void install_claude_code_config(const char *home, const char *binary_path

char settings_path[CLI_BUF_1K];
snprintf(settings_path, sizeof(settings_path), "%s/settings.json", config_dir);
if (!dry_run) {
if (!dry_run && install_hooks) {
cbm_upsert_claude_hooks(settings_path);
cbm_install_hook_gate_script(home, binary_path);
cbm_install_session_reminder_script(home);
cbm_upsert_session_hooks(settings_path);
printf(" hooks: PreToolUse (Grep/Glob search-graph augmenter, non-blocking)\n");
printf(" hooks: SessionStart (MCP usage reminder on startup/resume/clear/compact)\n");
} else if (!dry_run) {
printf(" hooks: skipped (pass --hooks to install search augment hooks)\n");
}
printf(" hooks: PreToolUse (Grep/Glob search-graph augmenter, non-blocking)\n");
printf(" hooks: SessionStart (MCP usage reminder on startup/resume/clear/compact)\n");

/* Migration nudge: when CLAUDE_CONFIG_DIR is set and a legacy ~/.claude tree
* still exists, mention it so users can clean up stale artifacts. */
Expand Down Expand Up @@ -3093,26 +3103,29 @@ static void install_generic_agent_config(const char *label, const char *binary_p

/* Install MCP configs for CLI-based agents (Codex, Gemini, OpenCode, Antigravity, Aider). */
/* Install Gemini CLI config with hooks. */
static void install_gemini_config(const char *home, const char *binary_path, bool dry_run) {
static void install_gemini_config(const char *home, const char *binary_path, bool dry_run,
bool install_hooks) {
char cp[CLI_BUF_1K];
char ip[CLI_BUF_1K];
snprintf(cp, sizeof(cp), "%s/.gemini/settings.json", home);
snprintf(ip, sizeof(ip), "%s/.gemini/GEMINI.md", home);
install_generic_agent_config("Gemini CLI", binary_path, cp, ip, dry_run,
cbm_install_editor_mcp);
if (g_install_plan) {
plan_record("Gemini CLI", "hook", cp); /* BeforeTool + SessionStart in settings.json */
if (install_hooks) {
plan_record("Gemini CLI", "hook", cp); /* BeforeTool + SessionStart in settings.json */
}
return;
}
if (!dry_run) {
if (!dry_run && install_hooks) {
cbm_upsert_gemini_hooks(cp);
cbm_upsert_gemini_session_hooks(cp);
printf(" hooks: BeforeTool + SessionStart (codebase-memory-mcp reminder)\n");
}
printf(" hooks: BeforeTool + SessionStart (codebase-memory-mcp reminder)\n");
}

static void install_cli_agent_configs(const cbm_detected_agents_t *agents, const char *home,
const char *binary_path, bool dry_run) {
const char *binary_path, bool dry_run, bool install_hooks) {
if (agents->codex) {
char cp[CLI_BUF_1K];
char ip[CLI_BUF_1K];
Expand All @@ -3121,16 +3134,16 @@ static void install_cli_agent_configs(const cbm_detected_agents_t *agents, const
install_generic_agent_config("Codex CLI", binary_path, cp, ip, dry_run,
cbm_upsert_codex_mcp);
if (g_install_plan) {
plan_record("Codex CLI", "hook", cp);
} else {
if (!dry_run) {
cbm_upsert_codex_hooks(cp);
if (install_hooks) {
plan_record("Codex CLI", "hook", cp);
}
} else if (!dry_run && install_hooks) {
cbm_upsert_codex_hooks(cp);
printf(" hooks: SessionStart (codebase-memory-mcp reminder)\n");
}
}
if (agents->gemini) {
install_gemini_config(home, binary_path, dry_run);
install_gemini_config(home, binary_path, dry_run, install_hooks);
}
if (agents->opencode) {
char cp[CLI_BUF_1K];
Expand Down Expand Up @@ -3160,11 +3173,11 @@ static void install_cli_agent_configs(const cbm_detected_agents_t *agents, const
char sp[CLI_BUF_1K];
snprintf(sp, sizeof(sp), "%s/.gemini/antigravity-cli/settings.json", home);
if (g_install_plan) {
plan_record("Antigravity", "hook", sp);
} else {
if (!dry_run) {
cbm_upsert_gemini_session_hooks(sp);
if (install_hooks) {
plan_record("Antigravity", "hook", sp);
}
} else if (!dry_run && install_hooks) {
cbm_upsert_gemini_session_hooks(sp);
printf(" hooks: SessionStart (codebase-memory-mcp reminder)\n");
}
}
Expand Down Expand Up @@ -3250,16 +3263,16 @@ static void install_editor_agent_configs(const cbm_detected_agents_t *agents, co
}

static void cbm_install_agent_configs(const char *home, const char *binary_path, bool force,
bool dry_run) {
bool dry_run, bool install_hooks) {
cbm_detected_agents_t agents = cbm_detect_agents(home);
if (!g_install_plan) {
print_detected_agents(&agents);
}

if (agents.claude_code) {
install_claude_code_config(home, binary_path, force, dry_run);
install_claude_code_config(home, binary_path, force, dry_run, install_hooks);
}
install_cli_agent_configs(&agents, home, binary_path, dry_run);
install_cli_agent_configs(&agents, home, binary_path, dry_run, install_hooks);
install_editor_agent_configs(&agents, home, binary_path, dry_run);
}

Expand Down Expand Up @@ -3317,7 +3330,7 @@ static void cbm_detect_self_path(char *buf, size_t buf_sz, const char *home) {
* the config / instruction / hook files `install` WOULD write, produced by
* running the real install dispatch in record-only mode (no mutation, no
* network). Returns a heap JSON string (caller frees) or NULL. */
char *cbm_build_install_plan_json(const char *home, const char *binary_path) {
char *cbm_build_install_plan_json(const char *home, const char *binary_path, bool install_hooks) {
if (!home || !binary_path) {
return NULL;
}
Expand All @@ -3326,7 +3339,7 @@ char *cbm_build_install_plan_json(const char *home, const char *binary_path) {
* site records into `plan` — so the receipt cannot drift from behavior. */
cbm_install_plan_t plan = {0};
g_install_plan = &plan;
cbm_install_agent_configs(home, binary_path, false, true);
cbm_install_agent_configs(home, binary_path, false, true, install_hooks);
g_install_plan = NULL;

cbm_detected_agents_t det = cbm_detect_agents(home);
Expand Down Expand Up @@ -3395,6 +3408,7 @@ int cbm_cmd_install(int argc, char **argv) {
bool dry_run = false;
bool force = false;
bool plan = false;
bool install_hooks = false;
for (int i = 0; i < argc; i++) {
if (strcmp(argv[i], "--dry-run") == 0) {
dry_run = true;
Expand All @@ -3405,6 +3419,9 @@ int cbm_cmd_install(int argc, char **argv) {
if (strcmp(argv[i], "--plan") == 0) {
plan = true;
}
if (strcmp(argv[i], "--hooks") == 0) {
install_hooks = true;
}
}

const char *home = cbm_get_home_dir();
Expand All @@ -3419,7 +3436,7 @@ int cbm_cmd_install(int argc, char **argv) {
if (plan) {
char self_path[CLI_BUF_1K] = {0};
cbm_detect_self_path(self_path, sizeof(self_path), home);
char *json = cbm_build_install_plan_json(home, self_path);
char *json = cbm_build_install_plan_json(home, self_path, install_hooks);
if (!json) {
(void)fprintf(stderr, "error: failed to build install plan\n");
return CLI_TRUE;
Expand Down Expand Up @@ -3515,7 +3532,7 @@ int cbm_cmd_install(int argc, char **argv) {
#endif

/* Step 3: Install/refresh all agent configs, pointing at the install target. */
cbm_install_agent_configs(home, bin_target, force, dry_run);
cbm_install_agent_configs(home, bin_target, force, dry_run, install_hooks);

/* Step 4: Ensure PATH */
char bin_dir[CLI_BUF_1K];
Expand Down Expand Up @@ -4110,7 +4127,7 @@ int cbm_cmd_update(int argc, char **argv) {

/* Step 6: Refresh all agent configs (skills, MCP entries, hooks) */
printf("Refreshing agent configurations...\n");
cbm_install_agent_configs(home, bin_dest, true, false);
cbm_install_agent_configs(home, bin_dest, true, false, false);

/* Step 7: Verify new version (exec directly, no shell interpretation) */
printf("\nUpdate complete. Verifying:\n");
Expand Down
3 changes: 2 additions & 1 deletion src/cli/cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ int cbm_config_delete(cbm_config_t *cfg, const char *key);
/* Well-known config keys */
#define CBM_CONFIG_AUTO_INDEX "auto_index"
#define CBM_CONFIG_AUTO_INDEX_LIMIT "auto_index_limit"
#define CBM_CONFIG_AUTO_WATCH "auto_watch"

/* ── Subcommands (wired from main.c) ─────────────────────────── */

Expand Down Expand Up @@ -291,6 +292,6 @@ int cbm_cmd_hook_augment(void);
* a machine-readable JSON list of the config/instruction/hook files `install`
* would write, produced WITHOUT mutating anything. Returns a heap JSON string
* (caller frees) or NULL on error. Exposed for `install --plan` and testing. */
char *cbm_build_install_plan_json(const char *home, const char *binary_path);
char *cbm_build_install_plan_json(const char *home, const char *binary_path, bool install_hooks);

#endif /* CBM_CLI_H */
28 changes: 21 additions & 7 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,21 @@ enum {
MAIN_MAX_PORT = 65536,
PARENT_WATCHDOG_STACK_SIZE = 64 * CBM_SZ_1K, /* watchdog only polls — tiny stack suffices */
};
#define MAIN_RAM_FRACTION 0.5
#define MAIN_RAM_FRACTION_DEFAULT 0.5
#define MAIN_RAM_FRACTION_16GB 0.25
#define MAIN_RAM_FRACTION_32GB 0.35
#define MAIN_RAM_BYTES_PER_GB (1024ULL * 1024 * 1024)

static double main_ram_fraction(void) {
cbm_system_info_t info = cbm_system_info();
if (info.total_ram <= 16ULL * MAIN_RAM_BYTES_PER_GB) {
return MAIN_RAM_FRACTION_16GB;
}
if (info.total_ram <= 32ULL * MAIN_RAM_BYTES_PER_GB) {
return MAIN_RAM_FRACTION_32GB;
}
return MAIN_RAM_FRACTION_DEFAULT;
}

#define SLEN(s) (sizeof(s) - 1)
#include "foundation/log.h"
Expand Down Expand Up @@ -284,7 +298,7 @@ static void print_help(void) {
printf("Usage:\n");
printf(" codebase-memory-mcp Run MCP server on stdio\n");
printf(" codebase-memory-mcp cli <tool> [json] Run a single tool\n");
printf(" codebase-memory-mcp install [-y|-n] [--force] [--dry-run]\n");
printf(" codebase-memory-mcp install [-y|-n] [--force] [--hooks] [--dry-run]\n");
printf(" codebase-memory-mcp uninstall [-y|-n] [--dry-run]\n");
printf(" codebase-memory-mcp update [-y|-n]\n");
printf(" codebase-memory-mcp config <list|get|set|reset>\n");
Expand Down Expand Up @@ -324,11 +338,11 @@ static int handle_subcommand(int argc, char **argv) {
return 0;
}
if (strcmp(argv[i], "cli") == 0) {
cbm_mem_init(MAIN_RAM_FRACTION);
cbm_mem_init(main_ram_fraction());
return run_cli(argc - i - SKIP_ONE, argv + i + SKIP_ONE);
}
if (strcmp(argv[i], "hook-augment") == 0) {
cbm_mem_init(MAIN_RAM_FRACTION);
cbm_mem_init(main_ram_fraction());
return cbm_cmd_hook_augment();
}
if (strcmp(argv[i], "install") == 0) {
Expand Down Expand Up @@ -424,9 +438,9 @@ int main(int argc, char **argv) {
#endif

/* Default: MCP server on stdio */
cbm_mem_init(MAIN_RAM_FRACTION); /* 50% of RAM — safe now because mimalloc tracks ALL
* memory (C + C++ allocations) via global override.
* No more untracked heap blind spots. */
/* tiered RAM budget — mimalloc tracks ALL memory (C + C++ allocations) via global
* override. No more untracked heap blind spots. */
cbm_mem_init(main_ram_fraction());
/* Store binary path for subprocess spawning + hook log sink */
cbm_http_server_set_binary_path(argv[0]);
cbm_log_set_sink(cbm_ui_log_append);
Expand Down
Loading