eagle-mem 4.10.13 → 4.12.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 (74) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +22 -22
  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/db/044_summary_capture_source.sql +12 -0
  11. package/docs/agent-compatibility/README.md +38 -0
  12. package/docs/agent-compatibility/claude-code.md +58 -0
  13. package/docs/agent-compatibility/codex.md +57 -0
  14. package/docs/agent-compatibility/opencode.md +72 -0
  15. package/hooks/post-tool-use.sh +8 -0
  16. package/hooks/pre-tool-use.sh +10 -1
  17. package/hooks/session-end.sh +3 -0
  18. package/hooks/session-start.sh +15 -17
  19. package/hooks/stop.sh +34 -5
  20. package/hooks/user-prompt-submit.sh +85 -10
  21. package/integrations/opencode_eagle_mem_plugin.js +387 -0
  22. package/lib/codex-hooks.sh +13 -6
  23. package/lib/common.sh +77 -7
  24. package/lib/db-events.sh +89 -0
  25. package/lib/db-graph.sh +154 -0
  26. package/lib/db-observations.sh +34 -0
  27. package/lib/db-orchestration.sh +149 -0
  28. package/lib/db-summaries.sh +70 -3
  29. package/lib/db.sh +2 -0
  30. package/lib/hooks.sh +41 -7
  31. package/lib/opencode-hooks.sh +105 -0
  32. package/lib/provider.sh +2 -2
  33. package/package.json +5 -2
  34. package/scripts/compaction.sh +109 -9
  35. package/scripts/dashboard.sh +372 -0
  36. package/scripts/doctor.sh +30 -3
  37. package/scripts/enrich-summary.sh +8 -2
  38. package/scripts/health.sh +40 -2
  39. package/scripts/help.sh +10 -2
  40. package/scripts/inspect.sh +285 -0
  41. package/scripts/install.sh +36 -7
  42. package/scripts/memories.sh +13 -0
  43. package/scripts/repair.sh +187 -0
  44. package/scripts/replay.sh +248 -0
  45. package/scripts/search.sh +44 -3
  46. package/scripts/session.sh +155 -18
  47. package/scripts/statusline-em.sh +34 -7
  48. package/scripts/tasks.sh +34 -0
  49. package/scripts/test.sh +13 -0
  50. package/scripts/uninstall.sh +9 -0
  51. package/scripts/update.sh +21 -2
  52. package/tests/fixtures/agent-hooks/claude-statusline.json +32 -0
  53. package/tests/fixtures/agent-hooks/claude-user-prompt-submit.json +9 -0
  54. package/tests/fixtures/agent-hooks/codex-pre-tool-use.json +10 -0
  55. package/tests/fixtures/agent-hooks/codex-user-prompt-submit.json +7 -0
  56. package/tests/fixtures/agent-hooks/opencode-chat-message.json +36 -0
  57. package/tests/fixtures/agent-hooks/opencode-session-compacting.json +9 -0
  58. package/tests/fixtures/agent-hooks/opencode-todo-updated.json +13 -0
  59. package/tests/fixtures/agent-hooks/opencode-tool-execute-after.json +15 -0
  60. package/tests/fixtures/agent-hooks/opencode-tool-execute-before.json +12 -0
  61. package/tests/test_agent_compatibility_docs_gate.sh +123 -0
  62. package/tests/test_auto_orchestration_detection.sh +109 -0
  63. package/tests/test_claude_stop_hook_registration.sh +56 -0
  64. package/tests/test_clean_session_capture.sh +105 -0
  65. package/tests/test_codex_hooks_config.sh +73 -0
  66. package/tests/test_compaction_survival_matrix.sh +237 -0
  67. package/tests/test_dashboard.sh +96 -0
  68. package/tests/test_eagle_events.sh +96 -0
  69. package/tests/test_opencode_hooks_config.sh +56 -0
  70. package/tests/test_opencode_plugin_adapter.sh +202 -0
  71. package/tests/test_recall_observability.sh +144 -0
  72. package/tests/test_repair.sh +63 -0
  73. package/tests/test_rust_migration_plan.sh +75 -0
  74. package/tests/test_trust_surfaces.sh +123 -0
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Eagle Mem — Database Repair
4
+ # Safe recovery workflow for malformed SQLite memory databases.
5
+ # ═══════════════════════════════════════════════════════════
6
+ set -euo pipefail
7
+
8
+ SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
9
+ LIB_DIR="$SCRIPTS_DIR/../lib"
10
+ PACKAGE_DIR="$(cd "$SCRIPTS_DIR/.." && pwd)"
11
+
12
+ . "$SCRIPTS_DIR/style.sh"
13
+ . "$LIB_DIR/common.sh"
14
+
15
+ json_output=false
16
+ dry_run=true
17
+ assume_yes=false
18
+ backup_dir=""
19
+
20
+ show_help() {
21
+ echo -e " ${BOLD}eagle-mem repair${RESET} — Diagnose and recover a malformed memory database"
22
+ echo ""
23
+ echo -e " ${BOLD}Usage:${RESET}"
24
+ echo -e " eagle-mem repair ${DIM}# preview repair plan${RESET}"
25
+ echo -e " eagle-mem repair ${CYAN}--dry-run${RESET} ${DIM}# preview repair plan${RESET}"
26
+ echo -e " eagle-mem repair ${CYAN}--yes${RESET} ${DIM}# backup, recover, verify, replace${RESET}"
27
+ echo ""
28
+ echo -e " ${BOLD}Options:${RESET}"
29
+ echo -e " ${CYAN}--backup-dir${RESET} <path> Backup directory (default: ~/.eagle-mem/backups)"
30
+ echo -e " ${CYAN}-j, --json${RESET} Output structured JSON"
31
+ echo ""
32
+ exit 0
33
+ }
34
+
35
+ while [ $# -gt 0 ]; do
36
+ case "$1" in
37
+ --dry-run) dry_run=true; shift ;;
38
+ --yes|-y) assume_yes=true; dry_run=false; shift ;;
39
+ --backup-dir) backup_dir="$2"; shift 2 ;;
40
+ --json|-j) json_output=true; shift ;;
41
+ --help|-h) show_help ;;
42
+ *)
43
+ eagle_err "Unknown option: $1"
44
+ exit 1
45
+ ;;
46
+ esac
47
+ done
48
+
49
+ [ -n "$backup_dir" ] || backup_dir="$EAGLE_MEM_DIR/backups"
50
+
51
+ repair_json() {
52
+ local status="$1" message="$2" db_status="$3" db_detail="$4" backup_path="${5:-}" recovered_path="${6:-}"
53
+ jq -nc \
54
+ --arg status "$status" \
55
+ --arg command "repair" \
56
+ --arg message "$message" \
57
+ --arg db "$EAGLE_MEM_DB" \
58
+ --arg backup_dir "$backup_dir" \
59
+ --arg backup_path "$backup_path" \
60
+ --arg recovered_path "$recovered_path" \
61
+ --arg db_status "$db_status" \
62
+ --arg db_detail "$db_detail" \
63
+ '{status:$status, command:$command, message:$message, db:$db,
64
+ backup_dir:$backup_dir, backup_path:$backup_path, recovered_path:$recovered_path,
65
+ database:{integrity:{status:$db_status, detail:$db_detail}}}'
66
+ }
67
+
68
+ repair_emit() {
69
+ local status="$1" message="$2" db_status="$3" db_detail="$4" backup_path="${5:-}" recovered_path="${6:-}"
70
+ if [ "$json_output" = true ]; then
71
+ repair_json "$status" "$message" "$db_status" "$db_detail" "$backup_path" "$recovered_path"
72
+ else
73
+ case "$status" in
74
+ ok) eagle_ok "$message" ;;
75
+ repaired) eagle_ok "$message" ;;
76
+ needs_repair) eagle_warn "$message" ;;
77
+ *) eagle_fail "$message" ;;
78
+ esac
79
+ eagle_kv "Database:" "$EAGLE_MEM_DB"
80
+ eagle_kv "Integrity:" "$db_status"
81
+ [ -n "$db_detail" ] && eagle_dim " $db_detail"
82
+ [ -n "$backup_path" ] && eagle_kv "Backup:" "$backup_path"
83
+ [ -n "$recovered_path" ] && eagle_kv "Recovered:" "$recovered_path"
84
+ fi
85
+ }
86
+
87
+ sqlite_bin=$(eagle_sqlite_path)
88
+ if [ -z "$sqlite_bin" ]; then
89
+ repair_emit "error" "SQLite with FTS5 is unavailable." "unavailable" "sqlite3 with FTS5 not found"
90
+ exit 1
91
+ fi
92
+
93
+ if [ ! -f "$EAGLE_MEM_DB" ]; then
94
+ repair_emit "error" "Memory database file is missing." "missing" "database file not found"
95
+ exit 1
96
+ fi
97
+
98
+ integrity_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
99
+ integrity_status="${integrity_check%%|*}"
100
+ integrity_detail="${integrity_check#*|}"
101
+ [ -n "$integrity_status" ] || integrity_status="unknown"
102
+ [ -n "$integrity_detail" ] || integrity_detail="not checked"
103
+
104
+ if [ "$integrity_status" = "ok" ]; then
105
+ repair_emit "ok" "Database integrity is already ok; no repair needed." "$integrity_status" "$integrity_detail"
106
+ exit 0
107
+ fi
108
+
109
+ if [ "$dry_run" = true ] || [ "$assume_yes" != true ]; then
110
+ repair_emit "needs_repair" "Database needs repair. Re-run with --yes to backup, recover, verify, and replace." "$integrity_status" "$integrity_detail"
111
+ exit 0
112
+ fi
113
+
114
+ timestamp=$(date -u +%Y%m%dT%H%M%SZ)
115
+ mkdir -p "$backup_dir"
116
+ backup_path="$backup_dir/memory-$timestamp.db"
117
+ recover_dir=$(mktemp -d "$EAGLE_MEM_DIR/.repair.XXXXXX")
118
+ recover_sql="$recover_dir/recover.sql"
119
+ recovered_path="$recover_dir/memory.db"
120
+ cleanup_repair() {
121
+ rm -rf "$recover_dir" 2>/dev/null || true
122
+ }
123
+ trap cleanup_repair EXIT
124
+
125
+ cp "$EAGLE_MEM_DB" "$backup_path"
126
+ chmod 600 "$backup_path" 2>/dev/null || true
127
+ for suffix in -wal -shm; do
128
+ if [ -f "$EAGLE_MEM_DB$suffix" ]; then
129
+ cp "$EAGLE_MEM_DB$suffix" "$backup_path$suffix"
130
+ chmod 600 "$backup_path$suffix" 2>/dev/null || true
131
+ fi
132
+ done
133
+
134
+ set +e
135
+ recover_output=$("$sqlite_bin" "$EAGLE_MEM_DB" ".recover" 2>&1 > "$recover_sql")
136
+ recover_rc=$?
137
+ set -e
138
+ if [ "$recover_rc" -ne 0 ] || [ ! -s "$recover_sql" ]; then
139
+ repair_emit "error" "SQLite recovery failed; original database was left untouched." "$integrity_status" "${recover_output:-recover output was empty}" "$backup_path"
140
+ exit 1
141
+ fi
142
+
143
+ if ! "$sqlite_bin" "$recovered_path" < "$recover_sql" >/dev/null 2>"$recover_dir/import.err"; then
144
+ import_error=$(tr '\n' ' ' < "$recover_dir/import.err" | sed 's/[[:space:]][[:space:]]*/ /g')
145
+ repair_emit "error" "Recovered SQL could not be imported; original database was left untouched." "$integrity_status" "$import_error" "$backup_path"
146
+ exit 1
147
+ fi
148
+
149
+ core_tables=$("$sqlite_bin" "$recovered_path" "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name IN ('sessions','observations','summaries','agent_memories','agent_tasks');" 2>/dev/null || printf '0')
150
+ if [ "${core_tables:-0}" -eq 0 ] 2>/dev/null; then
151
+ repair_emit "error" "Recovered SQL did not contain Eagle Mem core tables; original database was left untouched." "$integrity_status" "no recoverable Eagle Mem tables found" "$backup_path"
152
+ exit 1
153
+ fi
154
+
155
+ candidate_home="$recover_dir/home"
156
+ mkdir -p "$candidate_home"
157
+ mv "$recovered_path" "$candidate_home/memory.db"
158
+ if ! EAGLE_MEM_DIR="$candidate_home" "$PACKAGE_DIR/db/migrate.sh" >/dev/null 2>"$recover_dir/migrate.err"; then
159
+ migrate_error=$(tr '\n' ' ' < "$recover_dir/migrate.err" | sed 's/[[:space:]][[:space:]]*/ /g')
160
+ repair_emit "error" "Recovered database migrations failed; original database was left untouched." "$integrity_status" "$migrate_error" "$backup_path"
161
+ exit 1
162
+ fi
163
+ recovered_path="$candidate_home/memory.db"
164
+
165
+ post_check=$(eagle_db_integrity_status "$recovered_path" 2>/dev/null || true)
166
+ post_status="${post_check%%|*}"
167
+ post_detail="${post_check#*|}"
168
+ if [ "$post_status" != "ok" ]; then
169
+ repair_emit "error" "Recovered database failed integrity check; original database was left untouched." "$post_status" "$post_detail" "$backup_path" "$recovered_path"
170
+ exit 1
171
+ fi
172
+
173
+ cp "$recovered_path" "$EAGLE_MEM_DB"
174
+ chmod 600 "$EAGLE_MEM_DB" 2>/dev/null || true
175
+ rm -f "$EAGLE_MEM_DB-wal" "$EAGLE_MEM_DB-shm" 2>/dev/null || true
176
+
177
+ final_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
178
+ final_status="${final_check%%|*}"
179
+ final_detail="${final_check#*|}"
180
+ if [ "$final_status" != "ok" ]; then
181
+ cp "$backup_path" "$EAGLE_MEM_DB"
182
+ chmod 600 "$EAGLE_MEM_DB" 2>/dev/null || true
183
+ repair_emit "error" "Replacement failed integrity check; backup was restored." "$final_status" "$final_detail" "$backup_path"
184
+ exit 1
185
+ fi
186
+
187
+ repair_emit "repaired" "Database repaired successfully." "$final_status" "$final_detail" "$backup_path" "$EAGLE_MEM_DB"
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Eagle Mem — Replay
4
+ # Shows prompt → recall → injected context summary → observed work for a session.
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
+ session_id=""
16
+ project=""
17
+ project_was_explicit=false
18
+ cross_project=false
19
+ json_output=false
20
+ last=false
21
+
22
+ show_help() {
23
+ echo -e " ${BOLD}eagle-mem replay${RESET} — Replay what Eagle Mem did in a session"
24
+ echo ""
25
+ echo -e " ${BOLD}Usage:${RESET}"
26
+ echo -e " eagle-mem replay ${CYAN}<session-id>${RESET}"
27
+ echo -e " eagle-mem replay ${CYAN}--last${RESET}"
28
+ echo -e " eagle-mem replay ${CYAN}<session-id> --json${RESET}"
29
+ echo ""
30
+ echo -e " ${BOLD}Options:${RESET}"
31
+ echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current dir scope)"
32
+ echo -e " ${CYAN}--all${RESET} Search all projects when resolving --last"
33
+ echo -e " ${CYAN}--last${RESET} Replay latest recall event session"
34
+ echo -e " ${CYAN}-j, --json${RESET} Output structured JSON"
35
+ echo ""
36
+ exit 0
37
+ }
38
+
39
+ while [ $# -gt 0 ]; do
40
+ case "$1" in
41
+ --project|-p) project="$2"; project_was_explicit=true; shift 2 ;;
42
+ --all|-a) cross_project=true; shift ;;
43
+ --last) last=true; shift ;;
44
+ --json|-j) json_output=true; shift ;;
45
+ --help|-h) show_help ;;
46
+ -*)
47
+ eagle_err "Unknown option: $1"
48
+ exit 1
49
+ ;;
50
+ *)
51
+ session_id="$1"
52
+ shift
53
+ ;;
54
+ esac
55
+ done
56
+
57
+ replay_fail() {
58
+ local error_code="$1"
59
+ local message="$2"
60
+ local db_status="${3:-unknown}"
61
+ local db_detail="${4:-}"
62
+
63
+ if [ "$json_output" = true ]; then
64
+ jq -nc \
65
+ --arg status "error" \
66
+ --arg command "replay" \
67
+ --arg error "$error_code" \
68
+ --arg message "$message" \
69
+ --arg session_id "${session_id:-}" \
70
+ --arg project "${project:-}" \
71
+ --arg db_status "$db_status" \
72
+ --arg db_detail "$db_detail" \
73
+ '{status:$status, command:$command, error:$error, message:$message,
74
+ session_id:$session_id, project:$project,
75
+ database:{integrity:{status:$db_status, detail:$db_detail}}}'
76
+ else
77
+ eagle_err "$message"
78
+ [ -n "$db_detail" ] && eagle_dim " $db_detail"
79
+ fi
80
+ exit 1
81
+ }
82
+
83
+ json_array_or_empty() {
84
+ local value="${1:-}"
85
+ if [ -z "$value" ] || ! printf '%s' "$value" | jq -e 'type == "array"' >/dev/null 2>&1; then
86
+ printf '[]\n'
87
+ return 0
88
+ fi
89
+ printf '%s\n' "$value"
90
+ }
91
+
92
+ if ! eagle_ensure_db; then
93
+ replay_fail "database_unavailable" "Database is unavailable; SQLite/FTS5 setup failed." "unavailable" "eagle_ensure_db failed"
94
+ fi
95
+
96
+ db_integrity_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
97
+ db_integrity_status="${db_integrity_check%%|*}"
98
+ db_integrity_detail="${db_integrity_check#*|}"
99
+ [ -n "$db_integrity_status" ] || db_integrity_status="unknown"
100
+ [ -n "$db_integrity_detail" ] || db_integrity_detail="not checked"
101
+ if [ "$db_integrity_status" != "ok" ]; then
102
+ replay_fail "database_integrity" "Database integrity check failed; replay is unavailable." "$db_integrity_status" "$db_integrity_detail"
103
+ fi
104
+
105
+ if [ -z "$(eagle_db "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'recall_events' LIMIT 1;" 2>/dev/null || true)" ]; then
106
+ replay_fail "migration_missing" "Replay is unavailable until migrations create recall_events." "ok" "run: eagle-mem update"
107
+ fi
108
+
109
+ if [ -z "$project" ] && [ "$cross_project" = false ]; then
110
+ project=$(eagle_project_from_cwd "$(pwd)")
111
+ fi
112
+ if [ "$cross_project" = false ] && [ "$project_was_explicit" = false ]; then
113
+ project=$(eagle_recall_project_scope_from_cwd "$(pwd)" "$project")
114
+ fi
115
+
116
+ where_project="1 = 1"
117
+ if [ "$cross_project" = false ]; then
118
+ where_project=$(eagle_sql_project_scope_condition "project" "$project")
119
+ fi
120
+
121
+ if [ -z "$session_id" ] || [ "$last" = true ]; then
122
+ session_id=$(eagle_db "SELECT session_id
123
+ FROM recall_events
124
+ WHERE $where_project
125
+ AND session_id IS NOT NULL
126
+ AND session_id != ''
127
+ ORDER BY created_at DESC, id DESC
128
+ LIMIT 1;" 2>/dev/null | awk 'NF { print; exit }')
129
+ fi
130
+
131
+ [ -n "$session_id" ] || replay_fail "session_missing" "No session id provided and no replayable recall event was found." "ok" "run: eagle-mem replay <session-id>"
132
+
133
+ sid_sql=$(eagle_sql_escape "$session_id")
134
+
135
+ session_json=$(eagle_db_json "SELECT id, project, agent, cwd, model, source, started_at, ended_at, status
136
+ FROM sessions
137
+ WHERE id = '$sid_sql'
138
+ LIMIT 1;" 2>/dev/null || printf '[]')
139
+ summary_json=$(eagle_db_json "SELECT request, completed, learned, decisions, gotchas, key_files, agent, created_at
140
+ FROM summaries
141
+ WHERE session_id = '$sid_sql'
142
+ ORDER BY created_at DESC;" 2>/dev/null || printf '[]')
143
+ recall_json=$(eagle_db_json "SELECT prompt_snippet, fts_query, summary_matches, memory_matches, code_matches,
144
+ summary_refs, memory_refs, code_refs, injected_token_estimate,
145
+ status, error, agent, created_at
146
+ FROM recall_events
147
+ WHERE session_id = '$sid_sql'
148
+ ORDER BY created_at ASC, id ASC;" 2>/dev/null || printf '[]')
149
+ observation_json=$(eagle_db_json "SELECT tool_name, tool_input_summary, files_read, files_modified, created_at
150
+ FROM observations
151
+ WHERE session_id = '$sid_sql'
152
+ ORDER BY created_at ASC
153
+ LIMIT 100;" 2>/dev/null || printf '[]')
154
+
155
+ session_json=$(json_array_or_empty "$session_json")
156
+ summary_json=$(json_array_or_empty "$summary_json")
157
+ recall_json=$(json_array_or_empty "$recall_json")
158
+ observation_json=$(json_array_or_empty "$observation_json")
159
+
160
+ replay_json=$(jq -nc \
161
+ --arg status "ok" \
162
+ --arg command "replay" \
163
+ --arg session_id "$session_id" \
164
+ --arg db_integrity_status "$db_integrity_status" \
165
+ --arg db_integrity_detail "$db_integrity_detail" \
166
+ --argjson session "$session_json" \
167
+ --argjson summaries "$summary_json" \
168
+ --argjson recalls "$recall_json" \
169
+ --argjson observations "$observation_json" \
170
+ '{status:$status, command:$command, session_id:$session_id,
171
+ database:{integrity:{status:$db_integrity_status, detail:$db_integrity_detail}},
172
+ session:($session[0] // null),
173
+ summaries:$summaries,
174
+ recall_events:($recalls | map(
175
+ .summary_refs = ((.summary_refs // "[]") | fromjson? // []) |
176
+ .memory_refs = ((.memory_refs // "[]") | fromjson? // []) |
177
+ .code_refs = ((.code_refs // "[]") | fromjson? // [])
178
+ )),
179
+ observations:$observations}')
180
+
181
+ if [ "$json_output" = true ]; then
182
+ printf '%s\n' "$replay_json"
183
+ exit 0
184
+ fi
185
+
186
+ eagle_header "Replay"
187
+ eagle_info "Session: $session_id"
188
+ project_label=$(printf '%s' "$replay_json" | jq -r '.session.project // empty')
189
+ [ -n "$project_label" ] && eagle_info "Project: $project_label"
190
+ echo ""
191
+
192
+ recall_count=$(printf '%s' "$replay_json" | jq '.recall_events | length')
193
+ summary_count=$(printf '%s' "$replay_json" | jq '.summaries | length')
194
+ observation_count=$(printf '%s' "$replay_json" | jq '.observations | length')
195
+ eagle_kv "Recall events:" "$recall_count"
196
+ eagle_kv "Summaries:" "$summary_count"
197
+ eagle_kv "Observations:" "$observation_count"
198
+ echo ""
199
+
200
+ printf '%s' "$replay_json" | jq -c '.recall_events[]' | while IFS= read -r event; do
201
+ created=$(printf '%s' "$event" | jq -r '.created_at // ""')
202
+ agent=$(printf '%s' "$event" | jq -r '.agent // ""')
203
+ prompt=$(printf '%s' "$event" | jq -r '.prompt_snippet // ""')
204
+ query=$(printf '%s' "$event" | jq -r '.fts_query // ""')
205
+ tokens=$(printf '%s' "$event" | jq -r '.injected_token_estimate // 0')
206
+ summaries=$(printf '%s' "$event" | jq -r '.summary_matches // 0')
207
+ memories=$(printf '%s' "$event" | jq -r '.memory_matches // 0')
208
+ code=$(printf '%s' "$event" | jq -r '.code_matches // 0')
209
+
210
+ echo -e " ${BOLD}${created}${RESET} ${DIM}$(eagle_agent_label "$agent")${RESET}"
211
+ [ -n "$prompt" ] && echo -e " ${DIM}Prompt:${RESET} $prompt"
212
+ [ -n "$query" ] && echo -e " ${DIM}Query:${RESET} $query"
213
+ echo -e " ${DIM}Retrieved:${RESET} summaries=$summaries memories=$memories code=$code ${DIM}Injected:${RESET} ~${tokens} tokens"
214
+
215
+ summary_refs=$(printf '%s' "$event" | jq -r '.summary_refs[]? | "- summary: " + ((.completed // .request // "summary") | tostring)' 2>/dev/null)
216
+ memory_refs=$(printf '%s' "$event" | jq -r '.memory_refs[]? | "- memory: " + ((.name // "memory") | tostring)' 2>/dev/null)
217
+ code_refs=$(printf '%s' "$event" | jq -r '.code_refs[]? | "- code: " + ((.file // "file") | tostring) + ":" + ((.start_line // "?") | tostring) + "-" + ((.end_line // "?") | tostring)' 2>/dev/null)
218
+
219
+ if [ -n "$summary_refs$memory_refs$code_refs" ]; then
220
+ echo -e " ${DIM}Retrieved refs:${RESET}"
221
+ [ -n "$summary_refs" ] && printf '%s\n' "$summary_refs" | sed 's/^/ /'
222
+ [ -n "$memory_refs" ] && printf '%s\n' "$memory_refs" | sed 's/^/ /'
223
+ [ -n "$code_refs" ] && printf '%s\n' "$code_refs" | sed 's/^/ /'
224
+ fi
225
+ echo ""
226
+ done
227
+
228
+ if [ "$summary_count" -gt 0 ]; then
229
+ echo -e " ${BOLD}Session summaries${RESET}"
230
+ printf '%s' "$replay_json" | jq -c '.summaries[]' | while IFS= read -r row; do
231
+ completed=$(printf '%s' "$row" | jq -r '.completed // ""')
232
+ learned=$(printf '%s' "$row" | jq -r '.learned // ""')
233
+ [ -n "$completed" ] && echo -e " ${GREEN}Done:${RESET} $completed"
234
+ [ -n "$learned" ] && echo -e " ${YELLOW}Learned:${RESET} $learned"
235
+ done
236
+ echo ""
237
+ fi
238
+
239
+ if [ "$observation_count" -gt 0 ]; then
240
+ echo -e " ${BOLD}Observed tools${RESET}"
241
+ printf '%s' "$replay_json" | jq -c '.observations[-12:][]' | while IFS= read -r row; do
242
+ tool=$(printf '%s' "$row" | jq -r '.tool_name // ""')
243
+ detail=$(printf '%s' "$row" | jq -r '.tool_input_summary // ""')
244
+ created=$(printf '%s' "$row" | jq -r '.created_at // ""')
245
+ echo -e " ${DIM}${created}${RESET} ${BOLD}${tool}:${RESET} $detail"
246
+ done
247
+ echo ""
248
+ fi
package/scripts/search.sh CHANGED
@@ -12,8 +12,6 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
12
12
  . "$LIB_DIR/common.sh"
13
13
  . "$LIB_DIR/db.sh"
14
14
 
15
- eagle_ensure_db
16
-
17
15
  # ─── Help ────────────────────────────────────────────────
18
16
 
19
17
  show_help() {
@@ -77,6 +75,44 @@ while [ $# -gt 0 ]; do
77
75
  esac
78
76
  done
79
77
 
78
+ search_fail() {
79
+ local error_code="$1"
80
+ local message="$2"
81
+ local db_status="${3:-unknown}"
82
+ local db_detail="${4:-}"
83
+
84
+ if [ "$json_output" = true ]; then
85
+ jq -nc \
86
+ --arg status "error" \
87
+ --arg command "search" \
88
+ --arg mode "$mode" \
89
+ --arg error "$error_code" \
90
+ --arg message "$message" \
91
+ --arg project "${project:-}" \
92
+ --arg db_status "$db_status" \
93
+ --arg db_detail "$db_detail" \
94
+ '{status:$status, command:$command, mode:$mode, error:$error, message:$message,
95
+ project:$project, database:{integrity:{status:$db_status, detail:$db_detail}}}'
96
+ else
97
+ eagle_err "$message"
98
+ [ -n "$db_detail" ] && eagle_dim " $db_detail"
99
+ fi
100
+ exit 1
101
+ }
102
+
103
+ if ! eagle_ensure_db; then
104
+ search_fail "database_unavailable" "Database is unavailable; SQLite/FTS5 setup failed." "unavailable" "eagle_ensure_db failed"
105
+ fi
106
+
107
+ db_integrity_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
108
+ db_integrity_status="${db_integrity_check%%|*}"
109
+ db_integrity_detail="${db_integrity_check#*|}"
110
+ [ -n "$db_integrity_status" ] || db_integrity_status="unknown"
111
+ [ -n "$db_integrity_detail" ] || db_integrity_detail="not checked"
112
+ if [ "$db_integrity_status" != "ok" ]; then
113
+ search_fail "database_integrity" "Database integrity check failed; memory search is unavailable." "$db_integrity_status" "$db_integrity_detail"
114
+ fi
115
+
80
116
  [ -z "$project" ] && project=$(eagle_project_from_cwd "$(pwd)")
81
117
  project_scope="$project"
82
118
  if [ "$cross_project" = false ] && [ "$project_was_explicit" = false ]; then
@@ -438,7 +474,12 @@ search_stats() {
438
474
  --argjson observations "${observations:-0}" \
439
475
  --argjson tasks "${tasks:-0}" \
440
476
  --argjson code_chunks "${chunks:-0}" \
441
- '{project: $project, sessions: $sessions, sessions_claude: $sessions_claude, sessions_codex: $sessions_codex, summaries: $summaries, observations: $observations, tasks: $tasks, code_chunks: $code_chunks}'
477
+ --arg db_integrity_status "$db_integrity_status" \
478
+ --arg db_integrity_detail "$db_integrity_detail" \
479
+ '{status:"ok", project: $project,
480
+ database:{integrity:{status:$db_integrity_status, detail:$db_integrity_detail}},
481
+ sessions: $sessions, sessions_claude: $sessions_claude, sessions_codex: $sessions_codex,
482
+ summaries: $summaries, observations: $observations, tasks: $tasks, code_chunks: $code_chunks}'
442
483
  return
443
484
  fi
444
485