Skip to content

Commit ad178b2

Browse files
committed
Merge branch 'main' into fix/relative-non-code-imports
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2 parents 0baad94 + 171f99b commit ad178b2

2 files changed

Lines changed: 57 additions & 1 deletion

File tree

‎src/cli/hook_augment.c‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,13 @@ static char *ha_build_args(const char *project, const char *token) {
377377
return out; /* caller frees */
378378
}
379379

380+
/* Closes every search augmentation that found symbols: cbm's own statement,
381+
* outside the untrusted data block, naming the graph tool for the likely next
382+
* question at the moment the agent greps. Facts, not commands. */
383+
static const char HA_SEARCH_FOLLOW_UP[] =
384+
"\n[codebase-memory] For these symbols, trace_path lists callers and callees, "
385+
"get_code_snippet returns the exact source, and search_graph finds related symbols by name.";
386+
380387
/* Parse the MCP envelope returned by cbm_mcp_handle_tool and, if it is a
381388
* successful search_graph result with >=1 hit, format a compact
382389
* additionalContext string. Returns malloc'd text or NULL.
@@ -432,7 +439,7 @@ static char *ha_format_context(const char *envelope, const char *token, bool *is
432439
return NULL; /* valid project, just no matching symbols */
433440
}
434441

435-
char *text = malloc(4096);
442+
char *text = malloc(4096 + sizeof(HA_SEARCH_FOLLOW_UP));
436443
if (!text) {
437444
yyjson_doc_free(idoc);
438445
yyjson_doc_free(edoc);
@@ -476,6 +483,10 @@ static char *ha_format_context(const char *envelope, const char *token, bool *is
476483
safe_path, safe_label[0] ? " " : "", safe_label);
477484
}
478485
}
486+
/* off can run past the list cap when a row is truncated, so append at the
487+
* real end; the allocation reserves room for the whole follow-up line. */
488+
size_t used = strlen(text);
489+
snprintf(text + used, 4096 + sizeof(HA_SEARCH_FOLLOW_UP) - used, "%s", HA_SEARCH_FOLLOW_UP);
479490

480491
yyjson_doc_free(idoc);
481492
yyjson_doc_free(edoc);

‎tests/test_cli.c‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13498,6 +13498,50 @@ TEST(cli_hook_augment_context_tracks_search_json_shape) {
1349813498
PASS();
1349913499
}
1350013500

13501+
/* A search that found symbols closes with cbm's own follow-up line, after the
13502+
* untrusted symbol list and on its own line, naming the graph tools for the
13503+
* next question; it carries no repository-derived value. */
13504+
TEST(cli_hook_augment_context_names_graph_follow_ups) {
13505+
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
13506+
ASSERT_NOT_NULL(srv);
13507+
cbm_store_t *st = cbm_mcp_server_store(srv);
13508+
const char *proj = "hookproj";
13509+
cbm_mcp_server_set_project(srv, proj);
13510+
cbm_store_upsert_project(st, proj, "/tmp/hookproj");
13511+
cbm_node_t n = {.project = proj,
13512+
.label = "Function",
13513+
.name = "someIndexedSymbol",
13514+
.qualified_name = "hookproj.mod.someIndexedSymbol",
13515+
.file_path = "mod.py",
13516+
.start_line = 1,
13517+
.end_line = 4};
13518+
ASSERT_GT(cbm_store_upsert_node(st, &n), 0);
13519+
char *envelope =
13520+
cbm_mcp_handle_tool(srv, "search_graph",
13521+
"{\"project\":\"hookproj\",\"name_pattern\":\".*someIndexedSymbol.*\","
13522+
"\"limit\":5,\"format\":\"json\"}");
13523+
ASSERT_NOT_NULL(envelope);
13524+
13525+
bool is_error = true;
13526+
char *ctx =
13527+
cbm_hook_augment_format_context_for_testing(envelope, "someIndexedSymbol", &is_error);
13528+
ASSERT_FALSE(is_error);
13529+
ASSERT_NOT_NULL(ctx);
13530+
const char *list = strstr(ctx, "\n- hookproj.mod.someIndexedSymbol");
13531+
const char *follow = strstr(ctx, "\n[codebase-memory] For these symbols, trace_path lists "
13532+
"callers and callees, get_code_snippet returns the exact "
13533+
"source, and search_graph finds related symbols by name.");
13534+
ASSERT_NOT_NULL(list);
13535+
ASSERT_NOT_NULL(follow);
13536+
ASSERT_TRUE(follow > list);
13537+
ASSERT_NULL(strstr(follow, "someIndexedSymbol"));
13538+
ASSERT_NULL(strstr(follow, "mod.py"));
13539+
free(ctx);
13540+
free(envelope);
13541+
cbm_mcp_server_free(srv);
13542+
PASS();
13543+
}
13544+
1350113545
/* Exercise the real PreToolUse + Bash route instead of calling only the
1350213546
* tokenizer seam. This pins the event guard and the graph lookup path together.
1350313547
*/
@@ -18479,6 +18523,7 @@ SUITE(cli) {
1847918523
RUN_TEST(cli_codex_preflight_reports_heading_and_reason);
1848018524
#endif
1848118525
RUN_TEST(cli_hook_augment_context_tracks_search_json_shape);
18526+
RUN_TEST(cli_hook_augment_context_names_graph_follow_ups);
1848218527
RUN_TEST(cli_hook_augment_bash_pretooluse_reaches_augmenter);
1848318528
RUN_TEST(cli_hook_augment_bash_pattern_extractor);
1848418529
RUN_TEST(cli_hook_augment_coverage_requests_machine_json);

0 commit comments

Comments
 (0)