eagle-mem 4.10.13 → 4.11.0

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 (69) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +20 -20
  3. package/architecture.html +26 -14
  4. package/bin/eagle-mem +4 -0
  5. package/db/039_recall_events.sql +27 -0
  6. package/db/040_graph_decision_nodes.sql +21 -0
  7. package/db/041_graph_semantic_edge_types.sql +21 -0
  8. package/db/042_orchestration_auto_events.sql +23 -0
  9. package/db/043_eagle_events.sql +22 -0
  10. package/docs/agent-compatibility/README.md +38 -0
  11. package/docs/agent-compatibility/claude-code.md +50 -0
  12. package/docs/agent-compatibility/codex.md +51 -0
  13. package/docs/agent-compatibility/opencode.md +71 -0
  14. package/hooks/post-tool-use.sh +8 -0
  15. package/hooks/pre-tool-use.sh +10 -1
  16. package/hooks/session-end.sh +3 -0
  17. package/hooks/session-start.sh +7 -0
  18. package/hooks/stop.sh +10 -1
  19. package/hooks/user-prompt-submit.sh +79 -6
  20. package/integrations/opencode_eagle_mem_plugin.js +387 -0
  21. package/lib/codex-hooks.sh +13 -6
  22. package/lib/common.sh +63 -0
  23. package/lib/db-events.sh +89 -0
  24. package/lib/db-graph.sh +154 -0
  25. package/lib/db-observations.sh +34 -0
  26. package/lib/db-orchestration.sh +149 -0
  27. package/lib/db.sh +2 -0
  28. package/lib/hooks.sh +12 -7
  29. package/lib/opencode-hooks.sh +105 -0
  30. package/lib/provider.sh +2 -2
  31. package/package.json +5 -2
  32. package/scripts/compaction.sh +108 -8
  33. package/scripts/dashboard.sh +372 -0
  34. package/scripts/doctor.sh +30 -3
  35. package/scripts/health.sh +40 -2
  36. package/scripts/help.sh +10 -2
  37. package/scripts/inspect.sh +285 -0
  38. package/scripts/install.sh +31 -7
  39. package/scripts/memories.sh +13 -0
  40. package/scripts/repair.sh +187 -0
  41. package/scripts/replay.sh +248 -0
  42. package/scripts/search.sh +44 -3
  43. package/scripts/statusline-em.sh +34 -7
  44. package/scripts/tasks.sh +34 -0
  45. package/scripts/test.sh +13 -0
  46. package/scripts/uninstall.sh +9 -0
  47. package/scripts/update.sh +18 -2
  48. package/tests/fixtures/agent-hooks/claude-statusline.json +32 -0
  49. package/tests/fixtures/agent-hooks/claude-user-prompt-submit.json +9 -0
  50. package/tests/fixtures/agent-hooks/codex-pre-tool-use.json +10 -0
  51. package/tests/fixtures/agent-hooks/codex-user-prompt-submit.json +7 -0
  52. package/tests/fixtures/agent-hooks/opencode-chat-message.json +36 -0
  53. package/tests/fixtures/agent-hooks/opencode-session-compacting.json +9 -0
  54. package/tests/fixtures/agent-hooks/opencode-todo-updated.json +13 -0
  55. package/tests/fixtures/agent-hooks/opencode-tool-execute-after.json +15 -0
  56. package/tests/fixtures/agent-hooks/opencode-tool-execute-before.json +12 -0
  57. package/tests/test_agent_compatibility_docs_gate.sh +123 -0
  58. package/tests/test_auto_orchestration_detection.sh +109 -0
  59. package/tests/test_claude_stop_hook_registration.sh +56 -0
  60. package/tests/test_codex_hooks_config.sh +73 -0
  61. package/tests/test_compaction_survival_matrix.sh +237 -0
  62. package/tests/test_dashboard.sh +96 -0
  63. package/tests/test_eagle_events.sh +96 -0
  64. package/tests/test_opencode_hooks_config.sh +56 -0
  65. package/tests/test_opencode_plugin_adapter.sh +202 -0
  66. package/tests/test_recall_observability.sh +144 -0
  67. package/tests/test_repair.sh +63 -0
  68. package/tests/test_rust_migration_plan.sh +75 -0
  69. package/tests/test_trust_surfaces.sh +123 -0
package/scripts/health.sh CHANGED
@@ -29,6 +29,30 @@ if [ "$JSON_OUT" -eq 1 ]; then
29
29
  exec 3>&1 1>&2
30
30
  fi
31
31
 
32
+ health_fail() {
33
+ local error_code="$1"
34
+ local message="$2"
35
+ local db_status="${3:-unknown}"
36
+ local db_detail="${4:-}"
37
+
38
+ if [ "$JSON_OUT" -eq 1 ]; then
39
+ jq -nc \
40
+ --arg status "error" \
41
+ --arg command "health" \
42
+ --arg error "$error_code" \
43
+ --arg message "$message" \
44
+ --arg project "${project:-}" \
45
+ --arg db_status "$db_status" \
46
+ --arg db_detail "$db_detail" \
47
+ '{status:$status, command:$command, error:$error, message:$message,
48
+ project:$project, database:{integrity:{status:$db_status, detail:$db_detail}}}' >&3
49
+ else
50
+ eagle_fail "$message"
51
+ [ -n "$db_detail" ] && eagle_dim " $db_detail"
52
+ fi
53
+ exit 1
54
+ }
55
+
32
56
  eagle_header "Health Check"
33
57
 
34
58
  if [ -z "$project" ]; then
@@ -40,7 +64,18 @@ if [ -z "$project" ]; then
40
64
  exit 1
41
65
  fi
42
66
 
43
- eagle_ensure_db
67
+ if ! eagle_ensure_db; then
68
+ health_fail "database_unavailable" "Database is unavailable; SQLite/FTS5 setup failed." "unavailable" "eagle_ensure_db failed"
69
+ fi
70
+
71
+ db_integrity_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
72
+ db_integrity_status="${db_integrity_check%%|*}"
73
+ db_integrity_detail="${db_integrity_check#*|}"
74
+ [ -n "$db_integrity_status" ] || db_integrity_status="unknown"
75
+ [ -n "$db_integrity_detail" ] || db_integrity_detail="not checked"
76
+ if [ "$db_integrity_status" != "ok" ]; then
77
+ health_fail "database_integrity" "Database integrity check failed; memory state is unreadable." "$db_integrity_status" "$db_integrity_detail"
78
+ fi
44
79
 
45
80
  p_esc=$(eagle_sql_escape "$project")
46
81
 
@@ -340,7 +375,10 @@ if [ "$JSON_OUT" -eq 1 ]; then
340
375
  --arg updates_status "$updates_status" \
341
376
  --argjson noise_pct "$noise_pct" \
342
377
  --arg last_curated "${last_curated:-never}" \
343
- '{project:$project, score:$score, max:$max_score, pct:$pct, grade:$grade,
378
+ --arg db_integrity_status "$db_integrity_status" \
379
+ --arg db_integrity_detail "$db_integrity_detail" \
380
+ '{status:"ok", project:$project, score:$score, max:$max_score, pct:$pct, grade:$grade,
381
+ database:{integrity:{status:$db_integrity_status, detail:$db_integrity_detail}},
344
382
  capture:{sessions:$total_sessions, summaries:$total_summaries, heuristic:$heuristic_summaries},
345
383
  enrichment:$enriched_summaries,
346
384
  features:$features, provider:$provider, provider_chain:$provider_chain,
package/scripts/help.sh CHANGED
@@ -14,7 +14,7 @@ eagle_banner
14
14
 
15
15
  echo -e " ${BOLD}Eagle Mem${RESET} ${DIM}v${version}${RESET}"
16
16
  echo -e " ${DIM}Shared memory, release guardrails, RTK token protection, and worker lanes${RESET}"
17
- echo -e " ${DIM}for Claude Code, Codex, Grok, and Google Antigravity.${RESET}"
17
+ echo -e " ${DIM}for Claude Code, Codex, OpenCode, Grok, and Google Antigravity.${RESET}"
18
18
  echo ""
19
19
  echo -e " ${BOLD}Core commands:${RESET}"
20
20
  echo -e " ${CYAN}install${RESET} First-time setup: hooks, database, skills"
@@ -23,6 +23,10 @@ echo -e " ${CYAN}uninstall${RESET} Remove hooks and optionally delete data"
23
23
  echo -e " ${CYAN}search${RESET} Search past sessions, memories, and code"
24
24
  echo -e " ${CYAN}health${RESET} Diagnose pipeline health and background automation"
25
25
  echo -e " ${CYAN}doctor${RESET} Show install footprint, hooks, SQLite, manifest, and runtime drift"
26
+ echo -e " ${CYAN}repair${RESET} Preview or run safe SQLite memory DB recovery"
27
+ echo -e " ${CYAN}inspect${RESET} Inspect recall and hook observability events"
28
+ echo -e " ${CYAN}replay${RESET} Replay prompt, recall, injected context, and observed work"
29
+ echo -e " ${CYAN}dashboard${RESET} Generate a local HTML memory dashboard"
26
30
  echo -e " ${CYAN}logs${RESET} Inspect/prune command-scoped scan/index/curate logs"
27
31
  echo -e " ${CYAN}updates${RESET} Auto-update status and policy"
28
32
  echo -e " ${CYAN}overview${RESET} Build or view project overview"
@@ -60,6 +64,10 @@ echo -e " ${DIM}\$${RESET} eagle-mem search --memories ${DIM}# mirrored m
60
64
  echo -e " ${DIM}\$${RESET} eagle-mem search --tasks ${DIM}# in-flight tasks${RESET}"
61
65
  echo -e " ${DIM}\$${RESET} eagle-mem search --files ${DIM}# hot files${RESET}"
62
66
  echo -e " ${DIM}\$${RESET} eagle-mem search --stats ${DIM}# project stats${RESET}"
67
+ echo -e " ${DIM}\$${RESET} eagle-mem repair ${DIM}# preview DB recovery plan${RESET}"
68
+ echo -e " ${DIM}\$${RESET} eagle-mem inspect recall --last ${DIM}# latest UserPromptSubmit recall event${RESET}"
69
+ echo -e " ${DIM}\$${RESET} eagle-mem replay --last ${DIM}# replay latest recall session${RESET}"
70
+ echo -e " ${DIM}\$${RESET} eagle-mem dashboard ${DIM}# local HTML dashboard${RESET}"
63
71
  echo -e " ${DIM}\$${RESET} eagle-mem session save --summary \"fixed auth\""
64
72
  echo -e " ${DIM}\$${RESET} eagle-mem graph rebuild ${DIM}# rebuild code graph + chunks${RESET}"
65
73
  echo -e " ${DIM}\$${RESET} eagle-mem index --force ${DIM}# force source chunk/declaration indexing${RESET}"
@@ -78,7 +86,7 @@ echo -e " ${DIM}\$${RESET} eagle-mem orchestrate spawn api ${DIM}# work
78
86
  echo -e " ${DIM}\$${RESET} eagle-mem orchestrate sync ${DIM}# reconcile worker status${RESET}"
79
87
  echo -e " ${DIM}\$${RESET} eagle-mem orchestrate handoff --write docs/handoff-context.md"
80
88
  echo ""
81
- echo -e " ${BOLD}Skills${RESET} ${DIM}(inside Claude Code, Codex, and Grok sessions):${RESET}"
89
+ echo -e " ${BOLD}Skills${RESET} ${DIM}(inside Claude Code, Codex, OpenCode, and Grok sessions):${RESET}"
82
90
  echo -e " ${CYAN}eagle-mem-search${RESET} Search memory and past sessions"
83
91
  echo -e " ${CYAN}eagle-mem-overview${RESET} Build or update project overview"
84
92
  echo -e " ${CYAN}eagle-mem-memories${RESET} View/sync agent memories"
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Eagle Mem — Inspect
4
+ # Human-facing inspection surfaces for hook and recall observability.
5
+ # ═══════════════════════════════════════════════════════════
6
+ set -euo pipefail
7
+
8
+ SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
9
+ LIB_DIR="$SCRIPTS_DIR/../lib"
10
+
11
+ . "$SCRIPTS_DIR/style.sh"
12
+ . "$LIB_DIR/common.sh"
13
+ . "$LIB_DIR/db.sh"
14
+
15
+ action="${1:-recall}"
16
+ case "$action" in
17
+ recall|events) shift 2>/dev/null || true ;;
18
+ --help|-h|"") ;;
19
+ *)
20
+ eagle_err "Unknown inspect action: $action"
21
+ eagle_dim "Run: eagle-mem inspect --help"
22
+ exit 1
23
+ ;;
24
+ esac
25
+
26
+ project=""
27
+ project_was_explicit=false
28
+ cross_project=false
29
+ session_id=""
30
+ limit=10
31
+ json_output=false
32
+
33
+ show_help() {
34
+ echo -e " ${BOLD}eagle-mem inspect${RESET} — Inspect Eagle Mem observability events"
35
+ echo ""
36
+ echo -e " ${BOLD}Usage:${RESET}"
37
+ echo -e " eagle-mem inspect recall ${DIM}# recent recall events${RESET}"
38
+ echo -e " eagle-mem inspect events ${DIM}# recent hook/action events${RESET}"
39
+ echo -e " eagle-mem inspect recall ${CYAN}--last${RESET} ${DIM}# latest recall event${RESET}"
40
+ echo -e " eagle-mem inspect recall ${CYAN}--json${RESET} ${DIM}# structured output${RESET}"
41
+ echo ""
42
+ echo -e " ${BOLD}Options:${RESET}"
43
+ echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current dir scope)"
44
+ echo -e " ${CYAN}-s, --session${RESET} <id> Filter by agent session id"
45
+ echo -e " ${CYAN}-n, --limit${RESET} <N> Max events (default: 10)"
46
+ echo -e " ${CYAN}--all${RESET} Inspect all projects"
47
+ echo -e " ${CYAN}--last${RESET} Same as --limit 1"
48
+ echo -e " ${CYAN}-j, --json${RESET} Output structured JSON"
49
+ echo ""
50
+ exit 0
51
+ }
52
+
53
+ while [ $# -gt 0 ]; do
54
+ case "$1" in
55
+ --project|-p) project="$2"; project_was_explicit=true; shift 2 ;;
56
+ --session|-s) session_id="$2"; shift 2 ;;
57
+ --limit|-n) limit="$2"; shift 2 ;;
58
+ --last) limit=1; shift ;;
59
+ --all|-a) cross_project=true; shift ;;
60
+ --json|-j) json_output=true; shift ;;
61
+ --help|-h) show_help ;;
62
+ *)
63
+ eagle_err "Unknown option: $1"
64
+ exit 1
65
+ ;;
66
+ esac
67
+ done
68
+
69
+ inspect_fail() {
70
+ local error_code="$1"
71
+ local message="$2"
72
+ local db_status="${3:-unknown}"
73
+ local db_detail="${4:-}"
74
+
75
+ if [ "$json_output" = true ]; then
76
+ jq -nc \
77
+ --arg status "error" \
78
+ --arg command "inspect" \
79
+ --arg action "$action" \
80
+ --arg error "$error_code" \
81
+ --arg message "$message" \
82
+ --arg project "${project:-}" \
83
+ --arg db_status "$db_status" \
84
+ --arg db_detail "$db_detail" \
85
+ '{status:$status, command:$command, action:$action, error:$error, message:$message,
86
+ project:$project, database:{integrity:{status:$db_status, detail:$db_detail}}}'
87
+ else
88
+ eagle_err "$message"
89
+ [ -n "$db_detail" ] && eagle_dim " $db_detail"
90
+ fi
91
+ exit 1
92
+ }
93
+
94
+ if [ "$action" = "--help" ] || [ "$action" = "-h" ] || [ -z "$action" ]; then
95
+ show_help
96
+ fi
97
+
98
+ if ! eagle_ensure_db; then
99
+ inspect_fail "database_unavailable" "Database is unavailable; SQLite/FTS5 setup failed." "unavailable" "eagle_ensure_db failed"
100
+ fi
101
+
102
+ db_integrity_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
103
+ db_integrity_status="${db_integrity_check%%|*}"
104
+ db_integrity_detail="${db_integrity_check#*|}"
105
+ [ -n "$db_integrity_status" ] || db_integrity_status="unknown"
106
+ [ -n "$db_integrity_detail" ] || db_integrity_detail="not checked"
107
+ if [ "$db_integrity_status" != "ok" ]; then
108
+ inspect_fail "database_integrity" "Database integrity check failed; inspection is unavailable." "$db_integrity_status" "$db_integrity_detail"
109
+ fi
110
+
111
+ if [ "$action" = "recall" ] && [ -z "$(eagle_db "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'recall_events' LIMIT 1;" 2>/dev/null || true)" ]; then
112
+ inspect_fail "migration_missing" "Recall inspection is unavailable until migrations create recall_events." "ok" "run: eagle-mem update"
113
+ fi
114
+
115
+ if [ "$action" = "events" ] && [ -z "$(eagle_db "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'eagle_events' LIMIT 1;" 2>/dev/null || true)" ]; then
116
+ inspect_fail "migration_missing" "Event inspection is unavailable until migrations create eagle_events." "ok" "run: eagle-mem update"
117
+ fi
118
+
119
+ limit=$(eagle_sql_int "$limit")
120
+ [ "$limit" -le 0 ] 2>/dev/null && limit=10
121
+
122
+ if [ -z "$project" ] && [ "$cross_project" = false ]; then
123
+ project=$(eagle_project_from_cwd "$(pwd)")
124
+ fi
125
+ if [ "$cross_project" = false ] && [ "$project_was_explicit" = false ]; then
126
+ project=$(eagle_recall_project_scope_from_cwd "$(pwd)" "$project")
127
+ fi
128
+
129
+ recall_inspect() {
130
+ local where_clause="1 = 1"
131
+ local project_label="$project"
132
+
133
+ if [ "$cross_project" = false ]; then
134
+ where_clause="$where_clause AND $(eagle_sql_project_scope_condition "project" "$project")"
135
+ project_label=$(eagle_project_scope_label "$project")
136
+ else
137
+ project_label="all projects"
138
+ fi
139
+
140
+ if [ -n "$session_id" ]; then
141
+ local sid_sql
142
+ sid_sql=$(eagle_sql_escape "$session_id")
143
+ where_clause="$where_clause AND session_id = '$sid_sql'"
144
+ fi
145
+
146
+ local events_json
147
+ events_json=$(eagle_db_json "SELECT id, session_id, project, agent, hook_name, prompt_snippet, fts_query,
148
+ summary_matches, memory_matches, code_matches, injected_chars,
149
+ injected_token_estimate, status, error, created_at
150
+ FROM recall_events
151
+ WHERE $where_clause
152
+ ORDER BY created_at DESC, id DESC
153
+ LIMIT $limit;")
154
+ [ -n "$events_json" ] || events_json="[]"
155
+
156
+ if [ "$json_output" = true ]; then
157
+ jq -nc \
158
+ --arg status "ok" \
159
+ --arg command "inspect" \
160
+ --arg action "recall" \
161
+ --arg project "$project_label" \
162
+ --arg db_integrity_status "$db_integrity_status" \
163
+ --arg db_integrity_detail "$db_integrity_detail" \
164
+ --argjson events "$events_json" \
165
+ '{status:$status, command:$command, action:$action, project:$project,
166
+ database:{integrity:{status:$db_integrity_status, detail:$db_integrity_detail}},
167
+ events:$events}'
168
+ return
169
+ fi
170
+
171
+ eagle_header "Recall Inspector"
172
+ eagle_info "Project: $project_label"
173
+ [ -n "$session_id" ] && eagle_info "Session: $session_id"
174
+ echo ""
175
+
176
+ if [ "$(printf '%s' "$events_json" | jq 'length')" -eq 0 ]; then
177
+ eagle_dim "No recall events recorded yet."
178
+ eagle_dim "UserPromptSubmit records events after migrations include recall_events."
179
+ echo ""
180
+ return
181
+ fi
182
+
183
+ printf '%s' "$events_json" | jq -c '.[]' | while IFS= read -r event; do
184
+ local created agent prompt query summaries memories code tokens status event_project sid
185
+ created=$(printf '%s' "$event" | jq -r '.created_at // ""')
186
+ agent=$(printf '%s' "$event" | jq -r '.agent // ""')
187
+ event_project=$(printf '%s' "$event" | jq -r '.project // ""')
188
+ sid=$(printf '%s' "$event" | jq -r '.session_id // ""')
189
+ prompt=$(printf '%s' "$event" | jq -r '.prompt_snippet // ""')
190
+ query=$(printf '%s' "$event" | jq -r '.fts_query // ""')
191
+ summaries=$(printf '%s' "$event" | jq -r '.summary_matches // 0')
192
+ memories=$(printf '%s' "$event" | jq -r '.memory_matches // 0')
193
+ code=$(printf '%s' "$event" | jq -r '.code_matches // 0')
194
+ tokens=$(printf '%s' "$event" | jq -r '.injected_token_estimate // 0')
195
+ status=$(printf '%s' "$event" | jq -r '.status // "unknown"')
196
+
197
+ echo -e " ${BOLD}${created}${RESET} ${DIM}$(eagle_agent_label "$agent")${RESET} ${CYAN}${status}${RESET}"
198
+ [ "$cross_project" = true ] && echo -e " ${DIM}Project:${RESET} $event_project"
199
+ [ -n "$sid" ] && echo -e " ${DIM}Session:${RESET} $sid"
200
+ [ -n "$prompt" ] && echo -e " ${DIM}Prompt:${RESET} $prompt"
201
+ [ -n "$query" ] && echo -e " ${DIM}Query:${RESET} $query"
202
+ echo -e " ${DIM}Retrieved:${RESET} summaries=$summaries memories=$memories code=$code ${DIM}Injected:${RESET} ~${tokens} tokens"
203
+ echo ""
204
+ done
205
+ }
206
+
207
+ events_inspect() {
208
+ local where_clause="1 = 1"
209
+ local project_label="$project"
210
+
211
+ if [ "$cross_project" = false ]; then
212
+ where_clause="$where_clause AND $(eagle_sql_project_scope_condition "project" "$project")"
213
+ project_label=$(eagle_project_scope_label "$project")
214
+ else
215
+ project_label="all projects"
216
+ fi
217
+
218
+ if [ -n "$session_id" ]; then
219
+ local sid_sql
220
+ sid_sql=$(eagle_sql_escape "$session_id")
221
+ where_clause="$where_clause AND session_id = '$sid_sql'"
222
+ fi
223
+
224
+ local events_json
225
+ events_json=$(eagle_db_json "SELECT id, session_id, project, agent, event_type, command,
226
+ hook_event_name, status, detail_json, created_at
227
+ FROM eagle_events
228
+ WHERE $where_clause
229
+ ORDER BY created_at DESC, id DESC
230
+ LIMIT $limit;")
231
+ [ -n "$events_json" ] || events_json="[]"
232
+
233
+ if [ "$json_output" = true ]; then
234
+ jq -nc \
235
+ --arg status "ok" \
236
+ --arg command "inspect" \
237
+ --arg action "events" \
238
+ --arg project "$project_label" \
239
+ --arg db_integrity_status "$db_integrity_status" \
240
+ --arg db_integrity_detail "$db_integrity_detail" \
241
+ --argjson events "$events_json" \
242
+ '{status:$status, command:$command, action:$action, project:$project,
243
+ database:{integrity:{status:$db_integrity_status, detail:$db_integrity_detail}},
244
+ events:($events | map(.detail = ((.detail_json // "{}") | fromjson? // {}) | del(.detail_json)))}'
245
+ return
246
+ fi
247
+
248
+ eagle_header "Event Inspector"
249
+ eagle_info "Project: $project_label"
250
+ [ -n "$session_id" ] && eagle_info "Session: $session_id"
251
+ echo ""
252
+
253
+ if [ "$(printf '%s' "$events_json" | jq 'length')" -eq 0 ]; then
254
+ eagle_dim "No Eagle events recorded yet."
255
+ eagle_dim "Hooks record events after migrations include eagle_events."
256
+ echo ""
257
+ return
258
+ fi
259
+
260
+ printf '%s' "$events_json" | jq -c '.[]' | while IFS= read -r event; do
261
+ local created agent event_type hook status event_project sid detail
262
+ created=$(printf '%s' "$event" | jq -r '.created_at // ""')
263
+ agent=$(printf '%s' "$event" | jq -r '.agent // ""')
264
+ event_project=$(printf '%s' "$event" | jq -r '.project // ""')
265
+ sid=$(printf '%s' "$event" | jq -r '.session_id // ""')
266
+ event_type=$(printf '%s' "$event" | jq -r '.event_type // ""')
267
+ hook=$(printf '%s' "$event" | jq -r '.hook_event_name // ""')
268
+ status=$(printf '%s' "$event" | jq -r '.status // "unknown"')
269
+ detail=$(printf '%s' "$event" | jq -r '(.detail_json // "{}") | fromjson? | to_entries | map("\(.key)=\(.value|tostring)") | join(", ")' 2>/dev/null)
270
+
271
+ echo -e " ${BOLD}${created}${RESET} ${CYAN}${event_type}${RESET} ${DIM}${status}${RESET}"
272
+ [ "$cross_project" = true ] && echo -e " ${DIM}Project:${RESET} $event_project"
273
+ [ -n "$agent" ] && echo -e " ${DIM}Agent:${RESET} $(eagle_agent_label "$agent")"
274
+ [ -n "$sid" ] && echo -e " ${DIM}Session:${RESET} $sid"
275
+ [ -n "$hook" ] && echo -e " ${DIM}Hook:${RESET} $hook"
276
+ [ -n "$detail" ] && echo -e " ${DIM}Detail:${RESET} $detail"
277
+ echo ""
278
+ done
279
+ }
280
+
281
+ case "$action" in
282
+ recall) recall_inspect ;;
283
+ events) events_inspect ;;
284
+ *) show_help ;;
285
+ esac
@@ -50,6 +50,7 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
50
50
  . "$LIB_DIR/common.sh"
51
51
  . "$LIB_DIR/hooks.sh"
52
52
  . "$LIB_DIR/codex-hooks.sh"
53
+ . "$LIB_DIR/opencode-hooks.sh"
53
54
 
54
55
  SETTINGS="$EAGLE_SETTINGS"
55
56
 
@@ -184,6 +185,7 @@ ensure_rtk
184
185
  claude_found=false
185
186
  codex_found=false
186
187
  grok_found=false
188
+ opencode_found=false
187
189
 
188
190
  if [ -d "$HOME/.claude" ]; then
189
191
  eagle_ok "Claude Code ${DIM}(~/.claude/)${RESET}"
@@ -209,10 +211,17 @@ else
209
211
  eagle_warn "Grok not found ${DIM}(~/.grok/ does not exist)${RESET}"
210
212
  fi
211
213
 
212
- if [ "$claude_found" = false ] && [ "$codex_found" = false ] && [ "$grok_found" = false ]; then
214
+ if eagle_opencode_detected; then
215
+ opencode_found=true
216
+ eagle_ok "OpenCode ${DIM}($EAGLE_OPENCODE_DIR/)${RESET}"
217
+ else
218
+ eagle_warn "OpenCode not found ${DIM}(~/.config/opencode/ missing and opencode not on PATH)${RESET}"
219
+ fi
220
+
221
+ if [ "$claude_found" = false ] && [ "$codex_found" = false ] && [ "$grok_found" = false ] && [ "$opencode_found" = false ]; then
213
222
  eagle_fail "No supported agent install found"
214
223
  echo ""
215
- eagle_dim "Install Claude Code, Codex, or ensure ~/.grok/ exists, then re-run."
224
+ eagle_dim "Install Claude Code, Codex, OpenCode, or ensure ~/.grok/ exists, then re-run."
216
225
  echo ""
217
226
  exit 1
218
227
  fi
@@ -223,7 +232,7 @@ if [ "$prereqs_ok" = false ]; then
223
232
  exit 1
224
233
  fi
225
234
 
226
- eagle_runtime_change_plan "install" "$PACKAGE_DIR" "$claude_found" "$codex_found"
235
+ eagle_runtime_change_plan "install" "$PACKAGE_DIR" "$claude_found" "$codex_found" "$opencode_found"
227
236
 
228
237
  echo ""
229
238
 
@@ -278,14 +287,15 @@ if [ "$claude_found" = true ]; then
278
287
  "$EAGLE_MEM_DIR/hooks/session-start.sh" \
279
288
  "SessionStart hook"
280
289
 
281
- eagle_patch_hook "$SETTINGS" "Stop" "" \
282
- "$EAGLE_MEM_DIR/hooks/stop.sh" \
283
- "Stop hook"
284
-
285
290
  # Clean old registrations before re-registering (handles matcher changes across versions)
291
+ eagle_clean_hook_entries "$SETTINGS" "Stop" "$EAGLE_MEM_DIR/hooks/stop.sh"
286
292
  eagle_clean_hook_entries "$SETTINGS" "PostToolUse" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
287
293
  eagle_clean_hook_entries "$SETTINGS" "PreToolUse" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
288
294
 
295
+ eagle_patch_hook "$SETTINGS" "Stop" "" \
296
+ "bash \"$EAGLE_MEM_DIR/hooks/stop.sh\"" \
297
+ "Stop hook"
298
+
289
299
  eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskUpdate" \
290
300
  "$EAGLE_MEM_DIR/hooks/post-tool-use.sh" \
291
301
  "PostToolUse hook"
@@ -324,6 +334,12 @@ else
324
334
  eagle_info "Codex hooks skipped ${DIM}(Codex not detected)${RESET}"
325
335
  fi
326
336
 
337
+ if [ "$opencode_found" = true ]; then
338
+ eagle_install_opencode_plugin "$PACKAGE_DIR" "$DRY_RUN"
339
+ else
340
+ eagle_info "OpenCode plugin skipped ${DIM}(OpenCode not detected)${RESET}"
341
+ fi
342
+
327
343
  # ─── Install skills ────────────────────────────────────────
328
344
 
329
345
  if [ "$claude_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
@@ -374,6 +390,10 @@ if [ "$grok_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
374
390
  fi
375
391
  fi
376
392
 
393
+ if [ "$opencode_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
394
+ eagle_install_opencode_skills "$PACKAGE_DIR" "$DRY_RUN"
395
+ fi
396
+
377
397
  # ─── Statusline integration ───────────────────────────────
378
398
 
379
399
  if [ "$claude_found" = true ]; then
@@ -510,12 +530,16 @@ eagle_kv "Hooks:" "$EAGLE_MEM_DIR/hooks/"
510
530
  [ "$claude_found" = true ] && eagle_kv "Claude settings:" "$SETTINGS"
511
531
  [ "$codex_found" = true ] && eagle_kv "Codex hooks:" "$EAGLE_CODEX_HOOKS"
512
532
  [ "$grok_found" = true ] && eagle_kv "Grok skills:" "$EAGLE_GROK_SKILLS_DIR"
533
+ [ "$opencode_found" = true ] && eagle_kv "OpenCode plugin:" "$EAGLE_OPENCODE_PLUGIN"
513
534
  eagle_kv "Antigravity Hook:" "$EAGLE_MEM_DIR/integrations/google_antigravity_hook.py"
514
535
 
515
536
  echo ""
516
537
  if [ "$grok_found" = true ]; then
517
538
  eagle_dim "Grok skills installed. Run 'eagle-mem grok-bootstrap' for setup guidance and recall."
518
539
  fi
540
+ if [ "$opencode_found" = true ]; then
541
+ eagle_dim "Start a new OpenCode session without --pure — Eagle Mem will activate through the global local plugin."
542
+ fi
519
543
  if [ "$claude_found" = true ] || [ "$codex_found" = true ]; then
520
544
  eagle_dim "Start a new Claude Code or Codex session — Eagle Mem will activate automatically."
521
545
  fi
@@ -109,6 +109,17 @@ if [ "$cross_project" = false ] && [ "$project_was_explicit" = false ]; then
109
109
  project=$(eagle_recall_project_scope_from_cwd "$(pwd)" "$project")
110
110
  fi
111
111
 
112
+ db_integrity_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
113
+ db_integrity_status="${db_integrity_check%%|*}"
114
+ db_integrity_detail="${db_integrity_check#*|}"
115
+ [ -n "$db_integrity_status" ] || db_integrity_status="unknown"
116
+ [ -n "$db_integrity_detail" ] || db_integrity_detail="not checked"
117
+ if [ "$db_integrity_status" != "ok" ]; then
118
+ eagle_err "Database integrity check failed; memories and graph are unavailable."
119
+ eagle_dim " $db_integrity_detail"
120
+ exit 1
121
+ fi
122
+
112
123
  eagle_age_label() {
113
124
  local updated="${1:-}"
114
125
  [ -z "$updated" ] && { printf 'unknown'; return; }
@@ -975,6 +986,8 @@ memories_graph() {
975
986
  file_count=$(eagle_graph_rebuild_codebase "$project" "$target_dir")
976
987
  eagle_ok "Rebuilt file graph ($file_count files)"
977
988
  bash "$SCRIPTS_DIR/index.sh" --force "$target_dir"
989
+ context_count=$(eagle_graph_wire_project_context_edges "$project")
990
+ eagle_ok "Wired durable context graph ($context_count nodes)"
978
991
  eagle_footer "Graph rebuild complete."
979
992
  ;;
980
993
  summary|*)