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
package/scripts/session.sh
CHANGED
|
@@ -19,21 +19,30 @@ show_help() {
|
|
|
19
19
|
echo -e " eagle-mem session ${CYAN}save <text>${RESET}"
|
|
20
20
|
echo ""
|
|
21
21
|
echo -e " ${BOLD}Options for save:${RESET}"
|
|
22
|
-
echo -e " ${CYAN}--
|
|
22
|
+
echo -e " ${CYAN}--completed${RESET} <text> What was accomplished (alias: --summary)"
|
|
23
23
|
echo -e " ${CYAN}--request${RESET} <text> User request that caused the work"
|
|
24
|
+
echo -e " ${CYAN}--investigated${RESET} <text> What was explored or analyzed"
|
|
24
25
|
echo -e " ${CYAN}--learned${RESET} <text> Non-obvious discoveries"
|
|
25
26
|
echo -e " ${CYAN}--decisions${RESET} <text> Decisions and why"
|
|
26
27
|
echo -e " ${CYAN}--gotchas${RESET} <text> Surprises or pitfalls"
|
|
27
28
|
echo -e " ${CYAN}--next-steps${RESET} <text> Follow-up work"
|
|
28
|
-
echo -e " ${CYAN}--key-files${RESET} <text> Important files"
|
|
29
|
+
echo -e " ${CYAN}--key-files${RESET} <text> Important files (path — role)"
|
|
30
|
+
echo -e " ${CYAN}--files-read${RESET} <list> Comma-separated files read"
|
|
31
|
+
echo -e " ${CYAN}--files-modified${RESET} <list> Comma-separated files modified"
|
|
32
|
+
echo -e " ${CYAN}--affected-features${RESET} <text> Features touched (need re-verify)"
|
|
33
|
+
echo -e " ${CYAN}--verified-features${RESET} <text> Features verified this session"
|
|
34
|
+
echo -e " ${CYAN}--regression-risks${RESET} <text> Known risks introduced"
|
|
29
35
|
echo -e " ${CYAN}--notes${RESET} <text> Extra notes"
|
|
36
|
+
echo -e " ${CYAN}--session-id${RESET} <id> Live session id (merges into the active row;"
|
|
37
|
+
echo -e " omit for a standalone manual save)"
|
|
30
38
|
echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current git root)"
|
|
31
|
-
echo -e " ${CYAN}--agent${RESET} <name> Source
|
|
39
|
+
echo -e " ${CYAN}--agent${RESET} <name> Source: claude-code, codex, antigravity, opencode, grok"
|
|
32
40
|
echo -e " ${CYAN}--cwd${RESET} <path> Working directory for project detection"
|
|
33
41
|
echo -e " ${CYAN}--json${RESET} Output JSON"
|
|
34
42
|
echo ""
|
|
35
|
-
echo -e " ${DIM}
|
|
36
|
-
echo -e " ${DIM}
|
|
43
|
+
echo -e " ${DIM}Agents use this to capture a clean, branded session summary without printing${RESET}"
|
|
44
|
+
echo -e " ${DIM}raw blocks. Pass --session-id <id> to merge into the live session row. Stop${RESET}"
|
|
45
|
+
echo -e " ${DIM}hooks still capture automatically as a safety net when no save is made.${RESET}"
|
|
37
46
|
echo ""
|
|
38
47
|
}
|
|
39
48
|
|
|
@@ -49,24 +58,48 @@ json_string() {
|
|
|
49
58
|
jq -Rn --arg v "${1:-}" '$v'
|
|
50
59
|
}
|
|
51
60
|
|
|
61
|
+
# Comma-separated list → JSON array (matches hooks/stop.sh storage shape).
|
|
62
|
+
# Safe under set -e: empty input yields [] without a failing pipeline.
|
|
63
|
+
# Slurp the whole value (-s) so embedded newlines split into items rather than
|
|
64
|
+
# emitting one JSON array per line (which would corrupt the stored column).
|
|
65
|
+
csv_to_json_array() {
|
|
66
|
+
local raw="${1:-}"
|
|
67
|
+
[ -z "$raw" ] && { echo '[]'; return 0; }
|
|
68
|
+
printf '%s' "$raw" | jq -Rsc 'split("[,\n]"; "") | map(gsub("^\\s+|\\s+$";"")) | map(select(. != ""))'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Count items in a field separated by ';' or newlines (for the capture banner).
|
|
72
|
+
count_items() {
|
|
73
|
+
local text="${1:-}"
|
|
74
|
+
[ -z "$text" ] && { echo 0; return 0; }
|
|
75
|
+
printf '%s' "$text" | awk 'BEGIN{RS="[;\n]"} {gsub(/^[ \t]+|[ \t]+$/,""); if($0!="") n++} END{print n+0}'
|
|
76
|
+
}
|
|
77
|
+
|
|
52
78
|
save_session() {
|
|
53
79
|
local summary=""
|
|
54
|
-
local request="
|
|
80
|
+
local request=""
|
|
81
|
+
local investigated=""
|
|
55
82
|
local learned=""
|
|
56
83
|
local decisions=""
|
|
57
84
|
local gotchas=""
|
|
58
85
|
local next_steps=""
|
|
59
86
|
local key_files=""
|
|
87
|
+
local files_read_raw=""
|
|
88
|
+
local files_modified_raw=""
|
|
89
|
+
local affected_features=""
|
|
90
|
+
local verified_features=""
|
|
91
|
+
local regression_risks=""
|
|
60
92
|
local notes=""
|
|
61
93
|
local project=""
|
|
62
94
|
local cwd
|
|
63
95
|
cwd="$(pwd)"
|
|
64
96
|
local agent=""
|
|
97
|
+
local cli_session_id=""
|
|
65
98
|
local json_output=false
|
|
66
99
|
|
|
67
100
|
while [ $# -gt 0 ]; do
|
|
68
101
|
case "$1" in
|
|
69
|
-
--summary)
|
|
102
|
+
--summary|--completed)
|
|
70
103
|
require_value "$1" "${2:-}"
|
|
71
104
|
summary="$2"
|
|
72
105
|
shift 2
|
|
@@ -76,6 +109,11 @@ save_session() {
|
|
|
76
109
|
request="$2"
|
|
77
110
|
shift 2
|
|
78
111
|
;;
|
|
112
|
+
--investigated)
|
|
113
|
+
require_value "$1" "${2:-}"
|
|
114
|
+
investigated="$2"
|
|
115
|
+
shift 2
|
|
116
|
+
;;
|
|
79
117
|
--learned)
|
|
80
118
|
require_value "$1" "${2:-}"
|
|
81
119
|
learned="$2"
|
|
@@ -101,11 +139,41 @@ save_session() {
|
|
|
101
139
|
key_files="$2"
|
|
102
140
|
shift 2
|
|
103
141
|
;;
|
|
142
|
+
--files-read)
|
|
143
|
+
require_value "$1" "${2:-}"
|
|
144
|
+
files_read_raw="$2"
|
|
145
|
+
shift 2
|
|
146
|
+
;;
|
|
147
|
+
--files-modified)
|
|
148
|
+
require_value "$1" "${2:-}"
|
|
149
|
+
files_modified_raw="$2"
|
|
150
|
+
shift 2
|
|
151
|
+
;;
|
|
152
|
+
--affected-features)
|
|
153
|
+
require_value "$1" "${2:-}"
|
|
154
|
+
affected_features="$2"
|
|
155
|
+
shift 2
|
|
156
|
+
;;
|
|
157
|
+
--verified-features)
|
|
158
|
+
require_value "$1" "${2:-}"
|
|
159
|
+
verified_features="$2"
|
|
160
|
+
shift 2
|
|
161
|
+
;;
|
|
162
|
+
--regression-risks)
|
|
163
|
+
require_value "$1" "${2:-}"
|
|
164
|
+
regression_risks="$2"
|
|
165
|
+
shift 2
|
|
166
|
+
;;
|
|
104
167
|
--notes)
|
|
105
168
|
require_value "$1" "${2:-}"
|
|
106
169
|
notes="$2"
|
|
107
170
|
shift 2
|
|
108
171
|
;;
|
|
172
|
+
--session-id)
|
|
173
|
+
require_value "$1" "${2:-}"
|
|
174
|
+
cli_session_id="$2"
|
|
175
|
+
shift 2
|
|
176
|
+
;;
|
|
109
177
|
--project|-p)
|
|
110
178
|
require_value "$1" "${2:-}"
|
|
111
179
|
project="$2"
|
|
@@ -147,11 +215,25 @@ save_session() {
|
|
|
147
215
|
esac
|
|
148
216
|
done
|
|
149
217
|
|
|
150
|
-
if [ -z "$summary" ]
|
|
151
|
-
|
|
218
|
+
if [ -z "$summary" ] && [ -z "$learned" ] && [ -z "$decisions" ] && [ -z "$gotchas" ] \
|
|
219
|
+
&& [ -z "$next_steps" ] && [ -z "$key_files" ] && [ -z "$investigated" ] \
|
|
220
|
+
&& [ -z "$files_read_raw" ] && [ -z "$files_modified_raw" ] \
|
|
221
|
+
&& [ -z "$affected_features" ] && [ -z "$verified_features" ] && [ -z "$regression_risks" ]; then
|
|
222
|
+
eagle_err "Nothing to save. Pass --completed/--summary (or another field)."
|
|
152
223
|
exit 1
|
|
153
224
|
fi
|
|
154
225
|
|
|
226
|
+
if [ -n "$cli_session_id" ] && ! eagle_validate_session_id "$cli_session_id"; then
|
|
227
|
+
eagle_err "Invalid --session-id (allowed: letters, digits, '_', '-', max 128 chars)."
|
|
228
|
+
exit 1
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
# For a live session, prefer its recorded project so a save issued from a
|
|
232
|
+
# subdirectory (whose cwd derives a different key) does not trigger an
|
|
233
|
+
# unintended project rekey of the active session.
|
|
234
|
+
if [ -z "$project" ] && [ -n "$cli_session_id" ] && [ -f "${EAGLE_MEM_DB:-}" ]; then
|
|
235
|
+
project=$(eagle_db "SELECT project FROM sessions WHERE id='$(eagle_sql_escape "$cli_session_id")' LIMIT 1;" 2>/dev/null || true)
|
|
236
|
+
fi
|
|
155
237
|
[ -z "$project" ] && project=$(eagle_project_from_cwd "$cwd")
|
|
156
238
|
if [ -z "$project" ]; then
|
|
157
239
|
eagle_err "Could not determine project. Re-run with --project <name>."
|
|
@@ -165,44 +247,99 @@ save_session() {
|
|
|
165
247
|
codex|openai-codex) agent="codex" ;;
|
|
166
248
|
claude|claude-code|cloud-code) agent="claude-code" ;;
|
|
167
249
|
antigravity*|google-antigravity*|google_antigravity*) agent="antigravity" ;;
|
|
250
|
+
opencode) agent="opencode" ;;
|
|
251
|
+
grok|grok-cli) agent="grok" ;;
|
|
168
252
|
*)
|
|
169
|
-
eagle_err "--agent must be codex, claude-code, or
|
|
253
|
+
eagle_err "--agent must be codex, claude-code, antigravity, opencode, or grok"
|
|
170
254
|
exit 1
|
|
171
255
|
;;
|
|
172
256
|
esac
|
|
173
257
|
fi
|
|
174
258
|
|
|
259
|
+
# Default request only for manual (non-session-id) saves; for a live
|
|
260
|
+
# session leave it empty so the Stop hook's real request is preserved.
|
|
261
|
+
if [ -z "$request" ] && [ -z "$cli_session_id" ]; then
|
|
262
|
+
request="Manual session save"
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
local files_read files_modified
|
|
266
|
+
files_read=$(csv_to_json_array "$files_read_raw")
|
|
267
|
+
files_modified=$(csv_to_json_array "$files_modified_raw")
|
|
268
|
+
|
|
175
269
|
summary=$(printf '%s' "$summary" | eagle_redact)
|
|
176
270
|
request=$(printf '%s' "$request" | eagle_redact)
|
|
271
|
+
investigated=$(printf '%s' "$investigated" | eagle_redact)
|
|
177
272
|
learned=$(printf '%s' "$learned" | eagle_redact)
|
|
178
273
|
decisions=$(printf '%s' "$decisions" | eagle_redact)
|
|
179
274
|
gotchas=$(printf '%s' "$gotchas" | eagle_redact)
|
|
180
275
|
next_steps=$(printf '%s' "$next_steps" | eagle_redact)
|
|
181
276
|
key_files=$(printf '%s' "$key_files" | eagle_redact)
|
|
182
277
|
notes=$(printf '%s' "$notes" | eagle_redact)
|
|
278
|
+
affected_features=$(printf '%s' "$affected_features" | eagle_redact)
|
|
279
|
+
verified_features=$(printf '%s' "$verified_features" | eagle_redact)
|
|
280
|
+
regression_risks=$(printf '%s' "$regression_risks" | eagle_redact)
|
|
281
|
+
|
|
282
|
+
# Fold feature-tracking fields into notes (same shape as hooks/stop.sh)
|
|
283
|
+
local regression_notes=""
|
|
284
|
+
[ -n "$affected_features" ] && regression_notes+="affected_features: $affected_features"
|
|
285
|
+
if [ -n "$verified_features" ]; then
|
|
286
|
+
[ -n "$regression_notes" ] && regression_notes+="; "
|
|
287
|
+
regression_notes+="verified_features: $verified_features"
|
|
288
|
+
fi
|
|
289
|
+
if [ -n "$regression_risks" ]; then
|
|
290
|
+
[ -n "$regression_notes" ] && regression_notes+="; "
|
|
291
|
+
regression_notes+="regression_risks: $regression_risks"
|
|
292
|
+
fi
|
|
293
|
+
if [ -n "$regression_notes" ]; then
|
|
294
|
+
if [ -n "$notes" ]; then
|
|
295
|
+
notes="${notes}; ${regression_notes}"
|
|
296
|
+
else
|
|
297
|
+
notes="$regression_notes"
|
|
298
|
+
fi
|
|
299
|
+
fi
|
|
183
300
|
|
|
184
301
|
eagle_ensure_db
|
|
185
302
|
|
|
186
|
-
local
|
|
187
|
-
|
|
188
|
-
|
|
303
|
+
local session_id end_after_save=1
|
|
304
|
+
if [ -n "$cli_session_id" ]; then
|
|
305
|
+
# Live session: ensure the row exists and stays active. Empty source/cwd
|
|
306
|
+
# preserve whatever SessionStart already recorded; do NOT end the session.
|
|
307
|
+
session_id="$cli_session_id"
|
|
308
|
+
end_after_save=0
|
|
309
|
+
eagle_upsert_session "$session_id" "$project" "" "" "" "$agent"
|
|
310
|
+
else
|
|
311
|
+
local stamp
|
|
312
|
+
stamp=$(date -u +%Y%m%dT%H%M%SZ)
|
|
313
|
+
session_id="manual-${stamp}-$$-${RANDOM:-0}"
|
|
314
|
+
eagle_upsert_session "$session_id" "$project" "$cwd" "" "manual" "$agent"
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
# capture_source=agent: agent-authored capture is authoritative and must not
|
|
318
|
+
# be clobbered by later Stop-hook heuristics or background enrichment.
|
|
319
|
+
eagle_insert_summary "$session_id" "$project" "$request" "$investigated" "$learned" "$summary" "$next_steps" "$files_read" "$files_modified" "$notes" "$decisions" "$gotchas" "$key_files" "$agent" "agent"
|
|
320
|
+
|
|
321
|
+
[ "$end_after_save" = "1" ] && eagle_end_session "$session_id"
|
|
189
322
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
323
|
+
local n_dec n_got dec_word got_word
|
|
324
|
+
n_dec=$(count_items "$decisions")
|
|
325
|
+
n_got=$(count_items "$gotchas")
|
|
326
|
+
[ "$n_dec" = "1" ] && dec_word="decision" || dec_word="decisions"
|
|
327
|
+
[ "$n_got" = "1" ] && got_word="gotcha" || got_word="gotchas"
|
|
193
328
|
|
|
194
329
|
if [ "$json_output" = true ]; then
|
|
195
330
|
printf '{'
|
|
196
331
|
printf '"session_id":%s,' "$(json_string "$session_id")"
|
|
197
332
|
printf '"project":%s,' "$(json_string "$project")"
|
|
198
333
|
printf '"agent":%s,' "$(json_string "$agent")"
|
|
334
|
+
printf '"decisions":%s,' "$n_dec"
|
|
335
|
+
printf '"gotchas":%s,' "$n_got"
|
|
199
336
|
printf '"summary":%s' "$(json_string "$summary")"
|
|
200
337
|
printf '}\n'
|
|
201
338
|
else
|
|
202
|
-
|
|
339
|
+
printf ' %bEagle Mem%b | Session captured — %s %s, %s %s\n' \
|
|
340
|
+
"$CYAN" "$RESET" "$n_dec" "$dec_word" "$n_got" "$got_word"
|
|
203
341
|
eagle_kv "Project:" "$project"
|
|
204
342
|
eagle_kv "Source:" "$(eagle_agent_label "$agent")"
|
|
205
|
-
eagle_kv "Session:" "$session_id"
|
|
206
343
|
fi
|
|
207
344
|
}
|
|
208
345
|
|
package/scripts/statusline-em.sh
CHANGED
|
@@ -41,10 +41,11 @@ eagle_mem_statusline_stats() {
|
|
|
41
41
|
local session_id="${2:-}"
|
|
42
42
|
local statusline_input="${3:-}"
|
|
43
43
|
local current_dir="${4:-}"
|
|
44
|
-
local em_db="$HOME/.eagle-mem/memory.db"
|
|
44
|
+
local em_db="${EAGLE_MEM_DIR:-$HOME/.eagle-mem}/memory.db"
|
|
45
45
|
[ -f "$em_db" ] || return
|
|
46
46
|
|
|
47
47
|
_eagle_statusline_load_common
|
|
48
|
+
em_db="$EAGLE_MEM_DB"
|
|
48
49
|
|
|
49
50
|
local sqlite_bin
|
|
50
51
|
sqlite_bin=$(eagle_sqlite_path)
|
|
@@ -58,7 +59,7 @@ eagle_mem_statusline_stats() {
|
|
|
58
59
|
[ -z "$project_dir" ] && project_dir="$(pwd)"
|
|
59
60
|
[ -z "$current_dir" ] && current_dir="$project_dir"
|
|
60
61
|
|
|
61
|
-
local project_key project_scope project_condition stats sessions memories last_raw turns version latest
|
|
62
|
+
local project_key project_scope project_condition stats stats_rc sessions memories last_raw turns version latest db_status db_detail
|
|
62
63
|
project_key=$(eagle_project_from_statusline_input "$statusline_input" "$project_dir" "$current_dir" "$session_id")
|
|
63
64
|
[ -n "$project_key" ] || return
|
|
64
65
|
project_scope=$(eagle_recall_project_scope_from_cwd "${current_dir:-$project_dir}" "$project_key")
|
|
@@ -70,6 +71,20 @@ eagle_mem_statusline_stats() {
|
|
|
70
71
|
COALESCE(MAX(COALESCE(last_activity_at, started_at)), 'never')
|
|
71
72
|
FROM sessions
|
|
72
73
|
WHERE $project_condition;" 2>/dev/null)
|
|
74
|
+
stats_rc=$?
|
|
75
|
+
db_status="ok"
|
|
76
|
+
db_detail="ok"
|
|
77
|
+
if [ "$stats_rc" -ne 0 ] || [ -z "$stats" ]; then
|
|
78
|
+
local integrity_check
|
|
79
|
+
integrity_check=$(eagle_db_integrity_status "$em_db" 2>/dev/null || true)
|
|
80
|
+
db_status="${integrity_check%%|*}"
|
|
81
|
+
db_detail="${integrity_check#*|}"
|
|
82
|
+
[ -n "$db_status" ] || db_status="error"
|
|
83
|
+
[ -n "$db_detail" ] || db_detail="database query failed"
|
|
84
|
+
if [ "$db_status" != "ok" ]; then
|
|
85
|
+
stats="__DB_ERROR__|0|db_error"
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
73
88
|
IFS='|' read -r sessions memories last_raw <<< "${stats:-0|0|never}"
|
|
74
89
|
sessions=${sessions:-0}
|
|
75
90
|
memories=${memories:-0}
|
|
@@ -92,15 +107,20 @@ eagle_mem_statusline_stats() {
|
|
|
92
107
|
[ -z "$version" ] && version="?"
|
|
93
108
|
[ -z "$latest" ] && latest="$version"
|
|
94
109
|
|
|
95
|
-
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "$project_key" "$version" "$latest" "$sessions" "$memories" "$turns" "$last_raw"
|
|
110
|
+
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "$project_key" "$version" "$latest" "$sessions" "$memories" "$turns" "$last_raw" "$db_status"
|
|
96
111
|
}
|
|
97
112
|
|
|
98
113
|
eagle_mem_statusline() {
|
|
99
|
-
local stats project_key version latest sessions memories turns last_raw
|
|
114
|
+
local stats project_key version latest sessions memories turns last_raw db_status
|
|
100
115
|
stats=$(eagle_mem_statusline_stats "${1:-}" "${2:-}" "${3:-}" "${4:-}") || return
|
|
101
|
-
IFS=$'\t' read -r project_key version latest sessions memories turns last_raw <<< "$stats"
|
|
116
|
+
IFS=$'\t' read -r project_key version latest sessions memories turns last_raw db_status <<< "$stats"
|
|
102
117
|
|
|
103
118
|
local R='\033[0m' CYAN='\033[96m' WHT='\033[97m' DIM='\033[2m'
|
|
119
|
+
if [ "${db_status:-ok}" != "ok" ]; then
|
|
120
|
+
printf "%bEagle%b %bv%s%b | %bDB error%b | turn %b%s%b" \
|
|
121
|
+
"$CYAN" "$R" "$WHT" "$version" "$DIM" "$WHT" "$DIM" "$WHT" "${turns:-0}" "$R"
|
|
122
|
+
return
|
|
123
|
+
fi
|
|
104
124
|
printf "%bEagle%b %bv%s%b | %b%s%b sessions | %b%s%b memories | turn %b%s%b" \
|
|
105
125
|
"$CYAN" "$R" \
|
|
106
126
|
"$WHT" "$version" "$DIM" \
|
|
@@ -110,9 +130,9 @@ eagle_mem_statusline() {
|
|
|
110
130
|
}
|
|
111
131
|
|
|
112
132
|
eagle_mem_statusline_hud() {
|
|
113
|
-
local stats project_key version latest sessions memories turns last_raw last_label
|
|
133
|
+
local stats project_key version latest sessions memories turns last_raw last_label db_status
|
|
114
134
|
stats=$(eagle_mem_statusline_stats "${1:-}" "${2:-}" "${3:-}" "${4:-}") || return
|
|
115
|
-
IFS=$'\t' read -r project_key version latest sessions memories turns last_raw <<< "$stats"
|
|
135
|
+
IFS=$'\t' read -r project_key version latest sessions memories turns last_raw db_status <<< "$stats"
|
|
116
136
|
last_label=$(_eagle_statusline_relative_time "$last_raw")
|
|
117
137
|
|
|
118
138
|
local R='\033[0m' CYAN='\033[96m' WHT='\033[97m' DIM='\033[2m'
|
|
@@ -136,6 +156,13 @@ eagle_mem_statusline_hud() {
|
|
|
136
156
|
em_ver="${em_ver} ${GRN}✓${R}"
|
|
137
157
|
fi
|
|
138
158
|
|
|
159
|
+
if [ "${db_status:-ok}" != "ok" ]; then
|
|
160
|
+
printf "%bEagle Mem%b %b %b│%b %bDatabase:%b %bERROR%b %b│%b %bTurns:%b %b%s/30%b %b(%s)%b" \
|
|
161
|
+
"$CYAN" "$R" "$em_ver" "$DIM" "$R" "$DIM" "$R" "$RED" "$R" "$DIM" "$R" \
|
|
162
|
+
"$DIM" "$R" "$turn_color" "$turns" "$R" "$turn_color" "$pressure_label" "$R"
|
|
163
|
+
return
|
|
164
|
+
fi
|
|
165
|
+
|
|
139
166
|
printf "%bEagle Mem%b %b %b│%b %bSessions:%b %b%s%b %b│%b %bMemories:%b %b%s%b %b│%b %bTurns:%b %b%s/30%b %b(%s)%b %b│%b %bUpdated:%b %b%s%b" \
|
|
140
167
|
"$CYAN" "$R" "$em_ver" \
|
|
141
168
|
"$DIM" "$R" "$DIM" "$R" "$WHT" "${sessions:-0}" "$R" "$DIM" "$R" \
|
package/scripts/tasks.sh
CHANGED
|
@@ -71,6 +71,40 @@ project_sql=$(eagle_sql_escape "$project")
|
|
|
71
71
|
[ -z "$agent" ] && agent=$(eagle_agent_source)
|
|
72
72
|
agent_sql=$(eagle_sql_escape "$agent")
|
|
73
73
|
|
|
74
|
+
tasks_fail() {
|
|
75
|
+
local error_code="$1"
|
|
76
|
+
local message="$2"
|
|
77
|
+
local db_status="${3:-unknown}"
|
|
78
|
+
local db_detail="${4:-}"
|
|
79
|
+
|
|
80
|
+
if [ "$json_output" = true ]; then
|
|
81
|
+
jq -nc \
|
|
82
|
+
--arg status "error" \
|
|
83
|
+
--arg command "tasks" \
|
|
84
|
+
--arg action "$action" \
|
|
85
|
+
--arg error "$error_code" \
|
|
86
|
+
--arg message "$message" \
|
|
87
|
+
--arg project "$project" \
|
|
88
|
+
--arg db_status "$db_status" \
|
|
89
|
+
--arg db_detail "$db_detail" \
|
|
90
|
+
'{status:$status, command:$command, action:$action, error:$error, message:$message,
|
|
91
|
+
project:$project, database:{integrity:{status:$db_status, detail:$db_detail}}}'
|
|
92
|
+
else
|
|
93
|
+
eagle_err "$message"
|
|
94
|
+
[ -n "$db_detail" ] && eagle_dim " $db_detail"
|
|
95
|
+
fi
|
|
96
|
+
exit 1
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
db_integrity_check=$(eagle_db_integrity_status "$EAGLE_MEM_DB" 2>/dev/null || true)
|
|
100
|
+
db_integrity_status="${db_integrity_check%%|*}"
|
|
101
|
+
db_integrity_detail="${db_integrity_check#*|}"
|
|
102
|
+
[ -n "$db_integrity_status" ] || db_integrity_status="unknown"
|
|
103
|
+
[ -n "$db_integrity_detail" ] || db_integrity_detail="not checked"
|
|
104
|
+
if [ "$db_integrity_status" != "ok" ]; then
|
|
105
|
+
tasks_fail "database_integrity" "Database integrity check failed; durable tasks are unavailable." "$db_integrity_status" "$db_integrity_detail"
|
|
106
|
+
fi
|
|
107
|
+
|
|
74
108
|
# ─── List tasks ───────────────────────────────────────────
|
|
75
109
|
|
|
76
110
|
tasks_list() {
|
package/scripts/test.sh
CHANGED
|
@@ -55,6 +55,19 @@ run_check "Graph Memory Rebuild (isolated regression suite)" "bash \"$SCRIPTS_DI
|
|
|
55
55
|
run_check "Dream Cycle Memory Graph Wiring (isolated regression suite)" "bash \"$SCRIPTS_DIR/../tests/test_curate_graph_memories.sh\""
|
|
56
56
|
run_check "Reliability Guards (provider fallback, logs, autoscan, read scoring)" "bash \"$SCRIPTS_DIR/../tests/test_reliability_guards.sh\""
|
|
57
57
|
run_check "Feature Verification Gate (monorepo path collisions)" "bash \"$SCRIPTS_DIR/../tests/test_feature_verification_gate.sh\""
|
|
58
|
+
run_check "Compaction Survival Matrix (summary, memory, task, feature, recall, graph)" "bash \"$SCRIPTS_DIR/../tests/test_compaction_survival_matrix.sh\""
|
|
59
|
+
run_check "Auto Orchestration Detection (broad prompt creates lanes/tasks)" "bash \"$SCRIPTS_DIR/../tests/test_auto_orchestration_detection.sh\""
|
|
60
|
+
run_check "Agent Compatibility Docs Gate (official-doc verified hook contracts)" "bash \"$SCRIPTS_DIR/../tests/test_agent_compatibility_docs_gate.sh\""
|
|
61
|
+
run_check "Claude Stop Hook Registration (bash wrapper)" "bash \"$SCRIPTS_DIR/../tests/test_claude_stop_hook_registration.sh\""
|
|
62
|
+
run_check "Codex Hooks Config (canonical features.hooks flag)" "bash \"$SCRIPTS_DIR/../tests/test_codex_hooks_config.sh\""
|
|
63
|
+
run_check "OpenCode Hooks Config (global plugin and skill registration)" "bash \"$SCRIPTS_DIR/../tests/test_opencode_hooks_config.sh\""
|
|
64
|
+
run_check "OpenCode Plugin Adapter (hook normalization)" "bash \"$SCRIPTS_DIR/../tests/test_opencode_plugin_adapter.sh\""
|
|
65
|
+
run_check "Rust Migration Plan (compatibility-first contract)" "bash \"$SCRIPTS_DIR/../tests/test_rust_migration_plan.sh\""
|
|
66
|
+
run_check "Database Repair (safe preview and recovery failure path)" "bash \"$SCRIPTS_DIR/../tests/test_repair.sh\""
|
|
67
|
+
run_check "Trust Surfaces (DB integrity, JSON errors, statusline)" "bash \"$SCRIPTS_DIR/../tests/test_trust_surfaces.sh\""
|
|
68
|
+
run_check "Recall Observability (UserPromptSubmit recall event)" "bash \"$SCRIPTS_DIR/../tests/test_recall_observability.sh\""
|
|
69
|
+
run_check "Eagle Event Log (hook/action observability)" "bash \"$SCRIPTS_DIR/../tests/test_eagle_events.sh\""
|
|
70
|
+
run_check "Dashboard Surface (local HTML memory view)" "bash \"$SCRIPTS_DIR/../tests/test_dashboard.sh\""
|
|
58
71
|
|
|
59
72
|
echo ""
|
|
60
73
|
if [ "$errors" -eq 0 ]; then
|
package/scripts/uninstall.sh
CHANGED
|
@@ -11,6 +11,7 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
|
11
11
|
. "$SCRIPTS_DIR/style.sh"
|
|
12
12
|
. "$LIB_DIR/common.sh"
|
|
13
13
|
. "$LIB_DIR/codex-hooks.sh"
|
|
14
|
+
. "$LIB_DIR/opencode-hooks.sh"
|
|
14
15
|
|
|
15
16
|
SETTINGS="$EAGLE_SETTINGS"
|
|
16
17
|
dry_run=false
|
|
@@ -66,6 +67,12 @@ else
|
|
|
66
67
|
eagle_warn "Could not patch Codex hooks.json (jq not found or file missing)"
|
|
67
68
|
fi
|
|
68
69
|
|
|
70
|
+
if eagle_remove_opencode_plugin; then
|
|
71
|
+
eagle_ok "OpenCode plugin removed"
|
|
72
|
+
else
|
|
73
|
+
eagle_info "OpenCode plugin not present or not Eagle Mem-owned"
|
|
74
|
+
fi
|
|
75
|
+
|
|
69
76
|
# ─── Remove instruction blocks and statusline integration ───
|
|
70
77
|
|
|
71
78
|
claude_md="$HOME/.claude/CLAUDE.md"
|
|
@@ -132,6 +139,8 @@ if [ -d "$EAGLE_GROK_SKILLS_DIR" ]; then
|
|
|
132
139
|
done
|
|
133
140
|
fi
|
|
134
141
|
|
|
142
|
+
eagle_remove_opencode_skills >/dev/null 2>&1 || true
|
|
143
|
+
|
|
135
144
|
# ─── Optionally wipe data ─────────────────────────────────
|
|
136
145
|
|
|
137
146
|
if [ -d "$EAGLE_MEM_DIR" ]; then
|
package/scripts/update.sh
CHANGED
|
@@ -16,11 +16,13 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
|
16
16
|
. "$LIB_DIR/updater.sh"
|
|
17
17
|
. "$LIB_DIR/hooks.sh"
|
|
18
18
|
. "$LIB_DIR/codex-hooks.sh"
|
|
19
|
+
. "$LIB_DIR/opencode-hooks.sh"
|
|
19
20
|
|
|
20
21
|
SETTINGS="$EAGLE_SETTINGS"
|
|
21
22
|
claude_found=false
|
|
22
23
|
codex_found=false
|
|
23
24
|
grok_found=false
|
|
25
|
+
opencode_found=false
|
|
24
26
|
|
|
25
27
|
eagle_header "Update"
|
|
26
28
|
|
|
@@ -43,8 +45,11 @@ fi
|
|
|
43
45
|
if [ -d "$EAGLE_GROK_DIR" ]; then
|
|
44
46
|
grok_found=true
|
|
45
47
|
fi
|
|
48
|
+
if eagle_opencode_detected; then
|
|
49
|
+
opencode_found=true
|
|
50
|
+
fi
|
|
46
51
|
|
|
47
|
-
eagle_runtime_change_plan "update" "$PACKAGE_DIR" "$claude_found" "$codex_found"
|
|
52
|
+
eagle_runtime_change_plan "update" "$PACKAGE_DIR" "$claude_found" "$codex_found" "$opencode_found"
|
|
48
53
|
|
|
49
54
|
# ─── Update files ──────────────────────────────────────────
|
|
50
55
|
|
|
@@ -82,11 +87,12 @@ fi
|
|
|
82
87
|
|
|
83
88
|
if [ "$claude_found" = true ] && [ -f "$SETTINGS" ] && command -v jq &>/dev/null; then
|
|
84
89
|
# Clean old registrations before re-registering (handles matcher changes across versions)
|
|
90
|
+
eagle_clean_hook_entries "$SETTINGS" "Stop" "$EAGLE_MEM_DIR/hooks/stop.sh"
|
|
85
91
|
eagle_clean_hook_entries "$SETTINGS" "PostToolUse" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
86
92
|
eagle_clean_hook_entries "$SETTINGS" "PreToolUse" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
87
93
|
|
|
88
94
|
eagle_patch_hook "$SETTINGS" "SessionStart" "" "$EAGLE_MEM_DIR/hooks/session-start.sh"
|
|
89
|
-
eagle_patch_hook "$SETTINGS" "Stop" "" "$EAGLE_MEM_DIR/hooks/stop.sh"
|
|
95
|
+
eagle_patch_hook "$SETTINGS" "Stop" "" "bash \"$EAGLE_MEM_DIR/hooks/stop.sh\""
|
|
90
96
|
eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskUpdate" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
91
97
|
eagle_patch_hook "$SETTINGS" "TaskCreated" "" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
92
98
|
eagle_patch_hook "$SETTINGS" "TaskCompleted" "" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
@@ -94,6 +100,9 @@ if [ "$claude_found" = true ] && [ -f "$SETTINGS" ] && command -v jq &>/dev/null
|
|
|
94
100
|
eagle_patch_hook "$SETTINGS" "UserPromptSubmit" "" "$EAGLE_MEM_DIR/hooks/user-prompt-submit.sh"
|
|
95
101
|
eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash|Read|Edit|Write" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
96
102
|
|
|
103
|
+
# Allow agent-issued session capture to run without a permission prompt
|
|
104
|
+
eagle_patch_permission_allow "$SETTINGS" "Bash(eagle-mem session save:*)"
|
|
105
|
+
|
|
97
106
|
eagle_ok "Hooks registered"
|
|
98
107
|
fi
|
|
99
108
|
|
|
@@ -104,6 +113,12 @@ elif [ "$codex_found" = false ]; then
|
|
|
104
113
|
eagle_info "Codex hooks skipped ${DIM}(Codex not detected)${RESET}"
|
|
105
114
|
fi
|
|
106
115
|
|
|
116
|
+
if [ "$opencode_found" = true ]; then
|
|
117
|
+
eagle_install_opencode_plugin "$PACKAGE_DIR" "0"
|
|
118
|
+
else
|
|
119
|
+
eagle_info "OpenCode plugin skipped ${DIM}(OpenCode not detected)${RESET}"
|
|
120
|
+
fi
|
|
121
|
+
|
|
107
122
|
# ─── Update skill symlinks ────────────────────────────────
|
|
108
123
|
|
|
109
124
|
if [ "$claude_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
|
|
@@ -164,6 +179,10 @@ if [ "$grok_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
|
|
|
164
179
|
eagle_ok "Grok skills updated"
|
|
165
180
|
fi
|
|
166
181
|
|
|
182
|
+
if [ "$opencode_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
|
|
183
|
+
eagle_install_opencode_skills "$PACKAGE_DIR" "0"
|
|
184
|
+
fi
|
|
185
|
+
|
|
167
186
|
# ─── Refresh generated Claude statusline wrapper ───────────
|
|
168
187
|
|
|
169
188
|
statusline_wrapper="$EAGLE_MEM_DIR/scripts/statusline-wrapper.sh"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hook_event_name": "Status",
|
|
3
|
+
"session_id": "claude-status-fixture",
|
|
4
|
+
"transcript_path": "/tmp/eagle-mem/claude-status-fixture.jsonl",
|
|
5
|
+
"cwd": "/tmp/eagle-mem/project",
|
|
6
|
+
"model": {
|
|
7
|
+
"id": "claude-opus-4-1",
|
|
8
|
+
"display_name": "Opus"
|
|
9
|
+
},
|
|
10
|
+
"workspace": {
|
|
11
|
+
"current_dir": "/tmp/eagle-mem/project",
|
|
12
|
+
"project_dir": "/tmp/eagle-mem/project"
|
|
13
|
+
},
|
|
14
|
+
"version": "2.1.145",
|
|
15
|
+
"output_style": {
|
|
16
|
+
"name": "default"
|
|
17
|
+
},
|
|
18
|
+
"cost": {
|
|
19
|
+
"total_cost_usd": 0.01234,
|
|
20
|
+
"total_duration_ms": 45000,
|
|
21
|
+
"total_api_duration_ms": 2300,
|
|
22
|
+
"total_lines_added": 12,
|
|
23
|
+
"total_lines_removed": 2
|
|
24
|
+
},
|
|
25
|
+
"thinking": {
|
|
26
|
+
"enabled": true
|
|
27
|
+
},
|
|
28
|
+
"effort": {
|
|
29
|
+
"level": "xhigh"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"session_id": "claude-session-fixture",
|
|
3
|
+
"transcript_path": "/tmp/eagle-mem/claude-session-fixture.jsonl",
|
|
4
|
+
"cwd": "/tmp/eagle-mem/project",
|
|
5
|
+
"permission_mode": "default",
|
|
6
|
+
"hook_event_name": "UserPromptSubmit",
|
|
7
|
+
"prompt": "Review the OAuth memory before editing auth files"
|
|
8
|
+
}
|
|
9
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sessionID": "opencode-session-fixture",
|
|
3
|
+
"input": {
|
|
4
|
+
"sessionID": "opencode-session-fixture",
|
|
5
|
+
"agent": "build",
|
|
6
|
+
"model": {
|
|
7
|
+
"providerID": "openai",
|
|
8
|
+
"modelID": "gpt-5"
|
|
9
|
+
},
|
|
10
|
+
"messageID": "opencode-user-message-fixture"
|
|
11
|
+
},
|
|
12
|
+
"output": {
|
|
13
|
+
"message": {
|
|
14
|
+
"id": "opencode-user-message-fixture",
|
|
15
|
+
"sessionID": "opencode-session-fixture",
|
|
16
|
+
"role": "user",
|
|
17
|
+
"time": {
|
|
18
|
+
"created": 1780372800000
|
|
19
|
+
},
|
|
20
|
+
"agent": "build",
|
|
21
|
+
"model": {
|
|
22
|
+
"providerID": "openai",
|
|
23
|
+
"modelID": "gpt-5"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"parts": [
|
|
27
|
+
{
|
|
28
|
+
"id": "opencode-text-part-fixture",
|
|
29
|
+
"sessionID": "opencode-session-fixture",
|
|
30
|
+
"messageID": "opencode-user-message-fixture",
|
|
31
|
+
"type": "text",
|
|
32
|
+
"text": "Review the OAuth memory before editing auth files"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|