@@ -1838,7 +1838,16 @@ int cbm_remove_junie_mcp(const char *config_path) {
18381838#define CMM_HOOK_MATCHER "Grep|Glob|Read"
18391839/* Basename only; the full command path is resolved at install time via
18401840 * cbm_resolve_hook_command so $CLAUDE_CONFIG_DIR is honored. */
1841+ #ifdef _WIN32
1842+ /* #929: extensionless bash shims under %USERPROFILE%\\.claude\\hooks trigger
1843+ * the "How do you want to open this file?" dialog when editors (Cursor) scan
1844+ * the hooks dir, and cannot execute without bash anyway. Windows installs
1845+ * .cmd scripts; the extensionless legacy files are removed on upgrade. */
1846+ #define CMM_HOOK_GATE_SCRIPT "cbm-code-discovery-gate.cmd"
1847+ #else
18411848#define CMM_HOOK_GATE_SCRIPT "cbm-code-discovery-gate"
1849+ #endif
1850+ #define CMM_HOOK_GATE_SCRIPT_LEGACY "cbm-code-discovery-gate"
18421851/* Hard backstop in settings.json; the binary also self-bounds with an
18431852 * in-process deadline well under this. */
18441853#define CMM_HOOK_TIMEOUT_SEC 5
@@ -2086,6 +2095,20 @@ int cbm_remove_claude_hooks(const char *settings_path) {
20862095 * a missing/old/hung binary results in a silent exit 0 (issue #362/#288).
20872096 * The legacy filename `cbm-code-discovery-gate` is retained so existing
20882097 * settings.json entries and uninstall keep working with zero migration. */
2098+ /* #929 (Windows): remove the pre-.cmd extensionless twin so upgrades stop
2099+ * triggering the Open-With dialog. POSIX keeps the extensionless name, where
2100+ * legacy == current — never unlink there. */
2101+ static void cbm_remove_legacy_hook_script (const char * hooks_dir , const char * legacy_name ) {
2102+ #ifdef _WIN32
2103+ char legacy_path [CLI_BUF_1K ];
2104+ snprintf (legacy_path , sizeof (legacy_path ), "%s/%s" , hooks_dir , legacy_name );
2105+ cbm_unlink (legacy_path );
2106+ #else
2107+ (void )hooks_dir ;
2108+ (void )legacy_name ;
2109+ #endif
2110+ }
2111+
20892112void cbm_install_hook_gate_script (const char * home , const char * binary_path ) {
20902113 if (!home || !binary_path ) {
20912114 return ;
@@ -2106,13 +2129,25 @@ void cbm_install_hook_gate_script(const char *home, const char *binary_path) {
21062129 snprintf (hooks_dir , sizeof (hooks_dir ), "%s/hooks" , config_dir );
21072130 cbm_mkdir_p (hooks_dir , CLI_OCTAL_PERM );
21082131
2132+ cbm_remove_legacy_hook_script (hooks_dir , CMM_HOOK_GATE_SCRIPT_LEGACY );
21092133 char script_path [CLI_BUF_1K ];
21102134 snprintf (script_path , sizeof (script_path ), "%s/" CMM_HOOK_GATE_SCRIPT , hooks_dir );
21112135
21122136 FILE * f = fopen (script_path , "w" );
21132137 if (!f ) {
21142138 return ;
21152139 }
2140+ #ifdef _WIN32
2141+ (void )fprintf (f ,
2142+ "@echo off\r\n"
2143+ "REM codebase-memory-mcp search augmenter (Claude Code PreToolUse).\r\n"
2144+ "REM Never blocks a tool call - it only adds graph context.\r\n"
2145+ "REM Any failure is silent (exit 0, no output).\r\n"
2146+ "if not exist \"%s\" exit /b 0\r\n"
2147+ "\"%s\" hook-augment 2>NUL\r\n"
2148+ "exit /b 0\r\n" ,
2149+ binary_path , binary_path );
2150+ #else
21162151 (void )fprintf (f ,
21172152 "#!/usr/bin/env bash\n"
21182153 "# codebase-memory-mcp search augmenter (Claude Code PreToolUse).\n"
@@ -2124,6 +2159,7 @@ void cbm_install_hook_gate_script(const char *home, const char *binary_path) {
21242159 "\"$BIN\" hook-augment 2>/dev/null\n"
21252160 "exit 0\n" ,
21262161 binary_path );
2162+ #endif
21272163 /* fchmod before close to avoid TOCTOU race (CodeQL cpp/toctou-race-condition) */
21282164#ifndef _WIN32
21292165 fchmod (fileno (f ), CLI_OCTAL_PERM );
@@ -2135,7 +2171,12 @@ void cbm_install_hook_gate_script(const char *home, const char *binary_path) {
21352171}
21362172
21372173/* SessionStart hook: remind agent to use MCP tools on every context reset. */
2174+ #ifdef _WIN32
2175+ #define CMM_SESSION_REMINDER_SCRIPT "cbm-session-reminder.cmd"
2176+ #else
21382177#define CMM_SESSION_REMINDER_SCRIPT "cbm-session-reminder"
2178+ #endif
2179+ #define CMM_SESSION_REMINDER_SCRIPT_LEGACY "cbm-session-reminder"
21392180
21402181static void cbm_install_session_reminder_script (const char * home ) {
21412182 if (!home ) {
@@ -2150,13 +2191,33 @@ static void cbm_install_session_reminder_script(const char *home) {
21502191 snprintf (hooks_dir , sizeof (hooks_dir ), "%s/hooks" , config_dir );
21512192 cbm_mkdir_p (hooks_dir , CLI_OCTAL_PERM );
21522193
2194+ cbm_remove_legacy_hook_script (hooks_dir , CMM_SESSION_REMINDER_SCRIPT_LEGACY );
21532195 char script_path [CLI_BUF_1K ];
21542196 snprintf (script_path , sizeof (script_path ), "%s/" CMM_SESSION_REMINDER_SCRIPT , hooks_dir );
21552197
21562198 FILE * f = fopen (script_path , "w" );
21572199 if (!f ) {
21582200 return ;
21592201 }
2202+ #ifdef _WIN32
2203+ /* cmd variant: echo per line; `|` must be caret-escaped in cmd. */
2204+ (void )fprintf (
2205+ f ,
2206+ "@echo off\r\n"
2207+ "REM SessionStart hook: remind agent to use codebase-memory-mcp tools.\r\n"
2208+ "echo CRITICAL - Code Discovery Protocol:\r\n"
2209+ "echo 1. ALWAYS use codebase-memory-mcp tools FIRST for ANY code exploration:\r\n"
2210+ "echo - search_graph(name_pattern/label/qn_pattern) to find functions/classes/routes\r\n"
2211+ "echo - trace_path(function_name, mode=calls^|data_flow^|cross_service) for call "
2212+ "chains\r\n"
2213+ "echo - get_code_snippet(qualified_name) for exact symbol source (precise ranges)\r\n"
2214+ "echo - query_graph(query) for complex Cypher patterns\r\n"
2215+ "echo - get_architecture(aspects) for project structure\r\n"
2216+ "echo - search_code(pattern) for text search (graph-augmented grep)\r\n"
2217+ "echo 2. Use Grep/Glob/Read freely for text, configs, non-code files, and\r\n"
2218+ "echo always Read a file before editing it.\r\n"
2219+ "echo 3. If a project is not indexed yet, run index_repository FIRST.\r\n" );
2220+ #else
21602221 (void )fprintf (
21612222 f , "#!/usr/bin/env bash\n"
21622223 "# SessionStart hook: remind agent to use codebase-memory-mcp tools.\n"
@@ -2174,6 +2235,7 @@ static void cbm_install_session_reminder_script(const char *home) {
21742235 " always Read a file before editing it.\n"
21752236 "3. If a project is not indexed yet, run index_repository FIRST.\n"
21762237 "REMINDER\n" );
2238+ #endif
21772239#ifndef _WIN32
21782240 fchmod (fileno (f ), CLI_OCTAL_PERM );
21792241#endif
@@ -2220,7 +2282,12 @@ static int cbm_remove_session_hooks(const char *settings_path) {
22202282 * The text is a leaner variant of the SessionStart protocol: it omits the
22212283 * "run index_repository first" step, since the parent session has already
22222284 * indexed the project. Matcher "*" fires for every agent type. */
2285+ #ifdef _WIN32
2286+ #define CMM_SUBAGENT_REMINDER_SCRIPT "cbm-subagent-reminder.cmd"
2287+ #else
22232288#define CMM_SUBAGENT_REMINDER_SCRIPT "cbm-subagent-reminder"
2289+ #endif
2290+ #define CMM_SUBAGENT_REMINDER_SCRIPT_LEGACY "cbm-subagent-reminder"
22242291
22252292static void cbm_install_subagent_reminder_script (const char * home ) {
22262293 if (!home ) {
@@ -2235,6 +2302,7 @@ static void cbm_install_subagent_reminder_script(const char *home) {
22352302 snprintf (hooks_dir , sizeof (hooks_dir ), "%s/hooks" , config_dir );
22362303 cbm_mkdir_p (hooks_dir , CLI_OCTAL_PERM );
22372304
2305+ cbm_remove_legacy_hook_script (hooks_dir , CMM_SUBAGENT_REMINDER_SCRIPT_LEGACY );
22382306 char script_path [CLI_BUF_1K ];
22392307 snprintf (script_path , sizeof (script_path ), "%s/" CMM_SUBAGENT_REMINDER_SCRIPT , hooks_dir );
22402308
@@ -2245,6 +2313,17 @@ static void cbm_install_subagent_reminder_script(const char *home) {
22452313 /* The additionalContext value is a single line with no embedded quotes,
22462314 * backslashes, or newlines, so the JSON below is valid as written — no
22472315 * runtime escaping (and no python3/jq dependency) is required. */
2316+ #ifdef _WIN32
2317+ /* cmd variant: one echo with the JSON verbatim (quotes are literal in
2318+ * cmd echo; no cmd metacharacters appear in the payload). */
2319+ (void )fprintf (f , "@echo off\r\n"
2320+ "REM SubagentStart hook: tell subagents to use codebase-memory-mcp tools.\r\n"
2321+ "echo {\"hookSpecificOutput\":{\"hookEventName\":\"SubagentStart\","
2322+ "\"additionalContext\":\"Code discovery: prefer codebase-memory-mcp tools "
2323+ "(search_graph, trace_path, get_code_snippet, query_graph, get_architecture, "
2324+ "search_code) over grep/file-read for navigating code. Use Grep/Glob/Read for "
2325+ "text, configs, and non-code files.\"}}\r\n" );
2326+ #else
22482327 (void )fprintf (f ,
22492328 "#!/usr/bin/env bash\n"
22502329 "# SubagentStart hook: tell subagents to use codebase-memory-mcp tools.\n"
@@ -2257,6 +2336,7 @@ static void cbm_install_subagent_reminder_script(const char *home) {
22572336 "search_code) over grep/file-read for navigating code. Use Grep/Glob/Read for "
22582337 "text, configs, and non-code files.\"}}\n"
22592338 "REMINDER\n" );
2339+ #endif
22602340#ifndef _WIN32
22612341 fchmod (fileno (f ), CLI_OCTAL_PERM );
22622342#endif
0 commit comments