Check if a hook array entry is ours (current matcher or a known old one). * When require_command_substr is non-NULL, the matcher match is not sufficient: * the entry must ALSO carry a hooks[].command containing that substring. This * disambiguates our entry from a user's own hook that happens to share the same * matcher (notably "*", which a user is likely to pick for a catch-all hook), so *
| 1821 | * upsert/remove never clobber a foreign entry. NULL preserves matcher-only |
| 1822 | * matching for callers whose matcher is already CMM-specific (e.g. "startup"). */ |
| 1823 | static bool is_cmm_hook_entry(yyjson_mut_val *entry, const char *matcher_str, |
| 1824 | const char *const *old_matchers, const char *require_command_substr) { |
| 1825 | yyjson_mut_val *matcher = yyjson_mut_obj_get(entry, "matcher"); |
| 1826 | if (!matcher || !yyjson_mut_is_str(matcher)) { |
| 1827 | return false; |
| 1828 | } |
| 1829 | const char *val = yyjson_mut_get_str(matcher); |
| 1830 | if (!val) { |
| 1831 | return false; |
| 1832 | } |
| 1833 | bool matcher_ok = strcmp(val, matcher_str) == 0; |
| 1834 | /* Also match old versions for backwards-compatible upgrade */ |
| 1835 | for (int i = 0; !matcher_ok && old_matchers && old_matchers[i]; i++) { |
| 1836 | if (strcmp(val, old_matchers[i]) == 0) { |
| 1837 | matcher_ok = true; |
| 1838 | } |
| 1839 | } |
| 1840 | if (!matcher_ok) { |
| 1841 | return false; |
| 1842 | } |
| 1843 | if (require_command_substr) { |
| 1844 | yyjson_mut_val *hooks = yyjson_mut_obj_get(entry, "hooks"); |
| 1845 | if (!hooks || !yyjson_mut_is_arr(hooks)) { |
| 1846 | return false; |
| 1847 | } |
| 1848 | size_t idx; |
| 1849 | size_t max; |
| 1850 | yyjson_mut_val *h; |
| 1851 | yyjson_mut_arr_foreach(hooks, idx, max, h) { |
| 1852 | yyjson_mut_val *cmd = yyjson_mut_obj_get(h, "command"); |
| 1853 | if (cmd && yyjson_mut_is_str(cmd)) { |
| 1854 | const char *cs = yyjson_mut_get_str(cmd); |
| 1855 | if (cs && strstr(cs, require_command_substr)) { |
| 1856 | return true; |
| 1857 | } |
| 1858 | } |
| 1859 | } |
| 1860 | return false; |
| 1861 | } |
| 1862 | return true; |
| 1863 | } |
| 1864 | |
| 1865 | /* Generic hook upsert for both Claude Code and Gemini CLI */ |
| 1866 |
no outgoing calls
no test coverage detected