-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmemo_display.py
More file actions
executable file
·155 lines (124 loc) · 4.4 KB
/
memo_display.py
File metadata and controls
executable file
·155 lines (124 loc) · 4.4 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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python3
"""UserPromptSubmit hook: display pending memos in terminal.
General memos: show once, auto-delete.
Story memos: show on first message, then every 10 turns, persist until user deletes.
"""
import json
import os
import glob
import subprocess
BOT_REPO = os.path.expanduser("~/telegram-claude-bot")
MEMO_DIR = os.path.expanduser("~/telegram-claude-bot/memo/pending")
STORY_STATE = os.path.expanduser("~/telegram-claude-bot/memo/.story_state.json")
DONE_DIR = os.path.expanduser("~/telegram-claude-bot/memo/done")
def _pull_memos():
"""Quick git pull to sync VPS memos. Fail silently."""
try:
subprocess.run(
["git", "-C", BOT_REPO, "pull", "--ff-only", "--quiet", "origin", "main"],
capture_output=True, timeout=4
)
except Exception:
pass
def _load_story_state():
if os.path.exists(STORY_STATE):
with open(STORY_STATE) as f:
return json.load(f)
return {}
def _save_story_state(state):
os.makedirs(os.path.dirname(STORY_STATE), exist_ok=True)
with open(STORY_STATE, "w") as f:
json.dump(state, f)
def _parse_memo(path):
"""Parse memo file, return (type, text) or None."""
try:
with open(path) as f:
content = f.read()
except Exception:
return None
memo_type = "general"
text = content
if content.startswith("---\n"):
parts = content.split("---\n", 2)
if len(parts) >= 3:
frontmatter = parts[1]
text = parts[2].strip()
for line in frontmatter.splitlines():
if line.startswith("type:"):
memo_type = line.split(":", 1)[1].strip()
return memo_type, text
def _get_user_prompt():
"""Read user prompt from stdin (hook receives JSON with prompt field)."""
import sys
try:
data = json.load(sys.stdin)
return data.get("prompt", "").lower()
except Exception:
return ""
def _delete_all_story_memos():
"""Move all story memos to done/, clear story state."""
os.makedirs(DONE_DIR, exist_ok=True)
files = glob.glob(os.path.join(MEMO_DIR, "*.md"))
deleted = 0
for path in files:
result = _parse_memo(path)
if result and result[0] == "story":
os.rename(path, os.path.join(DONE_DIR, os.path.basename(path)))
deleted += 1
# Clear story state
if os.path.exists(STORY_STATE):
os.remove(STORY_STATE)
return deleted
def main():
_pull_memos()
if not os.path.isdir(MEMO_DIR):
return
# Check if user wants to delete story memos
prompt = _get_user_prompt()
if "delete story memo" in prompt or "delete storymemo" in prompt:
n = _delete_all_story_memos()
if n:
print(json.dumps({"systemMessage": f"Deleted {n} story memo(s)."}))
return
files = sorted(glob.glob(os.path.join(MEMO_DIR, "*.md")))
if not files:
return
story_state = _load_story_state()
output_lines = []
files_to_delete = []
for path in files:
result = _parse_memo(path)
if result is None:
continue
memo_type, text = result
basename = os.path.basename(path)
if memo_type == "story":
# Story memo: track turns, show on first + every 10 turns
if basename not in story_state:
story_state[basename] = {"turns": 0, "shown": False}
state = story_state[basename]
state["turns"] += 1
show = not state["shown"] or state["turns"] % 10 == 0
state["shown"] = True
if show:
output_lines.append(f"[STORY MEMO] {text}")
output_lines.append(f" (turn {state['turns']}, next reminder at turn {((state['turns'] // 10) + 1) * 10}. Say 'delete story memo' to dismiss)")
else:
# General memo: show once, mark for deletion
output_lines.append(f"[MEMO] {text}")
files_to_delete.append(path)
# Save story state
_save_story_state(story_state)
# Delete general memos after showing
os.makedirs(DONE_DIR, exist_ok=True)
for path in files_to_delete:
try:
os.rename(path, os.path.join(DONE_DIR, os.path.basename(path)))
except Exception:
pass
if output_lines:
print(json.dumps({
"additionalContext": "\n".join(output_lines)
}))
if __name__ == "__main__":
main()