MCPcopy Create free account
hub / github.com/DeusData/codebase-memory-mcp / handle_detect_changes

Function handle_detect_changes

src/mcp/mcp.c:5373–5545  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

5371}
5372
5373static char *handle_detect_changes(cbm_mcp_server_t *srv, const char *args) {
5374 char *project = get_project_arg(args);
5375 char *base_branch = cbm_mcp_get_string_arg(args, "base_branch");
5376 char *since = cbm_mcp_get_string_arg(args, "since");
5377 char *scope = cbm_mcp_get_string_arg(args, "scope");
5378 int depth = cbm_mcp_get_int_arg(args, "depth", MCP_DEFAULT_BFS_DEPTH);
5379 depth = clamp_mcp_depth(depth, "detect_changes");
5380
5381 /* scope: "files" = just changed files, "symbols" = files + symbols (default) */
5382 bool want_symbols = !scope || strcmp(scope, "symbols") == 0 || strcmp(scope, "impact") == 0;
5383
5384 /* `since` (e.g. "HEAD~10", "v0.5.0") is the documented diff base but was
5385 * previously parsed and never used: it takes precedence over base_branch.
5386 * Route it through base_branch so the shared shell-arg validation and the
5387 * existing `<base>...HEAD` (three-dot) diff apply unchanged — `since` thus
5388 * adopts the same merge-base semantics base_branch already uses. */
5389 if (since && since[0]) {
5390 free(base_branch);
5391 base_branch = since; /* transfer ownership */
5392 since = NULL;
5393 }
5394 free(since); /* no-op after the swap (since is NULL); frees it otherwise */
5395
5396 if (!base_branch) {
5397 base_branch = heap_strdup("main");
5398 }
5399
5400 /* Reject shell metacharacters, and a leading '-', in the user-supplied
5401 * branch name. base_branch is spliced into `git diff --name-only
5402 * "<base>"...HEAD`; a value starting with '-' would be read by git as an
5403 * option rather than a ref (e.g. `--output=<path>` writes the diff to an
5404 * arbitrary file). A real git ref never begins with '-'. */
5405 if (!cbm_validate_shell_arg(base_branch) || base_branch[0] == '-') {
5406 free(project);
5407 free(base_branch);
5408 free(scope);
5409 return cbm_mcp_text_result("base_branch contains invalid characters", true);
5410 }
5411
5412 char *root_path = get_project_root(srv, project);
5413 if (!root_path) {
5414 char *err = build_no_store_error(project);
5415 char *res = cbm_mcp_text_result(err, true);
5416 free(err);
5417 free(project);
5418 free(base_branch);
5419 free(scope);
5420 return res;
5421 }
5422
5423 if (!validate_search_path_arg(root_path)) {
5424 free(root_path);
5425 free(project);
5426 free(base_branch);
5427 free(scope);
5428 return cbm_mcp_text_result("project path contains invalid characters", true);
5429 }
5430

Callers 1

cbm_mcp_handle_toolFunction · 0.85

Calls 14

get_project_argFunction · 0.85
cbm_mcp_get_string_argFunction · 0.85
cbm_mcp_get_int_argFunction · 0.85
clamp_mcp_depthFunction · 0.85
cbm_validate_shell_argFunction · 0.85
cbm_mcp_text_resultFunction · 0.85
get_project_rootFunction · 0.85
build_no_store_errorFunction · 0.85
validate_search_path_argFunction · 0.85
cbm_popenFunction · 0.85
cbm_pcloseFunction · 0.85

Tested by

no test coverage detected