| 300 | } |
| 301 | |
| 302 | int cbm_cmd_hook_augment(void) { |
| 303 | ha_arm_deadline(); |
| 304 | |
| 305 | char *input = ha_read_stdin(); |
| 306 | if (!input) { |
| 307 | return 0; |
| 308 | } |
| 309 | yyjson_doc *doc = yyjson_read(input, strlen(input), 0); |
| 310 | if (!doc) { |
| 311 | free(input); |
| 312 | return 0; |
| 313 | } |
| 314 | yyjson_val *root = yyjson_doc_get_root(doc); |
| 315 | |
| 316 | const char *tool = ha_obj_str(root, "tool_name"); |
| 317 | if (!tool || (strcmp(tool, "Grep") != 0 && strcmp(tool, "Glob") != 0)) { |
| 318 | yyjson_doc_free(doc); |
| 319 | free(input); |
| 320 | return 0; |
| 321 | } |
| 322 | |
| 323 | yyjson_val *tin = yyjson_obj_get(root, "tool_input"); |
| 324 | const char *pattern = ha_obj_str(tin, "pattern"); |
| 325 | char token[HA_MAX_TOKEN + 1]; |
| 326 | if (!ha_extract_token(pattern, token, sizeof(token))) { |
| 327 | yyjson_doc_free(doc); |
| 328 | free(input); |
| 329 | return 0; |
| 330 | } |
| 331 | |
| 332 | const char *cwd = ha_obj_str(root, "cwd"); |
| 333 | char cwdbuf[4096]; |
| 334 | #ifndef _WIN32 |
| 335 | if (!cwd || !cbm_hook_path_is_abs(cwd)) { |
| 336 | if (!getcwd(cwdbuf, sizeof(cwdbuf))) { |
| 337 | yyjson_doc_free(doc); |
| 338 | free(input); |
| 339 | return 0; |
| 340 | } |
| 341 | cwd = cwdbuf; |
| 342 | } |
| 343 | #else |
| 344 | /* Windows: Claude Code passes an absolute drive-letter cwd in the hook |
| 345 | * payload (e.g. C:\repo). Normalize '\\' -> '/' and require an absolute |
| 346 | * path; the walk-up loop handles POSIX and "X:/..." roots alike. Without |
| 347 | * a usable cwd there is nothing to augment — fail open cleanly. */ |
| 348 | if (cwd) { |
| 349 | snprintf(cwdbuf, sizeof(cwdbuf), "%s", cwd); |
| 350 | for (char *p = cwdbuf; *p; p++) { |
| 351 | if (*p == '\\') { |
| 352 | *p = '/'; |
| 353 | } |
| 354 | } |
| 355 | cwd = cwdbuf; |
| 356 | } |
| 357 | if (!cwd || !cbm_hook_path_is_abs(cwd)) { |
| 358 | yyjson_doc_free(doc); |
| 359 | free(input); |
no test coverage detected