eagle-mem 4.9.6 → 4.9.8
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 +31 -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 +216 -9
- package/hooks/user-prompt-submit.sh +72 -15
- package/lib/common.sh +485 -35
- package/lib/db-mirrors.sh +9 -18
- package/lib/db-sessions.sh +22 -15
- package/lib/db-summaries.sh +19 -7
- 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 +258 -65
- package/scripts/orchestrate.sh +2 -2
- package/scripts/search.sh +240 -56
- package/scripts/statusline-em.sh +130 -26
- package/scripts/uninstall.sh +67 -0
- package/scripts/update.sh +8 -1
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
|
|
@@ -353,6 +362,121 @@ eagle_project_from_existing_ancestor() {
|
|
|
353
362
|
return 1
|
|
354
363
|
}
|
|
355
364
|
|
|
365
|
+
eagle_project_has_recall_rows() {
|
|
366
|
+
local project="${1:-}"
|
|
367
|
+
[ -n "$project" ] || return 1
|
|
368
|
+
|
|
369
|
+
eagle_project_has_table_row "agent_memories" "$project" \
|
|
370
|
+
|| eagle_project_has_table_row "agent_plans" "$project" \
|
|
371
|
+
|| eagle_project_has_table_row "agent_tasks" "$project" \
|
|
372
|
+
|| eagle_project_has_table_row "summaries" "$project"
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
eagle_recall_ancestor_project_from_cwd() {
|
|
376
|
+
local path="${1:-$(pwd)}"
|
|
377
|
+
local current_project="${2:-}"
|
|
378
|
+
local current key
|
|
379
|
+
|
|
380
|
+
current=$(eagle_normalize_project_path "$path")
|
|
381
|
+
eagle_is_ephemeral_project_path "$current" && return 1
|
|
382
|
+
|
|
383
|
+
[ -d "$current" ] || current=$(dirname "$current")
|
|
384
|
+
current=$(dirname "$current")
|
|
385
|
+
|
|
386
|
+
while [ -n "$current" ] && [ "$current" != "/" ]; do
|
|
387
|
+
if [ "$current" = "$HOME" ]; then
|
|
388
|
+
break
|
|
389
|
+
fi
|
|
390
|
+
|
|
391
|
+
key=$(eagle_project_key_from_target_dir "$current")
|
|
392
|
+
if [ -n "$key" ] && [ "$key" != "$current_project" ]; then
|
|
393
|
+
if eagle_project_has_recall_rows "$key"; then
|
|
394
|
+
printf '%s\n' "$key"
|
|
395
|
+
return 0
|
|
396
|
+
fi
|
|
397
|
+
fi
|
|
398
|
+
|
|
399
|
+
current=$(dirname "$current")
|
|
400
|
+
done
|
|
401
|
+
|
|
402
|
+
return 1
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
eagle_recall_project_scope_from_cwd() {
|
|
406
|
+
local path="${1:-$(pwd)}"
|
|
407
|
+
local project="${2:-}"
|
|
408
|
+
local ancestor
|
|
409
|
+
|
|
410
|
+
[ -z "$project" ] && project=$(eagle_project_from_cwd "$path")
|
|
411
|
+
if ancestor=$(eagle_recall_ancestor_project_from_cwd "$path" "$project"); then
|
|
412
|
+
if [ -n "$project" ] && [ "$ancestor" != "$project" ]; then
|
|
413
|
+
printf '%s|%s\n' "$project" "$ancestor"
|
|
414
|
+
return 0
|
|
415
|
+
fi
|
|
416
|
+
printf '%s\n' "$ancestor"
|
|
417
|
+
return 0
|
|
418
|
+
fi
|
|
419
|
+
|
|
420
|
+
printf '%s\n' "$project"
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
eagle_sql_project_scope_condition() {
|
|
424
|
+
local column="${1:-project}"
|
|
425
|
+
local scope="${2:-}"
|
|
426
|
+
local values="" count=0 item escaped
|
|
427
|
+
|
|
428
|
+
while IFS= read -r item; do
|
|
429
|
+
[ -n "$item" ] || continue
|
|
430
|
+
escaped=$(eagle_sql_escape "$item")
|
|
431
|
+
values="${values},'${escaped}'"
|
|
432
|
+
count=$((count + 1))
|
|
433
|
+
done <<EOF
|
|
434
|
+
$(printf '%s' "$scope" | tr '|' '\n')
|
|
435
|
+
EOF
|
|
436
|
+
|
|
437
|
+
if [ "$count" -eq 0 ]; then
|
|
438
|
+
printf '1 = 0\n'
|
|
439
|
+
elif [ "$count" -eq 1 ]; then
|
|
440
|
+
printf "%s = %s\n" "$column" "${values#,}"
|
|
441
|
+
else
|
|
442
|
+
printf "%s IN (%s)\n" "$column" "${values#,}"
|
|
443
|
+
fi
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
eagle_project_scope_label() {
|
|
447
|
+
local scope="${1:-}"
|
|
448
|
+
printf '%s\n' "$scope" | tr '|' ','
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
eagle_project_scope_contains() {
|
|
452
|
+
local scope="${1:-}"
|
|
453
|
+
local target="${2:-}"
|
|
454
|
+
local item
|
|
455
|
+
[ -n "$target" ] || return 1
|
|
456
|
+
|
|
457
|
+
while IFS= read -r item; do
|
|
458
|
+
[ -z "$item" ] && continue
|
|
459
|
+
[ "$item" = "$target" ] && return 0
|
|
460
|
+
done <<EOF
|
|
461
|
+
$(printf '%s' "$scope" | tr '|' '\n')
|
|
462
|
+
EOF
|
|
463
|
+
|
|
464
|
+
return 1
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
eagle_claude_project_dir_for_key() {
|
|
468
|
+
local project="${1:-}"
|
|
469
|
+
[ -n "$project" ] || return 1
|
|
470
|
+
|
|
471
|
+
local abs slug
|
|
472
|
+
case "$project" in
|
|
473
|
+
/*) abs="$project" ;;
|
|
474
|
+
*) abs="$HOME/$project" ;;
|
|
475
|
+
esac
|
|
476
|
+
slug=$(printf '%s' "$abs" | sed -E 's#[^[:alnum:].]+#-#g')
|
|
477
|
+
printf '%s/%s\n' "$EAGLE_CLAUDE_PROJECTS_DIR" "$slug"
|
|
478
|
+
}
|
|
479
|
+
|
|
356
480
|
eagle_project_from_workspace_path() {
|
|
357
481
|
if [ -n "${EAGLE_MEM_PROJECT:-}" ]; then
|
|
358
482
|
printf '%s\n' "$EAGLE_MEM_PROJECT"
|
|
@@ -413,7 +537,7 @@ eagle_project_from_statusline_input() {
|
|
|
413
537
|
local project_dir="${2:-}"
|
|
414
538
|
local cwd="${3:-}"
|
|
415
539
|
local session_id="${4:-}"
|
|
416
|
-
local project workspace_project_dir transcript_path
|
|
540
|
+
local project session_project workspace_project workspace_project_dir transcript_path
|
|
417
541
|
|
|
418
542
|
if [ -n "${EAGLE_MEM_PROJECT:-}" ]; then
|
|
419
543
|
printf '%s\n' "$EAGLE_MEM_PROJECT"
|
|
@@ -430,27 +554,49 @@ eagle_project_from_statusline_input() {
|
|
|
430
554
|
[ -z "$cwd" ] && cwd=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null)
|
|
431
555
|
fi
|
|
432
556
|
|
|
433
|
-
#
|
|
434
|
-
#
|
|
435
|
-
#
|
|
436
|
-
if [ -n "$workspace_project_dir" ]; then
|
|
437
|
-
|
|
438
|
-
|
|
557
|
+
# Claude can report $HOME as workspace.project_dir for a child project before
|
|
558
|
+
# the project is fully initialized. Treat that as too broad for statusline
|
|
559
|
+
# stats; the current working directory is the more useful project boundary.
|
|
560
|
+
if [ -n "$workspace_project_dir" ] && [ -n "$cwd" ]; then
|
|
561
|
+
local workspace_resolved cwd_resolved
|
|
562
|
+
workspace_resolved=$(eagle_normalize_project_path "$workspace_project_dir")
|
|
563
|
+
cwd_resolved=$(eagle_normalize_project_path "$cwd")
|
|
564
|
+
if [ "$workspace_resolved" = "$HOME" ] && [ "$cwd_resolved" != "$HOME" ]; then
|
|
565
|
+
workspace_project_dir="$cwd_resolved"
|
|
566
|
+
fi
|
|
439
567
|
fi
|
|
440
568
|
|
|
441
|
-
if [ -n "$
|
|
442
|
-
|
|
443
|
-
printf '%s\n' "$project"
|
|
444
|
-
return
|
|
445
|
-
fi
|
|
569
|
+
if [ -n "$workspace_project_dir" ]; then
|
|
570
|
+
workspace_project=$(eagle_project_from_workspace_path "$workspace_project_dir" || true)
|
|
446
571
|
fi
|
|
447
572
|
|
|
448
573
|
if [ -n "$session_id" ]; then
|
|
449
|
-
|
|
450
|
-
|
|
574
|
+
session_project=$(eagle_get_session_project_light "$session_id" || true)
|
|
575
|
+
if [ -n "$session_project" ]; then
|
|
576
|
+
# If an older row was captured under a nested folder key, a current
|
|
577
|
+
# workspace root should still be allowed to repair the display.
|
|
578
|
+
if [ -n "$workspace_project" ] && [ "$session_project" != "$workspace_project" ]; then
|
|
579
|
+
case "$session_project" in
|
|
580
|
+
"$workspace_project"/*)
|
|
581
|
+
printf '%s\n' "$workspace_project"
|
|
582
|
+
return
|
|
583
|
+
;;
|
|
584
|
+
esac
|
|
585
|
+
fi
|
|
586
|
+
printf '%s\n' "$session_project"
|
|
451
587
|
return
|
|
452
588
|
fi
|
|
453
|
-
|
|
589
|
+
fi
|
|
590
|
+
|
|
591
|
+
# Explicit workspace project and transcript start are stronger than generic
|
|
592
|
+
# path fallback because they repair pre-fix sessions that were stored under
|
|
593
|
+
# a nested folder key.
|
|
594
|
+
if [ -n "$workspace_project_dir" ]; then
|
|
595
|
+
[ -n "$workspace_project" ] && { printf '%s\n' "$workspace_project"; return; }
|
|
596
|
+
fi
|
|
597
|
+
|
|
598
|
+
if [ -n "$transcript_path" ]; then
|
|
599
|
+
if project=$(eagle_project_from_transcript_start "$transcript_path" "${cwd:-$project_dir}"); then
|
|
454
600
|
printf '%s\n' "$project"
|
|
455
601
|
return
|
|
456
602
|
fi
|
|
@@ -697,20 +843,15 @@ eagle_emit_context_for_agent() {
|
|
|
697
843
|
|
|
698
844
|
[ -z "$context" ] && return 0
|
|
699
845
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
}'
|
|
710
|
-
return 0
|
|
711
|
-
fi
|
|
712
|
-
|
|
713
|
-
printf '%s\n' "$context"
|
|
846
|
+
jq -cn \
|
|
847
|
+
--arg event "$hook_event" \
|
|
848
|
+
--arg context "$context" \
|
|
849
|
+
'{
|
|
850
|
+
hookSpecificOutput: {
|
|
851
|
+
hookEventName: $event,
|
|
852
|
+
additionalContext: $context
|
|
853
|
+
}
|
|
854
|
+
}'
|
|
714
855
|
}
|
|
715
856
|
|
|
716
857
|
eagle_config_get_light() {
|
|
@@ -1146,7 +1287,7 @@ eagle_statusline_script_from_command() {
|
|
|
1146
1287
|
eagle_statusline_script_uses_input() {
|
|
1147
1288
|
local sl_file="${1:-}"
|
|
1148
1289
|
[ -f "$sl_file" ] || return 1
|
|
1149
|
-
grep -Eq 'eagle_project_from_statusline_input|eagle_mem_statusline.*(\$\{input:-\}|\$input)' "$sl_file"
|
|
1290
|
+
grep -Eq 'eagle_project_from_statusline_input|eagle_mem_statusline.*(\$\{input:-\}|\$input)|statusline-em\.sh.*--hud' "$sl_file"
|
|
1150
1291
|
}
|
|
1151
1292
|
|
|
1152
1293
|
eagle_patch_statusline_script() {
|
|
@@ -1178,6 +1319,14 @@ eagle_patch_statusline_script() {
|
|
|
1178
1319
|
fi
|
|
1179
1320
|
|
|
1180
1321
|
perl -0pi -e '
|
|
1322
|
+
s{# [^\n]*EAGLE MEM[^\n]*\n.*?\n# [^\n]*CLOCK[^\n]*\n}{q~# -- EAGLE MEM (second line - branded) --------------------------------------
|
|
1323
|
+
em_line=""
|
|
1324
|
+
if [ -n "$cwd" ] && [ -f "$HOME/.eagle-mem/scripts/statusline-em.sh" ]; then
|
|
1325
|
+
em_line=$(printf "%s" "$input" | bash "$HOME/.eagle-mem/scripts/statusline-em.sh" --hud 2>/dev/null)
|
|
1326
|
+
fi
|
|
1327
|
+
|
|
1328
|
+
# -- CLOCK ---------------------------------------------------------------------
|
|
1329
|
+
~}ems;
|
|
1181
1330
|
s/(project_dir=\$\(echo "\$input" \| jq -r \x27)\.workspace\.current_dir \/\/ \.cwd/$1.workspace.project_dir \/\/ .workspace.current_dir \/\/ .cwd/g;
|
|
1182
1331
|
s/(project_dir=\$\(echo "\$input" \| jq -r ")\.workspace\.current_dir \/\/ \.cwd/$1.workspace.project_dir \/\/ .workspace.current_dir \/\/ .cwd/g;
|
|
1183
1332
|
s/eagle_mem_statusline "\$project_dir" "\$session_id" "\$\{input\}"/eagle_mem_statusline "\$project_dir" "\$session_id" "\${input:-}"/g;
|
|
@@ -1212,6 +1361,307 @@ eagle_patch_statusline_script() {
|
|
|
1212
1361
|
return 0
|
|
1213
1362
|
}
|
|
1214
1363
|
|
|
1364
|
+
eagle_runtime_change_plan() {
|
|
1365
|
+
local action="${1:-install}"
|
|
1366
|
+
local package_dir="${2:-}"
|
|
1367
|
+
local claude_found="${3:-false}"
|
|
1368
|
+
local codex_found="${4:-false}"
|
|
1369
|
+
|
|
1370
|
+
echo ""
|
|
1371
|
+
echo -e " ${BOLD:-}What will change${RESET:-}"
|
|
1372
|
+
echo -e " ${DIM:-}Eagle Mem shows this before touching runtime files or agent configs.${RESET:-}"
|
|
1373
|
+
echo ""
|
|
1374
|
+
echo -e " ${CYAN:-}->${RESET:-} Copy runtime files"
|
|
1375
|
+
echo -e " ${DIM:-}from:${RESET:-} ${package_dir:-current package}"
|
|
1376
|
+
echo -e " ${DIM:-}to: ${RESET:-} $EAGLE_MEM_DIR/{hooks,lib,db,scripts}"
|
|
1377
|
+
echo -e " ${CYAN:-}->${RESET:-} Open database"
|
|
1378
|
+
echo -e " ${DIM:-}path:${RESET:-} $EAGLE_MEM_DB"
|
|
1379
|
+
echo -e " ${DIM:-}mode:${RESET:-} migrate only when needed; existing memories are preserved"
|
|
1380
|
+
|
|
1381
|
+
if [ "$claude_found" = true ]; then
|
|
1382
|
+
echo -e " ${CYAN:-}->${RESET:-} Update Claude Code"
|
|
1383
|
+
echo -e " ${DIM:-}settings:${RESET:-} $EAGLE_SETTINGS"
|
|
1384
|
+
echo -e " ${DIM:-}hooks: ${RESET:-} SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop, SessionEnd"
|
|
1385
|
+
echo -e " ${DIM:-}status: ${RESET:-} patch Eagle Mem statusline block when auto-detectable; backup first"
|
|
1386
|
+
echo -e " ${DIM:-}skills: ${RESET:-} $EAGLE_SKILLS_DIR/eagle-mem-*"
|
|
1387
|
+
echo -e " ${DIM:-}guide: ${RESET:-} $HOME/.claude/CLAUDE.md Eagle Mem section"
|
|
1388
|
+
else
|
|
1389
|
+
echo -e " ${DIM:-}-> Claude Code not detected; Claude hooks/skills skipped${RESET:-}"
|
|
1390
|
+
fi
|
|
1391
|
+
|
|
1392
|
+
if [ "$codex_found" = true ]; then
|
|
1393
|
+
echo -e " ${CYAN:-}->${RESET:-} Update Codex"
|
|
1394
|
+
echo -e " ${DIM:-}config:${RESET:-} $EAGLE_CODEX_CONFIG"
|
|
1395
|
+
echo -e " ${DIM:-}hooks: ${RESET:-} $EAGLE_CODEX_HOOKS"
|
|
1396
|
+
echo -e " ${DIM:-}skills:${RESET:-} $EAGLE_CODEX_SKILLS_DIR/eagle-mem-*"
|
|
1397
|
+
echo -e " ${DIM:-}guide: ${RESET:-} $EAGLE_CODEX_AGENTS_MD Eagle Mem section"
|
|
1398
|
+
else
|
|
1399
|
+
echo -e " ${DIM:-}-> Codex not detected; Codex hooks/skills skipped${RESET:-}"
|
|
1400
|
+
fi
|
|
1401
|
+
|
|
1402
|
+
if [ "$action" = "update" ]; then
|
|
1403
|
+
echo -e " ${CYAN:-}->${RESET:-} Refresh installed version metadata"
|
|
1404
|
+
fi
|
|
1405
|
+
echo ""
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
eagle_uninstall_change_plan() {
|
|
1409
|
+
echo ""
|
|
1410
|
+
echo -e " ${BOLD:-}What will change${RESET:-}"
|
|
1411
|
+
echo -e " ${DIM:-}Uninstall removes Eagle Mem-owned integration points. Runtime data is deleted only if you confirm later.${RESET:-}"
|
|
1412
|
+
echo ""
|
|
1413
|
+
echo -e " ${CYAN:-}->${RESET:-} Remove Claude hooks from $EAGLE_SETTINGS"
|
|
1414
|
+
echo -e " ${CYAN:-}->${RESET:-} Remove Codex hooks from $EAGLE_CODEX_HOOKS"
|
|
1415
|
+
echo -e " ${CYAN:-}->${RESET:-} Remove Eagle Mem skill links from:"
|
|
1416
|
+
echo -e " ${DIM:-}$EAGLE_SKILLS_DIR${RESET:-}"
|
|
1417
|
+
echo -e " ${DIM:-}$EAGLE_CODEX_SKILLS_DIR${RESET:-}"
|
|
1418
|
+
echo -e " ${CYAN:-}->${RESET:-} Remove Eagle Mem instruction blocks from:"
|
|
1419
|
+
echo -e " ${DIM:-}$HOME/.claude/CLAUDE.md${RESET:-}"
|
|
1420
|
+
echo -e " ${DIM:-}$EAGLE_CODEX_AGENTS_MD${RESET:-}"
|
|
1421
|
+
echo -e " ${CYAN:-}->${RESET:-} Remove Eagle Mem statusline integration when auto-detectable"
|
|
1422
|
+
echo -e " ${DIM:-}-> Backups are written next to edited user config files.${RESET:-}"
|
|
1423
|
+
echo ""
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
eagle_backup_user_file() {
|
|
1427
|
+
local file="${1:-}"
|
|
1428
|
+
[ -f "$file" ] || return 1
|
|
1429
|
+
local backup
|
|
1430
|
+
backup="${file}.eagle-mem.uninstall-bak-$(date -u +%Y%m%dT%H%M%SZ)"
|
|
1431
|
+
cp "$file" "$backup" 2>/dev/null || return 1
|
|
1432
|
+
printf '%s\n' "$backup"
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
eagle_remove_marked_markdown_section() {
|
|
1436
|
+
local file="${1:-}"
|
|
1437
|
+
local marker="${2:-## Eagle Mem — Persistent Memory}"
|
|
1438
|
+
[ -f "$file" ] || return 1
|
|
1439
|
+
grep -qF "$marker" "$file" 2>/dev/null || return 1
|
|
1440
|
+
|
|
1441
|
+
local tmp
|
|
1442
|
+
tmp=$(mktemp) || return 1
|
|
1443
|
+
awk -v marker="$marker" '
|
|
1444
|
+
BEGIN { skip=0; pending_sep="" }
|
|
1445
|
+
$0 == "---" && !skip {
|
|
1446
|
+
pending_sep=$0 ORS
|
|
1447
|
+
next
|
|
1448
|
+
}
|
|
1449
|
+
index($0, marker) {
|
|
1450
|
+
skip=1
|
|
1451
|
+
pending_sep=""
|
|
1452
|
+
next
|
|
1453
|
+
}
|
|
1454
|
+
skip {
|
|
1455
|
+
if ($0 == "---") {
|
|
1456
|
+
skip=0
|
|
1457
|
+
pending_sep=""
|
|
1458
|
+
next
|
|
1459
|
+
}
|
|
1460
|
+
if ($0 ~ /^## /) {
|
|
1461
|
+
skip=0
|
|
1462
|
+
if (pending_sep != "") {
|
|
1463
|
+
printf "%s", pending_sep
|
|
1464
|
+
pending_sep=""
|
|
1465
|
+
}
|
|
1466
|
+
print
|
|
1467
|
+
}
|
|
1468
|
+
next
|
|
1469
|
+
}
|
|
1470
|
+
{
|
|
1471
|
+
if (pending_sep != "") {
|
|
1472
|
+
printf "%s", pending_sep
|
|
1473
|
+
pending_sep=""
|
|
1474
|
+
}
|
|
1475
|
+
print
|
|
1476
|
+
}
|
|
1477
|
+
END {
|
|
1478
|
+
if (!skip && pending_sep != "") {
|
|
1479
|
+
printf "%s", pending_sep
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
' "$file" > "$tmp" || { rm -f "$tmp"; return 1; }
|
|
1483
|
+
|
|
1484
|
+
if cmp -s "$file" "$tmp"; then
|
|
1485
|
+
rm -f "$tmp"
|
|
1486
|
+
return 1
|
|
1487
|
+
fi
|
|
1488
|
+
mv "$tmp" "$file"
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
eagle_remove_statusline_block() {
|
|
1492
|
+
local sl_file="${1:-}"
|
|
1493
|
+
[ -f "$sl_file" ] || return 1
|
|
1494
|
+
command -v perl >/dev/null 2>&1 || return 1
|
|
1495
|
+
grep -Eq 'EAGLE MEM|Eagle Mem|eagle_mem_statusline|statusline-em\.sh|agent_memories|claude_memories' "$sl_file" 2>/dev/null || return 1
|
|
1496
|
+
grep -Eq '\.eagle-mem|eagle_mem_statusline|statusline-em\.sh|agent_memories|claude_memories' "$sl_file" 2>/dev/null || return 1
|
|
1497
|
+
|
|
1498
|
+
local tmp backup mode
|
|
1499
|
+
tmp=$(mktemp) || return 1
|
|
1500
|
+
cp "$sl_file" "$tmp" || { rm -f "$tmp"; return 1; }
|
|
1501
|
+
|
|
1502
|
+
perl -0pi -e '
|
|
1503
|
+
s{\n?# [^\n]*(?:EAGLE MEM|Eagle Mem)[^\n]*\n.*?\n(# [^\n]*CLOCK[^\n]*\n)}{\n$1}is;
|
|
1504
|
+
' "$tmp" || { rm -f "$tmp"; return 1; }
|
|
1505
|
+
|
|
1506
|
+
if cmp -s "$sl_file" "$tmp"; then
|
|
1507
|
+
rm -f "$tmp"
|
|
1508
|
+
return 1
|
|
1509
|
+
fi
|
|
1510
|
+
|
|
1511
|
+
backup=$(eagle_backup_user_file "$sl_file" 2>/dev/null || true)
|
|
1512
|
+
mode=$(stat -f %Lp "$sl_file" 2>/dev/null || stat -c %a "$sl_file" 2>/dev/null || echo "")
|
|
1513
|
+
mv "$tmp" "$sl_file"
|
|
1514
|
+
[ -n "$mode" ] && chmod "$mode" "$sl_file" 2>/dev/null || chmod +x "$sl_file" 2>/dev/null || true
|
|
1515
|
+
[ -n "$backup" ] && printf '%s\n' "$backup"
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
eagle_remove_statusline_integration() {
|
|
1519
|
+
local settings="${1:-$EAGLE_SETTINGS}"
|
|
1520
|
+
[ -f "$settings" ] || return 1
|
|
1521
|
+
command -v jq >/dev/null 2>&1 || return 1
|
|
1522
|
+
|
|
1523
|
+
local command sl_file wrapper tmp changed=1
|
|
1524
|
+
command=$(jq -r '.statusLine.command // .statusline.command // empty' "$settings" 2>/dev/null)
|
|
1525
|
+
[ -n "$command" ] || return 1
|
|
1526
|
+
sl_file=$(eagle_statusline_script_from_command "$command" 2>/dev/null || true)
|
|
1527
|
+
wrapper="$EAGLE_MEM_DIR/scripts/statusline-wrapper.sh"
|
|
1528
|
+
|
|
1529
|
+
if printf '%s' "$command" | grep -q "$wrapper"; then
|
|
1530
|
+
eagle_backup_user_file "$settings" >/dev/null 2>&1 || true
|
|
1531
|
+
tmp=$(mktemp) || return 1
|
|
1532
|
+
jq 'del(.statusLine) | del(.statusline)' "$settings" > "$tmp" && mv "$tmp" "$settings"
|
|
1533
|
+
return 0
|
|
1534
|
+
fi
|
|
1535
|
+
|
|
1536
|
+
if [ -n "$sl_file" ] && [ -f "$sl_file" ]; then
|
|
1537
|
+
if eagle_remove_statusline_block "$sl_file" >/dev/null; then
|
|
1538
|
+
changed=0
|
|
1539
|
+
fi
|
|
1540
|
+
fi
|
|
1541
|
+
return "$changed"
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
eagle_runtime_manifest_path() {
|
|
1545
|
+
printf '%s/install-manifest.json\n' "$EAGLE_MEM_DIR"
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
eagle_file_sha256() {
|
|
1549
|
+
local file="${1:-}"
|
|
1550
|
+
[ -f "$file" ] || return 1
|
|
1551
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
1552
|
+
shasum -a 256 "$file" 2>/dev/null | awk '{print $1}'
|
|
1553
|
+
elif command -v sha256sum >/dev/null 2>&1; then
|
|
1554
|
+
sha256sum "$file" 2>/dev/null | awk '{print $1}'
|
|
1555
|
+
else
|
|
1556
|
+
return 1
|
|
1557
|
+
fi
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
eagle_runtime_manifest_write() {
|
|
1561
|
+
local package_dir="${1:-}"
|
|
1562
|
+
local action="${2:-update}"
|
|
1563
|
+
local manifest tmp_files tmp_json
|
|
1564
|
+
manifest=$(eagle_runtime_manifest_path)
|
|
1565
|
+
mkdir -p "$EAGLE_MEM_DIR"
|
|
1566
|
+
tmp_files=$(mktemp) || return 1
|
|
1567
|
+
tmp_json=$(mktemp) || { rm -f "$tmp_files"; return 1; }
|
|
1568
|
+
|
|
1569
|
+
local group rel file sha size mode package_version sqlite_bin sqlite_version files_json
|
|
1570
|
+
for group in hooks lib db scripts; do
|
|
1571
|
+
[ -d "$EAGLE_MEM_DIR/$group" ] || continue
|
|
1572
|
+
while IFS= read -r rel; do
|
|
1573
|
+
[ -n "$rel" ] || continue
|
|
1574
|
+
file="$EAGLE_MEM_DIR/$rel"
|
|
1575
|
+
[ -f "$file" ] || continue
|
|
1576
|
+
sha=$(eagle_file_sha256 "$file" 2>/dev/null || true)
|
|
1577
|
+
size=$(wc -c < "$file" 2>/dev/null | tr -d ' ')
|
|
1578
|
+
mode=$(stat -f %Lp "$file" 2>/dev/null || stat -c %a "$file" 2>/dev/null || echo "")
|
|
1579
|
+
printf '%s\t%s\t%s\t%s\n' "$rel" "${sha:-unknown}" "${size:-0}" "$mode"
|
|
1580
|
+
done < <(cd "$EAGLE_MEM_DIR" && find "$group" -type f | sort 2>/dev/null)
|
|
1581
|
+
done > "$tmp_files"
|
|
1582
|
+
|
|
1583
|
+
package_version=$(jq -r .version "$package_dir/package.json" 2>/dev/null || tr -d '[:space:]' < "$EAGLE_MEM_DIR/.version" 2>/dev/null || echo "unknown")
|
|
1584
|
+
sqlite_bin=$(eagle_sqlite_path)
|
|
1585
|
+
sqlite_version=$(eagle_sqlite_version)
|
|
1586
|
+
files_json=$(jq -R -s '
|
|
1587
|
+
split("\n")
|
|
1588
|
+
| map(select(length > 0)
|
|
1589
|
+
| split("\t")
|
|
1590
|
+
| {
|
|
1591
|
+
path: .[0],
|
|
1592
|
+
sha256: .[1],
|
|
1593
|
+
size: ((.[2] // "0") | tonumber),
|
|
1594
|
+
mode: (.[3] // "")
|
|
1595
|
+
})
|
|
1596
|
+
' "$tmp_files")
|
|
1597
|
+
|
|
1598
|
+
jq -nc \
|
|
1599
|
+
--arg schema "1" \
|
|
1600
|
+
--arg action "$action" \
|
|
1601
|
+
--arg package_name "eagle-mem" \
|
|
1602
|
+
--arg package_version "$package_version" \
|
|
1603
|
+
--arg package_dir "$package_dir" \
|
|
1604
|
+
--arg runtime_dir "$EAGLE_MEM_DIR" \
|
|
1605
|
+
--arg db "$EAGLE_MEM_DB" \
|
|
1606
|
+
--arg sqlite_bin "${sqlite_bin:-}" \
|
|
1607
|
+
--arg sqlite_version "${sqlite_version:-}" \
|
|
1608
|
+
--arg generated_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
1609
|
+
--argjson files "$files_json" \
|
|
1610
|
+
'{schema:$schema,
|
|
1611
|
+
generated_at:$generated_at,
|
|
1612
|
+
action:$action,
|
|
1613
|
+
package:{name:$package_name, version:$package_version, dir:$package_dir},
|
|
1614
|
+
runtime:{dir:$runtime_dir, db:$db},
|
|
1615
|
+
sqlite:{path:$sqlite_bin, version:$sqlite_version},
|
|
1616
|
+
files:$files}' > "$tmp_json" || { rm -f "$tmp_files" "$tmp_json"; return 1; }
|
|
1617
|
+
|
|
1618
|
+
mv "$tmp_json" "$manifest"
|
|
1619
|
+
chmod 600 "$manifest" 2>/dev/null || true
|
|
1620
|
+
rm -f "$tmp_files"
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
eagle_runtime_manifest_check() {
|
|
1624
|
+
local manifest
|
|
1625
|
+
manifest=$(eagle_runtime_manifest_path)
|
|
1626
|
+
if [ ! -f "$manifest" ]; then
|
|
1627
|
+
printf 'missing|0|0|0\n'
|
|
1628
|
+
return 0
|
|
1629
|
+
fi
|
|
1630
|
+
command -v jq >/dev/null 2>&1 || {
|
|
1631
|
+
printf 'unreadable|0|0|0\n'
|
|
1632
|
+
return 0
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
local checked=0 missing=0 drift=0 rel expected file actual
|
|
1636
|
+
while IFS=$'\t' read -r rel expected; do
|
|
1637
|
+
[ -n "$rel" ] || continue
|
|
1638
|
+
checked=$((checked + 1))
|
|
1639
|
+
file="$EAGLE_MEM_DIR/$rel"
|
|
1640
|
+
if [ ! -f "$file" ]; then
|
|
1641
|
+
missing=$((missing + 1))
|
|
1642
|
+
continue
|
|
1643
|
+
fi
|
|
1644
|
+
actual=$(eagle_file_sha256 "$file" 2>/dev/null || true)
|
|
1645
|
+
if [ -z "$actual" ] || [ "$actual" != "$expected" ]; then
|
|
1646
|
+
drift=$((drift + 1))
|
|
1647
|
+
fi
|
|
1648
|
+
done < <(jq -r '.files[]? | [.path, .sha256] | @tsv' "$manifest" 2>/dev/null)
|
|
1649
|
+
|
|
1650
|
+
if [ "$missing" -eq 0 ] && [ "$drift" -eq 0 ]; then
|
|
1651
|
+
printf 'ok|%s|0|0\n' "$checked"
|
|
1652
|
+
else
|
|
1653
|
+
printf 'drift|%s|%s|%s\n' "$checked" "$missing" "$drift"
|
|
1654
|
+
fi
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
eagle_runtime_manifest_field() {
|
|
1658
|
+
local field="${1:-}"
|
|
1659
|
+
local manifest
|
|
1660
|
+
manifest=$(eagle_runtime_manifest_path)
|
|
1661
|
+
[ -f "$manifest" ] || return 1
|
|
1662
|
+
jq -r "$field // empty" "$manifest" 2>/dev/null
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1215
1665
|
_eagle_claude_md_section() {
|
|
1216
1666
|
cat << 'EAGLE_MD'
|
|
1217
1667
|
|