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

Function handle_detect_changes

src/mcp/mcp.c:10292–10790  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

10290}
10291
10292static char *handle_detect_changes(cbm_mcp_server_t *srv, const char *args) {
10293 char *project = get_project_arg(args);
10294 char *base_branch = cbm_mcp_get_string_arg(args, "base_branch");
10295 char *since = cbm_mcp_get_string_arg(args, "since");
10296 char *scope = cbm_mcp_get_string_arg(args, "scope");
10297 int depth = cbm_mcp_get_int_arg(args, "depth", MCP_DEFAULT_BFS_DEPTH);
10298 depth = clamp_mcp_depth(depth, "detect_changes");
10299
10300 /* scope: "files" = just changed files, "symbols" = files + symbols (default) */
10301 bool want_symbols = !scope || strcmp(scope, "symbols") == 0 || strcmp(scope, "impact") == 0;
10302
10303 /* `since` (e.g. "HEAD~10", "v0.5.0") is the documented diff base but was
10304 * previously parsed and never used: it takes precedence over base_branch.
10305 * Route it through base_branch so the shared shell-arg validation and the
10306 * existing `<base>...HEAD` (three-dot) diff apply unchanged — `since` thus
10307 * adopts the same merge-base semantics base_branch already uses. */
10308 if (since && since[0]) {
10309 free(base_branch);
10310 base_branch = since; /* transfer ownership */
10311 since = NULL;
10312 }
10313 free(since); /* no-op after the swap (since is NULL); frees it otherwise */
10314
10315 if (!base_branch) {
10316 base_branch = heap_strdup("main");
10317 }
10318
10319 /* Reject shell metacharacters, and a leading '-', in the user-supplied
10320 * branch name. base_branch is spliced into `git diff --name-only
10321 * "<base>"...HEAD`; a value starting with '-' would be read by git as an
10322 * option rather than a ref (e.g. `--output=<path>` writes the diff to an
10323 * arbitrary file). A real git ref never begins with '-'. */
10324 if (!cbm_validate_shell_arg(base_branch) || base_branch[0] == '-' ||
10325 !validate_windows_cmd_interpolation_arg(base_branch)) {
10326 free(project);
10327 free(base_branch);
10328 free(scope);
10329 return cbm_mcp_text_result("base_branch contains invalid characters", true);
10330 }
10331
10332 char *root_path = get_project_root(srv, project);
10333 if (!root_path) {
10334 char *err = build_no_store_error(project);
10335 char *res = cbm_mcp_text_result(err, true);
10336 free(err);
10337 free(project);
10338 free(base_branch);
10339 free(scope);
10340 return res;
10341 }
10342
10343 if (!validate_search_path_arg(root_path) ||
10344 !validate_windows_cmd_interpolation_arg(root_path)) {
10345 free(root_path);
10346 free(project);
10347 free(base_branch);
10348 free(scope);
10349 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