feat: add strongly-typed hook inputs with TypedDict (#240)

Add typed hook input structures (PreToolUseHookInput,
PostToolUseHookInput, etc.) to provide better IDE autocomplete and type
safety for hook callbacks. Also convert HookContext from dataclass to
TypedDict to match runtime behavior.

Changes:
- Add BaseHookInput, PreToolUseHookInput, PostToolUseHookInput,
UserPromptSubmitHookInput, StopHookInput, SubagentStopHookInput, and
PreCompactHookInput TypedDict classes
- Update HookCallback signature to use HookInput union type
- Convert HookContext from dataclass to TypedDict (fixes type mismatch)
- Export all new hook input types from __init__.py
- Update all examples and tests to use typed hook inputs

Benefits:
- Zero breaking changes (TypedDict is dict-compatible at runtime)
- Full type safety and IDE autocomplete for hook callbacks
- Matches TypeScript SDK structure exactly
- Self-documenting hook input fields

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

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Ashwin Bhat 2025-10-10 16:22:13 -07:00 committed by GitHub
parent 48b62a05a3
commit d754e5cc1d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 111 additions and 23 deletions

View file

@ -18,11 +18,13 @@ from .query import query
from .types import (
AgentDefinition,
AssistantMessage,
BaseHookInput,
CanUseTool,
ClaudeAgentOptions,
ContentBlock,
HookCallback,
HookContext,
HookInput,
HookJSONOutput,
HookMatcher,
McpSdkServerConfig,
@ -33,8 +35,13 @@ from .types import (
PermissionResultAllow,
PermissionResultDeny,
PermissionUpdate,
PostToolUseHookInput,
PreCompactHookInput,
PreToolUseHookInput,
ResultMessage,
SettingSource,
StopHookInput,
SubagentStopHookInput,
SystemMessage,
TextBlock,
ThinkingBlock,
@ -42,6 +49,7 @@ from .types import (
ToolResultBlock,
ToolUseBlock,
UserMessage,
UserPromptSubmitHookInput,
)
# MCP Server Support
@ -307,8 +315,17 @@ __all__ = [
"PermissionResultAllow",
"PermissionResultDeny",
"PermissionUpdate",
# Hook support
"HookCallback",
"HookContext",
"HookInput",
"BaseHookInput",
"PreToolUseHookInput",
"PostToolUseHookInput",
"UserPromptSubmitHookInput",
"StopHookInput",
"SubagentStopHookInput",
"PreCompactHookInput",
"HookJSONOutput",
"HookMatcher",
# Agent support

View file

@ -157,6 +157,73 @@ HookEvent = (
)
# Hook input types - strongly typed for each hook event
class BaseHookInput(TypedDict):
"""Base hook input fields present across many hook events."""
session_id: str
transcript_path: str
cwd: str
permission_mode: NotRequired[str]
class PreToolUseHookInput(BaseHookInput):
"""Input data for PreToolUse hook events."""
hook_event_name: Literal["PreToolUse"]
tool_name: str
tool_input: dict[str, Any]
class PostToolUseHookInput(BaseHookInput):
"""Input data for PostToolUse hook events."""
hook_event_name: Literal["PostToolUse"]
tool_name: str
tool_input: dict[str, Any]
tool_response: Any
class UserPromptSubmitHookInput(BaseHookInput):
"""Input data for UserPromptSubmit hook events."""
hook_event_name: Literal["UserPromptSubmit"]
prompt: str
class StopHookInput(BaseHookInput):
"""Input data for Stop hook events."""
hook_event_name: Literal["Stop"]
stop_hook_active: bool
class SubagentStopHookInput(BaseHookInput):
"""Input data for SubagentStop hook events."""
hook_event_name: Literal["SubagentStop"]
stop_hook_active: bool
class PreCompactHookInput(BaseHookInput):
"""Input data for PreCompact hook events."""
hook_event_name: Literal["PreCompact"]
trigger: Literal["manual", "auto"]
custom_instructions: str | None
# Union type for all hook inputs
HookInput = (
PreToolUseHookInput
| PostToolUseHookInput
| UserPromptSubmitHookInput
| StopHookInput
| SubagentStopHookInput
| PreCompactHookInput
)
# Hook-specific output types
class PreToolUseHookSpecificOutput(TypedDict):
"""Hook-specific output for PreToolUse events."""
@ -265,21 +332,22 @@ class SyncHookJSONOutput(TypedDict):
HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutput
@dataclass
class HookContext:
"""Context information for hook callbacks."""
class HookContext(TypedDict):
"""Context information for hook callbacks.
signal: Any | None = None # Future: abort signal support
Fields:
signal: Reserved for future abort signal support. Currently always None.
"""
signal: Any | None # Future: abort signal support
HookCallback = Callable[
# HookCallback input parameters:
# - input
# See https://docs.anthropic.com/en/docs/claude-code/hooks#hook-input for
# the type of 'input', the first value.
# - tool_use_id
# - context
[dict[str, Any], str | None, HookContext],
# - input: Strongly-typed hook input with discriminated unions based on hook_event_name
# - tool_use_id: Optional tool use identifier
# - context: Hook context with abort signal support (currently placeholder)
[HookInput, str | None, HookContext],
Awaitable[HookJSONOutput],
]