eagle-mem 4.9.5 → 4.9.7
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/README.md +35 -4
- package/architecture.html +1791 -0
- package/bin/eagle-mem +1 -0
- package/db/migrate.sh +13 -2
- package/docs/visible-surface-ux-contract.md +53 -0
- package/hooks/post-tool-use.sh +2 -2
- package/hooks/pre-tool-use.sh +6 -6
- package/hooks/session-start.sh +188 -1
- package/hooks/user-prompt-submit.sh +35 -12
- package/lib/common.sh +361 -21
- package/lib/db-mirrors.sh +3 -6
- package/lib/db-summaries.sh +10 -1
- package/lib/hooks-posttool.sh +44 -10
- package/lib/updater.sh +12 -0
- package/package.json +4 -2
- package/scripts/doctor.sh +262 -0
- package/scripts/feature.sh +37 -11
- package/scripts/health.sh +10 -7
- package/scripts/help.sh +1 -0
- package/scripts/install.sh +9 -3
- package/scripts/memories.sh +189 -55
- package/scripts/orchestrate.sh +2 -2
- package/scripts/search.sh +195 -31
- package/scripts/statusline-em.sh +129 -26
- package/scripts/uninstall.sh +67 -0
- package/scripts/update.sh +20 -5
package/lib/common.sh
CHANGED
|
@@ -303,12 +303,14 @@ eagle_remember_session_project() {
|
|
|
303
303
|
eagle_get_session_project_light() {
|
|
304
304
|
local session_id="${1:-}"
|
|
305
305
|
eagle_validate_session_id "$session_id" || return 1
|
|
306
|
-
|
|
306
|
+
local sqlite_bin
|
|
307
|
+
sqlite_bin=$(eagle_sqlite_path)
|
|
308
|
+
[ -n "$sqlite_bin" ] || return 1
|
|
307
309
|
[ -f "$EAGLE_MEM_DB" ] || return 1
|
|
308
310
|
|
|
309
311
|
local sid_sql project
|
|
310
312
|
sid_sql=$(eagle_sql_escape "$session_id")
|
|
311
|
-
project=$(
|
|
313
|
+
project=$("$sqlite_bin" "$EAGLE_MEM_DB" "SELECT project FROM sessions WHERE id = '$sid_sql' AND project != '' LIMIT 1;" 2>/dev/null | awk 'NF { print; exit }')
|
|
312
314
|
[ -n "$project" ] || return 1
|
|
313
315
|
printf '%s\n' "$project"
|
|
314
316
|
}
|
|
@@ -317,12 +319,14 @@ eagle_project_has_table_row() {
|
|
|
317
319
|
local table="${1:-}"
|
|
318
320
|
local project="${2:-}"
|
|
319
321
|
[ -n "$table" ] && [ -n "$project" ] || return 1
|
|
320
|
-
|
|
322
|
+
local sqlite_bin
|
|
323
|
+
sqlite_bin=$(eagle_sqlite_path)
|
|
324
|
+
[ -n "$sqlite_bin" ] || return 1
|
|
321
325
|
[ -f "$EAGLE_MEM_DB" ] || return 1
|
|
322
326
|
|
|
323
327
|
local project_sql found
|
|
324
328
|
project_sql=$(eagle_sql_escape "$project")
|
|
325
|
-
found=$(
|
|
329
|
+
found=$("$sqlite_bin" "$EAGLE_MEM_DB" "SELECT 1 FROM $table WHERE project = '$project_sql' LIMIT 1;" 2>/dev/null | awk 'NF { print; exit }')
|
|
326
330
|
[ "$found" = "1" ]
|
|
327
331
|
}
|
|
328
332
|
|
|
@@ -330,11 +334,16 @@ eagle_project_from_existing_ancestor() {
|
|
|
330
334
|
local path="${1:-}"
|
|
331
335
|
[ -n "$path" ] || return 1
|
|
332
336
|
|
|
333
|
-
local current key
|
|
337
|
+
local start current key
|
|
334
338
|
current=$(eagle_normalize_project_path "$path")
|
|
339
|
+
start="$current"
|
|
335
340
|
eagle_is_ephemeral_project_path "$current" && return 1
|
|
336
341
|
|
|
337
342
|
while [ -n "$current" ] && [ "$current" != "/" ]; do
|
|
343
|
+
if [ "$current" = "$HOME" ] && [ "$start" != "$HOME" ]; then
|
|
344
|
+
break
|
|
345
|
+
fi
|
|
346
|
+
|
|
338
347
|
key=$(eagle_project_key_from_target_dir "$current")
|
|
339
348
|
if [ -n "$key" ]; then
|
|
340
349
|
# Prefer ancestors with durable memory/summary content. Session-only
|
|
@@ -413,7 +422,7 @@ eagle_project_from_statusline_input() {
|
|
|
413
422
|
local project_dir="${2:-}"
|
|
414
423
|
local cwd="${3:-}"
|
|
415
424
|
local session_id="${4:-}"
|
|
416
|
-
local project workspace_project_dir transcript_path
|
|
425
|
+
local project session_project workspace_project workspace_project_dir transcript_path
|
|
417
426
|
|
|
418
427
|
if [ -n "${EAGLE_MEM_PROJECT:-}" ]; then
|
|
419
428
|
printf '%s\n' "$EAGLE_MEM_PROJECT"
|
|
@@ -430,27 +439,49 @@ eagle_project_from_statusline_input() {
|
|
|
430
439
|
[ -z "$cwd" ] && cwd=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null)
|
|
431
440
|
fi
|
|
432
441
|
|
|
433
|
-
#
|
|
434
|
-
#
|
|
435
|
-
#
|
|
436
|
-
if [ -n "$workspace_project_dir" ]; then
|
|
437
|
-
|
|
438
|
-
|
|
442
|
+
# Claude can report $HOME as workspace.project_dir for a child project before
|
|
443
|
+
# the project is fully initialized. Treat that as too broad for statusline
|
|
444
|
+
# stats; the current working directory is the more useful project boundary.
|
|
445
|
+
if [ -n "$workspace_project_dir" ] && [ -n "$cwd" ]; then
|
|
446
|
+
local workspace_resolved cwd_resolved
|
|
447
|
+
workspace_resolved=$(eagle_normalize_project_path "$workspace_project_dir")
|
|
448
|
+
cwd_resolved=$(eagle_normalize_project_path "$cwd")
|
|
449
|
+
if [ "$workspace_resolved" = "$HOME" ] && [ "$cwd_resolved" != "$HOME" ]; then
|
|
450
|
+
workspace_project_dir="$cwd_resolved"
|
|
451
|
+
fi
|
|
439
452
|
fi
|
|
440
453
|
|
|
441
|
-
if [ -n "$
|
|
442
|
-
|
|
443
|
-
printf '%s\n' "$project"
|
|
444
|
-
return
|
|
445
|
-
fi
|
|
454
|
+
if [ -n "$workspace_project_dir" ]; then
|
|
455
|
+
workspace_project=$(eagle_project_from_workspace_path "$workspace_project_dir" || true)
|
|
446
456
|
fi
|
|
447
457
|
|
|
448
458
|
if [ -n "$session_id" ]; then
|
|
449
|
-
|
|
450
|
-
|
|
459
|
+
session_project=$(eagle_get_session_project_light "$session_id" || true)
|
|
460
|
+
if [ -n "$session_project" ]; then
|
|
461
|
+
# If an older row was captured under a nested folder key, a current
|
|
462
|
+
# workspace root should still be allowed to repair the display.
|
|
463
|
+
if [ -n "$workspace_project" ] && [ "$session_project" != "$workspace_project" ]; then
|
|
464
|
+
case "$session_project" in
|
|
465
|
+
"$workspace_project"/*)
|
|
466
|
+
printf '%s\n' "$workspace_project"
|
|
467
|
+
return
|
|
468
|
+
;;
|
|
469
|
+
esac
|
|
470
|
+
fi
|
|
471
|
+
printf '%s\n' "$session_project"
|
|
451
472
|
return
|
|
452
473
|
fi
|
|
453
|
-
|
|
474
|
+
fi
|
|
475
|
+
|
|
476
|
+
# Explicit workspace project and transcript start are stronger than generic
|
|
477
|
+
# path fallback because they repair pre-fix sessions that were stored under
|
|
478
|
+
# a nested folder key.
|
|
479
|
+
if [ -n "$workspace_project_dir" ]; then
|
|
480
|
+
[ -n "$workspace_project" ] && { printf '%s\n' "$workspace_project"; return; }
|
|
481
|
+
fi
|
|
482
|
+
|
|
483
|
+
if [ -n "$transcript_path" ]; then
|
|
484
|
+
if project=$(eagle_project_from_transcript_start "$transcript_path" "${cwd:-$project_dir}"); then
|
|
454
485
|
printf '%s\n' "$project"
|
|
455
486
|
return
|
|
456
487
|
fi
|
|
@@ -1146,7 +1177,7 @@ eagle_statusline_script_from_command() {
|
|
|
1146
1177
|
eagle_statusline_script_uses_input() {
|
|
1147
1178
|
local sl_file="${1:-}"
|
|
1148
1179
|
[ -f "$sl_file" ] || return 1
|
|
1149
|
-
grep -Eq 'eagle_project_from_statusline_input|eagle_mem_statusline.*(\$\{input:-\}|\$input)' "$sl_file"
|
|
1180
|
+
grep -Eq 'eagle_project_from_statusline_input|eagle_mem_statusline.*(\$\{input:-\}|\$input)|statusline-em\.sh.*--hud' "$sl_file"
|
|
1150
1181
|
}
|
|
1151
1182
|
|
|
1152
1183
|
eagle_patch_statusline_script() {
|
|
@@ -1178,6 +1209,14 @@ eagle_patch_statusline_script() {
|
|
|
1178
1209
|
fi
|
|
1179
1210
|
|
|
1180
1211
|
perl -0pi -e '
|
|
1212
|
+
s{# [^\n]*EAGLE MEM[^\n]*\n.*?\n# [^\n]*CLOCK[^\n]*\n}{q~# -- EAGLE MEM (second line - branded) --------------------------------------
|
|
1213
|
+
em_line=""
|
|
1214
|
+
if [ -n "$cwd" ] && [ -f "$HOME/.eagle-mem/scripts/statusline-em.sh" ]; then
|
|
1215
|
+
em_line=$(printf "%s" "$input" | bash "$HOME/.eagle-mem/scripts/statusline-em.sh" --hud 2>/dev/null)
|
|
1216
|
+
fi
|
|
1217
|
+
|
|
1218
|
+
# -- CLOCK ---------------------------------------------------------------------
|
|
1219
|
+
~}ems;
|
|
1181
1220
|
s/(project_dir=\$\(echo "\$input" \| jq -r \x27)\.workspace\.current_dir \/\/ \.cwd/$1.workspace.project_dir \/\/ .workspace.current_dir \/\/ .cwd/g;
|
|
1182
1221
|
s/(project_dir=\$\(echo "\$input" \| jq -r ")\.workspace\.current_dir \/\/ \.cwd/$1.workspace.project_dir \/\/ .workspace.current_dir \/\/ .cwd/g;
|
|
1183
1222
|
s/eagle_mem_statusline "\$project_dir" "\$session_id" "\$\{input\}"/eagle_mem_statusline "\$project_dir" "\$session_id" "\${input:-}"/g;
|
|
@@ -1212,6 +1251,307 @@ eagle_patch_statusline_script() {
|
|
|
1212
1251
|
return 0
|
|
1213
1252
|
}
|
|
1214
1253
|
|
|
1254
|
+
eagle_runtime_change_plan() {
|
|
1255
|
+
local action="${1:-install}"
|
|
1256
|
+
local package_dir="${2:-}"
|
|
1257
|
+
local claude_found="${3:-false}"
|
|
1258
|
+
local codex_found="${4:-false}"
|
|
1259
|
+
|
|
1260
|
+
echo ""
|
|
1261
|
+
echo -e " ${BOLD:-}What will change${RESET:-}"
|
|
1262
|
+
echo -e " ${DIM:-}Eagle Mem shows this before touching runtime files or agent configs.${RESET:-}"
|
|
1263
|
+
echo ""
|
|
1264
|
+
echo -e " ${CYAN:-}->${RESET:-} Copy runtime files"
|
|
1265
|
+
echo -e " ${DIM:-}from:${RESET:-} ${package_dir:-current package}"
|
|
1266
|
+
echo -e " ${DIM:-}to: ${RESET:-} $EAGLE_MEM_DIR/{hooks,lib,db,scripts}"
|
|
1267
|
+
echo -e " ${CYAN:-}->${RESET:-} Open database"
|
|
1268
|
+
echo -e " ${DIM:-}path:${RESET:-} $EAGLE_MEM_DB"
|
|
1269
|
+
echo -e " ${DIM:-}mode:${RESET:-} migrate only when needed; existing memories are preserved"
|
|
1270
|
+
|
|
1271
|
+
if [ "$claude_found" = true ]; then
|
|
1272
|
+
echo -e " ${CYAN:-}->${RESET:-} Update Claude Code"
|
|
1273
|
+
echo -e " ${DIM:-}settings:${RESET:-} $EAGLE_SETTINGS"
|
|
1274
|
+
echo -e " ${DIM:-}hooks: ${RESET:-} SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop, SessionEnd"
|
|
1275
|
+
echo -e " ${DIM:-}status: ${RESET:-} patch Eagle Mem statusline block when auto-detectable; backup first"
|
|
1276
|
+
echo -e " ${DIM:-}skills: ${RESET:-} $EAGLE_SKILLS_DIR/eagle-mem-*"
|
|
1277
|
+
echo -e " ${DIM:-}guide: ${RESET:-} $HOME/.claude/CLAUDE.md Eagle Mem section"
|
|
1278
|
+
else
|
|
1279
|
+
echo -e " ${DIM:-}-> Claude Code not detected; Claude hooks/skills skipped${RESET:-}"
|
|
1280
|
+
fi
|
|
1281
|
+
|
|
1282
|
+
if [ "$codex_found" = true ]; then
|
|
1283
|
+
echo -e " ${CYAN:-}->${RESET:-} Update Codex"
|
|
1284
|
+
echo -e " ${DIM:-}config:${RESET:-} $EAGLE_CODEX_CONFIG"
|
|
1285
|
+
echo -e " ${DIM:-}hooks: ${RESET:-} $EAGLE_CODEX_HOOKS"
|
|
1286
|
+
echo -e " ${DIM:-}skills:${RESET:-} $EAGLE_CODEX_SKILLS_DIR/eagle-mem-*"
|
|
1287
|
+
echo -e " ${DIM:-}guide: ${RESET:-} $EAGLE_CODEX_AGENTS_MD Eagle Mem section"
|
|
1288
|
+
else
|
|
1289
|
+
echo -e " ${DIM:-}-> Codex not detected; Codex hooks/skills skipped${RESET:-}"
|
|
1290
|
+
fi
|
|
1291
|
+
|
|
1292
|
+
if [ "$action" = "update" ]; then
|
|
1293
|
+
echo -e " ${CYAN:-}->${RESET:-} Refresh installed version metadata"
|
|
1294
|
+
fi
|
|
1295
|
+
echo ""
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
eagle_uninstall_change_plan() {
|
|
1299
|
+
echo ""
|
|
1300
|
+
echo -e " ${BOLD:-}What will change${RESET:-}"
|
|
1301
|
+
echo -e " ${DIM:-}Uninstall removes Eagle Mem-owned integration points. Runtime data is deleted only if you confirm later.${RESET:-}"
|
|
1302
|
+
echo ""
|
|
1303
|
+
echo -e " ${CYAN:-}->${RESET:-} Remove Claude hooks from $EAGLE_SETTINGS"
|
|
1304
|
+
echo -e " ${CYAN:-}->${RESET:-} Remove Codex hooks from $EAGLE_CODEX_HOOKS"
|
|
1305
|
+
echo -e " ${CYAN:-}->${RESET:-} Remove Eagle Mem skill links from:"
|
|
1306
|
+
echo -e " ${DIM:-}$EAGLE_SKILLS_DIR${RESET:-}"
|
|
1307
|
+
echo -e " ${DIM:-}$EAGLE_CODEX_SKILLS_DIR${RESET:-}"
|
|
1308
|
+
echo -e " ${CYAN:-}->${RESET:-} Remove Eagle Mem instruction blocks from:"
|
|
1309
|
+
echo -e " ${DIM:-}$HOME/.claude/CLAUDE.md${RESET:-}"
|
|
1310
|
+
echo -e " ${DIM:-}$EAGLE_CODEX_AGENTS_MD${RESET:-}"
|
|
1311
|
+
echo -e " ${CYAN:-}->${RESET:-} Remove Eagle Mem statusline integration when auto-detectable"
|
|
1312
|
+
echo -e " ${DIM:-}-> Backups are written next to edited user config files.${RESET:-}"
|
|
1313
|
+
echo ""
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
eagle_backup_user_file() {
|
|
1317
|
+
local file="${1:-}"
|
|
1318
|
+
[ -f "$file" ] || return 1
|
|
1319
|
+
local backup
|
|
1320
|
+
backup="${file}.eagle-mem.uninstall-bak-$(date -u +%Y%m%dT%H%M%SZ)"
|
|
1321
|
+
cp "$file" "$backup" 2>/dev/null || return 1
|
|
1322
|
+
printf '%s\n' "$backup"
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
eagle_remove_marked_markdown_section() {
|
|
1326
|
+
local file="${1:-}"
|
|
1327
|
+
local marker="${2:-## Eagle Mem — Persistent Memory}"
|
|
1328
|
+
[ -f "$file" ] || return 1
|
|
1329
|
+
grep -qF "$marker" "$file" 2>/dev/null || return 1
|
|
1330
|
+
|
|
1331
|
+
local tmp
|
|
1332
|
+
tmp=$(mktemp) || return 1
|
|
1333
|
+
awk -v marker="$marker" '
|
|
1334
|
+
BEGIN { skip=0; pending_sep="" }
|
|
1335
|
+
$0 == "---" && !skip {
|
|
1336
|
+
pending_sep=$0 ORS
|
|
1337
|
+
next
|
|
1338
|
+
}
|
|
1339
|
+
index($0, marker) {
|
|
1340
|
+
skip=1
|
|
1341
|
+
pending_sep=""
|
|
1342
|
+
next
|
|
1343
|
+
}
|
|
1344
|
+
skip {
|
|
1345
|
+
if ($0 == "---") {
|
|
1346
|
+
skip=0
|
|
1347
|
+
pending_sep=""
|
|
1348
|
+
next
|
|
1349
|
+
}
|
|
1350
|
+
if ($0 ~ /^## /) {
|
|
1351
|
+
skip=0
|
|
1352
|
+
if (pending_sep != "") {
|
|
1353
|
+
printf "%s", pending_sep
|
|
1354
|
+
pending_sep=""
|
|
1355
|
+
}
|
|
1356
|
+
print
|
|
1357
|
+
}
|
|
1358
|
+
next
|
|
1359
|
+
}
|
|
1360
|
+
{
|
|
1361
|
+
if (pending_sep != "") {
|
|
1362
|
+
printf "%s", pending_sep
|
|
1363
|
+
pending_sep=""
|
|
1364
|
+
}
|
|
1365
|
+
print
|
|
1366
|
+
}
|
|
1367
|
+
END {
|
|
1368
|
+
if (!skip && pending_sep != "") {
|
|
1369
|
+
printf "%s", pending_sep
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
' "$file" > "$tmp" || { rm -f "$tmp"; return 1; }
|
|
1373
|
+
|
|
1374
|
+
if cmp -s "$file" "$tmp"; then
|
|
1375
|
+
rm -f "$tmp"
|
|
1376
|
+
return 1
|
|
1377
|
+
fi
|
|
1378
|
+
mv "$tmp" "$file"
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
eagle_remove_statusline_block() {
|
|
1382
|
+
local sl_file="${1:-}"
|
|
1383
|
+
[ -f "$sl_file" ] || return 1
|
|
1384
|
+
command -v perl >/dev/null 2>&1 || return 1
|
|
1385
|
+
grep -Eq 'EAGLE MEM|Eagle Mem|eagle_mem_statusline|statusline-em\.sh|agent_memories|claude_memories' "$sl_file" 2>/dev/null || return 1
|
|
1386
|
+
grep -Eq '\.eagle-mem|eagle_mem_statusline|statusline-em\.sh|agent_memories|claude_memories' "$sl_file" 2>/dev/null || return 1
|
|
1387
|
+
|
|
1388
|
+
local tmp backup mode
|
|
1389
|
+
tmp=$(mktemp) || return 1
|
|
1390
|
+
cp "$sl_file" "$tmp" || { rm -f "$tmp"; return 1; }
|
|
1391
|
+
|
|
1392
|
+
perl -0pi -e '
|
|
1393
|
+
s{\n?# [^\n]*(?:EAGLE MEM|Eagle Mem)[^\n]*\n.*?\n(# [^\n]*CLOCK[^\n]*\n)}{\n$1}is;
|
|
1394
|
+
' "$tmp" || { rm -f "$tmp"; return 1; }
|
|
1395
|
+
|
|
1396
|
+
if cmp -s "$sl_file" "$tmp"; then
|
|
1397
|
+
rm -f "$tmp"
|
|
1398
|
+
return 1
|
|
1399
|
+
fi
|
|
1400
|
+
|
|
1401
|
+
backup=$(eagle_backup_user_file "$sl_file" 2>/dev/null || true)
|
|
1402
|
+
mode=$(stat -f %Lp "$sl_file" 2>/dev/null || stat -c %a "$sl_file" 2>/dev/null || echo "")
|
|
1403
|
+
mv "$tmp" "$sl_file"
|
|
1404
|
+
[ -n "$mode" ] && chmod "$mode" "$sl_file" 2>/dev/null || chmod +x "$sl_file" 2>/dev/null || true
|
|
1405
|
+
[ -n "$backup" ] && printf '%s\n' "$backup"
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
eagle_remove_statusline_integration() {
|
|
1409
|
+
local settings="${1:-$EAGLE_SETTINGS}"
|
|
1410
|
+
[ -f "$settings" ] || return 1
|
|
1411
|
+
command -v jq >/dev/null 2>&1 || return 1
|
|
1412
|
+
|
|
1413
|
+
local command sl_file wrapper tmp changed=1
|
|
1414
|
+
command=$(jq -r '.statusLine.command // .statusline.command // empty' "$settings" 2>/dev/null)
|
|
1415
|
+
[ -n "$command" ] || return 1
|
|
1416
|
+
sl_file=$(eagle_statusline_script_from_command "$command" 2>/dev/null || true)
|
|
1417
|
+
wrapper="$EAGLE_MEM_DIR/scripts/statusline-wrapper.sh"
|
|
1418
|
+
|
|
1419
|
+
if printf '%s' "$command" | grep -q "$wrapper"; then
|
|
1420
|
+
eagle_backup_user_file "$settings" >/dev/null 2>&1 || true
|
|
1421
|
+
tmp=$(mktemp) || return 1
|
|
1422
|
+
jq 'del(.statusLine) | del(.statusline)' "$settings" > "$tmp" && mv "$tmp" "$settings"
|
|
1423
|
+
return 0
|
|
1424
|
+
fi
|
|
1425
|
+
|
|
1426
|
+
if [ -n "$sl_file" ] && [ -f "$sl_file" ]; then
|
|
1427
|
+
if eagle_remove_statusline_block "$sl_file" >/dev/null; then
|
|
1428
|
+
changed=0
|
|
1429
|
+
fi
|
|
1430
|
+
fi
|
|
1431
|
+
return "$changed"
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
eagle_runtime_manifest_path() {
|
|
1435
|
+
printf '%s/install-manifest.json\n' "$EAGLE_MEM_DIR"
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
eagle_file_sha256() {
|
|
1439
|
+
local file="${1:-}"
|
|
1440
|
+
[ -f "$file" ] || return 1
|
|
1441
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
1442
|
+
shasum -a 256 "$file" 2>/dev/null | awk '{print $1}'
|
|
1443
|
+
elif command -v sha256sum >/dev/null 2>&1; then
|
|
1444
|
+
sha256sum "$file" 2>/dev/null | awk '{print $1}'
|
|
1445
|
+
else
|
|
1446
|
+
return 1
|
|
1447
|
+
fi
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
eagle_runtime_manifest_write() {
|
|
1451
|
+
local package_dir="${1:-}"
|
|
1452
|
+
local action="${2:-update}"
|
|
1453
|
+
local manifest tmp_files tmp_json
|
|
1454
|
+
manifest=$(eagle_runtime_manifest_path)
|
|
1455
|
+
mkdir -p "$EAGLE_MEM_DIR"
|
|
1456
|
+
tmp_files=$(mktemp) || return 1
|
|
1457
|
+
tmp_json=$(mktemp) || { rm -f "$tmp_files"; return 1; }
|
|
1458
|
+
|
|
1459
|
+
local group rel file sha size mode package_version sqlite_bin sqlite_version files_json
|
|
1460
|
+
for group in hooks lib db scripts; do
|
|
1461
|
+
[ -d "$EAGLE_MEM_DIR/$group" ] || continue
|
|
1462
|
+
while IFS= read -r rel; do
|
|
1463
|
+
[ -n "$rel" ] || continue
|
|
1464
|
+
file="$EAGLE_MEM_DIR/$rel"
|
|
1465
|
+
[ -f "$file" ] || continue
|
|
1466
|
+
sha=$(eagle_file_sha256 "$file" 2>/dev/null || true)
|
|
1467
|
+
size=$(wc -c < "$file" 2>/dev/null | tr -d ' ')
|
|
1468
|
+
mode=$(stat -f %Lp "$file" 2>/dev/null || stat -c %a "$file" 2>/dev/null || echo "")
|
|
1469
|
+
printf '%s\t%s\t%s\t%s\n' "$rel" "${sha:-unknown}" "${size:-0}" "$mode"
|
|
1470
|
+
done < <(cd "$EAGLE_MEM_DIR" && find "$group" -type f | sort 2>/dev/null)
|
|
1471
|
+
done > "$tmp_files"
|
|
1472
|
+
|
|
1473
|
+
package_version=$(jq -r .version "$package_dir/package.json" 2>/dev/null || tr -d '[:space:]' < "$EAGLE_MEM_DIR/.version" 2>/dev/null || echo "unknown")
|
|
1474
|
+
sqlite_bin=$(eagle_sqlite_path)
|
|
1475
|
+
sqlite_version=$(eagle_sqlite_version)
|
|
1476
|
+
files_json=$(jq -R -s '
|
|
1477
|
+
split("\n")
|
|
1478
|
+
| map(select(length > 0)
|
|
1479
|
+
| split("\t")
|
|
1480
|
+
| {
|
|
1481
|
+
path: .[0],
|
|
1482
|
+
sha256: .[1],
|
|
1483
|
+
size: ((.[2] // "0") | tonumber),
|
|
1484
|
+
mode: (.[3] // "")
|
|
1485
|
+
})
|
|
1486
|
+
' "$tmp_files")
|
|
1487
|
+
|
|
1488
|
+
jq -nc \
|
|
1489
|
+
--arg schema "1" \
|
|
1490
|
+
--arg action "$action" \
|
|
1491
|
+
--arg package_name "eagle-mem" \
|
|
1492
|
+
--arg package_version "$package_version" \
|
|
1493
|
+
--arg package_dir "$package_dir" \
|
|
1494
|
+
--arg runtime_dir "$EAGLE_MEM_DIR" \
|
|
1495
|
+
--arg db "$EAGLE_MEM_DB" \
|
|
1496
|
+
--arg sqlite_bin "${sqlite_bin:-}" \
|
|
1497
|
+
--arg sqlite_version "${sqlite_version:-}" \
|
|
1498
|
+
--arg generated_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
1499
|
+
--argjson files "$files_json" \
|
|
1500
|
+
'{schema:$schema,
|
|
1501
|
+
generated_at:$generated_at,
|
|
1502
|
+
action:$action,
|
|
1503
|
+
package:{name:$package_name, version:$package_version, dir:$package_dir},
|
|
1504
|
+
runtime:{dir:$runtime_dir, db:$db},
|
|
1505
|
+
sqlite:{path:$sqlite_bin, version:$sqlite_version},
|
|
1506
|
+
files:$files}' > "$tmp_json" || { rm -f "$tmp_files" "$tmp_json"; return 1; }
|
|
1507
|
+
|
|
1508
|
+
mv "$tmp_json" "$manifest"
|
|
1509
|
+
chmod 600 "$manifest" 2>/dev/null || true
|
|
1510
|
+
rm -f "$tmp_files"
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
eagle_runtime_manifest_check() {
|
|
1514
|
+
local manifest
|
|
1515
|
+
manifest=$(eagle_runtime_manifest_path)
|
|
1516
|
+
if [ ! -f "$manifest" ]; then
|
|
1517
|
+
printf 'missing|0|0|0\n'
|
|
1518
|
+
return 0
|
|
1519
|
+
fi
|
|
1520
|
+
command -v jq >/dev/null 2>&1 || {
|
|
1521
|
+
printf 'unreadable|0|0|0\n'
|
|
1522
|
+
return 0
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
local checked=0 missing=0 drift=0 rel expected file actual
|
|
1526
|
+
while IFS=$'\t' read -r rel expected; do
|
|
1527
|
+
[ -n "$rel" ] || continue
|
|
1528
|
+
checked=$((checked + 1))
|
|
1529
|
+
file="$EAGLE_MEM_DIR/$rel"
|
|
1530
|
+
if [ ! -f "$file" ]; then
|
|
1531
|
+
missing=$((missing + 1))
|
|
1532
|
+
continue
|
|
1533
|
+
fi
|
|
1534
|
+
actual=$(eagle_file_sha256 "$file" 2>/dev/null || true)
|
|
1535
|
+
if [ -z "$actual" ] || [ "$actual" != "$expected" ]; then
|
|
1536
|
+
drift=$((drift + 1))
|
|
1537
|
+
fi
|
|
1538
|
+
done < <(jq -r '.files[]? | [.path, .sha256] | @tsv' "$manifest" 2>/dev/null)
|
|
1539
|
+
|
|
1540
|
+
if [ "$missing" -eq 0 ] && [ "$drift" -eq 0 ]; then
|
|
1541
|
+
printf 'ok|%s|0|0\n' "$checked"
|
|
1542
|
+
else
|
|
1543
|
+
printf 'drift|%s|%s|%s\n' "$checked" "$missing" "$drift"
|
|
1544
|
+
fi
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
eagle_runtime_manifest_field() {
|
|
1548
|
+
local field="${1:-}"
|
|
1549
|
+
local manifest
|
|
1550
|
+
manifest=$(eagle_runtime_manifest_path)
|
|
1551
|
+
[ -f "$manifest" ] || return 1
|
|
1552
|
+
jq -r "$field // empty" "$manifest" 2>/dev/null
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1215
1555
|
_eagle_claude_md_section() {
|
|
1216
1556
|
cat << 'EAGLE_MD'
|
|
1217
1557
|
|
package/lib/db-mirrors.sh
CHANGED
|
@@ -82,8 +82,7 @@ WHERE file_path = '$fp_sql'
|
|
|
82
82
|
UPDATE agent_memories
|
|
83
83
|
SET origin_session_id = COALESCE(NULLIF('$origin_sql', ''), origin_session_id),
|
|
84
84
|
origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
|
|
85
|
-
project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END
|
|
86
|
-
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
85
|
+
project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END
|
|
87
86
|
WHERE file_path = '$fp_sql'
|
|
88
87
|
AND content_hash = '$hash_sql'
|
|
89
88
|
AND (('$proj_sql' != '' AND project != '$proj_sql')
|
|
@@ -178,8 +177,7 @@ WHERE file_path = '$fp_sql'
|
|
|
178
177
|
UPDATE agent_plans
|
|
179
178
|
SET origin_session_id = COALESCE(NULLIF('$origin_sql', ''), origin_session_id),
|
|
180
179
|
origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
|
|
181
|
-
project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END
|
|
182
|
-
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
180
|
+
project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END
|
|
183
181
|
WHERE file_path = '$fp_sql'
|
|
184
182
|
AND content_hash = '$hash_sql'
|
|
185
183
|
AND (('$proj_sql' != '' AND project != '$proj_sql')
|
|
@@ -291,8 +289,7 @@ WHERE file_path = '$fp_sql'
|
|
|
291
289
|
|
|
292
290
|
UPDATE agent_tasks
|
|
293
291
|
SET origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
|
|
294
|
-
project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END
|
|
295
|
-
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
292
|
+
project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END
|
|
296
293
|
WHERE file_path = '$fp_sql'
|
|
297
294
|
AND content_hash = '$hash_sql'
|
|
298
295
|
AND (('$proj_sql' != '' AND project != '$proj_sql')
|
package/lib/db-summaries.sh
CHANGED
|
@@ -89,8 +89,17 @@ eagle_search_summaries() {
|
|
|
89
89
|
AND COALESCE(s.request, '') NOT LIKE '%<local-command-caveat>%'
|
|
90
90
|
AND COALESCE(s.request, '') NOT LIKE '# AGENTS.md instructions%'
|
|
91
91
|
AND COALESCE(s.request, '') NOT LIKE '<environment_context>%'
|
|
92
|
+
AND COALESCE(s.request, '') NOT LIKE '<subagent_notification>%'
|
|
93
|
+
AND COALESCE(s.request, '') NOT LIKE '</subagent_notification>%'
|
|
92
94
|
$where_clause
|
|
93
|
-
ORDER BY
|
|
95
|
+
ORDER BY
|
|
96
|
+
CASE
|
|
97
|
+
WHEN julianday('now') - julianday(s.created_at) <= 14 THEN 0
|
|
98
|
+
WHEN julianday('now') - julianday(s.created_at) <= 45 THEN 1
|
|
99
|
+
ELSE 2
|
|
100
|
+
END,
|
|
101
|
+
s.created_at DESC,
|
|
102
|
+
rank
|
|
94
103
|
LIMIT $limit;"
|
|
95
104
|
}
|
|
96
105
|
|
package/lib/hooks-posttool.sh
CHANGED
|
@@ -60,6 +60,7 @@ eagle_posttool_mirror_tasks() {
|
|
|
60
60
|
|
|
61
61
|
eagle_posttool_stale_hint() {
|
|
62
62
|
local tool_name="$1" fp="$2" project="$3"
|
|
63
|
+
local agent="${4:-$(eagle_agent_source)}"
|
|
63
64
|
|
|
64
65
|
case "$tool_name" in
|
|
65
66
|
Write|Edit|apply_patch)
|
|
@@ -77,9 +78,16 @@ eagle_posttool_stale_hint() {
|
|
|
77
78
|
local stale_hit
|
|
78
79
|
stale_hit=$(eagle_search_stale_memories "$project" "$fts_query")
|
|
79
80
|
if [ -n "$stale_hit" ]; then
|
|
80
|
-
local stale_msg
|
|
81
|
+
local stale_msg
|
|
82
|
+
if [ "$agent" = "codex" ]; then
|
|
83
|
+
stale_msg="Eagle Mem memory check:
|
|
84
|
+
- Memory '${stale_hit}' may reference '${fname}'.
|
|
85
|
+
- If this edit contradicts that memory, update or correct the memory before relying on it."
|
|
86
|
+
else
|
|
87
|
+
stale_msg="=== Eagle Mem: Memory Check ===
|
|
81
88
|
Memory '${stale_hit}' may reference '${fname}'. If your edit contradicts it, update the memory.
|
|
82
89
|
================"
|
|
90
|
+
fi
|
|
83
91
|
jq -nc --arg ctx "$stale_msg" '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":$ctx}}'
|
|
84
92
|
fi
|
|
85
93
|
fi
|
|
@@ -93,6 +101,7 @@ Memory '${stale_hit}' may reference '${fname}'. If your edit contradicts it, upd
|
|
|
93
101
|
|
|
94
102
|
eagle_posttool_decision_surface() {
|
|
95
103
|
local tool_name="$1" fp="$2" project="$3"
|
|
104
|
+
local agent="${4:-$(eagle_agent_source)}"
|
|
96
105
|
|
|
97
106
|
case "$tool_name" in
|
|
98
107
|
Read)
|
|
@@ -110,10 +119,17 @@ eagle_posttool_decision_surface() {
|
|
|
110
119
|
local decision_hit
|
|
111
120
|
decision_hit=$(eagle_search_decisions_for_file "$project" "$fts_query")
|
|
112
121
|
if [ -n "$decision_hit" ]; then
|
|
113
|
-
|
|
122
|
+
if [ "$agent" = "codex" ]; then
|
|
123
|
+
read_context+="Eagle Mem decision recall:
|
|
124
|
+
- ${fname}: ${decision_hit}
|
|
125
|
+
- Do not revert without explicit user request.
|
|
126
|
+
"
|
|
127
|
+
else
|
|
128
|
+
read_context+="=== Eagle Mem: Decision Recall ===
|
|
114
129
|
${fname}: ${decision_hit} — Do not revert without explicit user request.
|
|
115
130
|
================
|
|
116
131
|
"
|
|
132
|
+
fi
|
|
117
133
|
fi
|
|
118
134
|
fi
|
|
119
135
|
fi
|
|
@@ -123,17 +139,35 @@ ${fname}: ${decision_hit} — Do not revert without explicit user request.
|
|
|
123
139
|
if [ -n "$feature_hit" ]; then
|
|
124
140
|
while IFS='|' read -r feat_name feat_desc feat_verified _role feat_deps feat_other_files feat_smoke; do
|
|
125
141
|
[ -z "$feat_name" ] && continue
|
|
126
|
-
|
|
142
|
+
if [ "$agent" = "codex" ]; then
|
|
143
|
+
read_context+="Eagle Mem feature guardrail:
|
|
144
|
+
- '${fname}' is part of '${feat_name}'"
|
|
145
|
+
[ -n "$feat_desc" ] && read_context+=": ${feat_desc}"
|
|
146
|
+
read_context+=".
|
|
147
|
+
"
|
|
148
|
+
[ -n "$feat_verified" ] && read_context+="- Last verified: ${feat_verified}.
|
|
149
|
+
"
|
|
150
|
+
[ -n "$feat_deps" ] && read_context+="- Dependencies: ${feat_deps}.
|
|
151
|
+
"
|
|
152
|
+
[ -n "$feat_other_files" ] && read_context+="- Related files: ${feat_other_files}.
|
|
153
|
+
"
|
|
154
|
+
[ -n "$feat_smoke" ] && read_context+="- Smoke tests: ${feat_smoke}.
|
|
155
|
+
"
|
|
156
|
+
read_context+="- Retest after changes before release.
|
|
157
|
+
"
|
|
158
|
+
else
|
|
159
|
+
read_context+="=== Eagle Mem: Feature Guardrail ===
|
|
127
160
|
'${fname}' is part of feature '${feat_name}'"
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
161
|
+
[ -n "$feat_desc" ] && read_context+=" ($feat_desc)"
|
|
162
|
+
read_context+="."
|
|
163
|
+
[ -n "$feat_verified" ] && read_context+=" Last verified: ${feat_verified}."
|
|
164
|
+
[ -n "$feat_deps" ] && read_context+=" Dependencies: ${feat_deps}."
|
|
165
|
+
[ -n "$feat_other_files" ] && read_context+=" Other files in pipeline: ${feat_other_files}."
|
|
166
|
+
[ -n "$feat_smoke" ] && read_context+=" Smoke tests: ${feat_smoke}."
|
|
167
|
+
read_context+=" Changes require re-testing after deploy.
|
|
135
168
|
================
|
|
136
169
|
"
|
|
170
|
+
fi
|
|
137
171
|
done <<< "$feature_hit"
|
|
138
172
|
fi
|
|
139
173
|
|
package/lib/updater.sh
CHANGED
|
@@ -185,6 +185,12 @@ eagle_update_backup_runtime() {
|
|
|
185
185
|
fi
|
|
186
186
|
done
|
|
187
187
|
|
|
188
|
+
for item in .version .latest-version config.toml install-manifest.json .last-update.json; do
|
|
189
|
+
if [ -f "$EAGLE_MEM_DIR/$item" ]; then
|
|
190
|
+
cp "$EAGLE_MEM_DIR/$item" "$backup_dir/$item" 2>/dev/null || true
|
|
191
|
+
fi
|
|
192
|
+
done
|
|
193
|
+
|
|
188
194
|
if [ -f "$EAGLE_MEM_DB" ]; then
|
|
189
195
|
local sqlite_bin
|
|
190
196
|
sqlite_bin=$(eagle_sqlite_path)
|
|
@@ -205,6 +211,12 @@ eagle_update_restore_runtime() {
|
|
|
205
211
|
fi
|
|
206
212
|
done
|
|
207
213
|
|
|
214
|
+
for item in .version .latest-version config.toml install-manifest.json .last-update.json; do
|
|
215
|
+
if [ -f "$backup_dir/$item" ]; then
|
|
216
|
+
cp "$backup_dir/$item" "$EAGLE_MEM_DIR/$item" 2>/dev/null || true
|
|
217
|
+
fi
|
|
218
|
+
done
|
|
219
|
+
|
|
208
220
|
if [ -f "$backup_dir/memory.db" ]; then
|
|
209
221
|
cp "$backup_dir/memory.db" "$EAGLE_MEM_DB" 2>/dev/null || true
|
|
210
222
|
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eagle-mem",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.7",
|
|
4
4
|
"description": "Shared memory, release guardrails, RTK token protection, and worker lanes for Claude Code and Codex",
|
|
5
5
|
"bin": {
|
|
6
6
|
"eagle-mem": "bin/eagle-mem"
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"hooks/",
|
|
12
12
|
"lib/",
|
|
13
13
|
"db/",
|
|
14
|
-
"skills/"
|
|
14
|
+
"skills/",
|
|
15
|
+
"docs/",
|
|
16
|
+
"architecture.html"
|
|
15
17
|
],
|
|
16
18
|
"keywords": [
|
|
17
19
|
"claude-code",
|