Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions s04_hooks/README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Extensions are added via `register_hook()`. The loop only calls `trigger_hooks()
## How It Works

**Hook registry**: a dict mapping event names to callback lists.
**Fault isolation**: Individual hook failures don't prevent other hooks from executing.

```python
HOOKS = {
Expand All @@ -70,11 +71,24 @@ def register_hook(event: str, callback):
HOOKS[event].append(callback)

def trigger_hooks(event: str, *args):
"""Execute all registered hooks for an event with fault isolation.

Individual hook failures do not prevent other hooks from executing.
Returns the first non-None blocking result, or None if all hooks pass.
"""
blocking_result = None

for callback in HOOKS[event]:
result = callback(*args)
if result is not None: # return value ≠ None → hook says "stop"
return result
return None
try:
result = callback(*args)
# Capture the first blocking result to prevent execution
if result is not None and blocking_result is None:
blocking_result = result
except Exception as e:
# Print error but continue executing remaining hooks
print(f"\033[31m[HOOK ERROR] {event} -> {callback.__name__}: {e}\033[0m")

return blocking_result
```

In the teaching version, PreToolUse returning non-None means block execution; Stop returning non-None means force continuation. UserPromptSubmit and PostToolUse return values are unused.
Expand Down
22 changes: 18 additions & 4 deletions s04_hooks/README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ s03 のループと権限ロジックは完全に保持される。唯一の変
## 仕組み

**フック登録簿**:イベント名をコールバックリストにマッピングする辞書。
**例外分離**:個々のフック失敗が他のフック実行を妨げない。

```python
HOOKS = {
Expand All @@ -70,11 +71,24 @@ def register_hook(event: str, callback):
HOOKS[event].append(callback)

def trigger_hooks(event: str, *args):
"""Execute all registered hooks for an event with fault isolation.

Individual hook failures do not prevent other hooks from executing.
Returns the first non-None blocking result, or None if all hooks pass.
"""
blocking_result = None

for callback in HOOKS[event]:
result = callback(*args)
if result is not None: # 戻り値 ≠ None → フックが「止め」と指示
return result
return None
try:
result = callback(*args)
# Capture the first blocking result to prevent execution
if result is not None and blocking_result is None:
blocking_result = result
except Exception as e:
# Print error but continue executing remaining hooks
print(f"\033[31m[HOOK ERROR] {event} -> {callback.__name__}: {e}\033[0m")

return blocking_result
```

教学版では、PreToolUse の非 None 戻り値は実行阻止を意味し、Stop の非 None 戻り値は強制続行を意味する。UserPromptSubmit と PostToolUse の戻り値は未使用。
Expand Down
22 changes: 18 additions & 4 deletions s04_hooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ s03 的循环和权限逻辑完全保留。唯一的变动是把 `check_permissi
## 工作原理

**hook 注册表**:一个字典,事件名映射到回调列表。
**异常隔离**:单个 hook 失败不影响其他 hook 执行。

```python
HOOKS = {
Expand All @@ -70,11 +71,24 @@ def register_hook(event: str, callback):
HOOKS[event].append(callback)

def trigger_hooks(event: str, *args):
"""Execute all registered hooks for an event with fault isolation.

Individual hook failures do not prevent other hooks from executing.
Returns the first non-None blocking result, or None if all hooks pass.
"""
blocking_result = None

for callback in HOOKS[event]:
result = callback(*args)
if result is not None: # 返回值 ≠ None → hook 说"停"
return result
return None
try:
result = callback(*args)
# Capture the first blocking result to prevent execution
if result is not None and blocking_result is None:
blocking_result = result
except Exception as e:
# Print error but continue executing remaining hooks
print(f"\033[31m[HOOK ERROR] {event} -> {callback.__name__}: {e}\033[0m")

return blocking_result
```

教学版中,PreToolUse 的非 None 返回值会阻止本次工具执行,Stop 的非 None 返回值会强制续跑。UserPromptSubmit 和 PostToolUse 的返回值未被使用。
Expand Down
16 changes: 12 additions & 4 deletions s04_hooks/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,19 @@ def register_hook(event: str, callback):
HOOKS[event].append(callback)

def trigger_hooks(event: str, *args):
blocking_result = None

for callback in HOOKS[event]:
result = callback(*args)
if result is not None: # teaching shortcut: block this tool call
return result
return None
try:
result = callback(*args)
# Capture the first blocking result to prevent execution
if result is not None and blocking_result is None:
blocking_result = result
except Exception as e:
# Print error but continue executing remaining hooks
print(f"\033[31m[HOOK ERROR] {event} -> {callback.__name__}: {e}\033[0m")

return blocking_result


# s03 permission check logic, now wrapped as a hook
Expand Down