-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path__init__.py
More file actions
65 lines (49 loc) · 2.45 KB
/
Copy path__init__.py
File metadata and controls
65 lines (49 loc) · 2.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"""Shared plumbing for the PreToolUse Bash reminder hooks.
Each sibling module is one check: a pure `reminder(command, cwd) -> str | None`. `__main__.py` is
the single entrypoint `.claude/settings.json` invokes; it reads the hook payload once and runs every
check. This module owns the I/O contract with Claude Code so the checks stay pure and self-evident:
read the Bash command (and the tool's working directory) from the payload on stdin, and, when a check
returns text, emit it as non-blocking `additionalContext` that the harness injects into the model's
context. Nothing here blocks the tool.
"""
import json
import re
import sys
def payload_from_stdin():
"""The parsed hook payload on stdin, or {} if unavailable."""
try:
return json.load(sys.stdin)
except Exception:
return {}
def command_of(payload):
"""The Bash command string from a hook payload, or ''."""
return (payload.get("tool_input") or {}).get("command") or ""
def cwd_of(payload):
"""The working directory Claude Code reports for the tool call, or None."""
return payload.get("cwd") or None
def command_from_stdin():
"""The Bash command string from the payload on stdin, or '' (single-check `run` path)."""
return command_of(payload_from_stdin())
def matches(command, *words):
"""True if `command` runs `words` as a whitespace-separated token sequence.
The hook payload only carries the raw command string (there is no structured "which binary
ran" signal), and fully parsing the shell (pipes, quoting, `$()`, aliases) is impractical and
still defeatable. A word-boundary regex is the right cost/benefit for an advisory, non-blocking
nudge: it tolerates extra whitespace and, unlike a plain substring test, does not fire on
hyphenated look-alikes like `gh-pr-create-helper`. A false positive only adds a harmless
reminder, so we do not chase quoted-string or comment edge cases.
"""
pattern = r"\b" + r"\s+".join(map(re.escape, words)) + r"\b"
return re.search(pattern, command) is not None
def emit(message):
"""Inject `message` into the model's context (non-blocking)."""
json.dump(
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "additionalContext": message}},
sys.stdout,
)
def run(reminder):
"""Wire a single check's `reminder` to the I/O contract for direct invocation. Returns 0."""
message = reminder(command_from_stdin())
if message:
emit(message)
return 0