Class: RubynCode::Hooks::ExternalDispatcher
- Inherits:
-
Object
- Object
- RubynCode::Hooks::ExternalDispatcher
- Defined in:
- lib/rubyn_code/hooks/external_dispatcher.rb
Overview
Dispatches hook events to external commands configured in settings.json.
The dispatcher sits alongside the in-process Hooks::Runner. It does NOT replace it — existing pre/post_tool_use YAML hooks and Ruby callables keep working. New code that wants Claude Code-style control flow (block, stopReason, additionalContext) fires through this dispatcher instead.
Wire it into the agent loop wherever a return value matters:
response = dispatcher.fire(:pre_tool_use, tool_name:, tool_input:)
raise ToolBlockedError, response.reason if response.block?
For events where return values don’t matter (e.g. SessionStart logging), call #fire and ignore the response — but still inspect #stop? when the caller wants to honour a stop signal mid-stream.
Constant Summary collapse
- DEFAULT_TIMEOUT =
60
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#project_root ⇒ Object
readonly
Returns the value of attribute project_root.
Instance Method Summary collapse
-
#configured_for?(internal_event) ⇒ Boolean
True if any external hook is configured for this event.
-
#fire(internal_event, **payload) ⇒ Response
Fires all configured external hooks for the given internal event.
-
#initialize(project_root:, config: nil, executor: nil, logger: nil) ⇒ ExternalDispatcher
constructor
A new instance of ExternalDispatcher.
Constructor Details
#initialize(project_root:, config: nil, executor: nil, logger: nil) ⇒ ExternalDispatcher
Returns a new instance of ExternalDispatcher.
34 35 36 37 38 39 |
# File 'lib/rubyn_code/hooks/external_dispatcher.rb', line 34 def initialize(project_root:, config: nil, executor: nil, logger: nil) @project_root = project_root @config = config || SettingsJsonLoader.new(project_root: project_root).load @executor = executor || SubprocessExecutor.new(project_root: project_root) @logger = logger || method(:default_log) end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
28 29 30 |
# File 'lib/rubyn_code/hooks/external_dispatcher.rb', line 28 def config @config end |
#project_root ⇒ Object (readonly)
Returns the value of attribute project_root.
28 29 30 |
# File 'lib/rubyn_code/hooks/external_dispatcher.rb', line 28 def project_root @project_root end |
Instance Method Details
#configured_for?(internal_event) ⇒ Boolean
Returns true if any external hook is configured for this event.
42 43 44 45 46 47 |
# File 'lib/rubyn_code/hooks/external_dispatcher.rb', line 42 def configured_for?(internal_event) external = EventMap.external(internal_event) return false unless external Array(@config[external]).any? end |
#fire(internal_event, **payload) ⇒ Response
Fires all configured external hooks for the given internal event.
Each matcher group’s commands are run sequentially (the order they appear in settings.json). Within a group, commands run in declared order. Hook errors and timeouts are logged but do not abort the remaining hooks — matching Claude Code’s “best effort” semantics.
60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/rubyn_code/hooks/external_dispatcher.rb', line 60 def fire(internal_event, **payload) external = EventMap.external(internal_event) return empty_response unless external groups = Array(@config[external]) return empty_response if groups.empty? envelope = build_envelope(external, payload) collected = collect_responses(groups, envelope, payload) Response.new(raw: build_merged(collected, external)) end |