Class: ClaudeHooks::Output::Stop
- Defined in:
- lib/claude_hooks/output/stop.rb
Overview
Note: In Stop hooks, 'decision: block' actually means "force Claude to continue" This is counterintuitive but matches Claude Code's expected behavior
Direct Known Subclasses
Instance Attribute Summary
Attributes inherited from Base
Class Method Summary collapse
-
.merge(*outputs) ⇒ Object
MERGE HELPER ===.
Instance Method Summary collapse
- #additional_context ⇒ Object
-
#decision ⇒ Object
DECISION ACCESSORS ===.
-
#exit_code ⇒ Object
EXIT CODE LOGIC ===.
-
#output_stream ⇒ Object
OUTPUT STREAM LOGIC ===.
- #reason ⇒ Object (also: #continue_instructions)
-
#should_continue? ⇒ Boolean
Check if Claude should be forced to continue (decision == 'block') Note: 'block' in Stop hooks means "block the stopping", i.e., continue.
-
#should_stop? ⇒ Boolean
Check if Claude should stop normally (decision != 'block').
Methods inherited from Base
#continue?, for_hook_type, #hook_specific_output, #initialize, #output_and_exit, #stop_reason, #suppress_output?, #system_message, #terminal_sequence, #to_json
Constructor Details
This class inherits a constructor from ClaudeHooks::Output::Base
Class Method Details
.merge(*outputs) ⇒ Object
MERGE HELPER ===
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/claude_hooks/output/stop.rb', line 58 def self.merge(*outputs) compacted_outputs = outputs.compact return compacted_outputs.first if compacted_outputs.length == 1 return super(*outputs) if compacted_outputs.empty? merged = super(*outputs) merged_data = merged.data # A blocking reason is actually a "continue instructions" blocking_reasons = [] contexts = [] compacted_outputs.each do |output| output_data = output.respond_to?(:data) ? output.data : output # Handle decision - if any hook says 'block', respect that if output_data['decision'] == 'block' merged_data['decision'] = 'block' reason = output_data['reason'] blocking_reasons << reason if reason && !reason.empty? end context = output_data.dig('hookSpecificOutput', 'additionalContext') contexts << context if context && !context.empty? end # Combine all blocking reasons / continue instructions merged_data['reason'] = blocking_reasons.join('; ') unless blocking_reasons.empty? unless contexts.empty? merged_data['hookSpecificOutput'] = { 'hookEventName' => merged_data.dig('hookSpecificOutput', 'hookEventName') || 'Stop', 'additionalContext' => contexts.join("\n\n") } end new(merged_data) end |
Instance Method Details
#additional_context ⇒ Object
21 22 23 |
# File 'lib/claude_hooks/output/stop.rb', line 21 def additional_context hook_specific_output['additionalContext'] || '' end |
#decision ⇒ Object
DECISION ACCESSORS ===
12 13 14 |
# File 'lib/claude_hooks/output/stop.rb', line 12 def decision @data['decision'] end |
#exit_code ⇒ Object
EXIT CODE LOGIC ===
Stop hooks use the advanced JSON API with exit code 0. Per Anthropic guidance: when using structured JSON with decision/reason fields, always output to stdout with exit 0 (not stderr with exit 2). Reference: https://github.com/anthropics/claude-code/issues/10875
44 45 46 |
# File 'lib/claude_hooks/output/stop.rb', line 44 def exit_code 0 end |
#output_stream ⇒ Object
OUTPUT STREAM LOGIC ===
Stop hooks always output to stdout when using the JSON API. This follows the same pattern as PreToolUse hooks.
52 53 54 |
# File 'lib/claude_hooks/output/stop.rb', line 52 def output_stream :stdout end |
#reason ⇒ Object Also known as: continue_instructions
16 17 18 |
# File 'lib/claude_hooks/output/stop.rb', line 16 def reason @data['reason'] || '' end |
#should_continue? ⇒ Boolean
Check if Claude should be forced to continue (decision == 'block') Note: 'block' in Stop hooks means "block the stopping", i.e., continue
29 30 31 |
# File 'lib/claude_hooks/output/stop.rb', line 29 def should_continue? decision == 'block' end |
#should_stop? ⇒ Boolean
Check if Claude should stop normally (decision != 'block')
34 35 36 |
# File 'lib/claude_hooks/output/stop.rb', line 34 def should_stop? decision != 'block' end |