Walk up from the file's parent directory to find the indexed project, then * check whether the file (repo-relative) is listed in its coverage report. * Mirrors ha_resolve_and_query: an MCP error means "not indexed here" → * climb; a valid project with no entry for this file → stop, no output. */
| 549 | * Mirrors ha_resolve_and_query: an MCP error means "not indexed here" → |
| 550 | * climb; a valid project with no entry for this file → stop, no output. */ |
| 551 | static char *ha_resolve_coverage(cbm_mcp_server_t *srv, const char *file_path) { |
| 552 | char dir[4096]; |
| 553 | snprintf(dir, sizeof(dir), "%s", file_path); |
| 554 | if (!ha_strip_last_component(dir)) { |
| 555 | return NULL; /* file directly at a root — nothing to resolve against */ |
| 556 | } |
| 557 | char project_root[4096]; |
| 558 | char *project = |
| 559 | ha_resolve_indexed_project_with_root(srv, dir, project_root, sizeof(project_root)); |
| 560 | if (!project) { |
| 561 | return NULL; |
| 562 | } |
| 563 | char canonical_file[4096]; |
| 564 | bool canonical = ha_canonical_path(file_path, canonical_file, sizeof(canonical_file)); |
| 565 | size_t root_len = strlen(project_root); |
| 566 | const char *rel = canonical && ha_path_contains(project_root, canonical_file) |
| 567 | ? canonical_file + root_len |
| 568 | : NULL; |
| 569 | if (rel && *rel == '/') { |
| 570 | rel++; |
| 571 | } |
| 572 | if (!rel || !rel[0]) { |
| 573 | free(project); |
| 574 | return NULL; |
| 575 | } |
| 576 | |
| 577 | yyjson_mut_doc *adoc = yyjson_mut_doc_new(NULL); |
| 578 | yyjson_mut_val *aroot = adoc ? yyjson_mut_obj(adoc) : NULL; |
| 579 | yyjson_mut_val *paths = adoc ? yyjson_mut_arr(adoc) : NULL; |
| 580 | if (!adoc || !aroot || !paths) { |
| 581 | if (adoc) { |
| 582 | yyjson_mut_doc_free(adoc); |
| 583 | } |
| 584 | free(project); |
| 585 | return NULL; |
| 586 | } |
| 587 | yyjson_mut_doc_set_root(adoc, aroot); |
| 588 | yyjson_mut_obj_add_str(adoc, aroot, "project", project); |
| 589 | yyjson_mut_arr_add_strcpy(adoc, paths, rel); |
| 590 | yyjson_mut_obj_add_val(adoc, aroot, "paths", paths); |
| 591 | char *args = yyjson_mut_write(adoc, 0, NULL); |
| 592 | yyjson_mut_doc_free(adoc); |
| 593 | free(project); |
| 594 | if (!args) { |
| 595 | return NULL; |
| 596 | } |
| 597 | char *res = cbm_mcp_handle_tool(srv, "check_index_coverage", args); |
| 598 | free(args); |
| 599 | if (!res) { |
| 600 | return NULL; |
| 601 | } |
| 602 | bool is_error = false; |
| 603 | char *context = ha_coverage_context(res, rel, &is_error); |
| 604 | free(res); |
| 605 | return context; |
| 606 | } |
| 607 | |
| 608 | /* Build one Claude-compatible lifecycle/tool additionalContext payload. Codex, |
no test coverage detected