Class: ClaudeHooks::Output::PermissionRequest

Inherits:
Base
  • Object
show all
Defined in:
lib/claude_hooks/output/permission_request.rb

Instance Attribute Summary

Attributes inherited from Base

#data

Class Method Summary collapse

Instance Method Summary collapse

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 ===



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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/claude_hooks/output/permission_request.rb', line 67

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

  # deny > allow (most restrictive wins)
  behavior = 'allow'
  messages = []
  updated_inputs = []
  interrupt = false
  updated_permissions = nil

  compacted_outputs.each do |output|
    output_data = output.respond_to?(:data) ? output.data : output

    # Support both nested and legacy flat shapes
    current_behavior = output_data.dig('hookSpecificOutput', 'decision', 'behavior') ||
                       output_data.dig('hookSpecificOutput', 'permissionDecision')
    next unless current_behavior

    behavior = 'deny' if current_behavior == 'deny'

    msg = output_data.dig('hookSpecificOutput', 'decision', 'message') ||
          output_data.dig('hookSpecificOutput', 'permissionDecisionReason')
    messages << msg if msg && !msg.empty?

    updated = output_data.dig('hookSpecificOutput', 'decision', 'updatedInput') ||
              output_data.dig('hookSpecificOutput', 'updatedInput')
    updated_inputs << updated if updated

    # Propagate interrupt (OR logic — if any output has it, merged should too)
    interrupt = true if output_data.dig('hookSpecificOutput', 'decision', 'interrupt') == true

    # Propagate updatedPermissions (last writer wins)
    perms = output_data.dig('hookSpecificOutput', 'decision', 'updatedPermissions')
    updated_permissions = perms unless perms.nil?
  end

  merged_data['hookSpecificOutput'] ||= { 'hookEventName' => 'PermissionRequest' }
  decision = { 'behavior' => behavior }
  decision['message'] = messages.join('; ') if messages.any?
  decision['updatedInput'] = updated_inputs.last if updated_inputs.any?
  decision['interrupt'] = interrupt
  decision['updatedPermissions'] = updated_permissions unless updated_permissions.nil?
  merged_data['hookSpecificOutput']['decision'] = decision

  new(merged_data)
end

Instance Method Details

#allowed?Boolean

SEMANTIC HELPERS ===

Returns:

  • (Boolean)


41
42
43
# File 'lib/claude_hooks/output/permission_request.rb', line 41

def allowed?
  permission_decision == 'allow'
end

#behaviorObject

PERMISSION DECISION ACCESSORS ===



10
11
12
# File 'lib/claude_hooks/output/permission_request.rb', line 10

def behavior
  hook_specific_output.dig('decision', 'behavior')
end

#denied?Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/claude_hooks/output/permission_request.rb', line 45

def denied?
  permission_decision == 'deny'
end

#exit_codeObject

EXIT CODE LOGIC ===



55
56
57
# File 'lib/claude_hooks/output/permission_request.rb', line 55

def exit_code
  0
end

#input_updated?Boolean

Returns:

  • (Boolean)


49
50
51
# File 'lib/claude_hooks/output/permission_request.rb', line 49

def input_updated?
  !updated_input.nil?
end

#interrupt?Boolean

Returns:

  • (Boolean)


35
36
37
# File 'lib/claude_hooks/output/permission_request.rb', line 35

def interrupt?
  hook_specific_output.dig('decision', 'interrupt') == true
end

#output_streamObject

OUTPUT STREAM LOGIC ===



61
62
63
# File 'lib/claude_hooks/output/permission_request.rb', line 61

def output_stream
  :stdout
end

#permission_decisionObject

Read nested decision.behavior with flat legacy fallback



15
16
17
# File 'lib/claude_hooks/output/permission_request.rb', line 15

def permission_decision
  behavior || hook_specific_output['permissionDecision']
end

#permission_reasonObject

Read decision.message with legacy permissionDecisionReason fallback



20
21
22
23
# File 'lib/claude_hooks/output/permission_request.rb', line 20

def permission_reason
  hook_specific_output.dig('decision', 'message') ||
    hook_specific_output['permissionDecisionReason'] || ''
end

#updated_inputObject

Read decision.updatedInput with flat legacy fallback



26
27
28
29
# File 'lib/claude_hooks/output/permission_request.rb', line 26

def updated_input
  hook_specific_output.dig('decision', 'updatedInput') ||
    hook_specific_output['updatedInput']
end

#updated_permissionsObject



31
32
33
# File 'lib/claude_hooks/output/permission_request.rb', line 31

def updated_permissions
  hook_specific_output.dig('decision', 'updatedPermissions')
end