eagle-mem 4.6.2 → 4.7.1

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.
Files changed (40) hide show
  1. package/README.md +49 -15
  2. package/db/023_guardrails.sql +3 -2
  3. package/db/024_guardrails_unique.sql +46 -0
  4. package/db/025_pending_feature_verifications.sql +30 -0
  5. package/db/026_agent_source.sql +18 -0
  6. package/db/027_feature_verification_fingerprints.sql +9 -0
  7. package/db/028_agent_artifact_tables.sql +124 -0
  8. package/hooks/post-tool-use.sh +42 -13
  9. package/hooks/pre-tool-use.sh +107 -14
  10. package/hooks/session-end.sh +3 -1
  11. package/hooks/session-start.sh +64 -15
  12. package/hooks/stop.sh +115 -21
  13. package/hooks/user-prompt-submit.sh +14 -5
  14. package/lib/codex-hooks.sh +194 -0
  15. package/lib/common.sh +345 -0
  16. package/lib/db-backfill.sh +3 -3
  17. package/lib/db-features.sh +222 -0
  18. package/lib/db-guardrails.sh +2 -1
  19. package/lib/db-mirrors.sh +79 -43
  20. package/lib/db-observations.sh +3 -2
  21. package/lib/db-sessions.sh +11 -7
  22. package/lib/db-summaries.sh +9 -6
  23. package/lib/hooks-posttool.sh +8 -6
  24. package/lib/provider.sh +190 -4
  25. package/package.json +7 -3
  26. package/scripts/config.sh +2 -0
  27. package/scripts/feature.sh +70 -2
  28. package/scripts/guard.sh +4 -1
  29. package/scripts/health.sh +5 -1
  30. package/scripts/help.sh +13 -8
  31. package/scripts/install.sh +130 -76
  32. package/scripts/memories.sh +71 -45
  33. package/scripts/refresh.sh +3 -3
  34. package/scripts/search.sh +57 -47
  35. package/scripts/statusline-em.sh +1 -1
  36. package/scripts/tasks.sh +186 -15
  37. package/scripts/uninstall.sh +7 -0
  38. package/scripts/update.sh +51 -7
  39. package/skills/eagle-mem-memories/SKILL.md +13 -13
  40. package/skills/eagle-mem-tasks/SKILL.md +21 -15
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  # ═══════════════════════════════════════════════════════════
3
- # Eagle Mem — Claude Code Memory Mirror CLI
4
- # List, show, search, and sync Claude Code auto-memories
3
+ # Eagle Mem — Agent Memory Mirror CLI
4
+ # List, show, search, and sync mirrored memories/plans/tasks
5
5
  # ═══════════════════════════════════════════════════════════
6
6
  set -euo pipefail
7
7
 
@@ -28,7 +28,7 @@ limit=20
28
28
  query=""
29
29
 
30
30
  show_help() {
31
- echo -e " ${BOLD}eagle-mem memories${RESET} — Claude Code memory, plan & task mirror"
31
+ echo -e " ${BOLD}eagle-mem memories${RESET} — Agent memory, plan & task mirror"
32
32
  echo ""
33
33
  echo -e " ${BOLD}Usage:${RESET}"
34
34
  echo -e " eagle-mem memories ${DIM}# list all mirrored memories${RESET}"
@@ -48,7 +48,7 @@ show_help() {
48
48
  echo -e " ${CYAN}-l, --limit${RESET} <N> Max results (default: 20)"
49
49
  echo ""
50
50
  echo -e " ${BOLD}How it works:${RESET}"
51
- echo -e " ${DOT} Eagle Mem intercepts Claude Code's auto-memory, plan, and task writes"
51
+ echo -e " ${DOT} Eagle Mem mirrors agent memory, plan, and task writes"
52
52
  echo -e " ${DOT} All are mirrored into Eagle Mem's SQLite + FTS5"
53
53
  echo -e " ${DOT} Use ${CYAN}sync${RESET} to backfill items written before mirroring was enabled"
54
54
  echo ""
@@ -92,19 +92,19 @@ memories_list() {
92
92
  eagle_header "Memories"
93
93
 
94
94
  local result
95
- result=$(eagle_list_claude_memories "$project" "$limit")
95
+ result=$(eagle_list_agent_memories "$project" "$limit")
96
96
 
97
97
  if [ -z "$result" ]; then
98
98
  eagle_dim "No mirrored memories found."
99
99
  echo ""
100
- eagle_dim "Memories are captured automatically when Claude Code writes to its auto-memory."
100
+ eagle_dim "Memories are captured automatically when supported agents write memory files."
101
101
  eagle_dim "Run 'eagle-mem memories sync' to backfill existing memories."
102
102
  echo ""
103
103
  return
104
104
  fi
105
105
 
106
106
  local count=0
107
- while IFS='|' read -r name mtype desc _fp updated; do
107
+ while IFS='|' read -r name mtype desc _fp updated origin_agent; do
108
108
  [ -z "$name" ] && continue
109
109
  count=$((count + 1))
110
110
 
@@ -116,7 +116,7 @@ memories_list() {
116
116
  reference) type_color="$BLUE" ;;
117
117
  esac
118
118
 
119
- echo -e " ${BOLD}${name}${RESET} ${type_color}[${mtype}]${RESET}"
119
+ echo -e " ${BOLD}${name}${RESET} ${type_color}[${mtype}]${RESET} ${DIM}[$(eagle_agent_label "$origin_agent")]${RESET}"
120
120
  [ -n "$desc" ] && echo -e " ${DIM}${desc}${RESET}"
121
121
  echo -e " ${DIM}updated: ${updated}${RESET}"
122
122
  echo ""
@@ -137,7 +137,7 @@ memories_search() {
137
137
  echo ""
138
138
 
139
139
  local result
140
- result=$(eagle_search_claude_memories "$query" "$project" "$limit")
140
+ result=$(eagle_search_agent_memories "$query" "$project" "$limit")
141
141
 
142
142
  if [ -z "$result" ]; then
143
143
  eagle_dim "No memories matching '$query'"
@@ -146,7 +146,7 @@ memories_search() {
146
146
  fi
147
147
 
148
148
  local count=0
149
- while IFS='|' read -r name mtype desc content _fp updated; do
149
+ while IFS='|' read -r name mtype desc content _fp updated origin_agent; do
150
150
  [ -z "$name" ] && continue
151
151
  count=$((count + 1))
152
152
 
@@ -158,7 +158,7 @@ memories_search() {
158
158
  reference) type_color="$BLUE" ;;
159
159
  esac
160
160
 
161
- echo -e " ${BOLD}${name}${RESET} ${type_color}[${mtype}]${RESET}"
161
+ echo -e " ${BOLD}${name}${RESET} ${type_color}[${mtype}]${RESET} ${DIM}[$(eagle_agent_label "$origin_agent")]${RESET}"
162
162
  [ -n "$desc" ] && echo -e " ${DIM}${desc}${RESET}"
163
163
  local snippet
164
164
  snippet=$(printf '%s' "$content" | head -c 200)
@@ -178,15 +178,15 @@ memories_show() {
178
178
  fi
179
179
 
180
180
  local meta
181
- meta=$(eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_session_id
182
- FROM claude_memories WHERE file_path = '$(eagle_sql_escape "$query")';")
181
+ meta=$(eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_session_id, origin_agent
182
+ FROM agent_memories WHERE file_path = '$(eagle_sql_escape "$query")';")
183
183
 
184
184
  if [ -z "$meta" ]; then
185
185
  eagle_err "Memory not found: $query"
186
186
  exit 1
187
187
  fi
188
188
 
189
- IFS='|' read -r name mtype desc fp updated origin <<< "$meta"
189
+ IFS='|' read -r name mtype desc fp updated origin origin_agent <<< "$meta"
190
190
 
191
191
  eagle_header "Memory Detail"
192
192
 
@@ -194,6 +194,7 @@ memories_show() {
194
194
  eagle_kv "Type:" "$mtype"
195
195
  eagle_kv "File:" "$fp"
196
196
  eagle_kv "Updated:" "$updated"
197
+ eagle_kv "Source:" "$(eagle_agent_label "$origin_agent")"
197
198
  [ -n "$origin" ] && eagle_kv "Session:" "$origin"
198
199
  echo ""
199
200
 
@@ -215,26 +216,26 @@ plans_list() {
215
216
  eagle_header "Plans"
216
217
 
217
218
  local result
218
- result=$(eagle_list_claude_plans "$project" "$limit")
219
+ result=$(eagle_list_agent_plans "$project" "$limit")
219
220
 
220
221
  if [ -z "$result" ]; then
221
222
  eagle_dim "No captured plans found."
222
223
  echo ""
223
- eagle_dim "Plans are captured when Claude Code writes to ~/.claude/plans/"
224
+ eagle_dim "Plans are captured when supported agents write plan files."
224
225
  eagle_dim "Run 'eagle-mem memories sync' to backfill existing plans."
225
226
  echo ""
226
227
  return
227
228
  fi
228
229
 
229
230
  local count=0
230
- while IFS='|' read -r title proj _fp updated; do
231
+ while IFS='|' read -r title proj _fp updated origin_agent; do
231
232
  [ -z "$title" ] && continue
232
233
  count=$((count + 1))
233
234
 
234
235
  local proj_label=""
235
236
  [ -n "$proj" ] && proj_label=" ${DIM}[${proj}]${RESET}"
236
237
 
237
- echo -e " ${BOLD}${title}${RESET}${proj_label}"
238
+ echo -e " ${BOLD}${title}${RESET}${proj_label} ${DIM}[$(eagle_agent_label "$origin_agent")]${RESET}"
238
239
  echo -e " ${DIM}updated: ${updated}${RESET}"
239
240
  echo ""
240
241
  done <<< "$result"
@@ -254,7 +255,7 @@ plans_search() {
254
255
  echo ""
255
256
 
256
257
  local result
257
- result=$(eagle_search_claude_plans "$query" "$project" "$limit")
258
+ result=$(eagle_search_agent_plans "$query" "$project" "$limit")
258
259
 
259
260
  if [ -z "$result" ]; then
260
261
  eagle_dim "No plans matching '$query'"
@@ -263,14 +264,14 @@ plans_search() {
263
264
  fi
264
265
 
265
266
  local count=0
266
- while IFS='|' read -r title proj snippet _fp updated; do
267
+ while IFS='|' read -r title proj snippet _fp updated origin_agent; do
267
268
  [ -z "$title" ] && continue
268
269
  count=$((count + 1))
269
270
 
270
271
  local proj_label=""
271
272
  [ -n "$proj" ] && proj_label=" ${DIM}[${proj}]${RESET}"
272
273
 
273
- echo -e " ${BOLD}${title}${RESET}${proj_label}"
274
+ echo -e " ${BOLD}${title}${RESET}${proj_label} ${DIM}[$(eagle_agent_label "$origin_agent")]${RESET}"
274
275
  [ -n "$snippet" ] && echo -e " ${snippet}"
275
276
  echo -e " ${DIM}updated: ${updated}${RESET}"
276
277
  echo ""
@@ -287,15 +288,15 @@ plans_show() {
287
288
  fi
288
289
 
289
290
  local meta
290
- meta=$(eagle_db "SELECT title, project, file_path, updated_at, origin_session_id
291
- FROM claude_plans WHERE file_path = '$(eagle_sql_escape "$query")';")
291
+ meta=$(eagle_db "SELECT title, project, file_path, updated_at, origin_session_id, origin_agent
292
+ FROM agent_plans WHERE file_path = '$(eagle_sql_escape "$query")';")
292
293
 
293
294
  if [ -z "$meta" ]; then
294
295
  eagle_err "Plan not found: $query"
295
296
  exit 1
296
297
  fi
297
298
 
298
- IFS='|' read -r title proj fp updated origin <<< "$meta"
299
+ IFS='|' read -r title proj fp updated origin origin_agent <<< "$meta"
299
300
 
300
301
  eagle_header "Plan Detail"
301
302
 
@@ -303,6 +304,7 @@ plans_show() {
303
304
  [ -n "$proj" ] && eagle_kv "Project:" "$proj"
304
305
  eagle_kv "File:" "$fp"
305
306
  eagle_kv "Updated:" "$updated"
307
+ eagle_kv "Source:" "$(eagle_agent_label "$origin_agent")"
306
308
  [ -n "$origin" ] && eagle_kv "Session:" "$origin"
307
309
  echo ""
308
310
 
@@ -318,22 +320,22 @@ plans_show() {
318
320
  }
319
321
 
320
322
  tasks_list() {
321
- eagle_header "Claude Code Tasks"
323
+ eagle_header "Agent Tasks"
322
324
 
323
325
  local result
324
- result=$(eagle_list_claude_tasks "$project" "$limit")
326
+ result=$(eagle_list_agent_tasks "$project" "$limit")
325
327
 
326
328
  if [ -z "$result" ]; then
327
329
  eagle_dim "No captured tasks found."
328
330
  echo ""
329
- eagle_dim "Tasks are captured when Claude Code uses TaskCreate/TaskUpdate."
331
+ eagle_dim "Tasks are captured when supported agents use task lifecycle tools."
330
332
  eagle_dim "Run 'eagle-mem memories sync' to backfill existing tasks."
331
333
  echo ""
332
334
  return
333
335
  fi
334
336
 
335
337
  local count=0
336
- while IFS='|' read -r subject status sid tid updated; do
338
+ while IFS='|' read -r subject status sid tid updated origin_agent; do
337
339
  [ -z "$subject" ] && continue
338
340
  count=$((count + 1))
339
341
 
@@ -344,7 +346,7 @@ tasks_list() {
344
346
  completed) status_color="$GREEN" ;;
345
347
  esac
346
348
 
347
- echo -e " ${BOLD}${subject}${RESET} ${status_color}[${status}]${RESET}"
349
+ echo -e " ${BOLD}${subject}${RESET} ${status_color}[${status}]${RESET} ${DIM}[$(eagle_agent_label "$origin_agent")]${RESET}"
348
350
  echo -e " ${DIM}session: ${sid:0:8}… task: #${tid} updated: ${updated}${RESET}"
349
351
  echo ""
350
352
  done <<< "$result"
@@ -364,7 +366,7 @@ tasks_search() {
364
366
  echo ""
365
367
 
366
368
  local result
367
- result=$(eagle_search_claude_tasks "$query" "$project" "$limit")
369
+ result=$(eagle_search_agent_tasks "$query" "$project" "$limit")
368
370
 
369
371
  if [ -z "$result" ]; then
370
372
  eagle_dim "No tasks matching '$query'"
@@ -373,7 +375,7 @@ tasks_search() {
373
375
  fi
374
376
 
375
377
  local count=0
376
- while IFS='|' read -r subject status desc sid tid updated; do
378
+ while IFS='|' read -r subject status desc sid tid updated origin_agent; do
377
379
  [ -z "$subject" ] && continue
378
380
  count=$((count + 1))
379
381
 
@@ -384,7 +386,7 @@ tasks_search() {
384
386
  completed) status_color="$GREEN" ;;
385
387
  esac
386
388
 
387
- echo -e " ${BOLD}${subject}${RESET} ${status_color}[${status}]${RESET}"
389
+ echo -e " ${BOLD}${subject}${RESET} ${status_color}[${status}]${RESET} ${DIM}[$(eagle_agent_label "$origin_agent")]${RESET}"
388
390
  [ -n "$desc" ] && echo -e " ${desc}"
389
391
  echo -e " ${DIM}session: ${sid:0:8}… task: #${tid} updated: ${updated}${RESET}"
390
392
  echo ""
@@ -401,15 +403,15 @@ tasks_show() {
401
403
  fi
402
404
 
403
405
  local meta
404
- meta=$(eagle_db "SELECT subject, status, description, active_form, source_session_id, source_task_id, file_path, updated_at
405
- FROM claude_tasks WHERE file_path = '$(eagle_sql_escape "$query")';")
406
+ meta=$(eagle_db "SELECT subject, status, description, active_form, source_session_id, source_task_id, file_path, updated_at, origin_agent
407
+ FROM agent_tasks WHERE file_path = '$(eagle_sql_escape "$query")';")
406
408
 
407
409
  if [ -z "$meta" ]; then
408
410
  eagle_err "Task not found: $query"
409
411
  exit 1
410
412
  fi
411
413
 
412
- IFS='|' read -r subject status desc af sid tid fp updated <<< "$meta"
414
+ IFS='|' read -r subject status desc af sid tid fp updated origin_agent <<< "$meta"
413
415
 
414
416
  eagle_header "Task Detail"
415
417
 
@@ -419,6 +421,7 @@ tasks_show() {
419
421
  eagle_kv "Session:" "$sid"
420
422
  eagle_kv "File:" "$fp"
421
423
  eagle_kv "Updated:" "$updated"
424
+ eagle_kv "Source:" "$(eagle_agent_label "$origin_agent")"
422
425
  echo ""
423
426
 
424
427
  [ -n "$desc" ] && echo -e " ${BOLD}Description:${RESET} $desc" && echo ""
@@ -439,7 +442,7 @@ memories_sync() {
439
442
  eagle_header "Memory, Plan & Task Sync"
440
443
 
441
444
  # ─── Sync memories ───────────────────────────────────
442
- eagle_info "Scanning for Claude Code auto-memory files..."
445
+ eagle_info "Scanning for agent memory files..."
443
446
  echo ""
444
447
 
445
448
  local claude_mem_root="$EAGLE_CLAUDE_PROJECTS_DIR"
@@ -453,7 +456,7 @@ memories_sync() {
453
456
  [ "$base" = "MEMORY.md" ] && continue
454
457
 
455
458
  local existing_hash
456
- existing_hash=$(eagle_db "SELECT content_hash FROM claude_memories WHERE file_path = '$(eagle_sql_escape "$memfile")';")
459
+ existing_hash=$(eagle_db "SELECT content_hash FROM agent_memories WHERE file_path = '$(eagle_sql_escape "$memfile")';")
457
460
  local new_hash
458
461
  new_hash=$(shasum -a 256 "$memfile" | awk '{print $1}')
459
462
 
@@ -462,17 +465,40 @@ memories_sync() {
462
465
  continue
463
466
  fi
464
467
 
465
- eagle_capture_claude_memory "$memfile" "" ""
468
+ eagle_capture_agent_memory "$memfile" "" ""
466
469
  mem_synced=$((mem_synced + 1))
467
470
  eagle_ok "Memory: $base"
468
471
  done < <(find "$claude_mem_root" -path "*/memory/*.md" -print0 2>/dev/null)
469
472
  fi
470
473
 
474
+ local codex_mem_root="$EAGLE_CODEX_MEMORIES_DIR"
475
+ if [ -d "$codex_mem_root" ]; then
476
+ local codex_project="$project"
477
+ [ -z "$codex_project" ] && codex_project=$(eagle_project_from_cwd "$(pwd)")
478
+ for memfile in "$codex_mem_root/MEMORY.md" "$codex_mem_root/memory_summary.md"; do
479
+ [ ! -f "$memfile" ] && continue
480
+
481
+ local existing_hash
482
+ existing_hash=$(eagle_db "SELECT content_hash FROM agent_memories WHERE file_path = '$(eagle_sql_escape "$memfile")';")
483
+ local new_hash
484
+ new_hash=$(shasum -a 256 "$memfile" | awk '{print $1}')
485
+
486
+ if [ "$existing_hash" = "$new_hash" ]; then
487
+ mem_skipped=$((mem_skipped + 1))
488
+ continue
489
+ fi
490
+
491
+ eagle_capture_agent_memory "$memfile" "" "$codex_project" "codex"
492
+ mem_synced=$((mem_synced + 1))
493
+ eagle_ok "Codex memory: $(basename "$memfile")"
494
+ done
495
+ fi
496
+
471
497
  eagle_kv "Memories:" "$mem_synced synced, $mem_skipped unchanged"
472
498
  echo ""
473
499
 
474
500
  # ─── Sync plans ──────────────────────────────────────
475
- eagle_info "Scanning for Claude Code plan files..."
501
+ eagle_info "Scanning for agent plan files..."
476
502
  echo ""
477
503
 
478
504
  local plans_dir="$EAGLE_CLAUDE_PLANS_DIR"
@@ -484,7 +510,7 @@ memories_sync() {
484
510
  [ ! -f "$planfile" ] && continue
485
511
 
486
512
  local existing_hash
487
- existing_hash=$(eagle_db "SELECT content_hash FROM claude_plans WHERE file_path = '$(eagle_sql_escape "$planfile")';")
513
+ existing_hash=$(eagle_db "SELECT content_hash FROM agent_plans WHERE file_path = '$(eagle_sql_escape "$planfile")';")
488
514
  local new_hash
489
515
  new_hash=$(shasum -a 256 "$planfile" | awk '{print $1}')
490
516
 
@@ -493,7 +519,7 @@ memories_sync() {
493
519
  continue
494
520
  fi
495
521
 
496
- eagle_capture_claude_plan "$planfile" "" ""
522
+ eagle_capture_agent_plan "$planfile" "" ""
497
523
  plan_synced=$((plan_synced + 1))
498
524
  local ptitle
499
525
  ptitle=$(awk '/^# /{print; exit}' "$planfile" | sed 's/^# //')
@@ -505,7 +531,7 @@ memories_sync() {
505
531
  echo ""
506
532
 
507
533
  # ─── Sync tasks ──────────────────────────────────────
508
- eagle_info "Scanning for Claude Code task files..."
534
+ eagle_info "Scanning for agent task files..."
509
535
  echo ""
510
536
 
511
537
  local tasks_dir="$EAGLE_CLAUDE_TASKS_DIR"
@@ -525,7 +551,7 @@ memories_sync() {
525
551
  [ ! -f "$taskfile" ] && continue
526
552
 
527
553
  local existing_hash
528
- existing_hash=$(eagle_db "SELECT content_hash FROM claude_tasks WHERE file_path = '$(eagle_sql_escape "$taskfile")';")
554
+ existing_hash=$(eagle_db "SELECT content_hash FROM agent_tasks WHERE file_path = '$(eagle_sql_escape "$taskfile")';")
529
555
  local new_hash
530
556
  new_hash=$(shasum -a 256 "$taskfile" | awk '{print $1}')
531
557
 
@@ -534,7 +560,7 @@ memories_sync() {
534
560
  continue
535
561
  fi
536
562
 
537
- eagle_capture_claude_task "$taskfile" "$sid" "$task_project"
563
+ eagle_capture_agent_task "$taskfile" "$sid" "$task_project"
538
564
  task_synced=$((task_synced + 1))
539
565
  done
540
566
  done
@@ -544,7 +570,7 @@ memories_sync() {
544
570
  echo ""
545
571
 
546
572
  # ─── Backfill project names ──────────────────────────
547
- eagle_info "Resolving project names from Claude Code transcripts..."
573
+ eagle_info "Resolving project names from agent transcripts..."
548
574
 
549
575
  local backfilled
550
576
  backfilled=$(eagle_backfill_projects)
@@ -54,7 +54,7 @@ run_step "Index" bash "$SCRIPTS_DIR/index.sh" "$TARGET_DIR"
54
54
  echo ""
55
55
 
56
56
  # ─── 3. Memory sync ─────────────────────────────────────
57
- eagle_info "Step 3/4: Syncing Claude Code memories, plans, and tasks..."
57
+ eagle_info "Step 3/4: Syncing agent memories, plans, and tasks..."
58
58
  run_step "Memory sync" bash "$SCRIPTS_DIR/memories.sh" sync
59
59
  echo ""
60
60
 
@@ -65,8 +65,8 @@ project_sql=$(eagle_sql_escape "$PROJECT")
65
65
  sessions=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE project = '$project_sql';")
66
66
  summaries=$(eagle_db "SELECT COUNT(*) FROM summaries WHERE project = '$project_sql';")
67
67
  chunks=$(eagle_db "SELECT COUNT(*) FROM code_chunks WHERE project = '$project_sql';")
68
- memories=$(eagle_db "SELECT COUNT(*) FROM claude_memories WHERE project = '$project_sql';")
69
- tasks=$(eagle_db "SELECT COUNT(*) FROM claude_tasks WHERE project = '$project_sql';")
68
+ memories=$(eagle_db "SELECT COUNT(*) FROM agent_memories WHERE project = '$project_sql';")
69
+ tasks=$(eagle_db "SELECT COUNT(*) FROM agent_tasks WHERE project = '$project_sql';")
70
70
 
71
71
  echo ""
72
72
  eagle_kv "Sessions:" "${sessions:-0}"