#!/usr/bin/env python3 # MATT-POCOCK-GRILLING-QUESTION-HOOK v2 """Claude Code hook: attach the AskUserQuestion policy when grilling starts. Registered for two events by install-question-tools.sh: PostToolUse (matcher "Skill") fires when Claude loads a grilling skill, including grilling called by grill-me, grill-with-docs, wayfinder, triage, etc. UserPromptSubmit fires when the user types /grilling or /loop-me (typed slash commands do not go through the Skill tool). It never blocks or edits anything. It only adds context. Any error exits 0 silently so a broken hook can never interrupt a session. Usage: python3 grilling-question-hook.py /absolute/path/grilling-question-tools.md """ import json import re import sys SKILLS = ('grilling', 'grill-me', 'grill-with-docs', 'loop-me') # /grill-me and /grill-with-docs always call the Skill tool for "grilling", so # PostToolUse already covers them; matching them here would inject twice. TYPED = ('grilling', 'loop-me') SLASH = re.compile(r'^/(?:[\w.-]+:)?(%s)(?=\s|$)' % '|'.join(map(re.escape, TYPED))) MAX_POLICY = 32768 def skill_name(value): # Plugin installs namespace skills, e.g. "mattpocock-skills:grilling". return value.strip().rsplit(':', 1)[-1] if isinstance(value, str) else '' def triggered(event): name = event.get('hook_event_name') if name == 'PostToolUse' and event.get('tool_name') == 'Skill': tool_input = event.get('tool_input') if isinstance(tool_input, dict) and skill_name(tool_input.get('skill')) in SKILLS: return name if name == 'UserPromptSubmit': prompt = event.get('prompt') if isinstance(prompt, str) and SLASH.match(prompt.lstrip()): return name return None def main(): if len(sys.argv) != 2: return event = json.load(sys.stdin) if not isinstance(event, dict): return name = triggered(event) if not name: return with open(sys.argv[1], 'rb') as stream: policy = stream.read(MAX_POLICY + 1).decode('utf-8') if len(policy) > MAX_POLICY or 'MATT-POCOCK-GRILLING-QUESTION-POLICY' not in policy: print('Grilling question policy missing or invalid: %s' % sys.argv[1], file=sys.stderr) return context = ('A Matt Pocock grilling flow is active. Deliver its questions with ' 'AskUserQuestion as this policy describes; everything else in the ' 'flow is unchanged.\n\n' + policy.strip() + '\n') json.dump({'hookSpecificOutput': {'hookEventName': name, 'additionalContext': context}}, sys.stdout) if __name__ == '__main__': try: main() except Exception as error: # Never break the session. print('Grilling question hook skipped: %s' % error, file=sys.stderr) sys.exit(0)