-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtool_failure.sh
More file actions
executable file
·86 lines (71 loc) · 2.39 KB
/
tool_failure.sh
File metadata and controls
executable file
·86 lines (71 loc) · 2.39 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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/bin/bash
# PostToolUseFailure hook: store error patterns as cortex memories
# When tools fail repeatedly, the error pattern becomes searchable knowledge
INPUT=$(cat 2>/dev/null)
/usr/bin/python3 -W ignore - "$INPUT" 2>/dev/null <<'PYEOF'
import sys, json, os, time, warnings, hashlib
warnings.filterwarnings("ignore")
raw = sys.argv[1] if len(sys.argv) > 1 else ""
try:
d = json.loads(raw)
except:
sys.exit(0)
tool_name = d.get("tool_name", "")
tool_input = d.get("tool_input", {})
error = d.get("error", "") or d.get("tool_error", "") or d.get("result", "")
cwd = d.get("cwd", "")
if not tool_name or not error:
sys.exit(0)
# Skip noisy/uninteresting failures
if tool_name in ("Read", "Glob", "Grep"):
sys.exit(0)
error_str = str(error)[:500]
# Dedup: check if we already logged this error pattern recently
FAILURE_LOG = os.path.expanduser("~/.claude/.cortex_failure_log")
error_hash = hashlib.md5(f"{tool_name}:{error_str[:100]}".encode()).hexdigest()[:12]
try:
if os.path.exists(FAILURE_LOG):
with open(FAILURE_LOG) as f:
recent = f.readlines()[-20:] # last 20 entries
for line in recent:
if error_hash in line:
sys.exit(0) # already logged recently
except:
pass
# Log this failure
try:
import stat
fd = os.open(FAILURE_LOG, os.O_WRONLY | os.O_CREAT | os.O_APPEND, stat.S_IRUSR | stat.S_IWUSR)
with os.fdopen(fd, "a") as f:
f.write(f"{time.strftime('%Y-%m-%dT%H:%M:%S')} {error_hash} {tool_name}\n")
except:
pass
# Detect project
project_name = ""
if cwd:
parts = cwd.rstrip("/").split("/")
skip = {"home", "Users", "projects", "src", "work", "dev", "repos", "code", ".claude", ""}
for p in reversed(parts):
if p not in skip:
project_name = p
break
# Build context summary for Claude
cmd_info = ""
if tool_name == "Bash":
cmd_info = f" Command: {str(tool_input.get('command', ''))[:150]}"
elif tool_name in ("Edit", "Write"):
cmd_info = f" File: {tool_input.get('file_path', '')}"
context = (
f"[cortex] Tool failure detected — {tool_name}{cmd_info}\n"
f" Error: {error_str[:300]}\n"
f" If this is a recurring issue, consider storing the fix as a cortex memory."
)
output = json.dumps({
"suppressOutput": True,
"hookSpecificOutput": {
"hookEventName": "PostToolUseFailure",
"additionalContext": context
}
})
print(output)
PYEOF