mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-10-26 06:43:15 +08:00
fix(hook-augment): register Bash search matcher
Wire Bash into the installed Claude PreToolUse matcher, add end-to-end reachability coverage, migrate the previous Grep|Glob matcher, and remove the unused rtk_grep parameter. Signed-off-by: Jake Abendroth <contact@jakea.net>
This commit is contained in:
+5
-4
@@ -3819,9 +3819,9 @@ static int cbm_remove_vibe_mcp_owned(const char *binary_path, const char *config
|
||||
|
||||
/* ── Claude Code pre-tool hooks ───────────────────────────────── */
|
||||
|
||||
/* Search augmentation runs before Grep/Glob; exact coverage context runs after
|
||||
/* Search augmentation runs before Grep/Glob/Bash; exact coverage context runs after
|
||||
* Read. Both adapters are context-only and fail open. */
|
||||
#define CMM_HOOK_SEARCH_MATCHER "Grep|Glob"
|
||||
#define CMM_HOOK_SEARCH_MATCHER "Grep|Glob|Bash"
|
||||
#define CMM_HOOK_READ_MATCHER "Read"
|
||||
/* Basename only; the full command path is resolved at install time via
|
||||
* cbm_resolve_hook_command so $CLAUDE_CONFIG_DIR is honored. */
|
||||
@@ -3845,6 +3845,7 @@ static int cbm_remove_vibe_mcp_owned(const char *binary_path, const char *config
|
||||
static const char *const cmm_claude_old_matchers[] = {
|
||||
"Grep|Glob|Read|Search",
|
||||
"Grep|Glob|Read",
|
||||
"Grep|Glob",
|
||||
NULL,
|
||||
};
|
||||
static const char *const cmm_gemini_old_matchers[] = {
|
||||
@@ -4961,7 +4962,7 @@ static int cbm_remove_owned_hook_script(const char *path, const char *expected_c
|
||||
|
||||
/* Install the search-augmenter shim to ~/.claude/hooks/.
|
||||
* The shim is a thin wrapper that delegates to `<binary> hook-augment`,
|
||||
* which adds graph context to Grep/Glob calls. It NEVER blocks a tool call:
|
||||
* which adds graph context to Grep/Glob/Bash search calls. It NEVER blocks a tool call:
|
||||
* a missing/old/hung binary results in a silent exit 0 (issue #362/#288).
|
||||
* The legacy filename `cbm-code-discovery-gate` is retained so existing
|
||||
* settings.json entries and uninstall keep working with zero migration. */
|
||||
@@ -7204,7 +7205,7 @@ static void install_claude_code_config(const char *home, const char *binary_path
|
||||
}
|
||||
}
|
||||
if (gate_ok) {
|
||||
printf(" hooks: PreToolUse Grep/Glob search augmentation + PostToolUse Read coverage "
|
||||
printf(" hooks: PreToolUse Grep/Glob/Bash search augmentation + PostToolUse Read coverage "
|
||||
"(non-blocking)\n");
|
||||
}
|
||||
if (session_ok) {
|
||||
|
||||
+1
-1
@@ -459,7 +459,7 @@ int cbm_cmd_config(int argc, char **argv);
|
||||
|
||||
/* hook-augment: stdin-driven Claude Code PreToolUse augmenter.
|
||||
* Reads the hook JSON from stdin and emits hookSpecificOutput.additionalContext
|
||||
* with search_graph hits for Grep/Glob calls. NEVER blocks: every failure
|
||||
* with search_graph hits for Grep/Glob/Bash search calls. NEVER blocks: every failure
|
||||
* path returns 0 with no stdout output. */
|
||||
int cbm_cmd_hook_augment(int argc, char **argv);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*
|
||||
* The underlying query is `search_graph` (pure SQLite, shell-free) — chosen
|
||||
* over `search_code` (which shells out to grep|xargs) so the hook stays cheap
|
||||
* enough to run before every Grep/Glob.
|
||||
* enough to run before every Grep/Glob/Bash call.
|
||||
*/
|
||||
|
||||
#include "cli/cli.h"
|
||||
@@ -1079,7 +1079,7 @@ static bool ha_is_env_assign(const char *t) {
|
||||
|
||||
typedef enum { HA_BIN_GREP, HA_BIN_RG, HA_BIN_AG, HA_BIN_ACK, HA_BIN_UGREP } ha_bin_t;
|
||||
|
||||
static const char *ha_search_bin_val_flags(ha_bin_t bin, bool rtk_grep) {
|
||||
static const char *ha_search_bin_val_flags(ha_bin_t bin) {
|
||||
switch (bin) {
|
||||
case HA_BIN_RG:
|
||||
return "ABCmtTgMP";
|
||||
@@ -1147,7 +1147,7 @@ static bool ha_parse_bash_search_pattern(const char *cmd, char *out, size_t out_
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *val_flags = ha_search_bin_val_flags(bin, rtk && bin == HA_BIN_GREP);
|
||||
const char *val_flags = ha_search_bin_val_flags(bin);
|
||||
const char *pattern = NULL;
|
||||
int e_count = 0;
|
||||
bool end_of_flags = false;
|
||||
|
||||
+46
-4
@@ -21,6 +21,7 @@
|
||||
#include <foundation/constants.h>
|
||||
#include <foundation/platform.h>
|
||||
#include <mcp/mcp.h>
|
||||
#include <pipeline/pipeline.h>
|
||||
#include <foundation/yaml.h>
|
||||
#include <store/store.h>
|
||||
#include <yyjson/yyjson.h>
|
||||
@@ -8791,6 +8792,44 @@ TEST(cli_hook_augment_context_tracks_search_json_shape) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* Exercise the real PreToolUse + Bash route instead of calling only the
|
||||
* tokenizer seam. This pins the event guard and the graph lookup path together.
|
||||
*/
|
||||
TEST(cli_hook_augment_bash_pretooluse_reaches_augmenter) {
|
||||
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
|
||||
ASSERT_NOT_NULL(srv);
|
||||
cbm_store_t *st = cbm_mcp_server_store(srv);
|
||||
ASSERT_NOT_NULL(st);
|
||||
char *project = cbm_project_name_from_path("/tmp/hookproj");
|
||||
ASSERT_NOT_NULL(project);
|
||||
cbm_mcp_server_set_project(srv, project);
|
||||
cbm_store_upsert_project(st, project, "/tmp/hookproj");
|
||||
char qualified_name[256];
|
||||
snprintf(qualified_name, sizeof(qualified_name), "%s.mod.someIndexedSymbol", project);
|
||||
cbm_node_t node = {.project = project,
|
||||
.label = "Function",
|
||||
.name = "someIndexedSymbol",
|
||||
.qualified_name = qualified_name,
|
||||
.file_path = "mod.py",
|
||||
.start_line = 1,
|
||||
.end_line = 4};
|
||||
ASSERT_GT(cbm_store_upsert_node(st, &node), 0);
|
||||
|
||||
const char *input = "{\"hook_event_name\":\"PreToolUse\",\"tool_name\":\"Bash\","
|
||||
"\"cwd\":\"/tmp/hookproj\",\"tool_input\":{"
|
||||
"\"command\":\"rg -n someIndexedSymbol .\"}}";
|
||||
char *output = cbm_hook_augment_process(srv, input);
|
||||
bool reached = output && strstr(output, "hookSpecificOutput") &&
|
||||
strstr(output, "someIndexedSymbol") && strstr(output, "mod.py");
|
||||
free(output);
|
||||
cbm_mcp_server_free(srv);
|
||||
free(project);
|
||||
|
||||
if (!reached)
|
||||
FAIL("PreToolUse Bash must reach graph search augmentation");
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(cli_hook_augment_bash_pattern_extractor) {
|
||||
char out[256];
|
||||
|
||||
@@ -10439,7 +10478,7 @@ TEST(cli_upsert_claude_hook_fresh) {
|
||||
ASSERT_NOT_NULL(data);
|
||||
ASSERT(strstr(data, "PreToolUse") != NULL);
|
||||
ASSERT(strstr(data, "PostToolUse") != NULL);
|
||||
ASSERT(strstr(data, "\"Grep|Glob\"") != NULL);
|
||||
ASSERT(strstr(data, "\"Grep|Glob|Bash\"") != NULL);
|
||||
ASSERT(strstr(data, "\"Read\"") != NULL);
|
||||
ASSERT(strstr(data, "\"Grep|Glob|Read\"") == NULL);
|
||||
ASSERT_EQ(test_count_substring(data, "cbm-code-discovery-gate"), 2U);
|
||||
@@ -10953,7 +10992,7 @@ TEST(cli_upsert_claude_hook_existing) {
|
||||
|
||||
const char *data = read_test_file(settingspath);
|
||||
ASSERT_NOT_NULL(data);
|
||||
ASSERT(strstr(data, "\"Grep|Glob\"") != NULL);
|
||||
ASSERT(strstr(data, "\"Grep|Glob|Bash\"") != NULL);
|
||||
ASSERT(strstr(data, "PostToolUse") != NULL);
|
||||
ASSERT(strstr(data, "\"Read\"") != NULL);
|
||||
/* Existing hook preserved */
|
||||
@@ -11034,7 +11073,8 @@ TEST(cli_upsert_claude_hook_replace) {
|
||||
const char *data = read_test_file(settingspath);
|
||||
ASSERT_NOT_NULL(data);
|
||||
ASSERT(strstr(data, "\"Grep|Glob|Read\"") == NULL);
|
||||
ASSERT(strstr(data, "\"Grep|Glob\"") != NULL);
|
||||
ASSERT(strstr(data, "\"Grep|Glob|Bash\"") != NULL);
|
||||
ASSERT(strstr(data, "\"Grep|Glob\"") == NULL);
|
||||
ASSERT(strstr(data, "PostToolUse") != NULL);
|
||||
ASSERT_EQ(test_count_substring(data, "cbm-code-discovery-gate"), 2U);
|
||||
|
||||
@@ -11086,7 +11126,8 @@ TEST(cli_remove_claude_hooks) {
|
||||
|
||||
const char *data = read_test_file(settingspath);
|
||||
ASSERT_NOT_NULL(data);
|
||||
ASSERT(strstr(data, "Grep|Glob|Read") == NULL);
|
||||
ASSERT(strstr(data, "\"Grep|Glob|Bash\"") == NULL);
|
||||
ASSERT(strstr(data, "\"Grep|Glob\"") == NULL);
|
||||
ASSERT(strstr(data, "cbm-code-discovery-gate") == NULL);
|
||||
|
||||
test_rmdir_r(tmpdir);
|
||||
@@ -12039,6 +12080,7 @@ SUITE(cli) {
|
||||
RUN_TEST(cli_claude_hook_commands_shell_quote_custom_config_dir);
|
||||
RUN_TEST(cli_codex_migrates_to_single_hook_representation);
|
||||
RUN_TEST(cli_hook_augment_context_tracks_search_json_shape);
|
||||
RUN_TEST(cli_hook_augment_bash_pretooluse_reaches_augmenter);
|
||||
RUN_TEST(cli_hook_augment_bash_pattern_extractor);
|
||||
RUN_TEST(cli_hook_augment_lifecycle_output_contract);
|
||||
RUN_TEST(cli_hook_augment_subagent_tier_router_contract);
|
||||
|
||||
Reference in New Issue
Block a user