Hook Registration
Vibe Check registers three lifecycle hooks inhooks/hooks.json:
- Empty
matcherto fire on every event type: "command"to execute shell commands${CLAUDE_PLUGIN_ROOT}to reference plugin scripts
SessionStart Hook
Purpose
Initializes or joins an existing session when Claude starts.Implementation
scripts/session_start.py:11-42:
Behavior
- Load existing state — Attempt to load from disk
- Check staleness — If
last_activeis within 60 minutes (default), join the session - Join or create — Preserve timers if active, otherwise create fresh state
- Print message — Output welcome message for Claude to relay
Output
The hook prints to stdout. Claude sees this output and includes it in the session context.UserPromptSubmit Hook
Purpose
Core reminder engine that fires before Claude processes each user prompt.Implementation
scripts/check_reminder.py:68-161:
Processing Layers
- Stale session check — Reset if inactive 60+ minutes
- Break compliance — Check if user took the last suggested break
- Spontaneous breaks — Detect 15+ minute gaps without pending reminder
- Deduplication — Prevent duplicate reminders within 30 seconds
- Timer checks — Check full → hydration → micro in priority order
- Fire reminder — Output JSON with
additionalContextif due
Hook Output Format
When a reminder is due, the hook outputs JSON:additionalContext into the model’s context before processing the user prompt.
Stop Hook
Purpose
Records the timestamp when Claude finishes generating a response.Implementation
scripts/stop_hook.py:9-22:
Behavior
- Acquire lock — Exclusive access to state
- Update timestamps — Set
last_response_endandlast_active - Save state — Write atomically
- Silent failure — Errors are swallowed to avoid interrupting conversation
Why This Matters
Thelast_response_end timestamp enables precise gap measurement:
- Whether the user took a suggested break (compliance)
- Whether the user took a spontaneous break (15+ minutes)
Hook Coordination
The three hooks work together:Timing Flow
- User sends prompt →
UserPromptSubmitfires - Check gap →
now - last_response_end= time since last response - Compliance → If gap ≥ break threshold, user took the break
- Response ends →
Stoprecords newlast_response_end - Next prompt → Gap measurement uses updated timestamp
Hook Lifecycle
Hooks are registered at session start and remain active for the entire session. To reload hooks after updating:- Exit Claude
- Restart Claude
- SessionStart hook runs with new code