-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfile_lock.py
More file actions
85 lines (67 loc) · 2.44 KB
/
file_lock.py
File metadata and controls
85 lines (67 loc) · 2.44 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
#!/usr/bin/env python3
"""PreToolUse hook: file lock — prevents two agents from editing the same file simultaneously.
Uses /tmp/claude_file_locks/ directory. Each lock file contains the PID + timestamp.
Lock expires after 60 seconds (stale agent detection).
"""
import json
import os
import sys
import time
from hashlib import md5
from pathlib import Path
LOCK_DIR = Path("/tmp/claude_file_locks")
LOCK_EXPIRE_SECONDS = 60
def main():
try:
input_data = json.load(sys.stdin)
except (json.JSONDecodeError, EOFError):
print("{}")
return
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
# Only check Edit and Write
if tool_name not in ("Edit", "Write"):
print("{}")
return
file_path = tool_input.get("file_path", "")
if not file_path:
print("{}")
return
LOCK_DIR.mkdir(exist_ok=True)
# Create a lock key from the file path
lock_key = md5(file_path.encode()).hexdigest()[:12]
lock_file = LOCK_DIR / f"{lock_key}.lock"
my_pid = os.getpid()
now = time.time()
# Check if lock exists
if lock_file.exists():
try:
content = lock_file.read_text().strip()
parts = content.split("|")
locked_pid = int(parts[0])
locked_time = float(parts[1])
locked_path = parts[2] if len(parts) > 2 else "unknown"
# Check if lock is stale (expired or dead process)
is_stale = (now - locked_time) > LOCK_EXPIRE_SECONDS
is_dead = False
try:
os.kill(locked_pid, 0) # Check if process alive
except OSError:
is_dead = True
if not is_stale and not is_dead and locked_pid != my_pid:
fname = Path(file_path).name
print(json.dumps({
"systemMessage": (
f"⚠️ **File lock: `{fname}` is being edited by another agent** (PID {locked_pid}, "
f"{int(now - locked_time)}s ago). Wait for it to finish or the lock expires in "
f"{int(LOCK_EXPIRE_SECONDS - (now - locked_time))}s."
)
}))
return
except (ValueError, IndexError):
pass # Corrupt lock file, overwrite it
# Acquire lock
lock_file.write_text(f"{my_pid}|{now}|{file_path}")
print("{}")
if __name__ == "__main__":
main()