| 87 | # ============================================================ |
| 88 | |
| 89 | class HookRunner: |
| 90 | def __init__( |
| 91 | self, |
| 92 | pre_tool_use: Optional[list[str]] = None, |
| 93 | post_tool_use: Optional[list[str]] = None, |
| 94 | ): |
| 95 | self._pre_tool_use = pre_tool_use or [] |
| 96 | self._post_tool_use = post_tool_use or [] |
| 97 | |
| 98 | @classmethod |
| 99 | def from_config(cls, config) -> "HookRunner": |
| 100 | """从 RuntimeConfig 加载。源码: hooks.rs:61-63""" |
| 101 | return cls( |
| 102 | pre_tool_use=config.hooks_pre(), |
| 103 | post_tool_use=config.hooks_post(), |
| 104 | ) |
| 105 | |
| 106 | def run_pre_tool_use(self, tool_name: str, tool_input: str) -> HookResult: |
| 107 | """执行所有 PreToolUse hook。源码: hooks.rs:66-75""" |
| 108 | return self._run_commands( |
| 109 | event=HookEvent.PRE_TOOL_USE, |
| 110 | commands=self._pre_tool_use, |
| 111 | tool_name=tool_name, |
| 112 | tool_input=tool_input, |
| 113 | tool_output=None, |
| 114 | is_error=False, |
| 115 | ) |
| 116 | |
| 117 | def run_post_tool_use( |
| 118 | self, |
| 119 | tool_name: str, |
| 120 | tool_input: str, |
| 121 | tool_output: str, |
| 122 | is_error: bool = False, |
| 123 | ) -> HookResult: |
| 124 | """执行所有 PostToolUse hook。源码: hooks.rs:78-93""" |
| 125 | return self._run_commands( |
| 126 | event=HookEvent.POST_TOOL_USE, |
| 127 | commands=self._post_tool_use, |
| 128 | tool_name=tool_name, |
| 129 | tool_input=tool_input, |
| 130 | tool_output=tool_output, |
| 131 | is_error=is_error, |
| 132 | ) |
| 133 | |
| 134 | def _run_commands( |
| 135 | self, |
| 136 | event: HookEvent, |
| 137 | commands: list[str], |
| 138 | tool_name: str, |
| 139 | tool_input: str, |
| 140 | tool_output: Optional[str], |
| 141 | is_error: bool, |
| 142 | ) -> HookResult: |
| 143 | """核心: 顺序执行命令,deny 时熔断。源码: hooks.rs:95-150 |
| 144 | |
| 145 | CC 的设计: 遍历命令列表,每个命令执行后检查结果。 |
| 146 | - Allow: 收集 message,继续下一个 |