Skip to content

feat(cli): add PreToolUse hook support for AskUserQuestion tool#66

Merged
guyskk merged 5 commits intomainfrom
002-askuserquestion-hook
Jan 20, 2026
Merged

feat(cli): add PreToolUse hook support for AskUserQuestion tool#66
guyskk merged 5 commits intomainfrom
002-askuserquestion-hook

Conversation

@guyskk
Copy link
Copy Markdown
Owner

@guyskk guyskk commented Jan 20, 2026

This extends the Supervisor Hook mechanism to also review AskUserQuestion
tool calls, not just Stop events. When Claude Code calls AskUserQuestion,
the Supervisor can now review and decide whether to allow or deny the call.

Summary

Architecture refactored for clean separation of concerns:

  • Output types centralized in supervisor package (single source of truth)
  • Input types clearly separated by event type in cli package
  • Event handling logic simplified with dedicated helper functions
  • No duplicate type definitions between packages

Changes

internal/supervisor/output.go - Output Types & Functions

New types and functions:

  • HookEventType constants (EventTypeStop, EventTypePreToolUse)
  • StopHookOutput - Stop event output format
  • PreToolUseSpecificOutput - PreToolUse specific fields
  • PreToolUseHookOutput - PreToolUse event output format
  • OutputPreToolUseDecision() - Output PreToolUse decisions
  • HookOutput - Backward compatible alias for StopHookOutput

internal/cli/hook.go - Input Types & Event Handling

Clean input type separation:

  • HookInputHeader - Minimal struct for event type detection
  • StopHookInput - Dedicated Stop event input
  • PreToolUseInput - Dedicated PreToolUse event input

Event handling:

  • detectEventType() - Reads stdin and detects event type
  • outputDecisionByEventType() - Routes to correct output function
  • runSupervisorReview() - Extracted supervisor review logic

internal/provider/provider.go - Hook Configuration

  • Added PreToolUse hook configuration with matcher: "AskUserQuestion"

Removed (cleanup)

  • cli.HookOutput - Now in supervisor package
  • cli.HookSpecificOutput - Now in supervisor package
  • cli.SupervisorResultToHookOutput() - Replaced by outputDecisionByEventType()
  • cli.outputHookOutput() - Use supervisor.Output* functions instead

Backward Compatibility

  • Stop events continue using existing output format (decision, reason)
  • HookOutput is a type alias for StopHookOutput
  • When hook_event_name is absent, defaults to "Stop" event
  • Original OutputDecision() function unchanged

Testing

  • All tests pass with race detector (go test -race ./...)
  • Lint checks pass (gofmt, go vet, shellcheck, markdownlint)
  • New output_test.go covers supervisor package output functions
  • Updated hook_test.go covers new input types and event detection

Documentation

SpecKit workflow documents in specs/002-askuserquestion-hook/:

  • spec.md - Feature specification
  • plan.md - Implementation plan
  • tasks.md - Task breakdown
  • research.md - Technical research
  • data-model.md - Data model definition
  • contracts/hook-input-output.md - API contracts
  • quickstart.md - Quick start guide

guyskk and others added 5 commits January 20, 2026 14:31
This extends the Supervisor Hook mechanism to also review AskUserQuestion
tool calls, not just Stop events. When Claude Code calls AskUserQuestion,
the Supervisor can now review and decide whether to allow or deny the call.

Changes:
- Extend HookInput structure to support all hook event types (Stop, PreToolUse)
- Add HookOutput and HookSpecificOutput structures for event-specific output
- Implement SupervisorResultToHookOutput converter for format translation
- Detect event type from hook_event_name field (defaults to "Stop" for backward compatibility)
- Add PreToolUse hook configuration with matcher "AskUserQuestion" in provider.go
- Add outputHookOutput helper to output JSON in correct format

The implementation maintains full backward compatibility:
- StopHookInput is now an alias for HookInput
- Stop events continue using existing output format (decision/reason)
- PreToolUse events use new format (hookSpecificOutput with permissionDecision)
- Iteration count is incremented for all event types (consistency)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add 11 unit tests covering:
- PreToolUse allow/deny decisions with feedback
- Stop allow/block decisions with feedback
- Empty feedback handling for both event types
- JSON marshaling/unmarshaling for input/output
- Backward compatibility with old Stop format

Coverage increased to 33.0%.

Addresses review feedback about missing unit tests.
Add comprehensive tests covering:
- Edge cases: unknown event types (default to Stop format)
- Event type case sensitivity (only exact "PreToolUse" match)
- Integration test for PreToolUse input parsing
- Full input/output validation with all fields

Coverage increased from 33.0% to 36.9%.

Addresses feedback about missing edge case tests and integration validation.
Based on detailed feedback, this commit adds:

1. Fixed integration tests:
   - Renamed TestRunSupervisorHook_PreToolUse_InputParsing to
     TestRunSupervisorHook_RecursiveCallProtection (clarifies purpose)
   - Added TestRunSupervisorHook_SupervisorModeDisabled (tests early return)

2. Added provider.go validation tests (3 new tests):
   - TestSwitchWithHook_PreToolUseConfiguration
   - TestSwitchWithHook_PreToolUseAndStopCoexist
   - TestSwitchWithHook_PreToolUseMatcherIsAskUserQuestion

3. Created end-to-end test script:
   - tests/e2e_pretooluse_hook_test.sh
   - 6 test scenarios covering backward compatibility, input parsing,
     output format, unknown event types, hook configuration, and
     iteration count

4. Created manual validation documentation:
   - docs/validate-pretooluse-hook.md
   - Step-by-step verification guide
   - Troubleshooting section
   - Performance metrics

Code coverage: 38.2% for internal/cli, 88.3% for internal/provider

Addresses all feedback points about test completeness and validation.
Major refactoring to address architectural issues in PreToolUse hook support:

1. **Centralized output types in supervisor package**
   - Added HookEventType constants (EventTypeStop, EventTypePreToolUse)
   - Added StopHookOutput and PreToolUseHookOutput types
   - Added OutputPreToolUseDecision() function
   - HookOutput is now an alias for StopHookOutput (backward compatible)

2. **Clean input type separation in cli package**
   - HookInputHeader: minimal struct for event type detection
   - StopHookInput: dedicated Stop event input
   - PreToolUseInput: dedicated PreToolUse event input
   - Removed the bloated unified HookInput struct

3. **Simplified event handling**
   - detectEventType(): reads stdin once and detects event type
   - outputDecisionByEventType(): routes to correct output function
   - runSupervisorReview(): extracted supervisor logic
   - Removed duplicate HookOutput, HookSpecificOutput types
   - Removed SupervisorResultToHookOutput conversion function
   - Removed outputHookOutput helper (use supervisor package instead)

4. **Test improvements**
   - Added comprehensive output_test.go for supervisor package
   - Updated hook_test.go to use new types
   - Reduced test code by 443 lines while maintaining coverage

This refactoring eliminates code duplication, establishes clear ownership
of types, and provides a cleaner foundation for future hook event types.
@guyskk guyskk merged commit 29e548f into main Jan 20, 2026
3 checks passed
@guyskk guyskk deleted the 002-askuserquestion-hook branch January 20, 2026 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant