fix(claude): accept async rewake metadata
This commit is contained in:
1 parent
653e644e4f
commit
ae04b0cbb4
4 files changed
+44
-3
No files matched your search
@@ -26,7 +26,7 @@ This repository owns the published `elydora` package, its CLI, synchronous and a
|
|||||||
- Model stable, legacy, and early-access hook generations as explicit contracts. Keep their activation requirements visible in CLI output and README guidance.
|
- Model stable, legacy, and early-access hook generations as explicit contracts. Keep their activation requirements visible in CLI output and README guidance.
|
||||||
- Resolve stable Kimi hooks through `$KIMI_CODE_HOME/config.toml` with `~/.kimi-code/config.toml` as the default, and treat an empty override as the default. Activate `~/.kimi/config.toml` only when the legacy home exists. Register exact `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` rules with ten-second commands; stable Kimi supports its current sixteen-event schema and legacy kimi-cli supports its thirteen-event schema. Preserve TOML comments and array style, then parse the complete rendered document before staging it. Preserve the native snake_case payload, propagate exit code `2`, and keep audit delivery fail-open with owner-only error logs. Use encoded PowerShell commands on Windows and exact two-argument shell commands on Unix. Commit every detected config, generated runtime, runtime config, and private key through one rollback-capable transaction.
|
- Resolve stable Kimi hooks through `$KIMI_CODE_HOME/config.toml` with `~/.kimi-code/config.toml` as the default, and treat an empty override as the default. Activate `~/.kimi/config.toml` only when the legacy home exists. Register exact `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` rules with ten-second commands; stable Kimi supports its current sixteen-event schema and legacy kimi-cli supports its thirteen-event schema. Preserve TOML comments and array style, then parse the complete rendered document before staging it. Preserve the native snake_case payload, propagate exit code `2`, and keep audit delivery fail-open with owner-only error logs. Use encoded PowerShell commands on Windows and exact two-argument shell commands on Unix. Commit every detected config, generated runtime, runtime config, and private key through one rollback-capable transaction.
|
||||||
- Resolve Grok Build user hooks through `$GROK_HOME/hooks/*.json` with `~/.grok/hooks/*.json` as the default, and treat an empty override as the default. Keep Claude Code, Cursor, project `.grok/hooks`, plugin, and `hooks-paths` sources read-only. Register exact `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` groups with ten-second command handlers, preserve the complete native camelCase payload, and emit Grok's documented deny JSON plus exit code `2` for frozen or revoked agents. Use encoded PowerShell commands on Windows and exact two-argument shell commands on Unix. Commit Grok's hook file, generated runtimes, runtime config, and private key through one rollback-capable transaction.
|
- Resolve Grok Build user hooks through `$GROK_HOME/hooks/*.json` with `~/.grok/hooks/*.json` as the default, and treat an empty override as the default. Keep Claude Code, Cursor, project `.grok/hooks`, plugin, and `hooks-paths` sources read-only. Register exact `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` groups with ten-second command handlers, preserve the complete native camelCase payload, and emit Grok's documented deny JSON plus exit code `2` for frozen or revoked agents. Use encoded PowerShell commands on Windows and exact two-argument shell commands on Unix. Commit Grok's hook file, generated runtimes, runtime config, and private key through one rollback-capable transaction.
|
||||||
- Resolve Claude Code user hooks through `$CLAUDE_CONFIG_DIR/settings.json` with `~/.claude/settings.json` when the variable is absent. Match Claude Code's path resolution exactly: relative and empty values resolve from the current working directory, and literal tildes remain path segments. Keep project, local, managed, plugin, skill, and agent hook sources read-only. Register exact matchless `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` groups with ten-second exec-form command handlers. Preserve the complete native snake_case payload and propagate freeze and revocation through exit code `2`. Commit Claude Code's user settings, generated runtimes, runtime config, and private key through one rollback-capable transaction. Reject installation while the user source sets `disableAllHooks`, then require `/hooks` and `claude doctor` verification for higher-scope policy effects.
|
- Resolve Claude Code user hooks through `$CLAUDE_CONFIG_DIR/settings.json` with `~/.claude/settings.json` when the variable is absent. Match Claude Code's path resolution exactly: relative and empty values resolve from the current working directory, and literal tildes remain path segments. Keep project, local, managed, plugin, skill, and agent hook sources read-only. Validate the complete shipped handler schema, including command exec form, async rewake metadata, HTTP, MCP tool, prompt, and agent handlers. Register exact matchless `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` groups with ten-second exec-form command handlers. Preserve the complete native snake_case payload and propagate freeze and revocation through exit code `2`. Commit Claude Code's user settings, generated runtimes, runtime config, and private key through one rollback-capable transaction. Reject installation while the user source sets `disableAllHooks`, then require `/hooks` and `claude doctor` verification for higher-scope policy effects.
|
||||||
- Resolve Gemini CLI user hooks through `$GEMINI_CLI_HOME/.gemini/settings.json` with `~/.gemini/settings.json` when the variable is absent or empty. Preserve relative and literal-tilde home values as Gemini CLI does. Keep workspace, system defaults, system overrides, and extension hooks read-only. Preserve JSON comments while rejecting trailing commas and duplicate keys. Register exact matchless `BeforeTool` and `AfterTool` groups named `elydora-guard` and `elydora-audit` with ten-second command handlers. Preserve the complete native snake_case payload, emit valid JSON on exit code `0`, and propagate freeze and revocation through exit code `2`. Use encoded PowerShell commands on Windows. Commit Gemini CLI user settings, generated runtimes, runtime config, and private key through one rollback-capable transaction. Respect `hooksConfig.enabled` and `hooksConfig.disabled`, then require `/hooks list` verification for higher-scope policy effects.
|
- Resolve Gemini CLI user hooks through `$GEMINI_CLI_HOME/.gemini/settings.json` with `~/.gemini/settings.json` when the variable is absent or empty. Preserve relative and literal-tilde home values as Gemini CLI does. Keep workspace, system defaults, system overrides, and extension hooks read-only. Preserve JSON comments while rejecting trailing commas and duplicate keys. Register exact matchless `BeforeTool` and `AfterTool` groups named `elydora-guard` and `elydora-audit` with ten-second command handlers. Preserve the complete native snake_case payload, emit valid JSON on exit code `0`, and propagate freeze and revocation through exit code `2`. Use encoded PowerShell commands on Windows. Commit Gemini CLI user settings, generated runtimes, runtime config, and private key through one rollback-capable transaction. Respect `hooksConfig.enabled` and `hooksConfig.disabled`, then require `/hooks list` verification for higher-scope policy effects.
|
||||||
- Write Auggie hooks only to `~/.augment/settings.json`; keep system and workspace settings read-only. Generate `.cmd` wrappers on Windows and `.sh` wrappers on Unix because Auggie dispatches supported script paths, and express hook timeouts in milliseconds.
|
- Write Auggie hooks only to `~/.augment/settings.json`; keep system and workspace settings read-only. Generate `.cmd` wrappers on Windows and `.sh` wrappers on Unix because Auggie dispatches supported script paths, and express hook timeouts in milliseconds.
|
||||||
- Validate Auggie matcher syntax during installation with Node.js `new RegExp`; keep status and uninstall independent from the JavaScript validator so recovery remains available offline.
|
- Validate Auggie matcher syntax during installation with Node.js `new RegExp`; keep status and uninstall independent from the JavaScript validator so recovery remains available offline.
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ HOOK_EVENTS = frozenset({
|
|||||||
COMMON_HANDLER_KEYS = frozenset({"if", "once", "statusMessage", "timeout"})
|
COMMON_HANDLER_KEYS = frozenset({"if", "once", "statusMessage", "timeout"})
|
||||||
HANDLER_KEYS = {
|
HANDLER_KEYS = {
|
||||||
"command": COMMON_HANDLER_KEYS | {
|
"command": COMMON_HANDLER_KEYS | {
|
||||||
"args", "async", "asyncRewake", "command", "shell", "type",
|
"args", "async", "asyncRewake", "command", "rewakeMessage",
|
||||||
|
"rewakeSummary", "shell", "type",
|
||||||
},
|
},
|
||||||
"prompt": COMMON_HANDLER_KEYS | {
|
"prompt": COMMON_HANDLER_KEYS | {
|
||||||
"continueOnBlock", "model", "prompt", "type",
|
"continueOnBlock", "model", "prompt", "type",
|
||||||
@@ -162,6 +163,14 @@ def _validate_handler(
|
|||||||
_string_array(handler["args"], "args", label)
|
_string_array(handler["args"], "args", label)
|
||||||
_optional_boolean(handler, "async", label)
|
_optional_boolean(handler, "async", label)
|
||||||
_optional_boolean(handler, "asyncRewake", 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"):
|
if "shell" in handler and handler["shell"] not in ("bash", "powershell"):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f'{label} field "shell" must be "bash" or "powershell"'
|
f'{label} field "shell" must be "bash" or "powershell"'
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import subprocess # nosec B404
|
import subprocess # nosec B404
|
||||||
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@@ -19,7 +20,18 @@ CLAUDE_BINARY = os.environ.get("ELYDORA_CLAUDE_BINARY")
|
|||||||
def test_official_claude_code_accepts_installed_settings(
|
def test_official_claude_code_accepts_installed_settings(
|
||||||
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||||
) -> None:
|
) -> 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()
|
fixture.install()
|
||||||
environment = {
|
environment = {
|
||||||
**os.environ,
|
**os.environ,
|
||||||
|
|||||||
@@ -75,6 +75,13 @@ def test_install_preserves_settings_and_writes_one_exact_triple(
|
|||||||
"timeout": 5,
|
"timeout": 5,
|
||||||
}],
|
}],
|
||||||
}],
|
}],
|
||||||
|
"Stop": [{"hooks": [{
|
||||||
|
"type": "command",
|
||||||
|
"command": "background-check",
|
||||||
|
"asyncRewake": True,
|
||||||
|
"rewakeMessage": "Background validation failed",
|
||||||
|
"rewakeSummary": "Validation feedback",
|
||||||
|
}]}],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fixture = prepare_fixture(
|
fixture = prepare_fixture(
|
||||||
@@ -88,6 +95,7 @@ def test_install_preserves_settings_and_writes_one_exact_triple(
|
|||||||
assert installed["model"] == "sonnet"
|
assert installed["model"] == "sonnet"
|
||||||
assert installed["hooks"]["Notification"] == existing["hooks"]["Notification"]
|
assert installed["hooks"]["Notification"] == existing["hooks"]["Notification"]
|
||||||
assert installed["hooks"]["PreToolUse"][0] == existing["hooks"]["PreToolUse"][0]
|
assert installed["hooks"]["PreToolUse"][0] == existing["hooks"]["PreToolUse"][0]
|
||||||
|
assert installed["hooks"]["Stop"] == existing["hooks"]["Stop"]
|
||||||
assert_managed_triple(installed, fixture)
|
assert_managed_triple(installed, fixture)
|
||||||
first_source = fixture.config_path.read_text(encoding="utf-8")
|
first_source = fixture.config_path.read_text(encoding="utf-8")
|
||||||
|
|
||||||
@@ -240,6 +248,18 @@ INVALID_SETTINGS = [
|
|||||||
}]}]}}),
|
}]}]}}),
|
||||||
"positive finite number",
|
"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": [{
|
json.dumps({"hooks": {"PreToolUse": [{"hooks": [{
|
||||||
"type": "http", "url": "https://example.test", "headers": {"A": 1},
|
"type": "http", "url": "https://example.test", "headers": {"A": 1},
|
||||||
|
|||||||
Reference in new issue
Block a user