Files
gh-kylesnowschwartz-simplec…/hooks/handlers/stop_you_are_not_right.rb
2025-11-30 08:36:27 +08:00

117 lines
3.5 KiB
Ruby
Executable File

#!/usr/bin/env ruby
# frozen_string_literal: true
require 'claude_hooks'
require 'json'
require_relative 'reflexive_agreement_detector'
# Stop Hook for Real-Time Reflexive Agreement Correction
#
# PURPOSE: Detect reflexive agreement in Claude's just-completed response
# TRIGGERS: When Claude Code finishes generating a response
# ACTION: Force continuation with corrective instructions if reflexive agreement detected
#
# DETECTION STRATEGY:
# Reflexive agreement = agreement phrase in first sentence WITHOUT research/substantive work
# - Checks first sentence for agreement patterns
# - Verifies no tool use in response (no Read, Grep, WebFetch, etc.)
# - Checks for substantive pivots or follow-up analysis
class StopYouAreNotRight < ClaudeHooks::Stop
include ReflexiveAgreementDetector
def call
log 'Checking if Claude just used reflexive agreement in response'
if just_used_reflexive_agreement?
log 'Detected reflexive agreement - forcing corrective continuation', level: :warn
force_substantive_response!
else
log 'No reflexive agreement detected - allowing normal stop'
allow_normal_stop!
end
output
end
private
def just_used_reflexive_agreement?
return false unless transcript_path
# Get Claude's most recent message (the one that just completed)
last_message = get_last_assistant_message
return false unless last_message
text = extract_text(last_message)
return false unless text.is_a?(String)
log "Analyzing response: '#{text[0, 100]}...'"
# Use shared detection logic
is_reflexive = reflexive_agreement?(last_message)
if is_reflexive
first_sentence = extract_first_sentence(text)
log "Reflexive agreement detected: '#{first_sentence}'"
else
log "Not reflexive agreement"
end
is_reflexive
end
def get_last_assistant_message
return nil unless File.exist?(transcript_path)
# Read transcript in reverse to find the most recent assistant message
assistant_message = nil
File.readlines(transcript_path).reverse_each do |line|
next unless line.include?('"role":"assistant"')
begin
item = JSON.parse(line.strip)
next unless item['type'] == 'assistant'
# Get full message including all content blocks
assistant_message = item
break # Found the most recent one
rescue JSON::ParserError
next
end
end
assistant_message
end
def force_substantive_response!
correction_instructions = <<~INSTRUCTIONS.strip
I notice I just used a reflexive agreement phrase. Let me provide a more substantive response:
Instead of simply agreeing, let me analyze your point with specific technical reasoning, consider potential edge cases or alternative approaches, and offer constructive insights that build collaboratively on your observation.
INSTRUCTIONS
# Force Claude to continue with corrective instructions
continue_with_instructions!(correction_instructions)
log 'Forced continuation with corrective instructions'
end
def allow_normal_stop!
# Allow Claude to stop normally (default behavior)
ensure_stopping!
# Don't add any output to the transcript for normal stops
suppress_output!
end
end
# Testing support - run this file directly to test with sample data
if __FILE__ == $PROGRAM_NAME
ClaudeHooks::CLI.test_runner(StopYouAreNotRight) do |input_data|
input_data['session_id'] = 'stop-correction-test'
input_data['stop_hook_active'] = false
end
end