memento-mcp 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memento-mcp",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "mcpName": "io.github.myrakrusemark/memento-protocol",
5
5
  "description": "The Memento Protocol — persistent memory for AI agents",
6
6
  "type": "module",
@@ -2,7 +2,7 @@
2
2
  # hook-toast.sh — tmux popup notifications for agent hooks.
3
3
  #
4
4
  # Usage:
5
- # hook-toast.sh <system> <message> # one-shot toast (auto-closes after 2s)
5
+ # hook-toast.sh <system> <message> # queued toast (shows for 2s each)
6
6
  # hook-toast.sh <system> --status <id> # start a multi-stage toast (poll mode)
7
7
  # hook-toast.sh --update <id> <message> # update a running toast
8
8
  # hook-toast.sh --close <id> # close a running toast (after delay)
@@ -13,6 +13,11 @@
13
13
  #
14
14
  # Any other system name works too — gets a default icon and grey border.
15
15
  #
16
+ # One-shot toasts are queued per-session — multiple hooks can fire toasts
17
+ # without clobbering each other. A single popup reads from the queue,
18
+ # showing each message for 2s before advancing. Popups target the session
19
+ # that spawned them via -t, so they stay in the right terminal.
20
+ #
16
21
  # Multi-stage example (PreCompact):
17
22
  # hook-toast.sh memento --status precompact
18
23
  # hook-toast.sh --update precompact "⏳ Getting context..."
@@ -20,14 +25,22 @@
20
25
  # hook-toast.sh --update precompact "✓ Stored 7 memories"
21
26
  # hook-toast.sh --close precompact
22
27
 
23
- TOAST_DIR="/tmp/hook-toast"
24
- mkdir -p "$TOAST_DIR"
25
-
26
28
  # Bail silently if not in tmux
27
29
  if ! tmux info &>/dev/null; then
28
30
  exit 0
29
31
  fi
30
32
 
33
+ # Derive session name — used for per-session queue isolation and -t targeting
34
+ SESSION=$(tmux display-message -p '#{session_name}' 2>/dev/null)
35
+ if [ -z "$SESSION" ]; then
36
+ exit 0
37
+ fi
38
+
39
+ TOAST_DIR="/tmp/hook-toast/${SESSION}"
40
+ QUEUE_FILE="$TOAST_DIR/queue"
41
+ READER_PID_FILE="$TOAST_DIR/reader.pid"
42
+ mkdir -p "$TOAST_DIR"
43
+
31
44
  get_style() {
32
45
  case "$1" in
33
46
  memento) echo "colour37" ;;
@@ -52,21 +65,55 @@ get_title() {
52
65
  esac
53
66
  }
54
67
 
55
- # One-shot toast
68
+ # Check if the queue reader popup is still alive
69
+ reader_alive() {
70
+ local pid
71
+ pid=$(cat "$READER_PID_FILE" 2>/dev/null) || return 1
72
+ [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null
73
+ }
74
+
75
+ # Queued one-shot toast — append to queue, spawn reader if needed
56
76
  toast_oneshot() {
57
77
  local system="$1"
58
78
  local message="$2"
59
- local colour icon title
60
- colour=$(get_style "$system")
79
+ local icon
61
80
  icon=$(get_icon "$system")
62
- title=$(get_title "$system")
63
81
 
64
- (tmux display-popup -x R -y 0 -w 42 -h 3 \
65
- -s "fg=$colour" -T " $icon $title " -E \
66
- "echo ' $message'; sleep 2" &>/dev/null &)
82
+ # Append to queue: icon|colour|title|message
83
+ echo "${icon}|$(get_style "$system")|$(get_title "$system")|$message" >> "$QUEUE_FILE"
84
+
85
+ # If reader is already running, it will pick up the new entry
86
+ if reader_alive; then
87
+ return
88
+ fi
89
+
90
+ # Spawn a reader popup targeting this session
91
+ (tmux display-popup -t "$SESSION" -x R -y 0 -w 42 -h 3 \
92
+ -s "fg=colour245" -E "
93
+ echo \$\$ > '$READER_PID_FILE'
94
+ while true; do
95
+ LINE=\$(head -1 '$QUEUE_FILE' 2>/dev/null)
96
+ if [ -z \"\$LINE\" ]; then
97
+ rm -f '$READER_PID_FILE'
98
+ break
99
+ fi
100
+ TAIL=\$(tail -n +2 '$QUEUE_FILE' 2>/dev/null)
101
+ if [ -n \"\$TAIL\" ]; then
102
+ echo \"\$TAIL\" > '$QUEUE_FILE'
103
+ else
104
+ > '$QUEUE_FILE'
105
+ fi
106
+ ICON=\$(echo \"\$LINE\" | cut -d'|' -f1)
107
+ MSG=\$(echo \"\$LINE\" | cut -d'|' -f4-)
108
+ clear
109
+ echo \" \$ICON \$MSG\"
110
+ sleep 2
111
+ done
112
+ rm -f '$QUEUE_FILE' '$READER_PID_FILE'
113
+ " &>/dev/null &)
67
114
  }
68
115
 
69
- # Start a multi-stage toast (polling status file)
116
+ # Start a multi-stage toast (polling status file) — targets this session
70
117
  toast_start() {
71
118
  local system="$1"
72
119
  local id="$2"
@@ -78,7 +125,7 @@ toast_start() {
78
125
 
79
126
  echo "⏳ Starting..." > "$status_file"
80
127
 
81
- (tmux display-popup -x R -y 0 -w 42 -h 3 \
128
+ (tmux display-popup -t "$SESSION" -x R -y 0 -w 42 -h 3 \
82
129
  -s "fg=$colour" -T " $icon $title " -E "
83
130
  LAST=''
84
131
  while [ -f '$status_file' ]; do
@@ -104,7 +151,6 @@ toast_update() {
104
151
  # Close a running toast (remove status file — popup exits on next poll)
105
152
  toast_close() {
106
153
  local id="$1"
107
- # Give the popup time to display the final message
108
154
  sleep 2
109
155
  rm -f "$TOAST_DIR/$id"
110
156
  }
@@ -10,9 +10,8 @@ set -o pipefail
10
10
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
11
11
  TOAST="$SCRIPT_DIR/hook-toast.sh"
12
12
 
13
- # Toast: start multi-stage
14
- "$TOAST" memento --status distill &>/dev/null
15
- "$TOAST" --update distill "⏳ Getting context..." &>/dev/null
13
+ # Toast: progress via queue (one-shot)
14
+ "$TOAST" memento "⏳ Distilling memories..." &>/dev/null
16
15
 
17
16
  INPUT=$(cat)
18
17
 
@@ -20,15 +19,13 @@ TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path' | sed "s|~|$HOME|")
20
19
 
21
20
  # Only distill if transcript exists and has content
22
21
  if [ ! -f "$TRANSCRIPT_PATH" ]; then
23
- "$TOAST" --update distill "✗ No transcript found" &>/dev/null
24
- ("$TOAST" --close distill &>/dev/null &)
22
+ "$TOAST" memento "✗ No transcript found" &>/dev/null
25
23
  exit 0
26
24
  fi
27
25
 
28
26
  LINE_COUNT=$(wc -l < "$TRANSCRIPT_PATH")
29
27
  if [ "$LINE_COUNT" -lt 2 ]; then
30
- "$TOAST" --update distill "✓ Skipped (tiny conversation)" &>/dev/null
31
- ("$TOAST" --close distill &>/dev/null &)
28
+ "$TOAST" memento "✓ Skipped (tiny conversation)" &>/dev/null
32
29
  exit 0
33
30
  fi
34
31
 
@@ -89,14 +86,10 @@ else
89
86
  fi
90
87
 
91
88
  if [ ${#TRANSCRIPT_TEXT} -lt 200 ]; then
92
- "$TOAST" --update distill "✓ Skipped (too short)" &>/dev/null
93
- ("$TOAST" --close distill &>/dev/null &)
89
+ "$TOAST" memento "✓ Skipped (too short)" &>/dev/null
94
90
  exit 0 # Too short to distill anything useful
95
91
  fi
96
92
 
97
- # Toast: extracting
98
- "$TOAST" --update distill "⏳ Extracting memories..." &>/dev/null
99
-
100
93
  DISTILL_MODEL="${DISTILL_MODEL:-llama}"
101
94
 
102
95
  # claude-code path: run extraction locally via claude -p, push to /v1/memories/ingest
@@ -181,14 +174,12 @@ print(json.dumps({'memories': parsed, 'source': 'distill:claude-code'}))
181
174
  -d "$INGEST_PAYLOAD" \
182
175
  "$MEMENTO_API/v1/memories/ingest" > /dev/null 2>&1
183
176
 
184
- "$TOAST" --update distill "✓ Stored ${MEMORY_COUNT} memories" &>/dev/null
185
- ("$TOAST" --close distill &>/dev/null &)
177
+ "$TOAST" memento "✓ Stored ${MEMORY_COUNT} memories" &>/dev/null
186
178
 
187
179
  python3 -c "import json,sys; print(json.dumps({'systemMessage': sys.argv[1]}))" \
188
180
  "Memento Distill (claude-code): ${MEMORY_COUNT} memories — ${TYPE_BREAKDOWN}"
189
181
  else
190
- "$TOAST" --update distill "✓ No memories extracted" &>/dev/null
191
- ("$TOAST" --close distill &>/dev/null &)
182
+ "$TOAST" memento "✓ No memories extracted" &>/dev/null
192
183
 
193
184
  python3 -c "import json,sys; print(json.dumps({'systemMessage': sys.argv[1]}))" \
194
185
  "Memento Distill (claude-code): no memories extracted"
@@ -227,14 +218,12 @@ MEMORY_COUNT=$(echo "$DISTILL_SUMMARY" | cut -d'|' -f1)
227
218
  TYPE_BREAKDOWN=$(echo "$DISTILL_SUMMARY" | cut -d'|' -f2)
228
219
 
229
220
  if [ "${MEMORY_COUNT:-0}" -gt 0 ] 2>/dev/null; then
230
- "$TOAST" --update distill "✓ Stored ${MEMORY_COUNT} memories" &>/dev/null
231
- ("$TOAST" --close distill &>/dev/null &)
221
+ "$TOAST" memento "✓ Stored ${MEMORY_COUNT} memories" &>/dev/null
232
222
 
233
223
  python3 -c "import json,sys; print(json.dumps({'systemMessage': sys.argv[1]}))" \
234
224
  "Memento Distill: ${MEMORY_COUNT} memories — ${TYPE_BREAKDOWN}"
235
225
  else
236
- "$TOAST" --update distill "✓ No memories extracted" &>/dev/null
237
- ("$TOAST" --close distill &>/dev/null &)
226
+ "$TOAST" memento "✓ No memories extracted" &>/dev/null
238
227
 
239
228
  python3 -c "import json,sys; print(json.dumps({'systemMessage': sys.argv[1]}))" \
240
229
  "Memento Distill: no memories extracted"