eagle-mem 4.10.4 → 4.10.6
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 +26 -0
- package/README.md +23 -2
- package/architecture.html +11 -0
- package/db/036_graph_constraints.sql +4 -4
- package/db/037_task_dedup.sql +20 -0
- package/db/038_graph_node_types.sql +22 -0
- package/hooks/post-tool-use.sh +3 -3
- package/lib/common.sh +6 -1
- package/lib/db-graph.sh +310 -3
- package/lib/db-summaries.sh +4 -0
- package/package.json +4 -2
- package/scripts/curate.sh +37 -46
- package/scripts/help.sh +4 -1
- package/scripts/index.sh +44 -3
- package/scripts/install.sh +211 -112
- package/scripts/memories.sh +23 -0
- package/scripts/scan.sh +3 -3
- package/scripts/test.sh +21 -2
- package/scripts/update.sh +25 -1
- package/skills/eagle-mem-memories/SKILL.md +12 -3
- package/tests/test_antigravity_hook.py +114 -0
- package/tests/test_graph_memory.sh +148 -0
- package/integrations/__pycache__/google_antigravity_hook.cpython-314.pyc +0 -0
package/scripts/curate.sh
CHANGED
|
@@ -25,12 +25,32 @@ DRY_RUN=0
|
|
|
25
25
|
FULL=0
|
|
26
26
|
project=""
|
|
27
27
|
|
|
28
|
+
show_help() {
|
|
29
|
+
cat <<EOF
|
|
30
|
+
Usage: eagle-mem curate [options]
|
|
31
|
+
|
|
32
|
+
Options:
|
|
33
|
+
-h, --help Show this help message and exit
|
|
34
|
+
--dry-run Analyze gotchas, decisions, features, and memories without saving to db
|
|
35
|
+
--full Run full session compression and historical curation (slower)
|
|
36
|
+
-p, --project <dir> Target project directory (defaults to auto-detected cwd)
|
|
37
|
+
EOF
|
|
38
|
+
}
|
|
39
|
+
|
|
28
40
|
while [ $# -gt 0 ]; do
|
|
29
41
|
case "$1" in
|
|
42
|
+
-h|--help)
|
|
43
|
+
show_help
|
|
44
|
+
exit 0
|
|
45
|
+
;;
|
|
30
46
|
--dry-run) DRY_RUN=1; shift ;;
|
|
31
47
|
--full) FULL=1; shift ;;
|
|
32
48
|
-p|--project) project="$2"; shift 2 ;;
|
|
33
|
-
*)
|
|
49
|
+
*)
|
|
50
|
+
echo "Unknown option: $1" >&2
|
|
51
|
+
echo "Run with -h or --help for usage details." >&2
|
|
52
|
+
exit 1
|
|
53
|
+
;;
|
|
34
54
|
esac
|
|
35
55
|
done
|
|
36
56
|
|
|
@@ -78,7 +98,7 @@ Only promote gotchas that are:
|
|
|
78
98
|
|
|
79
99
|
If none qualify, output: NONE"
|
|
80
100
|
|
|
81
|
-
gotcha_result=$(eagle_llm_call "$gotcha_prompt" "You analyze software development patterns. Be concise. Only output PROMOTE lines or NONE." 512)
|
|
101
|
+
gotcha_result=$(eagle_llm_call "$gotcha_prompt" "You analyze software development patterns. Be concise. Only output PROMOTE lines or NONE." 512 || true)
|
|
82
102
|
|
|
83
103
|
if [ -n "$gotcha_result" ] && ! echo "$gotcha_result" | grep -q "^NONE$"; then
|
|
84
104
|
promoted=0
|
|
@@ -126,7 +146,7 @@ SUPERSEDED: <old decision> → <new decision> | file: <affected file if known>
|
|
|
126
146
|
|
|
127
147
|
If none are superseded, output: NONE"
|
|
128
148
|
|
|
129
|
-
decision_result=$(eagle_llm_call "$decision_prompt" "You detect contradicting software decisions. Be precise." 512)
|
|
149
|
+
decision_result=$(eagle_llm_call "$decision_prompt" "You detect contradicting software decisions. Be precise." 512 || true)
|
|
130
150
|
|
|
131
151
|
if [ -n "$decision_result" ] && ! echo "$decision_result" | grep -q "^NONE$"; then
|
|
132
152
|
superseded=0
|
|
@@ -208,7 +228,7 @@ Where:
|
|
|
208
228
|
|
|
209
229
|
If no rules needed, output: NONE"
|
|
210
230
|
|
|
211
|
-
cmd_result=$(eagle_llm_call "$cmd_prompt" "You optimize CLI output for AI assistants. Be conservative — only suggest rules for genuinely noisy commands." 512)
|
|
231
|
+
cmd_result=$(eagle_llm_call "$cmd_prompt" "You optimize CLI output for AI assistants. Be conservative — only suggest rules for genuinely noisy commands." 512 || true)
|
|
212
232
|
|
|
213
233
|
if [ -n "$cmd_result" ] && ! echo "$cmd_result" | grep -q "^NONE$"; then
|
|
214
234
|
rules_count=0
|
|
@@ -305,7 +325,7 @@ Rules:
|
|
|
305
325
|
- Don't re-discover existing features
|
|
306
326
|
- If no new features found, output: NONE"
|
|
307
327
|
|
|
308
|
-
feature_result=$(eagle_llm_call "$feature_prompt" "You identify software features from development session data. Be specific and evidence-based." 512)
|
|
328
|
+
feature_result=$(eagle_llm_call "$feature_prompt" "You identify software features from development session data. Be specific and evidence-based." 512 || true)
|
|
309
329
|
|
|
310
330
|
if [ -n "$feature_result" ] && ! echo "$feature_result" | grep -q "^NONE$"; then
|
|
311
331
|
features_count=0
|
|
@@ -428,7 +448,7 @@ if [ -n "$co_edit_data" ]; then
|
|
|
428
448
|
|
|
429
449
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
430
450
|
printf '%s\n' "$co_map_output" | while IFS='|' read -r f partners; do
|
|
431
|
-
[ -z "$f" ]
|
|
451
|
+
if [ -z "$f" ]; then continue; fi
|
|
432
452
|
eagle_info " $(basename "$f") → $partners"
|
|
433
453
|
done
|
|
434
454
|
else
|
|
@@ -436,7 +456,7 @@ if [ -n "$co_edit_data" ]; then
|
|
|
436
456
|
echo "BEGIN;"
|
|
437
457
|
echo "DELETE FROM file_hints WHERE project = '$(eagle_sql_escape "$project")' AND hint_type = 'co_edit';"
|
|
438
458
|
printf '%s\n' "$co_map_output" | while IFS='|' read -r f partners; do
|
|
439
|
-
[ -z "$f" ]
|
|
459
|
+
if [ -z "$f" ]; then continue; fi
|
|
440
460
|
local_f=$(eagle_sql_escape "$f")
|
|
441
461
|
local_v=$(eagle_sql_escape "$partners")
|
|
442
462
|
echo "INSERT INTO file_hints (project, hint_type, file_path, hint_value) VALUES ('$(eagle_sql_escape "$project")', 'co_edit', '$local_f', '$local_v') ON CONFLICT(project, hint_type, file_path) DO UPDATE SET hint_value = excluded.hint_value, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');"
|
|
@@ -492,7 +512,7 @@ if [ -n "$hot_file_data" ]; then
|
|
|
492
512
|
hot_count=0
|
|
493
513
|
|
|
494
514
|
while IFS='|' read -r hf_path hf_reads hf_sessions hf_rps; do
|
|
495
|
-
[ -z "$hf_path" ]
|
|
515
|
+
if [ -z "$hf_path" ]; then continue; fi
|
|
496
516
|
if [ -n "$hot_files" ]; then
|
|
497
517
|
hot_files+=","
|
|
498
518
|
fi
|
|
@@ -529,7 +549,7 @@ eagle_info "Executing Dream Cycle (Knowledge Graph & Memory Consolidation)..."
|
|
|
529
549
|
if [ -n "$co_edit_data" ]; then
|
|
530
550
|
co_wire_count=0
|
|
531
551
|
while IFS='|' read -r f1 f2 co_sessions; do
|
|
532
|
-
[ -z "$f1" ] || [ -z "$f2" ]
|
|
552
|
+
if [ -z "$f1" ] || [ -z "$f2" ]; then continue; fi
|
|
533
553
|
f1_id=$(eagle_graph_get_node_id "$project" "file" "$f1")
|
|
534
554
|
f2_id=$(eagle_graph_get_node_id "$project" "file" "$f2")
|
|
535
555
|
if [ -n "$f1_id" ] && [ -n "$f2_id" ]; then
|
|
@@ -543,41 +563,12 @@ if [ -n "$co_edit_data" ]; then
|
|
|
543
563
|
fi
|
|
544
564
|
|
|
545
565
|
# 7.2 Wire session nodes and access edges
|
|
546
|
-
recent_sessions=$(eagle_db "SELECT id
|
|
566
|
+
recent_sessions=$(eagle_db "SELECT id FROM sessions WHERE project = '$p_esc' ORDER BY started_at DESC LIMIT 15;")
|
|
547
567
|
if [ -n "$recent_sessions" ]; then
|
|
548
|
-
session_wire_count
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
eagle_graph_add_node "$project" "session" "$sid" "Session run on $sstart using $smodel" ""
|
|
553
|
-
sid_node=$(eagle_graph_get_node_id "$project" "session" "$sid")
|
|
554
|
-
if [ -n "$sid_node" ]; then
|
|
555
|
-
# Find read/modified files in this session from observations
|
|
556
|
-
session_files=$(eagle_db "SELECT files_read, files_modified FROM observations WHERE session_id = '$(eagle_sql_escape "$sid")';")
|
|
557
|
-
if [ -n "$session_files" ]; then
|
|
558
|
-
while IFS='|' read -r f_read f_mod; do
|
|
559
|
-
# Parse files_read JSON list
|
|
560
|
-
if [ -n "$f_read" ] && [ "$f_read" != "[]" ]; then
|
|
561
|
-
echo "$f_read" | grep -oE '"[^"]+"' | tr -d '"' | while read -r rf; do
|
|
562
|
-
[ -z "$rf" ] && continue
|
|
563
|
-
rfid=$(eagle_graph_get_node_id "$project" "file" "$rf")
|
|
564
|
-
[ -n "$rfid" ] && eagle_graph_add_edge "$project" "$sid_node" "$rfid" "read" 1.0
|
|
565
|
-
done
|
|
566
|
-
fi
|
|
567
|
-
# Parse files_modified JSON list
|
|
568
|
-
if [ -n "$f_mod" ] && [ "$f_mod" != "[]" ]; then
|
|
569
|
-
echo "$f_mod" | grep -oE '"[^"]+"' | tr -d '"' | while read -r mf; do
|
|
570
|
-
[ -z "$mf" ] && continue
|
|
571
|
-
mfid=$(eagle_graph_get_node_id "$project" "file" "$mf")
|
|
572
|
-
[ -n "$mfid" ] && eagle_graph_add_edge "$project" "$sid_node" "$mfid" "modified" 2.0
|
|
573
|
-
done
|
|
574
|
-
fi
|
|
575
|
-
done <<< "$session_files"
|
|
576
|
-
fi
|
|
577
|
-
fi
|
|
578
|
-
fi
|
|
579
|
-
session_wire_count=$((session_wire_count + 1))
|
|
580
|
-
done <<< "$recent_sessions"
|
|
568
|
+
session_wire_count=$(printf '%s\n' "$recent_sessions" | awk 'NF {count++} END {print count + 0}')
|
|
569
|
+
if [ "$DRY_RUN" -eq 0 ]; then
|
|
570
|
+
session_wire_count=$(eagle_graph_wire_recent_session_edges "$project" 15)
|
|
571
|
+
fi
|
|
581
572
|
eagle_ok "Wired $session_wire_count recent session nodes and edges"
|
|
582
573
|
fi
|
|
583
574
|
|
|
@@ -603,7 +594,7 @@ CONSOLIDATE: <original memory name 1>, <original memory name 2> -> <new consolid
|
|
|
603
594
|
|
|
604
595
|
If no memories need consolidation, output: NONE"
|
|
605
596
|
|
|
606
|
-
consolidation_result=$(eagle_llm_call "$consolidation_prompt" "You consolidate software development memories into a single compiled truth. Be precise. Output CONSOLIDATE lines or NONE." 1024)
|
|
597
|
+
consolidation_result=$(eagle_llm_call "$consolidation_prompt" "You consolidate software development memories into a single compiled truth. Be precise. Output CONSOLIDATE lines or NONE." 1024 || true)
|
|
607
598
|
|
|
608
599
|
if [ -n "$consolidation_result" ] && ! echo "$consolidation_result" | grep -q "^NONE$"; then
|
|
609
600
|
cons_count=0
|
|
@@ -620,7 +611,7 @@ If no memories need consolidation, output: NONE"
|
|
|
620
611
|
desc_part=$(echo "$rest_part" | grep -oE "description:[[:space:]]*[^|]+" | sed 's/description:[[:space:]]*//')
|
|
621
612
|
val_part=$(echo "$rest_part" | grep -oE "value:[[:space:]]*.+" | sed 's/value:[[:space:]]*//')
|
|
622
613
|
|
|
623
|
-
[ -z "$new_name" ] || [ -z "$names_part" ]
|
|
614
|
+
if [ -z "$new_name" ] || [ -z "$names_part" ]; then continue; fi
|
|
624
615
|
|
|
625
616
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
626
617
|
eagle_info " Would consolidate: $names_part → $new_name"
|
|
@@ -633,7 +624,7 @@ If no memories need consolidation, output: NONE"
|
|
|
633
624
|
IFS=',' read -ra name_arr <<< "$names_part"
|
|
634
625
|
for old_n in "${name_arr[@]}"; do
|
|
635
626
|
old_n=$(echo "$old_n" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
636
|
-
[ -z "$old_n" ]
|
|
627
|
+
if [ -z "$old_n" ]; then continue; fi
|
|
637
628
|
old_node_id=$(eagle_graph_get_node_id "$project" "memory" "$old_n")
|
|
638
629
|
if [ -n "$old_node_id" ] && [ -n "$new_node_id" ]; then
|
|
639
630
|
eagle_graph_add_edge "$project" "$new_node_id" "$old_node_id" "supersedes" 1.0
|
package/scripts/help.sh
CHANGED
|
@@ -14,7 +14,7 @@ eagle_banner
|
|
|
14
14
|
|
|
15
15
|
echo -e " ${BOLD}Eagle Mem${RESET} ${DIM}v${version}${RESET}"
|
|
16
16
|
echo -e " ${DIM}Shared memory, release guardrails, RTK token protection, and worker lanes${RESET}"
|
|
17
|
-
echo -e " ${DIM}for Claude Code, Codex, and
|
|
17
|
+
echo -e " ${DIM}for Claude Code, Codex, Grok, and Google Antigravity.${RESET}"
|
|
18
18
|
echo ""
|
|
19
19
|
echo -e " ${BOLD}Core commands:${RESET}"
|
|
20
20
|
echo -e " ${CYAN}install${RESET} First-time setup: hooks, database, skills"
|
|
@@ -28,6 +28,7 @@ echo -e " ${CYAN}overview${RESET} Build or view project overview"
|
|
|
28
28
|
echo -e " ${CYAN}session${RESET} Save a manual session summary"
|
|
29
29
|
echo -e " ${CYAN}memories${RESET} View/sync agent memories"
|
|
30
30
|
echo -e " ${CYAN}tasks${RESET} View mirrored tasks"
|
|
31
|
+
echo -e " ${CYAN}graph${RESET} View, query, or rebuild the codebase knowledge graph"
|
|
31
32
|
echo ""
|
|
32
33
|
echo -e " ${BOLD}Safety and token controls:${RESET}"
|
|
33
34
|
echo -e " ${CYAN}feature${RESET} Track, verify, and unblock feature changes"
|
|
@@ -59,6 +60,8 @@ echo -e " ${DIM}\$${RESET} eagle-mem search --tasks ${DIM}# in-flight
|
|
|
59
60
|
echo -e " ${DIM}\$${RESET} eagle-mem search --files ${DIM}# hot files${RESET}"
|
|
60
61
|
echo -e " ${DIM}\$${RESET} eagle-mem search --stats ${DIM}# project stats${RESET}"
|
|
61
62
|
echo -e " ${DIM}\$${RESET} eagle-mem session save --summary \"fixed auth\""
|
|
63
|
+
echo -e " ${DIM}\$${RESET} eagle-mem graph rebuild ${DIM}# rebuild code graph + chunks${RESET}"
|
|
64
|
+
echo -e " ${DIM}\$${RESET} eagle-mem index --force ${DIM}# force source chunk/declaration indexing${RESET}"
|
|
62
65
|
echo ""
|
|
63
66
|
echo -e " ${BOLD}Anti-regression:${RESET}"
|
|
64
67
|
echo -e " ${DIM}\$${RESET} eagle-mem feature pending ${DIM}# pending release blockers${RESET}"
|
package/scripts/index.sh
CHANGED
|
@@ -15,7 +15,28 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
|
15
15
|
|
|
16
16
|
eagle_ensure_db
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
force=false
|
|
19
|
+
args=()
|
|
20
|
+
|
|
21
|
+
show_help() {
|
|
22
|
+
echo -e " ${BOLD}eagle-mem index${RESET} — Index source files and wire code graph declarations"
|
|
23
|
+
echo ""
|
|
24
|
+
echo -e " ${BOLD}Usage:${RESET}"
|
|
25
|
+
echo -e " eagle-mem index [path] ${DIM}# incrementally index changed files${RESET}"
|
|
26
|
+
echo -e " eagle-mem index ${CYAN}--force${RESET} [path] ${DIM}# rebuild chunks and static code graph edges${RESET}"
|
|
27
|
+
echo ""
|
|
28
|
+
exit 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
while [ $# -gt 0 ]; do
|
|
32
|
+
case "$1" in
|
|
33
|
+
--force|-f) force=true; shift ;;
|
|
34
|
+
--help|-h) show_help ;;
|
|
35
|
+
*) args+=("$1"); shift ;;
|
|
36
|
+
esac
|
|
37
|
+
done
|
|
38
|
+
|
|
39
|
+
TARGET_DIR="${args[0]:-.}"
|
|
19
40
|
TARGET_DIR="$(cd "$TARGET_DIR" && pwd)"
|
|
20
41
|
PROJECT=$(eagle_project_from_cwd "$TARGET_DIR")
|
|
21
42
|
|
|
@@ -106,10 +127,20 @@ NEEDS_INDEX="$TMPDIR_IDX/needs_index"
|
|
|
106
127
|
|
|
107
128
|
skipped_count=0
|
|
108
129
|
|
|
130
|
+
if [ "$force" = true ]; then
|
|
131
|
+
eagle_info "Force rebuild requested: clearing chunks, declarations, and import edges"
|
|
132
|
+
eagle_graph_clear_index_state "$PROJECT"
|
|
133
|
+
fi
|
|
134
|
+
|
|
109
135
|
while IFS= read -r file; do
|
|
110
136
|
full_path="$TARGET_DIR/$file"
|
|
111
137
|
current_mtime=$(stat -f '%m' "$full_path" 2>/dev/null || stat -c '%Y' "$full_path" 2>/dev/null || echo "0")
|
|
112
138
|
|
|
139
|
+
if [ "$force" = true ]; then
|
|
140
|
+
echo "$file"
|
|
141
|
+
continue
|
|
142
|
+
fi
|
|
143
|
+
|
|
113
144
|
stored_mtime=$(eagle_db "SELECT MAX(mtime) FROM code_chunks WHERE project = '$project_sql' AND file_path = '$(eagle_sql_escape "$file")';")
|
|
114
145
|
|
|
115
146
|
if [ -n "$stored_mtime" ] && [ "$stored_mtime" = "$current_mtime" ]; then
|
|
@@ -180,16 +211,26 @@ COMMIT;"
|
|
|
180
211
|
|
|
181
212
|
# Static syntax relation extraction & graph wiring
|
|
182
213
|
# Wire node & edge static parser
|
|
214
|
+
overview=$(eagle_get_overview "$PROJECT" 2>/dev/null || true)
|
|
215
|
+
eagle_graph_add_node "$PROJECT" "project" "$PROJECT" "$overview" ""
|
|
216
|
+
project_node_id=$(eagle_graph_get_node_id "$PROJECT" "project" "$PROJECT")
|
|
217
|
+
eagle_graph_add_node "$PROJECT" "file" "$file" "" "$full_path"
|
|
183
218
|
file_node_id=$(eagle_graph_get_node_id "$PROJECT" "file" "$file")
|
|
184
219
|
if [ -n "$file_node_id" ]; then
|
|
220
|
+
if [ -n "$project_node_id" ]; then
|
|
221
|
+
eagle_graph_add_edge "$PROJECT" "$project_node_id" "$file_node_id" "contains" 1.0 >/dev/null || true
|
|
222
|
+
fi
|
|
223
|
+
eagle_graph_reset_file_static_edges "$PROJECT" "$file" "$file_node_id"
|
|
224
|
+
|
|
185
225
|
# 1. Parse function/class declarations
|
|
186
226
|
# e.g., "def name", "class name", "fn name", "function name", "func name"
|
|
187
227
|
declarations=$(grep -oE '\<(class|struct|function|def|fn|func)[[:space:]]+[A-Za-z0-9_]+' "$full_path" 2>/dev/null | awk '{print $1 ":" $2}' | sort -u || true)
|
|
188
228
|
if [ -n "$declarations" ]; then
|
|
189
229
|
while IFS=':' read -r dtype dname; do
|
|
190
230
|
[ -z "$dname" ] && continue
|
|
191
|
-
|
|
192
|
-
|
|
231
|
+
decl_node_name=$(eagle_graph_declaration_node_name "$file" "$dname")
|
|
232
|
+
eagle_graph_add_node "$PROJECT" "$dtype" "$decl_node_name" "Declared $dname in $file" "$file"
|
|
233
|
+
decl_node_id=$(eagle_graph_get_node_id "$PROJECT" "$dtype" "$decl_node_name")
|
|
193
234
|
if [ -n "$decl_node_id" ]; then
|
|
194
235
|
eagle_graph_add_edge "$PROJECT" "$file_node_id" "$decl_node_id" "declares" 1.0
|
|
195
236
|
fi
|