mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-10-28 07:03:20 +08:00
Merge pull request #849 from DeusData/distill/625-auto-watch
feat(watcher): auto_watch config gate for background watch registration (default on)
This commit is contained in:
@@ -122,6 +122,8 @@ codebase-memory-mcp config set auto_index true
|
||||
|
||||
When enabled, new projects are indexed automatically on first connection. Previously-indexed projects are registered with the background watcher for ongoing git-based change detection. Configurable file limit: `config set auto_index_limit 50000`.
|
||||
|
||||
Watcher registration is controlled separately by `auto_watch` (default `true`). Set `config set auto_watch false` to keep a session from registering its project with the background watcher — useful when working across many projects and you want each session contained to explicit indexing.
|
||||
|
||||
### Keeping Up to Date
|
||||
|
||||
```bash
|
||||
@@ -466,6 +468,7 @@ See [docs/cbmignore.md](docs/cbmignore.md) for the full `.cbmignore` how-to: syn
|
||||
codebase-memory-mcp config list # show all settings
|
||||
codebase-memory-mcp config set auto_index true # auto-index on session start
|
||||
codebase-memory-mcp config set auto_index_limit 50000 # max files for auto-index
|
||||
codebase-memory-mcp config set auto_watch false # don't register background git watcher (default: true)
|
||||
codebase-memory-mcp config reset auto_index # reset to default
|
||||
```
|
||||
|
||||
|
||||
@@ -2822,6 +2822,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, "true",
|
||||
"Register background git watcher on session connect");
|
||||
printf(" %-25s default=%-10s %s\n", CBM_CONFIG_UI_LANG, "auto",
|
||||
"Pin graph UI language: en, zh, or auto");
|
||||
return 0;
|
||||
@@ -2849,6 +2851,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, "true"));
|
||||
printf(" %-25s = %-10s\n", CBM_CONFIG_UI_LANG,
|
||||
cbm_config_get(cfg, CBM_CONFIG_UI_LANG, "auto"));
|
||||
} else if (strcmp(argv[0], "get") == 0) {
|
||||
|
||||
@@ -297,6 +297,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"
|
||||
#define CBM_CONFIG_UI_LANG "ui-lang"
|
||||
|
||||
/* ── Subcommands (wired from main.c) ─────────────────────────── */
|
||||
|
||||
+26
-7
@@ -5632,6 +5632,30 @@ static void detect_session(cbm_mcp_server_t *srv) {
|
||||
}
|
||||
}
|
||||
|
||||
/* auto_watch config: gates background watcher registration (default on).
|
||||
* Multi-project users can contain a session to its own project with
|
||||
* `config set auto_watch false`. */
|
||||
static bool auto_watch_enabled(cbm_mcp_server_t *srv) {
|
||||
if (!srv->config) {
|
||||
return true; /* default on */
|
||||
}
|
||||
return cbm_config_get_bool(srv->config, CBM_CONFIG_AUTO_WATCH, true);
|
||||
}
|
||||
|
||||
/* Register the session project with the background watcher for ongoing
|
||||
* change detection — unless auto_watch is disabled. */
|
||||
static void register_watcher_if_enabled(cbm_mcp_server_t *srv) {
|
||||
if (!srv->watcher || srv->session_project[0] == '\0' || srv->session_root[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
if (!auto_watch_enabled(srv)) {
|
||||
cbm_log_info("watcher.register.skipped", "reason", "auto_watch_off", "project",
|
||||
srv->session_project);
|
||||
return;
|
||||
}
|
||||
cbm_watcher_watch(srv->watcher, srv->session_project, srv->session_root);
|
||||
}
|
||||
|
||||
/* Background auto-index thread function */
|
||||
static void *autoindex_thread(void *arg) {
|
||||
cbm_mcp_server_t *srv = (cbm_mcp_server_t *)arg;
|
||||
@@ -5654,10 +5678,7 @@ static void *autoindex_thread(void *arg) {
|
||||
|
||||
if (rc == 0) {
|
||||
cbm_log_info("autoindex.done", "project", srv->session_project);
|
||||
/* Register with watcher for ongoing change detection */
|
||||
if (srv->watcher) {
|
||||
cbm_watcher_watch(srv->watcher, srv->session_project, srv->session_root);
|
||||
}
|
||||
register_watcher_if_enabled(srv);
|
||||
} else {
|
||||
cbm_log_warn("autoindex.err", "msg", "pipeline_run_failed");
|
||||
}
|
||||
@@ -5680,9 +5701,7 @@ static void maybe_auto_index(cbm_mcp_server_t *srv) {
|
||||
/* Already indexed → register watcher for change detection */
|
||||
cbm_log_info("autoindex.skip", "reason", "already_indexed", "project",
|
||||
srv->session_project);
|
||||
if (srv->watcher) {
|
||||
cbm_watcher_watch(srv->watcher, srv->session_project, srv->session_root);
|
||||
}
|
||||
register_watcher_if_enabled(srv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
#include "../src/foundation/constants.h"
|
||||
#include "../src/foundation/log.h"
|
||||
#include "test_framework.h"
|
||||
#include "test_helpers.h"
|
||||
#include <cli/cli.h>
|
||||
#include <mcp/mcp.h>
|
||||
#include <pipeline/pipeline.h>
|
||||
#include <store/store.h>
|
||||
#include <watcher/watcher.h>
|
||||
#include <yyjson/yyjson.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@@ -4032,6 +4034,124 @@ TEST(readonly_query_succeeds_on_readonly_fs) {
|
||||
|
||||
#undef ROQ_PROJECT
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════
|
||||
* AUTO_WATCH GATE (distilled from PR #625)
|
||||
*
|
||||
* Background watcher registration on session connect is gated by the
|
||||
* `auto_watch` config key (default TRUE = existing behavior).
|
||||
* ══════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* Drive the already-indexed connect path (initialize → maybe_auto_index →
|
||||
* watcher registration) and return the resulting watch count.
|
||||
* auto_watch_value: NULL leaves the key unset (exercises the default),
|
||||
* otherwise the key is set to that value before initialize.
|
||||
* Returns a negative code on fixture setup failure. */
|
||||
static int auto_watch_connect_watch_count(const char *auto_watch_value) {
|
||||
char cache[256];
|
||||
snprintf(cache, sizeof(cache), "/tmp/cbm-autowatch-cache-XXXXXX");
|
||||
if (!cbm_mkdtemp(cache)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char repodir[512];
|
||||
snprintf(repodir, sizeof(repodir), "%s/repo", cache);
|
||||
if (th_mkdir_p(repodir) != 0) {
|
||||
th_rmtree(cache);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Same derivation detect_session uses on the cwd — realpath-based, so
|
||||
* the name matches even where /tmp is a symlink (macOS). */
|
||||
char *project = cbm_project_name_from_path(repodir);
|
||||
if (!project) {
|
||||
th_rmtree(cache);
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Pre-create <cache>/<project>.db so maybe_auto_index takes the
|
||||
* "already indexed" branch — the watcher-registration site under test. */
|
||||
char db_path[1024];
|
||||
snprintf(db_path, sizeof(db_path), "%s/%s.db", cache, project);
|
||||
if (th_write_file(db_path, "") != 0) {
|
||||
free(project);
|
||||
th_rmtree(cache);
|
||||
return -4;
|
||||
}
|
||||
free(project);
|
||||
|
||||
const char *saved = getenv("CBM_CACHE_DIR");
|
||||
char *saved_copy = saved ? strdup(saved) : NULL;
|
||||
cbm_setenv("CBM_CACHE_DIR", cache, 1);
|
||||
|
||||
char old_cwd[1024];
|
||||
if (!cbm_getcwd(old_cwd, sizeof(old_cwd)) || cbm_chdir(repodir) != 0) {
|
||||
restore_cache_dir(saved_copy);
|
||||
free(saved_copy);
|
||||
th_rmtree(cache);
|
||||
return -5;
|
||||
}
|
||||
|
||||
int count = -6;
|
||||
cbm_config_t *cfg = cbm_config_open(cache);
|
||||
cbm_store_t *wstore = cbm_store_open_memory();
|
||||
cbm_watcher_t *watcher = wstore ? cbm_watcher_new(wstore, NULL, NULL) : NULL;
|
||||
if (cfg && watcher) {
|
||||
if (auto_watch_value) {
|
||||
cbm_config_set(cfg, CBM_CONFIG_AUTO_WATCH, auto_watch_value);
|
||||
}
|
||||
|
||||
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
|
||||
if (srv) {
|
||||
cbm_mcp_server_set_watcher(srv, watcher);
|
||||
cbm_mcp_server_set_config(srv, cfg);
|
||||
char *resp = cbm_mcp_server_handle(
|
||||
srv, "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{}}");
|
||||
free(resp);
|
||||
count = cbm_watcher_watch_count(watcher);
|
||||
cbm_mcp_server_free(srv);
|
||||
}
|
||||
}
|
||||
|
||||
if (watcher) {
|
||||
cbm_watcher_free(watcher);
|
||||
}
|
||||
if (wstore) {
|
||||
cbm_store_close(wstore);
|
||||
}
|
||||
if (cfg) {
|
||||
cbm_config_close(cfg);
|
||||
}
|
||||
|
||||
(void)cbm_chdir(old_cwd);
|
||||
restore_cache_dir(saved_copy);
|
||||
free(saved_copy);
|
||||
th_rmtree(cache);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Default (key unset) → watcher registered on connect. Guards the
|
||||
* no-behavior-change promise of the auto_watch gate: existing users keep
|
||||
* background auto-sync without touching config. */
|
||||
TEST(mcp_auto_watch_default_registers_watcher_on_connect) {
|
||||
int count = auto_watch_connect_watch_count(NULL);
|
||||
if (count < 0) {
|
||||
PASS(); /* fixture setup failed (tmpdir/cwd unavailable) — skip */
|
||||
}
|
||||
ASSERT_EQ(count, 1);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* auto_watch=false → NO watcher registered on connect. RED on pre-gate code
|
||||
* (registration was unconditional and the key did not exist). */
|
||||
TEST(mcp_auto_watch_false_skips_watcher_on_connect) {
|
||||
int count = auto_watch_connect_watch_count("false");
|
||||
if (count < 0) {
|
||||
PASS(); /* fixture setup failed (tmpdir/cwd unavailable) — skip */
|
||||
}
|
||||
ASSERT_EQ(count, 0);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════
|
||||
* SUITE
|
||||
* ══════════════════════════════════════════════════════════════════ */
|
||||
@@ -4206,4 +4326,8 @@ SUITE(mcp) {
|
||||
RUN_TEST(tool_bad_project_name_no_overflow_issue235);
|
||||
RUN_TEST(tool_bad_project_error_valid_json_issue235);
|
||||
RUN_TEST(tool_resolve_store_by_internal_name_issue704);
|
||||
|
||||
/* auto_watch gate (distilled from PR #625) */
|
||||
RUN_TEST(mcp_auto_watch_default_registers_watcher_on_connect);
|
||||
RUN_TEST(mcp_auto_watch_false_skips_watcher_on_connect);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user