From fad1968c25c0f3487aa76de3bb1d858148bde65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 20 Jul 2026 09:06:13 -0400 Subject: [PATCH] fix(python): mirror Claude async rewake metadata --- .../elydora/plugins/claudecode_contract.py | 11 +++++++++- sdks/python/tests/test_claudecode_official.py | 14 ++++++++++++- sdks/python/tests/test_claudecode_plugin.py | 20 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/sdks/python/elydora/plugins/claudecode_contract.py b/sdks/python/elydora/plugins/claudecode_contract.py index 19bf2ca..f0f482b 100644 --- a/sdks/python/elydora/plugins/claudecode_contract.py +++ b/sdks/python/elydora/plugins/claudecode_contract.py @@ -35,7 +35,8 @@ HOOK_EVENTS = frozenset({ COMMON_HANDLER_KEYS = frozenset({"if", "once", "statusMessage", "timeout"}) HANDLER_KEYS = { "command": COMMON_HANDLER_KEYS | { - "args", "async", "asyncRewake", "command", "shell", "type", + "args", "async", "asyncRewake", "command", "rewakeMessage", + "rewakeSummary", "shell", "type", }, "prompt": COMMON_HANDLER_KEYS | { "continueOnBlock", "model", "prompt", "type", @@ -162,6 +163,14 @@ def _validate_handler( _string_array(handler["args"], "args", label) _optional_boolean(handler, "async", label) _optional_boolean(handler, "asyncRewake", label) + if "rewakeMessage" in handler: + _require_non_empty_string( + handler["rewakeMessage"], "rewakeMessage", label + ) + if "rewakeSummary" in handler: + _require_non_empty_string( + handler["rewakeSummary"], "rewakeSummary", label + ) if "shell" in handler and handler["shell"] not in ("bash", "powershell"): raise ValueError( f'{label} field "shell" must be "bash" or "powershell"' diff --git a/sdks/python/tests/test_claudecode_official.py b/sdks/python/tests/test_claudecode_official.py index 9371443..9940996 100644 --- a/sdks/python/tests/test_claudecode_official.py +++ b/sdks/python/tests/test_claudecode_official.py @@ -3,6 +3,7 @@ from __future__ import annotations import os from pathlib import Path import subprocess # nosec B404 +import sys import pytest @@ -19,7 +20,18 @@ CLAUDE_BINARY = os.environ.get("ELYDORA_CLAUDE_BINARY") def test_official_claude_code_accepts_installed_settings( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: - fixture = prepare_fixture(monkeypatch, tmp_path) + fixture = prepare_fixture( + monkeypatch, + tmp_path, + existing_settings={"hooks": {"Stop": [{"hooks": [{ + "type": "command", + "command": sys.executable, + "args": ["--version"], + "asyncRewake": True, + "rewakeMessage": "Background validation failed", + "rewakeSummary": "Validation feedback", + }]}]}}, + ) fixture.install() environment = { **os.environ, diff --git a/sdks/python/tests/test_claudecode_plugin.py b/sdks/python/tests/test_claudecode_plugin.py index 5457dc8..a3486e5 100644 --- a/sdks/python/tests/test_claudecode_plugin.py +++ b/sdks/python/tests/test_claudecode_plugin.py @@ -75,6 +75,13 @@ def test_install_preserves_settings_and_writes_one_exact_triple( "timeout": 5, }], }], + "Stop": [{"hooks": [{ + "type": "command", + "command": "background-check", + "asyncRewake": True, + "rewakeMessage": "Background validation failed", + "rewakeSummary": "Validation feedback", + }]}], }, } fixture = prepare_fixture( @@ -88,6 +95,7 @@ def test_install_preserves_settings_and_writes_one_exact_triple( assert installed["model"] == "sonnet" assert installed["hooks"]["Notification"] == existing["hooks"]["Notification"] assert installed["hooks"]["PreToolUse"][0] == existing["hooks"]["PreToolUse"][0] + assert installed["hooks"]["Stop"] == existing["hooks"]["Stop"] assert_managed_triple(installed, fixture) first_source = fixture.config_path.read_text(encoding="utf-8") @@ -240,6 +248,18 @@ INVALID_SETTINGS = [ }]}]}}), "positive finite number", ), + ( + json.dumps({"hooks": {"Stop": [{"hooks": [{ + "type": "command", "command": "x", "rewakeMessage": "", + }]}]}}), + "rewakeMessage.*non-empty string", + ), + ( + json.dumps({"hooks": {"Stop": [{"hooks": [{ + "type": "command", "command": "x", "rewakeSummary": "", + }]}]}}), + "rewakeSummary.*non-empty string", + ), ( json.dumps({"hooks": {"PreToolUse": [{"hooks": [{ "type": "http", "url": "https://example.test", "headers": {"A": 1},