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.
- package/CHANGELOG.md +25 -0
- package/README.md +22 -22
- package/architecture.html +26 -14
- package/bin/eagle-mem +4 -0
- package/db/039_recall_events.sql +27 -0
- package/db/040_graph_decision_nodes.sql +21 -0
- package/db/041_graph_semantic_edge_types.sql +21 -0
- package/db/042_orchestration_auto_events.sql +23 -0
- package/db/043_eagle_events.sql +22 -0
- package/db/044_summary_capture_source.sql +12 -0
- package/docs/agent-compatibility/README.md +38 -0
- package/docs/agent-compatibility/claude-code.md +58 -0
- package/docs/agent-compatibility/codex.md +57 -0
- package/docs/agent-compatibility/opencode.md +72 -0
- package/hooks/post-tool-use.sh +8 -0
- package/hooks/pre-tool-use.sh +10 -1
- package/hooks/session-end.sh +3 -0
- package/hooks/session-start.sh +15 -17
- package/hooks/stop.sh +34 -5
- package/hooks/user-prompt-submit.sh +85 -10
- package/integrations/opencode_eagle_mem_plugin.js +387 -0
- package/lib/codex-hooks.sh +13 -6
- package/lib/common.sh +77 -7
- package/lib/db-events.sh +89 -0
- package/lib/db-graph.sh +154 -0
- package/lib/db-observations.sh +34 -0
- package/lib/db-orchestration.sh +149 -0
- package/lib/db-summaries.sh +70 -3
- package/lib/db.sh +2 -0
- package/lib/hooks.sh +41 -7
- package/lib/opencode-hooks.sh +105 -0
- package/lib/provider.sh +2 -2
- package/package.json +5 -2
- package/scripts/compaction.sh +109 -9
- package/scripts/dashboard.sh +372 -0
- package/scripts/doctor.sh +30 -3
- package/scripts/enrich-summary.sh +8 -2
- package/scripts/health.sh +40 -2
- package/scripts/help.sh +10 -2
- package/scripts/inspect.sh +285 -0
- package/scripts/install.sh +36 -7
- package/scripts/memories.sh +13 -0
- package/scripts/repair.sh +187 -0
- package/scripts/replay.sh +248 -0
- package/scripts/search.sh +44 -3
- package/scripts/session.sh +155 -18
- package/scripts/statusline-em.sh +34 -7
- package/scripts/tasks.sh +34 -0
- package/scripts/test.sh +13 -0
- package/scripts/uninstall.sh +9 -0
- package/scripts/update.sh +21 -2
- package/tests/fixtures/agent-hooks/claude-statusline.json +32 -0
- package/tests/fixtures/agent-hooks/claude-user-prompt-submit.json +9 -0
- package/tests/fixtures/agent-hooks/codex-pre-tool-use.json +10 -0
- package/tests/fixtures/agent-hooks/codex-user-prompt-submit.json +7 -0
- package/tests/fixtures/agent-hooks/opencode-chat-message.json +36 -0
- package/tests/fixtures/agent-hooks/opencode-session-compacting.json +9 -0
- package/tests/fixtures/agent-hooks/opencode-todo-updated.json +13 -0
- package/tests/fixtures/agent-hooks/opencode-tool-execute-after.json +15 -0
- package/tests/fixtures/agent-hooks/opencode-tool-execute-before.json +12 -0
- package/tests/test_agent_compatibility_docs_gate.sh +123 -0
- package/tests/test_auto_orchestration_detection.sh +109 -0
- package/tests/test_claude_stop_hook_registration.sh +56 -0
- package/tests/test_clean_session_capture.sh +105 -0
- package/tests/test_codex_hooks_config.sh +73 -0
- package/tests/test_compaction_survival_matrix.sh +237 -0
- package/tests/test_dashboard.sh +96 -0
- package/tests/test_eagle_events.sh +96 -0
- package/tests/test_opencode_hooks_config.sh +56 -0
- package/tests/test_opencode_plugin_adapter.sh +202 -0
- package/tests/test_recall_observability.sh +144 -0
- package/tests/test_repair.sh +63 -0
- package/tests/test_rust_migration_plan.sh +75 -0
- 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
|
-
|
|
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
|
|