@@ -285,6 +285,10 @@ get_managed_claude_event_pattern() {
285285 printf ' %s\n' ' (claude-notify|code-notify.*notifier\.sh|(?:^|[\\/])notify\.(?:ps1|sh)).*(SubagentStart|SubagentStop|TeammateIdle|TaskCreated|TaskCompleted)(?:\s|$)'
286286}
287287
288+ get_managed_claude_pre_tool_use_pattern () {
289+ printf ' %s\n' ' (claude-notify|code-notify.*notifier\.sh|(?:^|[\\/])notify\.(?:ps1|sh)).*PreToolUse(?:\s|$)'
290+ }
291+
288292get_managed_claude_notification_pattern () {
289293 printf ' %s\n' ' (claude-notify|code-notify.*notifier\.sh|(?:^|[\\/])notify\.(?:ps1|sh)).*(notification|PreToolUse)(?:\s|$)'
290294}
@@ -935,6 +939,8 @@ PYTHON
935939register_ask_user_hook () {
936940 local file=" $1 "
937941 local pre_tool_cmd=" $2 "
942+ local pre_tool_pattern
943+ pre_tool_pattern=" $( get_managed_claude_pre_tool_use_pattern) "
938944
939945 if ! is_notify_type_enabled " ask_user" ; then
940946 return 0
@@ -947,12 +953,34 @@ register_ask_user_hook() {
947953 fi
948954
949955 local new_settings
950- new_settings=$( printf ' %s\n' " $settings " | jq --arg cmd " $pre_tool_cmd " '
956+ new_settings=$( printf ' %s\n' " $settings " | jq \
957+ --arg cmd " $pre_tool_cmd " \
958+ --arg pattern " $pre_tool_pattern " \
959+ '
951960 def array_or_empty:
952961 if type == "array" then . else [] end;
962+ def is_managed_hook($exact; $pattern):
963+ ((.type // "") == "command") and
964+ (
965+ ((.command // "") == $exact) or
966+ (((.command // "") | test($pattern)) // false)
967+ );
968+ def strip_managed_ask_user($exact; $pattern):
969+ array_or_empty
970+ | map(
971+ if (.matcher // "") == "AskUserQuestion" and ((.hooks | type) == "array") then
972+ .hooks = (
973+ (.hooks | array_or_empty)
974+ | map(select((is_managed_hook($exact; $pattern)) | not))
975+ )
976+ else
977+ .
978+ end
979+ )
980+ | map(select(((.hooks | type) != "array") or ((.hooks | length) > 0)));
953981 .hooks = (if (.hooks | type) == "object" then .hooks else {} end) |
954982 .hooks.PreToolUse = (
955- (.hooks.PreToolUse | array_or_empty | map(select(.matcher != "AskUserQuestion") )) + [{
983+ (.hooks.PreToolUse | strip_managed_ask_user($cmd; $pattern )) + [{
956984 "matcher": "AskUserQuestion",
957985 "hooks": [{
958986 "type": "command",
@@ -975,10 +1003,11 @@ register_ask_user_hook() {
9751003 tmp_json=$( mktemp) || return 1
9761004 printf ' %s\n' " $settings " > " $tmp_json "
9771005
978- python3 - " $file " " $pre_tool_cmd " " $tmp_json " << 'PYTHON '
1006+ python3 - " $file " " $pre_tool_cmd " " $pre_tool_pattern " " $ tmp_json" << 'PYTHON '
9791007import json, os, sys, tempfile
1008+ import re
9801009
981- file_path, pre_tool_cmd, json_file = sys.argv[1 :4 ]
1010+ file_path, pre_tool_cmd, pre_tool_pattern, json_file = sys.argv[1 :5 ]
9821011
9831012try :
9841013 with open (json_file, " r" ) as fh:
@@ -995,7 +1024,30 @@ if not isinstance(hooks, dict):
9951024else :
9961025 hooks = dict (hooks)
9971026
998- pre_tool_entries = [e for e in hooks.get(" PreToolUse" , []) if e.get(" matcher" ) != " AskUserQuestion" ]
1027+ pre_tool_regex = re.compile(pre_tool_pattern)
1028+
1029+ def is_managed_hook (hook ):
1030+ if not isinstance (hook, dict ) or hook.get(" type" ) != " command" :
1031+ return False
1032+ command = hook.get(" command" )
1033+ if not isinstance (command, str ):
1034+ return False
1035+ return command == pre_tool_cmd or bool (pre_tool_regex.search(command))
1036+
1037+ pre_tool_entries = []
1038+ for entry in hooks.get(" PreToolUse" , []):
1039+ if not isinstance (entry, dict ):
1040+ pre_tool_entries.append(entry)
1041+ continue
1042+ if entry.get(" matcher" , " " ) != " AskUserQuestion" :
1043+ pre_tool_entries.append(entry)
1044+ continue
1045+ filtered_hooks = [hook for hook in entry.get(" hooks" , []) if not is_managed_hook(hook)]
1046+ if filtered_hooks:
1047+ new_entry = dict (entry)
1048+ new_entry[" hooks" ] = filtered_hooks
1049+ pre_tool_entries.append(new_entry)
1050+
9991051pre_tool_entries.append({
10001052 " matcher" : " AskUserQuestion" ,
10011053 " hooks" : [{" type" : " command" , " command" : pre_tool_cmd}]
@@ -1026,6 +1078,9 @@ PYTHON
10261078# Unregister PreToolUse hook for AskUserQuestion
10271079unregister_ask_user_hook () {
10281080 local file=" $1 "
1081+ local pre_tool_cmd=" ${2:- } "
1082+ local pre_tool_pattern
1083+ pre_tool_pattern=" $( get_managed_claude_pre_tool_use_pattern) "
10291084
10301085 if [[ ! -f " $file " ]]; then
10311086 return 0
@@ -1036,14 +1091,32 @@ unregister_ask_user_hook() {
10361091 settings=$( cat " $file " )
10371092
10381093 local new_settings
1039- new_settings=$( printf ' %s\n' " $settings " | jq '
1094+ new_settings=$( printf ' %s\n' " $settings " | jq \
1095+ --arg cmd " $pre_tool_cmd " \
1096+ --arg pattern " $pre_tool_pattern " \
1097+ '
10401098 def array_or_empty:
10411099 if type == "array" then . else [] end;
1100+ def is_managed_hook($exact; $pattern):
1101+ ((.type // "") == "command") and
1102+ (
1103+ ((($exact != "") and ((.command // "") == $exact))) or
1104+ (((.command // "") | test($pattern)) // false)
1105+ );
10421106 if (.hooks // {}).PreToolUse then
10431107 .hooks.PreToolUse = (
1044- (.hooks.PreToolUse | array_or_empty) | map(
1045- select(.matcher != "AskUserQuestion")
1108+ (.hooks.PreToolUse | array_or_empty)
1109+ | map(
1110+ if (.matcher // "") == "AskUserQuestion" and ((.hooks | type) == "array") then
1111+ .hooks = (
1112+ (.hooks | array_or_empty)
1113+ | map(select((is_managed_hook($cmd; $pattern)) | not))
1114+ )
1115+ else
1116+ .
1117+ end
10461118 )
1119+ | map(select(((.hooks | type) != "array") or ((.hooks | length) > 0)))
10471120 ) |
10481121 if (.hooks.PreToolUse | length) == 0 then del(.hooks.PreToolUse) else . end |
10491122 if (.hooks | length) == 0 then del(.hooks) else . end
@@ -1058,10 +1131,11 @@ unregister_ask_user_hook() {
10581131 tmp_json=$( mktemp) || return 1
10591132 cat " $file " > " $tmp_json "
10601133
1061- python3 - " $file " " $tmp_json " << 'PYTHON '
1134+ python3 - " $file " " $pre_tool_cmd " " $pre_tool_pattern " " $ tmp_json" << 'PYTHON '
10621135import json, os, sys, tempfile
1136+ import re
10631137
1064- file_path, json_file = sys.argv[1 :3 ]
1138+ file_path, pre_tool_cmd, pre_tool_pattern, json_file = sys.argv[1 :5 ]
10651139
10661140try :
10671141 with open (json_file, " r" ) as fh:
@@ -1074,8 +1148,32 @@ finally:
10741148
10751149hooks = settings.get(" hooks" , {})
10761150pre_tool = hooks.get(" PreToolUse" , [])
1077- hooks[" PreToolUse" ] = [e for e in pre_tool if e.get(" matcher" ) != " AskUserQuestion" ]
1078- if not hooks[" PreToolUse" ]:
1151+ pre_tool_regex = re.compile(pre_tool_pattern)
1152+
1153+ def is_managed_hook (hook ):
1154+ if not isinstance (hook, dict ) or hook.get(" type" ) != " command" :
1155+ return False
1156+ command = hook.get(" command" )
1157+ if not isinstance (command, str ):
1158+ return False
1159+ return (pre_tool_cmd and command == pre_tool_cmd) or bool (pre_tool_regex.search(command))
1160+
1161+ filtered_entries = []
1162+ for entry in pre_tool:
1163+ if not isinstance (entry, dict ):
1164+ filtered_entries.append(entry)
1165+ continue
1166+ if entry.get(" matcher" , " " ) != " AskUserQuestion" :
1167+ filtered_entries.append(entry)
1168+ continue
1169+ filtered_hooks = [hook for hook in entry.get(" hooks" , []) if not is_managed_hook(hook)]
1170+ if filtered_hooks:
1171+ new_entry = dict (entry)
1172+ new_entry[" hooks" ] = filtered_hooks
1173+ filtered_entries.append(new_entry)
1174+
1175+ hooks[" PreToolUse" ] = filtered_entries
1176+ if " PreToolUse" in hooks and not hooks[" PreToolUse" ]:
10791177 del hooks[" PreToolUse" ]
10801178if hooks:
10811179 settings[" hooks" ] = hooks
@@ -1142,7 +1240,7 @@ disable_hooks_in_settings() {
11421240 " claude"
11431241
11441242 # Remove PreToolUse hook for AskUserQuestion
1145- unregister_ask_user_hook " $GLOBAL_SETTINGS_FILE "
1243+ unregister_ask_user_hook " $GLOBAL_SETTINGS_FILE " " $( get_global_claude_pre_tool_use_command ) "
11461244}
11471245
11481246# Disable hooks in project settings.json
@@ -1165,7 +1263,7 @@ disable_project_hooks_in_settings() {
11651263 " claude $( shell_quote " $project_name " ) "
11661264
11671265 # Remove PreToolUse hook for AskUserQuestion
1168- unregister_ask_user_hook " $project_settings "
1266+ unregister_ask_user_hook " $project_settings " " $( get_project_claude_pre_tool_use_command " $project_name " ) "
11691267}
11701268
11711269# Enable hooks in project settings.json
0 commit comments