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

Function handle_detect_changes

src/mcp/mcp.c:10536–11034  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

10534}
10535
10536static char *handle_detect_changes(cbm_mcp_server_t *srv, const char *args) {
10537 char *project = get_project_arg(args);
10538 char *base_branch = cbm_mcp_get_string_arg(args, "base_branch");
10539 char *since = cbm_mcp_get_string_arg(args, "since");
10540 char *scope = cbm_mcp_get_string_arg(args, "scope");
10541 int depth = cbm_mcp_get_int_arg(args, "depth", MCP_DEFAULT_BFS_DEPTH);
10542 depth = clamp_mcp_depth(depth, "detect_changes");
10543
10544 /* scope: "files" = just changed files, "symbols" = files + symbols (default) */
10545 bool want_symbols = !scope || strcmp(scope, "symbols") == 0 || strcmp(scope, "impact") == 0;
10546
10547 /* `since` (e.g. "HEAD~10", "v0.5.0") is the documented diff base but was
10548 * previously parsed and never used: it takes precedence over base_branch.
10549 * Route it through base_branch so the shared shell-arg validation and the
10550 * existing `<base>...HEAD` (three-dot) diff apply unchanged — `since` thus
10551 * adopts the same merge-base semantics base_branch already uses. */
10552 if (since && since[0]) {
10553 free(base_branch);
10554 base_branch = since; /* transfer ownership */
10555 since = NULL;
10556 }
10557 free(since); /* no-op after the swap (since is NULL); frees it otherwise */
10558
10559 if (!base_branch) {
10560 base_branch = heap_strdup("main");
10561 }
10562
10563 /* Reject shell metacharacters, and a leading '-', in the user-supplied
10564 * branch name. base_branch is spliced into `git diff --name-only
10565 * "<base>"...HEAD`; a value starting with '-' would be read by git as an
10566 * option rather than a ref (e.g. `--output=<path>` writes the diff to an
10567 * arbitrary file). A real git ref never begins with '-'. */
10568 if (!cbm_validate_shell_arg(base_branch) || base_branch[0] == '-' ||
10569 !validate_windows_cmd_interpolation_arg(base_branch)) {
10570 free(project);
10571 free(base_branch);
10572 free(scope);
10573 return cbm_mcp_text_result("base_branch contains invalid characters", true);
10574 }
10575
10576 char *root_path = get_project_root(srv, project);
10577 if (!root_path) {
10578 char *err = build_no_store_error(project);
10579 char *res = cbm_mcp_text_result(err, true);
10580 free(err);
10581 free(project);
10582 free(base_branch);
10583 free(scope);
10584 return res;
10585 }
10586
10587 if (!validate_search_path_arg(root_path) ||
10588 !validate_windows_cmd_interpolation_arg(root_path)) {
10589 free(root_path);
10590 free(project);
10591 free(base_branch);
10592 free(scope);
10593 return cbm_mcp_text_result("project path contains invalid characters", true);

Callers 1

dispatch_toolFunction · 0.85

Calls 15

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
mcp_request_cancelledFunction · 0.85

Tested by

no test coverage detected