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

Function handle_detect_changes

src/mcp/mcp.c:10216–10714  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

10214}
10215
10216static char *handle_detect_changes(cbm_mcp_server_t *srv, const char *args) {
10217 char *project = get_project_arg(args);
10218 char *base_branch = cbm_mcp_get_string_arg(args, "base_branch");
10219 char *since = cbm_mcp_get_string_arg(args, "since");
10220 char *scope = cbm_mcp_get_string_arg(args, "scope");
10221 int depth = cbm_mcp_get_int_arg(args, "depth", MCP_DEFAULT_BFS_DEPTH);
10222 depth = clamp_mcp_depth(depth, "detect_changes");
10223
10224 /* scope: "files" = just changed files, "symbols" = files + symbols (default) */
10225 bool want_symbols = !scope || strcmp(scope, "symbols") == 0 || strcmp(scope, "impact") == 0;
10226
10227 /* `since` (e.g. "HEAD~10", "v0.5.0") is the documented diff base but was
10228 * previously parsed and never used: it takes precedence over base_branch.
10229 * Route it through base_branch so the shared shell-arg validation and the
10230 * existing `<base>...HEAD` (three-dot) diff apply unchanged — `since` thus
10231 * adopts the same merge-base semantics base_branch already uses. */
10232 if (since && since[0]) {
10233 free(base_branch);
10234 base_branch = since; /* transfer ownership */
10235 since = NULL;
10236 }
10237 free(since); /* no-op after the swap (since is NULL); frees it otherwise */
10238
10239 if (!base_branch) {
10240 base_branch = heap_strdup("main");
10241 }
10242
10243 /* Reject shell metacharacters, and a leading '-', in the user-supplied
10244 * branch name. base_branch is spliced into `git diff --name-only
10245 * "<base>"...HEAD`; a value starting with '-' would be read by git as an
10246 * option rather than a ref (e.g. `--output=<path>` writes the diff to an
10247 * arbitrary file). A real git ref never begins with '-'. */
10248 if (!cbm_validate_shell_arg(base_branch) || base_branch[0] == '-' ||
10249 !validate_windows_cmd_interpolation_arg(base_branch)) {
10250 free(project);
10251 free(base_branch);
10252 free(scope);
10253 return cbm_mcp_text_result("base_branch contains invalid characters", true);
10254 }
10255
10256 char *root_path = get_project_root(srv, project);
10257 if (!root_path) {
10258 char *err = build_no_store_error(project);
10259 char *res = cbm_mcp_text_result(err, true);
10260 free(err);
10261 free(project);
10262 free(base_branch);
10263 free(scope);
10264 return res;
10265 }
10266
10267 if (!validate_search_path_arg(root_path) ||
10268 !validate_windows_cmd_interpolation_arg(root_path)) {
10269 free(root_path);
10270 free(project);
10271 free(base_branch);
10272 free(scope);
10273 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