Skip to content
Merged
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
85 changes: 73 additions & 12 deletions .chock/bin/claude_code.py

Large diffs are not rendered by default.

61 changes: 51 additions & 10 deletions .chock/bin/codex_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from pathlib import PurePosixPath as _chock_PurePosixPath, PureWindowsPath as _chock_PureWindowsPath
import traceback
import warnings as _warnings
import zlib as _zlib

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.5)
Expand Down Expand Up @@ -262,12 +263,22 @@ def _rejected_by_markers(c, raw):
for marker in c.get("reject_markers", ()):
if marker in raw:
return True
for key, values in c.get("reject_values", {}).items():
if key in raw and raw[key] in values:
return True
for probe, markers in c.get("reject_markers_unless_probe", {}).items():
if any(marker in raw for marker in markers) and not PROBES[probe](raw):
return True
return any(PROBES[probe](raw) for probe in c.get("reject_probes", ()))

def _accepted_by_values(c, raw):
"""True unless the entry names accepted values and the payload carries none of them."""
values = c.get("accept_values")
return not values or any(key in raw and raw[key] in allowed for key, allowed in values.items())

def _accepted_by_markers(c, raw, name):
if not _accepted_by_values(c, raw):
return False
accept = c.get("accept_markers", ())
if not accept or any(marker in raw for marker in accept):
return True
Expand Down Expand Up @@ -306,12 +317,25 @@ def _segment(node, part):
return items[i] if isinstance(items, (list, tuple)) and len(items) > i else None
return node.get(part) if isinstance(node, dict) else None

def _list_at(node, path):
"""The list under `path`, decoding the JSON text Cline sends in a list's place."""
items = _walk(node, path)
if isinstance(items, str) and items.lstrip()[:1] == "[":
try:
items = _json.loads(items)
except (ValueError, RecursionError):
return None
return items if isinstance(items, (list, tuple)) else None

def _walk(node, path):
"""A dotted path off `node`; `a[].b` joins `b` over `a`'s dict items, `a[0]` indexes."""
"""A dotted path off `node`; `a[].b` joins `b` over `a`'s dict items, `a[]` its strings, `a[0]` indexes."""
if path.endswith("[]"):
joined = "\n".join(item for item in _list_at(node, path[:-2]) or () if isinstance(item, str))
return joined or None
if "[]." in path:
head, sub = path.split("[].", 1)
items = _walk(node, head)
if not isinstance(items, (list, tuple)):
items = _list_at(node, head)
if items is None:
return None
joined = "\n".join(str(item.get(sub, "")) for item in items if isinstance(item, dict))
return joined or None
Expand Down Expand Up @@ -341,7 +365,7 @@ def _tool_input_raw(cfg, raw):
if not isinstance(raw, dict):
return None
for key in cfg["fields"].get("tool_input", ("tool_input",)):
value = raw.get(key)
value = _walk(raw, key)
if value is not None:
return value
return None
Expand Down Expand Up @@ -563,6 +587,8 @@ def hj_respond(cfg, decision, event, wire=None):
return "", 0
if gate["grammar"] == "G2":
return _g2(v, gate, decision, name)
if gate["grammar"] == "G7":
return _g7(v, decision, wire)
return _g1(v, gate, decision, wire, name)

_WINDOWS_KEYS = ("commandWindows", "windows")
Expand All @@ -578,16 +604,25 @@ def _hook_dict(cfg, command):
entry[key] = value
return entry

def _flat_list_rule(hook_entry, ev, name, command, matcher):
"""One `[[hooks]]` rule, under the vendor's own key names where `rule_keys` renames them."""
rule = {"event": name, "command": command}
if hook_entry["matcher"] and ev in _MATCHER_EVENTS:
matcher = matcher or hook_entry.get("default_match")
if matcher:
rule["matcher"] = matcher
if "rule_name_prefix" in hook_entry:
# Vibe dedupes [[hooks]] by name across files; the command's checksum keeps two owners' rules apart.
rule["name"] = "%s%s-%08x" % (hook_entry["rule_name_prefix"], name, _zlib.crc32(command.encode("utf-8")))
keys = hook_entry.get("rule_keys", {})
return {keys.get(key, key): value for key, value in rule.items()}

def _flat_list_wrapper(hook_entry, reverse, canonical_events, command, matcher):
rules = []
for ev in canonical_events:
name = reverse.get(ev)
if not name:
continue
rule = {"event": name, "command": command}
if matcher and hook_entry["matcher"] and ev in _MATCHER_EVENTS:
rule["matcher"] = matcher
rules.append(rule)
if name:
rules.append(_flat_list_rule(hook_entry, ev, name, command, matcher))
return rules

def _cursor_wrapper(cfg, reverse, canonical_events, command, *, fail_closed):
Expand Down Expand Up @@ -616,6 +651,10 @@ def _flat_entries_wrapper(hook_entry, reverse, canonical_events, command):
hooks.setdefault(extra, []).append({"command": command})
return {"hooks": hooks}

def _hook_dir_wrapper(reverse, canonical_events, command):
"""File name -> command: one executable per event, named exactly as the wire event (cline)."""
return {reverse[ev]: command for ev in canonical_events if reverse.get(ev)}

def _default_wrapper(cfg, reverse, canonical_events, command, matcher):
hook_entry = cfg["hook_entry"]
hooks = {}
Expand Down Expand Up @@ -646,6 +685,8 @@ def hook_entry_config(cfg, canonical_events, command, matcher=None, *, fail_clos
return _cursor_wrapper(cfg, reverse, canonical_events, command, fail_closed=fail_closed)
if wrapper == "flat_entries":
return _flat_entries_wrapper(hook_entry, reverse, canonical_events, command)
if wrapper == "hook_dir":
return _hook_dir_wrapper(reverse, canonical_events, command)
return _default_wrapper(cfg, reverse, canonical_events, command, matcher)


Expand Down
49 changes: 39 additions & 10 deletions .chock/bin/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from pathlib import PurePosixPath as _chock_PurePosixPath, PureWindowsPath as _chock_PureWindowsPath
import traceback
import warnings as _warnings
import zlib as _zlib

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.5)
Expand Down Expand Up @@ -253,12 +254,25 @@ def _segment(node, part):
return items[i] if isinstance(items, (list, tuple)) and len(items) > i else None
return node.get(part) if isinstance(node, dict) else None

def _list_at(node, path):
"""The list under `path`, decoding the JSON text Cline sends in a list's place."""
items = _walk(node, path)
if isinstance(items, str) and items.lstrip()[:1] == "[":
try:
items = _json.loads(items)
except (ValueError, RecursionError):
return None
return items if isinstance(items, (list, tuple)) else None

def _walk(node, path):
"""A dotted path off `node`; `a[].b` joins `b` over `a`'s dict items, `a[0]` indexes."""
"""A dotted path off `node`; `a[].b` joins `b` over `a`'s dict items, `a[]` its strings, `a[0]` indexes."""
if path.endswith("[]"):
joined = "\n".join(item for item in _list_at(node, path[:-2]) or () if isinstance(item, str))
return joined or None
if "[]." in path:
head, sub = path.split("[].", 1)
items = _walk(node, head)
if not isinstance(items, (list, tuple)):
items = _list_at(node, head)
if items is None:
return None
joined = "\n".join(str(item.get(sub, "")) for item in items if isinstance(item, dict))
return joined or None
Expand Down Expand Up @@ -288,7 +302,7 @@ def _tool_input_raw(cfg, raw):
if not isinstance(raw, dict):
return None
for key in cfg["fields"].get("tool_input", ("tool_input",)):
value = raw.get(key)
value = _walk(raw, key)
if value is not None:
return value
return None
Expand Down Expand Up @@ -369,16 +383,25 @@ def _hook_dict(cfg, command):
entry[key] = value
return entry

def _flat_list_rule(hook_entry, ev, name, command, matcher):
"""One `[[hooks]]` rule, under the vendor's own key names where `rule_keys` renames them."""
rule = {"event": name, "command": command}
if hook_entry["matcher"] and ev in _MATCHER_EVENTS:
matcher = matcher or hook_entry.get("default_match")
if matcher:
rule["matcher"] = matcher
if "rule_name_prefix" in hook_entry:
# Vibe dedupes [[hooks]] by name across files; the command's checksum keeps two owners' rules apart.
rule["name"] = "%s%s-%08x" % (hook_entry["rule_name_prefix"], name, _zlib.crc32(command.encode("utf-8")))
keys = hook_entry.get("rule_keys", {})
return {keys.get(key, key): value for key, value in rule.items()}

def _flat_list_wrapper(hook_entry, reverse, canonical_events, command, matcher):
rules = []
for ev in canonical_events:
name = reverse.get(ev)
if not name:
continue
rule = {"event": name, "command": command}
if matcher and hook_entry["matcher"] and ev in _MATCHER_EVENTS:
rule["matcher"] = matcher
rules.append(rule)
if name:
rules.append(_flat_list_rule(hook_entry, ev, name, command, matcher))
return rules

def _cursor_wrapper(cfg, reverse, canonical_events, command, *, fail_closed):
Expand Down Expand Up @@ -407,6 +430,10 @@ def _flat_entries_wrapper(hook_entry, reverse, canonical_events, command):
hooks.setdefault(extra, []).append({"command": command})
return {"hooks": hooks}

def _hook_dir_wrapper(reverse, canonical_events, command):
"""File name -> command: one executable per event, named exactly as the wire event (cline)."""
return {reverse[ev]: command for ev in canonical_events if reverse.get(ev)}

def _default_wrapper(cfg, reverse, canonical_events, command, matcher):
hook_entry = cfg["hook_entry"]
hooks = {}
Expand Down Expand Up @@ -437,6 +464,8 @@ def hook_entry_config(cfg, canonical_events, command, matcher=None, *, fail_clos
return _cursor_wrapper(cfg, reverse, canonical_events, command, fail_closed=fail_closed)
if wrapper == "flat_entries":
return _flat_entries_wrapper(hook_entry, reverse, canonical_events, command)
if wrapper == "hook_dir":
return _hook_dir_wrapper(reverse, canonical_events, command)
return _default_wrapper(cfg, reverse, canonical_events, command, matcher)

_AMBIGUOUS_NAMES = (
Expand Down
83 changes: 72 additions & 11 deletions .chock/bin/devin.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from pathlib import PurePosixPath as _chock_PurePosixPath, PureWindowsPath as _chock_PureWindowsPath
import traceback
import warnings as _warnings
import zlib as _zlib

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.5)
Expand Down Expand Up @@ -235,11 +236,31 @@ def tool_input_of(raw):
"effort",
)

_MCP_PREFIX = "mcp_"

_CLAUDE_MCP_PREFIX = "mcp__"

def looks_like_claude_code(raw):
"""True when the payload carries a field only Claude Code has been seen to send."""
return isinstance(raw, dict) and any(marker in raw for marker in OBSERVED_MARKERS)

PROBES = {"looks_like_claude_code": looks_like_claude_code}
def names_a_single_underscore_mcp_tool(raw):
"""True when tool_name is an MCP tool ID in a spelling Claude Code never sends."""
name = raw.get("tool_name") if isinstance(raw, dict) else None
return isinstance(name, str) and name.startswith(_MCP_PREFIX) and not name.startswith(_CLAUDE_MCP_PREFIX)

def event_keys_agree(raw):
"""True when hookEventName names hook_event_name's event: the same text, or Grok Build's snake_case twin."""
snake, pascal = (raw.get("hookEventName"), raw.get("hook_event_name")) if isinstance(raw, dict) else (None, None)
if not isinstance(pascal, str) or not isinstance(snake, str):
return False
return snake == pascal or (snake.islower() and snake.replace("_", "") == pascal.lower())

PROBES = {
"looks_like_claude_code": looks_like_claude_code,
"names_a_single_underscore_mcp_tool": names_a_single_underscore_mcp_tool,
"event_keys_agree": event_keys_agree,
}

UNREADABLE_NAME = ""

Expand All @@ -265,12 +286,22 @@ def _rejected_by_markers(c, raw):
for marker in c.get("reject_markers", ()):
if marker in raw:
return True
for key, values in c.get("reject_values", {}).items():
if key in raw and raw[key] in values:
return True
for probe, markers in c.get("reject_markers_unless_probe", {}).items():
if any(marker in raw for marker in markers) and not PROBES[probe](raw):
return True
return any(PROBES[probe](raw) for probe in c.get("reject_probes", ()))

def _accepted_by_values(c, raw):
"""True unless the entry names accepted values and the payload carries none of them."""
values = c.get("accept_values")
return not values or any(key in raw and raw[key] in allowed for key, allowed in values.items())

def _accepted_by_markers(c, raw, name):
if not _accepted_by_values(c, raw):
return False
accept = c.get("accept_markers", ())
if not accept or any(marker in raw for marker in accept):
return True
Expand Down Expand Up @@ -309,12 +340,25 @@ def _segment(node, part):
return items[i] if isinstance(items, (list, tuple)) and len(items) > i else None
return node.get(part) if isinstance(node, dict) else None

def _list_at(node, path):
"""The list under `path`, decoding the JSON text Cline sends in a list's place."""
items = _walk(node, path)
if isinstance(items, str) and items.lstrip()[:1] == "[":
try:
items = _json.loads(items)
except (ValueError, RecursionError):
return None
return items if isinstance(items, (list, tuple)) else None

def _walk(node, path):
"""A dotted path off `node`; `a[].b` joins `b` over `a`'s dict items, `a[0]` indexes."""
"""A dotted path off `node`; `a[].b` joins `b` over `a`'s dict items, `a[]` its strings, `a[0]` indexes."""
if path.endswith("[]"):
joined = "\n".join(item for item in _list_at(node, path[:-2]) or () if isinstance(item, str))
return joined or None
if "[]." in path:
head, sub = path.split("[].", 1)
items = _walk(node, head)
if not isinstance(items, (list, tuple)):
items = _list_at(node, head)
if items is None:
return None
joined = "\n".join(str(item.get(sub, "")) for item in items if isinstance(item, dict))
return joined or None
Expand Down Expand Up @@ -344,7 +388,7 @@ def _tool_input_raw(cfg, raw):
if not isinstance(raw, dict):
return None
for key in cfg["fields"].get("tool_input", ("tool_input",)):
value = raw.get(key)
value = _walk(raw, key)
if value is not None:
return value
return None
Expand Down Expand Up @@ -535,6 +579,8 @@ def hj_respond(cfg, decision, event, wire=None):
return "", 0
if gate["grammar"] == "G2":
return _g2(v, gate, decision, name)
if gate["grammar"] == "G7":
return _g7(v, decision, wire)
return _g1(v, gate, decision, wire, name)

_WINDOWS_KEYS = ("commandWindows", "windows")
Expand All @@ -550,16 +596,25 @@ def _hook_dict(cfg, command):
entry[key] = value
return entry

def _flat_list_rule(hook_entry, ev, name, command, matcher):
"""One `[[hooks]]` rule, under the vendor's own key names where `rule_keys` renames them."""
rule = {"event": name, "command": command}
if hook_entry["matcher"] and ev in _MATCHER_EVENTS:
matcher = matcher or hook_entry.get("default_match")
if matcher:
rule["matcher"] = matcher
if "rule_name_prefix" in hook_entry:
# Vibe dedupes [[hooks]] by name across files; the command's checksum keeps two owners' rules apart.
rule["name"] = "%s%s-%08x" % (hook_entry["rule_name_prefix"], name, _zlib.crc32(command.encode("utf-8")))
keys = hook_entry.get("rule_keys", {})
return {keys.get(key, key): value for key, value in rule.items()}

def _flat_list_wrapper(hook_entry, reverse, canonical_events, command, matcher):
rules = []
for ev in canonical_events:
name = reverse.get(ev)
if not name:
continue
rule = {"event": name, "command": command}
if matcher and hook_entry["matcher"] and ev in _MATCHER_EVENTS:
rule["matcher"] = matcher
rules.append(rule)
if name:
rules.append(_flat_list_rule(hook_entry, ev, name, command, matcher))
return rules

def _cursor_wrapper(cfg, reverse, canonical_events, command, *, fail_closed):
Expand Down Expand Up @@ -588,6 +643,10 @@ def _flat_entries_wrapper(hook_entry, reverse, canonical_events, command):
hooks.setdefault(extra, []).append({"command": command})
return {"hooks": hooks}

def _hook_dir_wrapper(reverse, canonical_events, command):
"""File name -> command: one executable per event, named exactly as the wire event (cline)."""
return {reverse[ev]: command for ev in canonical_events if reverse.get(ev)}

def _default_wrapper(cfg, reverse, canonical_events, command, matcher):
hook_entry = cfg["hook_entry"]
hooks = {}
Expand Down Expand Up @@ -618,6 +677,8 @@ def hook_entry_config(cfg, canonical_events, command, matcher=None, *, fail_clos
return _cursor_wrapper(cfg, reverse, canonical_events, command, fail_closed=fail_closed)
if wrapper == "flat_entries":
return _flat_entries_wrapper(hook_entry, reverse, canonical_events, command)
if wrapper == "hook_dir":
return _hook_dir_wrapper(reverse, canonical_events, command)
return _default_wrapper(cfg, reverse, canonical_events, command, matcher)


Expand Down
Loading
Loading