eagle-mem 4.9.6 → 4.9.7
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/README.md +31 -4
- package/architecture.html +1791 -0
- package/bin/eagle-mem +1 -0
- package/db/migrate.sh +13 -2
- package/docs/visible-surface-ux-contract.md +53 -0
- package/hooks/post-tool-use.sh +2 -2
- package/hooks/pre-tool-use.sh +6 -6
- package/hooks/session-start.sh +188 -1
- package/hooks/user-prompt-submit.sh +35 -12
- package/lib/common.sh +361 -21
- package/lib/db-mirrors.sh +3 -6
- package/lib/db-summaries.sh +10 -1
- package/lib/hooks-posttool.sh +44 -10
- package/lib/updater.sh +12 -0
- package/package.json +4 -2
- package/scripts/doctor.sh +262 -0
- package/scripts/feature.sh +37 -11
- package/scripts/health.sh +10 -7
- package/scripts/help.sh +1 -0
- package/scripts/install.sh +9 -3
- package/scripts/memories.sh +189 -55
- package/scripts/orchestrate.sh +2 -2
- package/scripts/search.sh +195 -31
- package/scripts/statusline-em.sh +129 -26
- package/scripts/uninstall.sh +67 -0
- package/scripts/update.sh +8 -1
package/scripts/search.sh
CHANGED
|
@@ -34,6 +34,7 @@ show_help() {
|
|
|
34
34
|
echo -e " ${CYAN}-n, --limit${RESET} <N> Max results (default: 10)"
|
|
35
35
|
echo -e " ${CYAN}-a, --all${RESET} Search across all projects"
|
|
36
36
|
echo -e " ${CYAN}-j, --json${RESET} Output as JSON"
|
|
37
|
+
echo -e " ${CYAN}--raw, --debug${RESET} Show raw IDs and detailed observation rows"
|
|
37
38
|
echo ""
|
|
38
39
|
exit 0
|
|
39
40
|
}
|
|
@@ -45,6 +46,7 @@ project=""
|
|
|
45
46
|
limit=10
|
|
46
47
|
session_id=""
|
|
47
48
|
json_output=false
|
|
49
|
+
raw_output=false
|
|
48
50
|
query=""
|
|
49
51
|
cross_project=false
|
|
50
52
|
|
|
@@ -61,6 +63,7 @@ while [ $# -gt 0 ]; do
|
|
|
61
63
|
--limit|-n) limit="$2"; shift 2 ;;
|
|
62
64
|
--all|-a) cross_project=true; shift ;;
|
|
63
65
|
--json|-j) json_output=true; shift ;;
|
|
66
|
+
--raw|--debug) raw_output=true; shift ;;
|
|
64
67
|
--help|-h) show_help ;;
|
|
65
68
|
-*)
|
|
66
69
|
eagle_err "Unknown option: $1"
|
|
@@ -93,23 +96,57 @@ search_keyword() {
|
|
|
93
96
|
fi
|
|
94
97
|
|
|
95
98
|
if [ "$json_output" = true ]; then
|
|
96
|
-
eagle_db_json "SELECT s.id, s.project, s.agent, s.request, s.completed, s.learned, s.created_at
|
|
99
|
+
eagle_db_json "SELECT s.id, s.project, s.agent, s.request, s.completed, s.learned, s.created_at,
|
|
100
|
+
CASE
|
|
101
|
+
WHEN julianday('now') - julianday(s.created_at) <= 14 THEN 'Fresh'
|
|
102
|
+
WHEN julianday('now') - julianday(s.created_at) <= 45 THEN 'Recent'
|
|
103
|
+
ELSE 'Older'
|
|
104
|
+
END AS freshness
|
|
97
105
|
FROM summaries s
|
|
98
106
|
JOIN summaries_fts f ON f.rowid = s.id
|
|
99
107
|
WHERE summaries_fts MATCH '$q'
|
|
108
|
+
AND COALESCE(s.request, '') NOT LIKE '%<local-command-caveat>%'
|
|
109
|
+
AND COALESCE(s.request, '') NOT LIKE '# AGENTS.md instructions%'
|
|
110
|
+
AND COALESCE(s.request, '') NOT LIKE '<environment_context>%'
|
|
111
|
+
AND COALESCE(s.request, '') NOT LIKE '<subagent_notification>%'
|
|
112
|
+
AND COALESCE(s.request, '') NOT LIKE '</subagent_notification>%'
|
|
100
113
|
$where_project
|
|
101
|
-
ORDER BY
|
|
114
|
+
ORDER BY
|
|
115
|
+
CASE
|
|
116
|
+
WHEN julianday('now') - julianday(s.created_at) <= 14 THEN 0
|
|
117
|
+
WHEN julianday('now') - julianday(s.created_at) <= 45 THEN 1
|
|
118
|
+
ELSE 2
|
|
119
|
+
END,
|
|
120
|
+
s.created_at DESC,
|
|
121
|
+
rank
|
|
102
122
|
LIMIT $limit;"
|
|
103
123
|
return
|
|
104
124
|
fi
|
|
105
125
|
|
|
106
126
|
local results
|
|
107
|
-
results=$(eagle_db "SELECT s.id, s.project, s.agent, s.request, s.completed, s.learned, s.created_at
|
|
127
|
+
results=$(eagle_db "SELECT s.id, s.project, s.agent, s.request, s.completed, s.learned, s.created_at,
|
|
128
|
+
CASE
|
|
129
|
+
WHEN julianday('now') - julianday(s.created_at) <= 14 THEN 'Fresh'
|
|
130
|
+
WHEN julianday('now') - julianday(s.created_at) <= 45 THEN 'Recent'
|
|
131
|
+
ELSE 'Older'
|
|
132
|
+
END AS freshness
|
|
108
133
|
FROM summaries s
|
|
109
134
|
JOIN summaries_fts f ON f.rowid = s.id
|
|
110
135
|
WHERE summaries_fts MATCH '$q'
|
|
136
|
+
AND COALESCE(s.request, '') NOT LIKE '%<local-command-caveat>%'
|
|
137
|
+
AND COALESCE(s.request, '') NOT LIKE '# AGENTS.md instructions%'
|
|
138
|
+
AND COALESCE(s.request, '') NOT LIKE '<environment_context>%'
|
|
139
|
+
AND COALESCE(s.request, '') NOT LIKE '<subagent_notification>%'
|
|
140
|
+
AND COALESCE(s.request, '') NOT LIKE '</subagent_notification>%'
|
|
111
141
|
$where_project
|
|
112
|
-
ORDER BY
|
|
142
|
+
ORDER BY
|
|
143
|
+
CASE
|
|
144
|
+
WHEN julianday('now') - julianday(s.created_at) <= 14 THEN 0
|
|
145
|
+
WHEN julianday('now') - julianday(s.created_at) <= 45 THEN 1
|
|
146
|
+
ELSE 2
|
|
147
|
+
END,
|
|
148
|
+
s.created_at DESC,
|
|
149
|
+
rank
|
|
113
150
|
LIMIT $limit;")
|
|
114
151
|
|
|
115
152
|
if [ -z "$results" ]; then
|
|
@@ -118,17 +155,18 @@ search_keyword() {
|
|
|
118
155
|
fi
|
|
119
156
|
|
|
120
157
|
echo ""
|
|
121
|
-
|
|
158
|
+
local idx=1
|
|
159
|
+
while IFS='|' read -r sid sproj sagent req completed learned created_at freshness; do
|
|
122
160
|
[ -z "$sid" ] && continue
|
|
123
|
-
echo -e " ${BOLD}
|
|
161
|
+
echo -e " ${BOLD}${idx}. ${req:-Memory match}${RESET}"
|
|
162
|
+
echo -e " ${DIM}source:${RESET} $(eagle_agent_label "$sagent") ${DIM}date:${RESET} $created_at ${DIM}freshness:${RESET} $freshness"
|
|
124
163
|
if [ "$cross_project" = true ]; then
|
|
125
164
|
echo -e " ${DIM}project:${RESET} $sproj"
|
|
126
165
|
fi
|
|
127
|
-
echo -e " ${DIM}source:${RESET} $(eagle_agent_label "$sagent")"
|
|
128
|
-
[ -n "$req" ] && echo -e " ${CYAN}Request:${RESET} $req"
|
|
129
166
|
[ -n "$completed" ] && echo -e " ${GREEN}Done:${RESET} $completed"
|
|
130
167
|
[ -n "$learned" ] && echo -e " ${YELLOW}Learned:${RESET} $learned"
|
|
131
168
|
echo ""
|
|
169
|
+
idx=$((idx + 1))
|
|
132
170
|
done <<< "$results"
|
|
133
171
|
}
|
|
134
172
|
|
|
@@ -163,21 +201,45 @@ search_timeline() {
|
|
|
163
201
|
echo -e " ${DIM}─────────────────────────────────────${RESET}"
|
|
164
202
|
echo ""
|
|
165
203
|
|
|
204
|
+
local idx=1
|
|
166
205
|
while IFS='|' read -r sid sagent req completed learned next_steps created_at; do
|
|
167
206
|
[ -z "$sid" ] && continue
|
|
168
|
-
|
|
207
|
+
if [ "$raw_output" = true ]; then
|
|
208
|
+
echo -e " ${BOLD}#$sid${RESET} ${DIM}$created_at${RESET}"
|
|
209
|
+
else
|
|
210
|
+
echo -e " ${BOLD}${idx}. Session summary${RESET} ${DIM}$created_at${RESET}"
|
|
211
|
+
fi
|
|
169
212
|
echo -e " ${DIM}source:${RESET} $(eagle_agent_label "$sagent")"
|
|
170
213
|
[ -n "$req" ] && echo -e " ${CYAN}Request:${RESET} $req"
|
|
171
214
|
[ -n "$completed" ] && echo -e " ${GREEN}Done:${RESET} $completed"
|
|
172
215
|
[ -n "$learned" ] && echo -e " ${YELLOW}Learned:${RESET} $learned"
|
|
173
216
|
[ -n "$next_steps" ] && echo -e " ${DIM}Next:${RESET} $next_steps"
|
|
174
217
|
echo ""
|
|
218
|
+
idx=$((idx + 1))
|
|
175
219
|
done <<< "$results"
|
|
220
|
+
if [ "$raw_output" = false ]; then
|
|
221
|
+
eagle_dim "Run with --raw to show summary IDs."
|
|
222
|
+
fi
|
|
176
223
|
}
|
|
177
224
|
|
|
178
225
|
# ─── Session details ──────────────────────────────────────
|
|
179
226
|
|
|
180
227
|
search_session() {
|
|
228
|
+
if [ -z "$session_id" ]; then
|
|
229
|
+
eagle_err "Usage: eagle-mem search --session <session-id-or-summary-id>"
|
|
230
|
+
exit 1
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
local lookup_sql resolved_session
|
|
234
|
+
lookup_sql=$(eagle_sql_escape "$session_id")
|
|
235
|
+
case "$session_id" in
|
|
236
|
+
*[!0-9]*) ;;
|
|
237
|
+
*)
|
|
238
|
+
resolved_session=$(eagle_db "SELECT session_id FROM summaries WHERE id = '$lookup_sql' LIMIT 1;" 2>/dev/null | awk 'NF { print; exit }')
|
|
239
|
+
[ -n "$resolved_session" ] && session_id="$resolved_session"
|
|
240
|
+
;;
|
|
241
|
+
esac
|
|
242
|
+
|
|
181
243
|
local sid_sql; sid_sql=$(eagle_sql_escape "$session_id")
|
|
182
244
|
|
|
183
245
|
if [ "$json_output" = true ]; then
|
|
@@ -188,33 +250,111 @@ search_session() {
|
|
|
188
250
|
return
|
|
189
251
|
fi
|
|
190
252
|
|
|
191
|
-
local
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
253
|
+
local results_json
|
|
254
|
+
results_json=$(eagle_db_json "SELECT o.agent, o.tool_name, o.tool_input_summary, o.files_read, o.files_modified, o.created_at
|
|
255
|
+
FROM observations o
|
|
256
|
+
WHERE o.session_id = '$sid_sql'
|
|
257
|
+
ORDER BY o.created_at ASC;")
|
|
196
258
|
|
|
197
|
-
if [ -z "$
|
|
259
|
+
if [ -z "$results_json" ] || [ "$(printf '%s' "$results_json" | jq 'length' 2>/dev/null)" = "0" ]; then
|
|
198
260
|
eagle_dim "No observations found for session '$session_id'"
|
|
199
261
|
return
|
|
200
262
|
fi
|
|
201
263
|
|
|
264
|
+
if [ "$raw_output" = true ]; then
|
|
265
|
+
echo ""
|
|
266
|
+
echo -e " ${BOLD}Session${RESET} ${DIM}$session_id${RESET}"
|
|
267
|
+
echo -e " ${DIM}─────────────────────────────────────${RESET}"
|
|
268
|
+
echo ""
|
|
269
|
+
|
|
270
|
+
printf '%s' "$results_json" | jq -c '.[]' | while IFS= read -r row; do
|
|
271
|
+
oagent=$(printf '%s' "$row" | jq -r '.agent // ""')
|
|
272
|
+
tool_name=$(printf '%s' "$row" | jq -r '.tool_name // ""')
|
|
273
|
+
summary=$(printf '%s' "$row" | jq -r '.tool_input_summary // ""')
|
|
274
|
+
created_at=$(printf '%s' "$row" | jq -r '.created_at // ""')
|
|
275
|
+
[ -z "$tool_name" ] && continue
|
|
276
|
+
local icon="$DOT"
|
|
277
|
+
case "$tool_name" in
|
|
278
|
+
Read) icon="${CYAN}R${RESET}" ;;
|
|
279
|
+
Write) icon="${GREEN}W${RESET}" ;;
|
|
280
|
+
Edit) icon="${YELLOW}E${RESET}" ;;
|
|
281
|
+
Bash|exec_command|shell_command|unified_exec) icon="${BLUE}\$${RESET}" ;;
|
|
282
|
+
esac
|
|
283
|
+
echo -e " ${icon} ${DIM}$created_at $(eagle_agent_label "$oagent")${RESET} $summary"
|
|
284
|
+
done
|
|
285
|
+
echo ""
|
|
286
|
+
return
|
|
287
|
+
fi
|
|
288
|
+
|
|
289
|
+
local first_seen last_seen total_obs agent_counts tool_counts
|
|
290
|
+
first_seen=$(eagle_db "SELECT MIN(created_at) FROM observations WHERE session_id = '$sid_sql';")
|
|
291
|
+
last_seen=$(eagle_db "SELECT MAX(created_at) FROM observations WHERE session_id = '$sid_sql';")
|
|
292
|
+
total_obs=$(printf '%s' "$results_json" | jq 'length' 2>/dev/null)
|
|
293
|
+
agent_counts=$(eagle_db "SELECT agent || ':' || COUNT(*) FROM observations WHERE session_id = '$sid_sql' GROUP BY agent ORDER BY COUNT(*) DESC;")
|
|
294
|
+
tool_counts=$(eagle_db "SELECT tool_name || ':' || COUNT(*) FROM observations WHERE session_id = '$sid_sql' GROUP BY tool_name ORDER BY COUNT(*) DESC LIMIT 8;")
|
|
295
|
+
|
|
202
296
|
echo ""
|
|
203
|
-
echo -e " ${BOLD}Session
|
|
297
|
+
echo -e " ${BOLD}Session Activity${RESET}"
|
|
204
298
|
echo -e " ${DIM}─────────────────────────────────────${RESET}"
|
|
205
299
|
echo ""
|
|
206
|
-
|
|
207
|
-
|
|
300
|
+
eagle_kv "Observations:" "${total_obs:-0}"
|
|
301
|
+
[ -n "$first_seen" ] && eagle_kv "Started:" "$first_seen"
|
|
302
|
+
[ -n "$last_seen" ] && eagle_kv "Last seen:" "$last_seen"
|
|
303
|
+
if [ -n "$agent_counts" ]; then
|
|
304
|
+
local agents_display=""
|
|
305
|
+
while IFS=':' read -r a count; do
|
|
306
|
+
[ -z "$a" ] && continue
|
|
307
|
+
[ -n "$agents_display" ] && agents_display+=", "
|
|
308
|
+
agents_display+="$(eagle_agent_label "$a") $count"
|
|
309
|
+
done <<< "$agent_counts"
|
|
310
|
+
[ -n "$agents_display" ] && eagle_kv "Agents:" "$agents_display"
|
|
311
|
+
fi
|
|
312
|
+
if [ -n "$tool_counts" ]; then
|
|
313
|
+
local tools_display=""
|
|
314
|
+
while IFS=':' read -r t count; do
|
|
315
|
+
[ -z "$t" ] && continue
|
|
316
|
+
[ -n "$tools_display" ] && tools_display+=", "
|
|
317
|
+
tools_display+="$t $count"
|
|
318
|
+
done <<< "$tool_counts"
|
|
319
|
+
[ -n "$tools_display" ] && eagle_kv "Tools:" "$tools_display"
|
|
320
|
+
fi
|
|
321
|
+
echo ""
|
|
322
|
+
echo -e " ${BOLD}Recent activity${RESET}"
|
|
323
|
+
|
|
324
|
+
printf '%s' "$results_json" | jq -c --argjson limit "$limit" '.[-$limit:][]' | while IFS= read -r row; do
|
|
325
|
+
oagent=$(printf '%s' "$row" | jq -r '.agent // ""')
|
|
326
|
+
tool_name=$(printf '%s' "$row" | jq -r '.tool_name // ""')
|
|
327
|
+
summary=$(printf '%s' "$row" | jq -r '.tool_input_summary // ""')
|
|
328
|
+
files_r=$(printf '%s' "$row" | jq -r '.files_read // "[]"')
|
|
329
|
+
files_m=$(printf '%s' "$row" | jq -r '.files_modified // "[]"')
|
|
330
|
+
created_at=$(printf '%s' "$row" | jq -r '.created_at // ""')
|
|
208
331
|
[ -z "$tool_name" ] && continue
|
|
209
|
-
local
|
|
332
|
+
local label detail
|
|
333
|
+
label="$tool_name"
|
|
334
|
+
detail=$(eagle_trim_text "$summary" 120)
|
|
210
335
|
case "$tool_name" in
|
|
211
|
-
Read)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
336
|
+
Read)
|
|
337
|
+
label="Read"
|
|
338
|
+
detail=$(printf '%s' "$files_r" | jq -r 'fromjson? | .[0] // empty' 2>/dev/null)
|
|
339
|
+
[ -n "$detail" ] && detail=$(basename "$detail")
|
|
340
|
+
;;
|
|
341
|
+
Write|Edit)
|
|
342
|
+
label="$tool_name"
|
|
343
|
+
detail=$(printf '%s' "$files_m" | jq -r 'fromjson? | .[0] // empty' 2>/dev/null)
|
|
344
|
+
[ -n "$detail" ] && detail=$(basename "$detail")
|
|
345
|
+
;;
|
|
346
|
+
apply_patch)
|
|
347
|
+
label="Patch"
|
|
348
|
+
;;
|
|
349
|
+
Bash|exec_command|shell_command|unified_exec)
|
|
350
|
+
label="Command"
|
|
351
|
+
;;
|
|
215
352
|
esac
|
|
216
|
-
|
|
217
|
-
|
|
353
|
+
[ -z "$detail" ] && detail="$(eagle_agent_label "$oagent") activity"
|
|
354
|
+
echo -e " ${DOT} ${DIM}$created_at${RESET} ${BOLD}$label:${RESET} $detail"
|
|
355
|
+
done
|
|
356
|
+
echo ""
|
|
357
|
+
eagle_dim "Run with --raw for full observation rows and session ID."
|
|
218
358
|
echo ""
|
|
219
359
|
}
|
|
220
360
|
|
|
@@ -355,21 +495,45 @@ search_memories() {
|
|
|
355
495
|
fi
|
|
356
496
|
|
|
357
497
|
if [ "$json_output" = true ]; then
|
|
358
|
-
eagle_db_json "SELECT m.memory_name, m.memory_type, m.description, m.project, m.updated_at, m.origin_agent
|
|
498
|
+
eagle_db_json "SELECT m.memory_name, m.memory_type, m.description, m.project, m.updated_at, m.origin_agent,
|
|
499
|
+
CASE
|
|
500
|
+
WHEN julianday('now') - julianday(m.updated_at) <= 14 THEN 'Fresh'
|
|
501
|
+
WHEN julianday('now') - julianday(m.updated_at) <= 45 THEN 'Recent'
|
|
502
|
+
ELSE 'Older'
|
|
503
|
+
END AS freshness
|
|
359
504
|
FROM agent_memories m
|
|
360
505
|
JOIN agent_memories_fts f ON f.rowid = m.id
|
|
361
506
|
$where_match
|
|
362
|
-
ORDER BY
|
|
507
|
+
ORDER BY
|
|
508
|
+
CASE
|
|
509
|
+
WHEN julianday('now') - julianday(m.updated_at) <= 14 THEN 0
|
|
510
|
+
WHEN julianday('now') - julianday(m.updated_at) <= 45 THEN 1
|
|
511
|
+
ELSE 2
|
|
512
|
+
END,
|
|
513
|
+
m.updated_at DESC,
|
|
514
|
+
rank
|
|
363
515
|
LIMIT $limit;"
|
|
364
516
|
return
|
|
365
517
|
fi
|
|
366
518
|
|
|
367
519
|
local results
|
|
368
|
-
results=$(eagle_db "SELECT m.memory_name, m.memory_type, m.description, m.project, m.updated_at, m.origin_agent
|
|
520
|
+
results=$(eagle_db "SELECT m.memory_name, m.memory_type, m.description, m.project, m.updated_at, m.origin_agent,
|
|
521
|
+
CASE
|
|
522
|
+
WHEN julianday('now') - julianday(m.updated_at) <= 14 THEN 'Fresh'
|
|
523
|
+
WHEN julianday('now') - julianday(m.updated_at) <= 45 THEN 'Recent'
|
|
524
|
+
ELSE 'Older'
|
|
525
|
+
END AS freshness
|
|
369
526
|
FROM agent_memories m
|
|
370
527
|
JOIN agent_memories_fts f ON f.rowid = m.id
|
|
371
528
|
$where_match
|
|
372
|
-
ORDER BY
|
|
529
|
+
ORDER BY
|
|
530
|
+
CASE
|
|
531
|
+
WHEN julianday('now') - julianday(m.updated_at) <= 14 THEN 0
|
|
532
|
+
WHEN julianday('now') - julianday(m.updated_at) <= 45 THEN 1
|
|
533
|
+
ELSE 2
|
|
534
|
+
END,
|
|
535
|
+
m.updated_at DESC,
|
|
536
|
+
rank
|
|
373
537
|
LIMIT $limit;")
|
|
374
538
|
|
|
375
539
|
if [ -z "$results" ]; then
|
|
@@ -378,9 +542,9 @@ search_memories() {
|
|
|
378
542
|
fi
|
|
379
543
|
|
|
380
544
|
echo ""
|
|
381
|
-
while IFS='|' read -r mname mtype mdesc mproj mupdated morigin; do
|
|
545
|
+
while IFS='|' read -r mname mtype mdesc mproj mupdated morigin freshness; do
|
|
382
546
|
[ -z "$mname" ] && continue
|
|
383
|
-
echo -e " ${BOLD}$mname${RESET} ${DIM}[$mtype][$(eagle_agent_label "$morigin")]${RESET}"
|
|
547
|
+
echo -e " ${BOLD}$mname${RESET} ${DIM}[$mtype][$(eagle_agent_label "$morigin")][$freshness]${RESET}"
|
|
384
548
|
[ "$cross_project" = true ] && echo -e " ${DIM}project:${RESET} $mproj"
|
|
385
549
|
[ -n "$mdesc" ] && echo -e " $mdesc"
|
|
386
550
|
echo ""
|
package/scripts/statusline-em.sh
CHANGED
|
@@ -1,35 +1,79 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Eagle Mem statusline
|
|
3
|
-
#
|
|
4
|
-
#
|
|
2
|
+
# Eagle Mem statusline renderers.
|
|
3
|
+
# Usage:
|
|
4
|
+
# source this file and call eagle_mem_statusline "$project_dir" "$session_id" "$input"
|
|
5
|
+
# printf '%s' "$input" | bash ~/.eagle-mem/scripts/statusline-em.sh --hud
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
_eagle_statusline_load_common() {
|
|
8
|
+
local script_dir
|
|
9
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
. "$script_dir/../lib/common.sh"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
_eagle_statusline_relative_time() {
|
|
14
|
+
local raw="${1:-}"
|
|
15
|
+
[ -n "$raw" ] && [ "$raw" != "never" ] || { printf 'never\n'; return; }
|
|
16
|
+
|
|
17
|
+
local last_epoch now_epoch diff_sec
|
|
18
|
+
last_epoch=$(date -j -u -f "%Y-%m-%dT%H:%M:%S" "${raw%%.*}" "+%s" 2>/dev/null \
|
|
19
|
+
|| date -u -d "$raw" "+%s" 2>/dev/null \
|
|
20
|
+
|| true)
|
|
21
|
+
now_epoch=$(date "+%s" 2>/dev/null || true)
|
|
22
|
+
if [ -z "$last_epoch" ] || [ -z "$now_epoch" ]; then
|
|
23
|
+
printf '%s\n' "$raw"
|
|
24
|
+
return
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
diff_sec=$((now_epoch - last_epoch))
|
|
28
|
+
if [ "$diff_sec" -lt 60 ]; then
|
|
29
|
+
printf 'just now\n'
|
|
30
|
+
elif [ "$diff_sec" -lt 3600 ]; then
|
|
31
|
+
printf '%sm ago\n' "$((diff_sec / 60))"
|
|
32
|
+
elif [ "$diff_sec" -lt 86400 ]; then
|
|
33
|
+
printf '%sh ago\n' "$((diff_sec / 3600))"
|
|
34
|
+
else
|
|
35
|
+
printf '%sd ago\n' "$((diff_sec / 86400))"
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
eagle_mem_statusline_stats() {
|
|
7
40
|
local project_dir="${1:-}"
|
|
8
41
|
local session_id="${2:-}"
|
|
9
42
|
local statusline_input="${3:-}"
|
|
43
|
+
local current_dir="${4:-}"
|
|
10
44
|
local em_db="$HOME/.eagle-mem/memory.db"
|
|
11
45
|
[ -f "$em_db" ] || return
|
|
12
46
|
|
|
13
|
-
|
|
14
|
-
|
|
47
|
+
_eagle_statusline_load_common
|
|
48
|
+
|
|
15
49
|
local sqlite_bin
|
|
16
50
|
sqlite_bin=$(eagle_sqlite_path)
|
|
17
51
|
[ -n "$sqlite_bin" ] || return
|
|
18
52
|
|
|
19
|
-
|
|
53
|
+
if [ -n "$statusline_input" ]; then
|
|
54
|
+
[ -z "$session_id" ] && session_id=$(printf '%s' "$statusline_input" | jq -r '.session_id // .session.id // empty' 2>/dev/null)
|
|
55
|
+
[ -z "$project_dir" ] && project_dir=$(printf '%s' "$statusline_input" | jq -r '.workspace.project_dir // .workspace.current_dir // .cwd // empty' 2>/dev/null)
|
|
56
|
+
[ -z "$current_dir" ] && current_dir=$(printf '%s' "$statusline_input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null)
|
|
57
|
+
fi
|
|
20
58
|
[ -z "$project_dir" ] && project_dir="$(pwd)"
|
|
21
|
-
|
|
22
|
-
[ -z "$proj" ] && return
|
|
59
|
+
[ -z "$current_dir" ] && current_dir="$project_dir"
|
|
23
60
|
|
|
24
|
-
|
|
61
|
+
local project_key project_sql stats sessions memories last_raw turns version latest
|
|
62
|
+
project_key=$(eagle_project_from_statusline_input "$statusline_input" "$project_dir" "$current_dir" "$session_id")
|
|
63
|
+
[ -n "$project_key" ] || return
|
|
64
|
+
project_sql=$(eagle_sql_escape "$project_key")
|
|
65
|
+
|
|
66
|
+
stats=$("$sqlite_bin" "$em_db" "SELECT
|
|
67
|
+
COUNT(*) || '|' ||
|
|
68
|
+
(SELECT COUNT(*) FROM agent_memories WHERE project = '$project_sql') || '|' ||
|
|
69
|
+
COALESCE(MAX(COALESCE(last_activity_at, started_at)), 'never')
|
|
70
|
+
FROM sessions
|
|
71
|
+
WHERE project = '$project_sql';" 2>/dev/null)
|
|
72
|
+
IFS='|' read -r sessions memories last_raw <<< "${stats:-0|0|never}"
|
|
73
|
+
sessions=${sessions:-0}
|
|
74
|
+
memories=${memories:-0}
|
|
75
|
+
last_raw=${last_raw:-never}
|
|
25
76
|
|
|
26
|
-
local version cnt mem turns
|
|
27
|
-
version=$(tr -d '[:space:]' < "$HOME/.eagle-mem/.version" 2>/dev/null)
|
|
28
|
-
[ -z "$version" ] && version="?"
|
|
29
|
-
cnt=$(echo ".headers off
|
|
30
|
-
SELECT COUNT(*) FROM sessions WHERE project = '${proj}';" | "$sqlite_bin" "$em_db" 2>/dev/null | tr -d '[:space:]')
|
|
31
|
-
mem=$(echo ".headers off
|
|
32
|
-
SELECT COUNT(*) FROM agent_memories WHERE project = '${proj}';" | "$sqlite_bin" "$em_db" 2>/dev/null | tr -d '[:space:]')
|
|
33
77
|
if [ -n "$session_id" ] && [ -f "$HOME/.eagle-mem/.turn-counter.${session_id}" ]; then
|
|
34
78
|
turns=$(tr -d '[:space:]' < "$HOME/.eagle-mem/.turn-counter.${session_id}" 2>/dev/null)
|
|
35
79
|
else
|
|
@@ -40,26 +84,85 @@ SELECT COUNT(*) FROM agent_memories WHERE project = '${proj}';" | "$sqlite_bin"
|
|
|
40
84
|
done \
|
|
41
85
|
| awk '($1+0)>max{max=$1+0} END{print max+0}')
|
|
42
86
|
fi
|
|
43
|
-
cnt=${cnt:-0}; mem=${mem:-0}
|
|
44
87
|
turns=${turns:-0}
|
|
45
88
|
|
|
89
|
+
version=$(tr -d '[:space:]' < "$HOME/.eagle-mem/.version" 2>/dev/null)
|
|
90
|
+
latest=$(tr -d '[:space:]' < "$HOME/.eagle-mem/.latest-version" 2>/dev/null)
|
|
91
|
+
[ -z "$version" ] && version="?"
|
|
92
|
+
[ -z "$latest" ] && latest="$version"
|
|
93
|
+
|
|
94
|
+
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "$project_key" "$version" "$latest" "$sessions" "$memories" "$turns" "$last_raw"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
eagle_mem_statusline() {
|
|
98
|
+
local stats project_key version latest sessions memories turns last_raw
|
|
99
|
+
stats=$(eagle_mem_statusline_stats "${1:-}" "${2:-}" "${3:-}" "${4:-}") || return
|
|
100
|
+
IFS=$'\t' read -r project_key version latest sessions memories turns last_raw <<< "$stats"
|
|
101
|
+
|
|
46
102
|
local R='\033[0m' CYAN='\033[96m' WHT='\033[97m' DIM='\033[2m'
|
|
47
|
-
printf "%
|
|
103
|
+
printf "%bEagle%b %bv%s%b | %b%s%b sessions | %b%s%b memories | turn %b%s%b" \
|
|
48
104
|
"$CYAN" "$R" \
|
|
49
105
|
"$WHT" "$version" "$DIM" \
|
|
50
|
-
"$WHT" "$
|
|
51
|
-
"$WHT" "$
|
|
52
|
-
"$WHT" "$turns" "$R"
|
|
106
|
+
"$WHT" "${sessions:-0}" "$DIM" \
|
|
107
|
+
"$WHT" "${memories:-0}" "$DIM" \
|
|
108
|
+
"$WHT" "${turns:-0}" "$R"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
eagle_mem_statusline_hud() {
|
|
112
|
+
local stats project_key version latest sessions memories turns last_raw last_label
|
|
113
|
+
stats=$(eagle_mem_statusline_stats "${1:-}" "${2:-}" "${3:-}" "${4:-}") || return
|
|
114
|
+
IFS=$'\t' read -r project_key version latest sessions memories turns last_raw <<< "$stats"
|
|
115
|
+
last_label=$(_eagle_statusline_relative_time "$last_raw")
|
|
116
|
+
|
|
117
|
+
local R='\033[0m' CYAN='\033[96m' WHT='\033[97m' DIM='\033[2m'
|
|
118
|
+
local GRN='\033[92m' ORG='\033[38;5;214m' RED='\033[91m'
|
|
119
|
+
local turn_color pressure_label em_ver newest
|
|
120
|
+
|
|
121
|
+
turns=${turns:-0}
|
|
122
|
+
if [ "$turns" -ge 30 ] 2>/dev/null; then
|
|
123
|
+
turn_color="$RED"; pressure_label="CRITICAL"
|
|
124
|
+
elif [ "$turns" -ge 20 ] 2>/dev/null; then
|
|
125
|
+
turn_color="$ORG"; pressure_label="HIGH"
|
|
126
|
+
else
|
|
127
|
+
turn_color="$GRN"; pressure_label="OK"
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
em_ver="v${version:-?}"
|
|
131
|
+
newest=$(printf '%s\n' "${version:-}" "${latest:-}" | sort -V 2>/dev/null | tail -1 || true)
|
|
132
|
+
if [ -n "$latest" ] && [ -n "$newest" ] && [ "$newest" != "$version" ]; then
|
|
133
|
+
em_ver="${em_ver} ${ORG}↑${latest}${R}"
|
|
134
|
+
elif [ -n "$latest" ] && [ "$latest" = "$version" ]; then
|
|
135
|
+
em_ver="${em_ver} ${GRN}✓${R}"
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
printf "%bEagle Mem%b %b %b│%b %bSessions:%b %b%s%b %b│%b %bMemories:%b %b%s%b %b│%b %bTurns:%b %b%s/30%b %b(%s)%b %b│%b %bUpdated:%b %b%s%b" \
|
|
139
|
+
"$CYAN" "$R" "$em_ver" \
|
|
140
|
+
"$DIM" "$R" "$DIM" "$R" "$WHT" "${sessions:-0}" "$R" "$DIM" "$R" \
|
|
141
|
+
"$DIM" "$R" "$WHT" "${memories:-0}" "$R" "$DIM" "$R" \
|
|
142
|
+
"$DIM" "$R" "$turn_color" "$turns" "$R" "$turn_color" "$pressure_label" "$R" "$DIM" "$R" \
|
|
143
|
+
"$DIM" "$R" "$WHT" "$last_label" "$R"
|
|
53
144
|
}
|
|
54
145
|
|
|
55
|
-
# When run directly, read project_dir from stdin JSON (statusline format)
|
|
56
146
|
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
|
147
|
+
mode="${1:-compact}"
|
|
57
148
|
if [ -t 0 ]; then
|
|
58
149
|
input=""
|
|
59
150
|
else
|
|
60
151
|
input=$(cat)
|
|
61
152
|
fi
|
|
62
|
-
project_dir=$(
|
|
63
|
-
|
|
64
|
-
|
|
153
|
+
project_dir=$(printf '%s' "$input" | jq -r '.workspace.project_dir // .workspace.current_dir // .cwd // empty' 2>/dev/null)
|
|
154
|
+
current_dir=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null)
|
|
155
|
+
session_id=$(printf '%s' "$input" | jq -r '.session_id // .session.id // empty' 2>/dev/null)
|
|
156
|
+
|
|
157
|
+
case "$mode" in
|
|
158
|
+
--hud|hud)
|
|
159
|
+
eagle_mem_statusline_hud "${project_dir:-$(pwd)}" "$session_id" "$input" "$current_dir"
|
|
160
|
+
;;
|
|
161
|
+
--stats|stats)
|
|
162
|
+
eagle_mem_statusline_stats "${project_dir:-$(pwd)}" "$session_id" "$input" "$current_dir"
|
|
163
|
+
;;
|
|
164
|
+
*)
|
|
165
|
+
eagle_mem_statusline "${project_dir:-$(pwd)}" "$session_id" "$input" "$current_dir"
|
|
166
|
+
;;
|
|
167
|
+
esac
|
|
65
168
|
fi
|
package/scripts/uninstall.sh
CHANGED
|
@@ -13,12 +13,32 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
|
13
13
|
. "$LIB_DIR/codex-hooks.sh"
|
|
14
14
|
|
|
15
15
|
SETTINGS="$EAGLE_SETTINGS"
|
|
16
|
+
dry_run=false
|
|
17
|
+
|
|
18
|
+
for arg in "$@"; do
|
|
19
|
+
case "$arg" in
|
|
20
|
+
--dry-run|--check)
|
|
21
|
+
dry_run=true
|
|
22
|
+
;;
|
|
23
|
+
--help|-h)
|
|
24
|
+
echo "Usage: eagle-mem uninstall [--dry-run]"
|
|
25
|
+
exit 0
|
|
26
|
+
;;
|
|
27
|
+
esac
|
|
28
|
+
done
|
|
16
29
|
|
|
17
30
|
eagle_header "Uninstall"
|
|
31
|
+
eagle_uninstall_change_plan
|
|
32
|
+
|
|
33
|
+
if [ "$dry_run" = true ]; then
|
|
34
|
+
eagle_footer "Dry run complete. No files changed."
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
18
37
|
|
|
19
38
|
# ─── Remove hooks from settings.json ──────────────────────
|
|
20
39
|
|
|
21
40
|
if [ -f "$SETTINGS" ] && command -v jq &>/dev/null; then
|
|
41
|
+
settings_backup=$(eagle_backup_user_file "$SETTINGS" 2>/dev/null || true)
|
|
22
42
|
for event in SessionStart Stop PostToolUse PreToolUse SessionEnd UserPromptSubmit; do
|
|
23
43
|
if jq -e ".hooks.${event}" "$SETTINGS" &>/dev/null; then
|
|
24
44
|
tmp=$(mktemp)
|
|
@@ -30,16 +50,55 @@ if [ -f "$SETTINGS" ] && command -v jq &>/dev/null; then
|
|
|
30
50
|
tmp=$(mktemp)
|
|
31
51
|
jq 'if .hooks == {} then del(.hooks) else . end' "$SETTINGS" > "$tmp" && mv "$tmp" "$SETTINGS"
|
|
32
52
|
eagle_ok "Hooks removed from settings.json"
|
|
53
|
+
[ -n "${settings_backup:-}" ] && eagle_dim " Backup: $settings_backup"
|
|
33
54
|
else
|
|
34
55
|
eagle_warn "Could not patch settings.json (jq not found or file missing)"
|
|
35
56
|
fi
|
|
36
57
|
|
|
58
|
+
codex_hooks_backup=""
|
|
59
|
+
if [ -f "$EAGLE_CODEX_HOOKS" ]; then
|
|
60
|
+
codex_hooks_backup=$(eagle_backup_user_file "$EAGLE_CODEX_HOOKS" 2>/dev/null || true)
|
|
61
|
+
fi
|
|
37
62
|
if eagle_remove_codex_hooks; then
|
|
38
63
|
eagle_ok "Hooks removed from Codex hooks.json"
|
|
64
|
+
[ -n "$codex_hooks_backup" ] && eagle_dim " Backup: $codex_hooks_backup"
|
|
39
65
|
else
|
|
40
66
|
eagle_warn "Could not patch Codex hooks.json (jq not found or file missing)"
|
|
41
67
|
fi
|
|
42
68
|
|
|
69
|
+
# ─── Remove instruction blocks and statusline integration ───
|
|
70
|
+
|
|
71
|
+
claude_md="$HOME/.claude/CLAUDE.md"
|
|
72
|
+
if [ -f "$claude_md" ] && grep -qF "## Eagle Mem — Persistent Memory" "$claude_md" 2>/dev/null; then
|
|
73
|
+
claude_md_backup=$(eagle_backup_user_file "$claude_md" 2>/dev/null || true)
|
|
74
|
+
if eagle_remove_marked_markdown_section "$claude_md"; then
|
|
75
|
+
eagle_ok "Eagle Mem block removed from Claude CLAUDE.md"
|
|
76
|
+
[ -n "${claude_md_backup:-}" ] && eagle_dim " Backup: $claude_md_backup"
|
|
77
|
+
else
|
|
78
|
+
eagle_warn "Could not remove Eagle Mem block from Claude CLAUDE.md"
|
|
79
|
+
fi
|
|
80
|
+
else
|
|
81
|
+
eagle_info "Claude CLAUDE.md block not present"
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
if [ -f "$EAGLE_CODEX_AGENTS_MD" ] && grep -qF "## Eagle Mem — Persistent Memory" "$EAGLE_CODEX_AGENTS_MD" 2>/dev/null; then
|
|
85
|
+
codex_agents_backup=$(eagle_backup_user_file "$EAGLE_CODEX_AGENTS_MD" 2>/dev/null || true)
|
|
86
|
+
if eagle_remove_marked_markdown_section "$EAGLE_CODEX_AGENTS_MD"; then
|
|
87
|
+
eagle_ok "Eagle Mem block removed from Codex AGENTS.md"
|
|
88
|
+
[ -n "${codex_agents_backup:-}" ] && eagle_dim " Backup: $codex_agents_backup"
|
|
89
|
+
else
|
|
90
|
+
eagle_warn "Could not remove Eagle Mem block from Codex AGENTS.md"
|
|
91
|
+
fi
|
|
92
|
+
else
|
|
93
|
+
eagle_info "Codex AGENTS.md block not present"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
if eagle_remove_statusline_integration "$SETTINGS"; then
|
|
97
|
+
eagle_ok "Eagle Mem statusline integration removed"
|
|
98
|
+
else
|
|
99
|
+
eagle_info "Statusline integration not present or not auto-removable"
|
|
100
|
+
fi
|
|
101
|
+
|
|
43
102
|
# ─── Remove skill symlinks ────────────────────────────────
|
|
44
103
|
|
|
45
104
|
if [ -d "$EAGLE_SKILLS_DIR" ]; then
|
|
@@ -50,6 +109,14 @@ if [ -d "$EAGLE_SKILLS_DIR" ]; then
|
|
|
50
109
|
done
|
|
51
110
|
fi
|
|
52
111
|
|
|
112
|
+
if [ -d "$EAGLE_CODEX_SKILLS_DIR" ]; then
|
|
113
|
+
for target in "$EAGLE_CODEX_SKILLS_DIR"/eagle-mem-*; do
|
|
114
|
+
[ -L "$target" ] || [ -d "$target" ] || continue
|
|
115
|
+
rm -rf "$target"
|
|
116
|
+
eagle_ok "Codex skill removed: $(basename "$target")"
|
|
117
|
+
done
|
|
118
|
+
fi
|
|
119
|
+
|
|
53
120
|
# ─── Optionally wipe data ─────────────────────────────────
|
|
54
121
|
|
|
55
122
|
if [ -d "$EAGLE_MEM_DIR" ]; then
|