diff --git a/src/Hooks/Response.php b/src/Hooks/Response.php index 9df8d01..3ed7b26 100644 --- a/src/Hooks/Response.php +++ b/src/Hooks/Response.php @@ -38,6 +38,19 @@ public function block(string $reason): self return $this; } + + /** + * Block the tool call and immediately send the response + * Useful for replacing tool output entirely + * + * @param string $reason The reason/content to provide instead of tool execution + */ + public function blockAndSend(string $reason): never + { + $this->data['decision'] = 'block'; + $this->data['reason'] = $reason; + $this->send(); + } /** * Approve the tool call (PreToolUse only) @@ -51,6 +64,20 @@ public function approve(string $reason = ''): self return $this; } + + /** + * Approve the tool call and immediately send the response + * + * @param string $reason Optional approval reason + */ + public function approveAndSend(string $reason = ''): never + { + $this->data['decision'] = 'approve'; + if ($reason) { + $this->data['reason'] = $reason; + } + $this->send(); + } /** * Suppress output from transcript mode diff --git a/tests/Hooks/ExtendedResponseTest.php b/tests/Hooks/ExtendedResponseTest.php new file mode 100644 index 0000000..8b80c4f --- /dev/null +++ b/tests/Hooks/ExtendedResponseTest.php @@ -0,0 +1,61 @@ +blockAndSend('Tool execution blocked'); + } catch (SystemExit $e) { + // Expected + } + $output = ob_get_clean(); + + $data = json_decode($output, true); + expect($data)->toBeArray(); + expect($data['decision'])->toBe('block'); + expect($data['reason'])->toBe('Tool execution blocked'); + } + }; + + $response->testBlockAndSend(); +}); + +it('can approve and send immediately', function () { + $response = new class extends Response { + public function testApproveAndSend(): void { + ob_start(); + try { + $this->approveAndSend('Tool execution approved'); + } catch (SystemExit $e) { + // Expected + } + $output = ob_get_clean(); + + $data = json_decode($output, true); + expect($data)->toBeArray(); + expect($data['decision'])->toBe('approve'); + expect($data['reason'])->toBe('Tool execution approved'); + } + }; + + $response->testApproveAndSend(); +}); + +it('blockAndSend terminates execution', function () { + $response = new Response(); + + expect(function () use ($response) { + $response->blockAndSend('Blocked'); + })->toThrow(SystemExit::class); +}); + +it('approveAndSend terminates execution', function () { + $response = new Response(); + + expect(function () use ($response) { + $response->approveAndSend('Approved'); + })->toThrow(SystemExit::class); +}); \ No newline at end of file