Module: ClaudeHooks::CLI

Defined in:
lib/claude_hooks/cli.rb

Class Method Summary collapse

Class Method Details

.entrypoint(hook_class = nil, on_error: :allow, &block) ⇒ Object

Deprecated.

Use run_hook instead.



51
52
53
54
# File 'lib/claude_hooks/cli.rb', line 51

def entrypoint(hook_class = nil, on_error: :allow, &block)
  warn "[ClaudeHooks] CLI.entrypoint is deprecated — use CLI.run_hook instead."
  run_hook(hook_class, on_error: on_error, &block)
end

.run_hook(hook_class = nil, on_error: :allow, &block) ⇒ Object

Run a hook script from the command line. Reads JSON from STDIN, calls hook.call, and exits with the correct code.

on_error controls what happens when the hook raises an unexpected exception:

:allow (default) — exit 1, non-blocking; Claude continues as if the hook didn't run.
:block           — exit 2, blocking; Claude stops and shows the error. Use this for
                 security/policy hooks where a crash should never silently pass through.

Usage patterns:

  1. Single hook class: ClaudeHooks::CLI.run_hook(MyHook) ClaudeHooks::CLI.run_hook(MyHook, on_error: :block) # fail-closed

  2. Multiple hooks with merging: ClaudeHooks::CLI.run_hook(on_error: :block) do |input_data| hook1 = Hook1.new(input_data) hook2 = Hook2.new(input_data) hook1.call hook2.call ClaudeHooks::Output::PreToolUse.merge(hook1.output, hook2.output).output_and_exit end



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/claude_hooks/cli.rb', line 30

def run_hook(hook_class = nil, on_error: :allow, &block)
  input_data = JSON.parse(STDIN.read)

  if block_given?
    yield(input_data)
  elsif hook_class
    hook = hook_class.new(input_data)
    hook.call
    hook.output_and_exit
  else
    raise ArgumentError, "Either provide a hook_class or a block"
  end

rescue JSON::ParserError => e
  handle_run_error("JSON parsing error: #{e.message}", on_error)

rescue StandardError => e
  handle_run_error("Hook execution error: #{e.message}", on_error, backtrace: e.backtrace)
end

.run_with_sample_data(hook_class, sample_data = {}) {|input_data| ... } ⇒ Object

Run a hook with synthetic sample data (no STDIN needed).

Yields:

  • (input_data)


66
67
68
69
70
71
72
73
74
75
76
# File 'lib/claude_hooks/cli.rb', line 66

def run_with_sample_data(hook_class, sample_data = {}, &block)
  input_data = {
    'session_id' => 'test-session',
    'transcript_path' => '/tmp/test_transcript.md',
    'cwd' => Dir.pwd,
    'hook_event_name' => hook_class.hook_type
  }.merge(sample_data)

  yield(input_data) if block_given?
  run_hook_with_data(hook_class, input_data)
end

.test_runner(hook_class) {|input_data| ... } ⇒ Object

Run a hook with input read from STDIN, with optional block to mutate input_data before running.

Yields:

  • (input_data)


59
60
61
62
63
# File 'lib/claude_hooks/cli.rb', line 59

def test_runner(hook_class, &block)
  input_data = read_stdin_input
  yield(input_data) if block_given?
  run_hook_with_data(hook_class, input_data)
end