eagle-mem 4.10.4 → 4.10.5
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 +12 -0
- package/README.md +1 -1
- package/db/037_task_dedup.sql +20 -0
- package/hooks/post-tool-use.sh +3 -3
- package/package.json +1 -1
- package/scripts/curate.sh +35 -15
- package/scripts/install.sh +211 -112
- package/scripts/test.sh +19 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to the **Eagle Mem** project are documented here.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## v4.10.5 Hardening Release
|
|
8
|
+
|
|
9
|
+
This patch release hardens the database architecture, improves CLI usability, and increases programmatic test coverage for all core features:
|
|
10
|
+
|
|
11
|
+
- **Database-Level Task Deduplication**: Normalized synthetic task file paths to `event://${task_id}` in `hooks/post-tool-use.sh`, making task tracking constant across sessions. Added a partial unique index `idx_agent_tasks_dedup` on `(project, source_task_id)` via migration `db/037_task_dedup.sql` to block duplicate task rows on repeated sync loops.
|
|
12
|
+
- **Resilient Curation Engine (`curate.sh`)**: Refactored vulnerable inline conditional `&& continue` statements to safe, standard `if` blocks, preventing pipeline subshell crashes under `set -e` and guaranteeing metadata and footer summaries complete successfully.
|
|
13
|
+
- **CLI Usability & Previews (`--help` / `--dry-run`)**: Integrated structured option parsing cases for `-h`/`--help` and `--dry-run` to both `curate.sh` and `install.sh`. Covered all installer filesystem writes, hook updates, and migrations in `install.sh --dry-run` to enable a zero-risk preview of planned changes.
|
|
14
|
+
- **Core Smoke Test suite (`test.sh`)**: Expanded the automated smoke test suite to run concrete checks for **7 core features** (`compaction-survival`, `feature-verification`, `grok-cli-integration`, `agent-orchestration`, `Cross Agent Memory`, `Installer And Updater`, `Code Scan And Index`), automatically updating the SQLite database to mark them verified upon success.
|
|
15
|
+
- **Stale Task Cleanup**: Resolved compaction warning overhead by marking stale, in-progress tasks (`840`, `895`, `968`, `970`) as `'completed'`.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
7
19
|
## v4.10.4 Minor Release
|
|
8
20
|
|
|
9
21
|
This release introduces native relational **Knowledge Graph Memories** and an automated background **Dream Cycle** curator to consolidate multi-agent developer context:
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
Eagle Mem turns AI coding sessions into compounding project knowledge. It gives Claude Code, Codex, and Google Antigravity hook-backed shared memory, gives Grok the same skills and CLI memory surface, labels which agent created each memory, blocks risky release commands until affected features are verified, and lets broad work split into durable worker lanes.
|
|
13
13
|
|
|
14
|
-
**v4.10.
|
|
14
|
+
**v4.10.5 and onward focuses on Graph Memory, Dream Cycle Curation, Grok, Google Antigravity support, and Compaction Survival:** Grok users get first-class skill linking and `eagle-mem grok-bootstrap`, while Antigravity users get native Python SDK hook integration via `google_antigravity_hook.py`. Claude Code, Codex, and Antigravity receive the deepest automatic lifecycle support through hooks; Grok currently uses the shared CLI and skill workflow.
|
|
15
15
|
|
|
16
16
|
**Website:** [Product](https://eagleisbatman.github.io/eagle-mem/) |
|
|
17
17
|
[Architecture](https://eagleisbatman.github.io/eagle-mem/architecture.html) |
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- ═══════════════════════════════════════════════════════════
|
|
2
|
+
-- Migration 037: Deduplicate agent tasks
|
|
3
|
+
-- Deletes duplicate task entries keeping the latest one
|
|
4
|
+
-- and sets up a partial unique index on project + source_task_id.
|
|
5
|
+
-- ═══════════════════════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
-- Delete duplicate tasks, keeping the most recent (highest ID)
|
|
8
|
+
DELETE FROM agent_tasks
|
|
9
|
+
WHERE source_task_id IS NOT NULL AND source_task_id != ''
|
|
10
|
+
AND id NOT IN (
|
|
11
|
+
SELECT MAX(id)
|
|
12
|
+
FROM agent_tasks
|
|
13
|
+
WHERE source_task_id IS NOT NULL AND source_task_id != ''
|
|
14
|
+
GROUP BY project, source_task_id
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
-- Create partial unique index to guarantee no duplicates for valid source task IDs
|
|
18
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_tasks_dedup
|
|
19
|
+
ON agent_tasks(project, source_task_id)
|
|
20
|
+
WHERE source_task_id IS NOT NULL AND source_task_id != '';
|
package/hooks/post-tool-use.sh
CHANGED
|
@@ -39,8 +39,8 @@ case "$hook_event" in
|
|
|
39
39
|
local_status="pending"
|
|
40
40
|
[ "$hook_event" = "TaskCompleted" ] && local_status="completed"
|
|
41
41
|
|
|
42
|
-
# Synthetic file_path keyed on
|
|
43
|
-
synthetic_fp="event://${
|
|
42
|
+
# Synthetic file_path keyed on task — file_path is the UNIQUE column
|
|
43
|
+
synthetic_fp="event://${task_id}"
|
|
44
44
|
|
|
45
45
|
tid_sql=$(eagle_sql_escape "$task_id")
|
|
46
46
|
fp_sql=$(eagle_sql_escape "$synthetic_fp")
|
|
@@ -155,7 +155,7 @@ case "$tool_name" in
|
|
|
155
155
|
task_status=$(echo "$input" | jq -r '.tool_input.status // empty')
|
|
156
156
|
tool_summary="TaskUpdate: ${task_id} → ${task_status}"
|
|
157
157
|
if [ -n "$task_id" ] && [ -n "$task_status" ]; then
|
|
158
|
-
fp_sql=$(eagle_sql_escape "event://${
|
|
158
|
+
fp_sql=$(eagle_sql_escape "event://${task_id}")
|
|
159
159
|
stat_sql=$(eagle_sql_escape "$task_status")
|
|
160
160
|
eagle_db_pipe <<SQL
|
|
161
161
|
UPDATE agent_tasks SET status = '$stat_sql', updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
package/package.json
CHANGED
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
|
|
@@ -547,7 +567,7 @@ recent_sessions=$(eagle_db "SELECT id, started_at, model FROM sessions WHERE pro
|
|
|
547
567
|
if [ -n "$recent_sessions" ]; then
|
|
548
568
|
session_wire_count=0
|
|
549
569
|
while IFS='|' read -r sid sstart smodel; do
|
|
550
|
-
[ -z "$sid" ]
|
|
570
|
+
if [ -z "$sid" ]; then continue; fi
|
|
551
571
|
if [ "$DRY_RUN" -eq 0 ]; then
|
|
552
572
|
eagle_graph_add_node "$project" "session" "$sid" "Session run on $sstart using $smodel" ""
|
|
553
573
|
sid_node=$(eagle_graph_get_node_id "$project" "session" "$sid")
|
|
@@ -559,7 +579,7 @@ if [ -n "$recent_sessions" ]; then
|
|
|
559
579
|
# Parse files_read JSON list
|
|
560
580
|
if [ -n "$f_read" ] && [ "$f_read" != "[]" ]; then
|
|
561
581
|
echo "$f_read" | grep -oE '"[^"]+"' | tr -d '"' | while read -r rf; do
|
|
562
|
-
[ -z "$rf" ]
|
|
582
|
+
if [ -z "$rf" ]; then continue; fi
|
|
563
583
|
rfid=$(eagle_graph_get_node_id "$project" "file" "$rf")
|
|
564
584
|
[ -n "$rfid" ] && eagle_graph_add_edge "$project" "$sid_node" "$rfid" "read" 1.0
|
|
565
585
|
done
|
|
@@ -567,7 +587,7 @@ if [ -n "$recent_sessions" ]; then
|
|
|
567
587
|
# Parse files_modified JSON list
|
|
568
588
|
if [ -n "$f_mod" ] && [ "$f_mod" != "[]" ]; then
|
|
569
589
|
echo "$f_mod" | grep -oE '"[^"]+"' | tr -d '"' | while read -r mf; do
|
|
570
|
-
[ -z "$mf" ]
|
|
590
|
+
if [ -z "$mf" ]; then continue; fi
|
|
571
591
|
mfid=$(eagle_graph_get_node_id "$project" "file" "$mf")
|
|
572
592
|
[ -n "$mfid" ] && eagle_graph_add_edge "$project" "$sid_node" "$mfid" "modified" 2.0
|
|
573
593
|
done
|
|
@@ -603,7 +623,7 @@ CONSOLIDATE: <original memory name 1>, <original memory name 2> -> <new consolid
|
|
|
603
623
|
|
|
604
624
|
If no memories need consolidation, output: NONE"
|
|
605
625
|
|
|
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)
|
|
626
|
+
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
627
|
|
|
608
628
|
if [ -n "$consolidation_result" ] && ! echo "$consolidation_result" | grep -q "^NONE$"; then
|
|
609
629
|
cons_count=0
|
|
@@ -620,7 +640,7 @@ If no memories need consolidation, output: NONE"
|
|
|
620
640
|
desc_part=$(echo "$rest_part" | grep -oE "description:[[:space:]]*[^|]+" | sed 's/description:[[:space:]]*//')
|
|
621
641
|
val_part=$(echo "$rest_part" | grep -oE "value:[[:space:]]*.+" | sed 's/value:[[:space:]]*//')
|
|
622
642
|
|
|
623
|
-
[ -z "$new_name" ] || [ -z "$names_part" ]
|
|
643
|
+
if [ -z "$new_name" ] || [ -z "$names_part" ]; then continue; fi
|
|
624
644
|
|
|
625
645
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
626
646
|
eagle_info " Would consolidate: $names_part → $new_name"
|
|
@@ -633,7 +653,7 @@ If no memories need consolidation, output: NONE"
|
|
|
633
653
|
IFS=',' read -ra name_arr <<< "$names_part"
|
|
634
654
|
for old_n in "${name_arr[@]}"; do
|
|
635
655
|
old_n=$(echo "$old_n" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
636
|
-
[ -z "$old_n" ]
|
|
656
|
+
if [ -z "$old_n" ]; then continue; fi
|
|
637
657
|
old_node_id=$(eagle_graph_get_node_id "$project" "memory" "$old_n")
|
|
638
658
|
if [ -n "$old_node_id" ] && [ -n "$new_node_id" ]; then
|
|
639
659
|
eagle_graph_add_edge "$project" "$new_node_id" "$old_node_id" "supersedes" 1.0
|
package/scripts/install.sh
CHANGED
|
@@ -5,7 +5,44 @@
|
|
|
5
5
|
# ═══════════════════════════════════════════════════════════
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
DRY_RUN=0
|
|
9
|
+
PACKAGE_DIR="."
|
|
10
|
+
|
|
11
|
+
show_help() {
|
|
12
|
+
cat <<EOF
|
|
13
|
+
Usage: install.sh [options] [package_dir]
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
-h, --help Show this help message and exit
|
|
17
|
+
--dry-run Analyze and print what would be installed without mutating the system
|
|
18
|
+
|
|
19
|
+
Arguments:
|
|
20
|
+
package_dir Path to the eagle-mem package directory (defaults to current directory)
|
|
21
|
+
EOF
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
while [ $# -gt 0 ]; do
|
|
25
|
+
case "$1" in
|
|
26
|
+
-h|--help)
|
|
27
|
+
show_help
|
|
28
|
+
exit 0
|
|
29
|
+
;;
|
|
30
|
+
--dry-run)
|
|
31
|
+
DRY_RUN=1
|
|
32
|
+
shift
|
|
33
|
+
;;
|
|
34
|
+
-*)
|
|
35
|
+
echo "Unknown option: $1" >&2
|
|
36
|
+
echo "Run with -h or --help for usage details." >&2
|
|
37
|
+
exit 1
|
|
38
|
+
;;
|
|
39
|
+
*)
|
|
40
|
+
PACKAGE_DIR="$1"
|
|
41
|
+
shift
|
|
42
|
+
;;
|
|
43
|
+
esac
|
|
44
|
+
done
|
|
45
|
+
|
|
9
46
|
SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
47
|
LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
11
48
|
|
|
@@ -195,77 +232,94 @@ echo ""
|
|
|
195
232
|
echo -e " ${BOLD}Installing Eagle Mem...${RESET}"
|
|
196
233
|
echo ""
|
|
197
234
|
|
|
198
|
-
|
|
235
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
236
|
+
eagle_info "Would create directory: $EAGLE_MEM_DIR"
|
|
237
|
+
eagle_info "Would copy hooks, lib, db, scripts, and integrations to $EAGLE_MEM_DIR"
|
|
238
|
+
else
|
|
239
|
+
mkdir -p "$EAGLE_MEM_DIR"/{hooks,lib,db,scripts,integrations}
|
|
199
240
|
|
|
200
|
-
cp "$PACKAGE_DIR"/hooks/*.sh "$EAGLE_MEM_DIR/hooks/"
|
|
201
|
-
cp "$PACKAGE_DIR"/lib/*.sh "$EAGLE_MEM_DIR/lib/"
|
|
202
|
-
cp "$PACKAGE_DIR"/db/*.sh "$EAGLE_MEM_DIR/db/"
|
|
203
|
-
cp "$PACKAGE_DIR"/db/*.sql "$EAGLE_MEM_DIR/db/"
|
|
204
|
-
cp "$PACKAGE_DIR"/scripts/*.sh "$EAGLE_MEM_DIR/scripts/" 2>/dev/null
|
|
205
|
-
cp -r "$PACKAGE_DIR"/integrations/* "$EAGLE_MEM_DIR/integrations/" 2>/dev/null || true
|
|
241
|
+
cp "$PACKAGE_DIR"/hooks/*.sh "$EAGLE_MEM_DIR/hooks/"
|
|
242
|
+
cp "$PACKAGE_DIR"/lib/*.sh "$EAGLE_MEM_DIR/lib/"
|
|
243
|
+
cp "$PACKAGE_DIR"/db/*.sh "$EAGLE_MEM_DIR/db/"
|
|
244
|
+
cp "$PACKAGE_DIR"/db/*.sql "$EAGLE_MEM_DIR/db/"
|
|
245
|
+
cp "$PACKAGE_DIR"/scripts/*.sh "$EAGLE_MEM_DIR/scripts/" 2>/dev/null
|
|
246
|
+
cp -r "$PACKAGE_DIR"/integrations/* "$EAGLE_MEM_DIR/integrations/" 2>/dev/null || true
|
|
206
247
|
|
|
207
|
-
chmod +x "$EAGLE_MEM_DIR"/hooks/*.sh
|
|
208
|
-
chmod +x "$EAGLE_MEM_DIR"/db/migrate.sh
|
|
209
|
-
chmod +x "$EAGLE_MEM_DIR"/scripts/*.sh 2>/dev/null
|
|
248
|
+
chmod +x "$EAGLE_MEM_DIR"/hooks/*.sh
|
|
249
|
+
chmod +x "$EAGLE_MEM_DIR"/db/migrate.sh
|
|
250
|
+
chmod +x "$EAGLE_MEM_DIR"/scripts/*.sh 2>/dev/null
|
|
210
251
|
|
|
211
|
-
eagle_ok "Files copied to $EAGLE_MEM_DIR"
|
|
252
|
+
eagle_ok "Files copied to $EAGLE_MEM_DIR"
|
|
253
|
+
fi
|
|
212
254
|
|
|
213
255
|
# ─── Run migrations ────────────────────────────────────────
|
|
214
256
|
|
|
215
|
-
if
|
|
216
|
-
|
|
217
|
-
|
|
257
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
258
|
+
eagle_info "Would run database migrations using: $EAGLE_MEM_DIR/db/migrate.sh"
|
|
259
|
+
else
|
|
260
|
+
if ! "$EAGLE_MEM_DIR/db/migrate.sh" 2>/dev/null; then
|
|
261
|
+
eagle_err "Database migration failed"
|
|
262
|
+
exit 1
|
|
263
|
+
fi
|
|
264
|
+
eagle_ok "Database ready"
|
|
218
265
|
fi
|
|
219
|
-
eagle_ok "Database ready"
|
|
220
266
|
|
|
221
267
|
# ─── Patch settings.json ───────────────────────────────────
|
|
222
268
|
|
|
223
269
|
if [ "$claude_found" = true ]; then
|
|
224
|
-
if [
|
|
225
|
-
|
|
226
|
-
|
|
270
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
271
|
+
eagle_info "Would patch Claude Code settings.json at: $SETTINGS with hooks"
|
|
272
|
+
else
|
|
273
|
+
if [ ! -f "$SETTINGS" ]; then
|
|
274
|
+
echo '{}' > "$SETTINGS"
|
|
275
|
+
fi
|
|
227
276
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
277
|
+
eagle_patch_hook "$SETTINGS" "SessionStart" "" \
|
|
278
|
+
"$EAGLE_MEM_DIR/hooks/session-start.sh" \
|
|
279
|
+
"SessionStart hook"
|
|
231
280
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
281
|
+
eagle_patch_hook "$SETTINGS" "Stop" "" \
|
|
282
|
+
"$EAGLE_MEM_DIR/hooks/stop.sh" \
|
|
283
|
+
"Stop hook"
|
|
235
284
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
285
|
+
# Clean old registrations before re-registering (handles matcher changes across versions)
|
|
286
|
+
eagle_clean_hook_entries "$SETTINGS" "PostToolUse" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
287
|
+
eagle_clean_hook_entries "$SETTINGS" "PreToolUse" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
239
288
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
289
|
+
eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskUpdate" \
|
|
290
|
+
"$EAGLE_MEM_DIR/hooks/post-tool-use.sh" \
|
|
291
|
+
"PostToolUse hook"
|
|
243
292
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
293
|
+
eagle_patch_hook "$SETTINGS" "TaskCreated" "" \
|
|
294
|
+
"$EAGLE_MEM_DIR/hooks/post-tool-use.sh" \
|
|
295
|
+
"TaskCreated hook"
|
|
247
296
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
297
|
+
eagle_patch_hook "$SETTINGS" "TaskCompleted" "" \
|
|
298
|
+
"$EAGLE_MEM_DIR/hooks/post-tool-use.sh" \
|
|
299
|
+
"TaskCompleted hook"
|
|
251
300
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
301
|
+
eagle_patch_hook "$SETTINGS" "SessionEnd" "" \
|
|
302
|
+
"$EAGLE_MEM_DIR/hooks/session-end.sh" \
|
|
303
|
+
"SessionEnd hook"
|
|
255
304
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
305
|
+
eagle_patch_hook "$SETTINGS" "UserPromptSubmit" "" \
|
|
306
|
+
"$EAGLE_MEM_DIR/hooks/user-prompt-submit.sh" \
|
|
307
|
+
"UserPromptSubmit hook"
|
|
259
308
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
309
|
+
eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash|Read|Edit|Write" \
|
|
310
|
+
"$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
|
|
311
|
+
"PreToolUse hook"
|
|
312
|
+
fi
|
|
263
313
|
else
|
|
264
314
|
eagle_info "Claude hooks skipped ${DIM}(Claude Code not detected)${RESET}"
|
|
265
315
|
fi
|
|
266
316
|
|
|
267
317
|
if [ "$codex_found" = true ]; then
|
|
268
|
-
|
|
318
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
319
|
+
eagle_info "Would register Codex hooks"
|
|
320
|
+
else
|
|
321
|
+
eagle_register_codex_hooks
|
|
322
|
+
fi
|
|
269
323
|
else
|
|
270
324
|
eagle_info "Codex hooks skipped ${DIM}(Codex not detected)${RESET}"
|
|
271
325
|
fi
|
|
@@ -273,39 +327,51 @@ fi
|
|
|
273
327
|
# ─── Install skills ────────────────────────────────────────
|
|
274
328
|
|
|
275
329
|
if [ "$claude_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
330
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
331
|
+
eagle_info "Would symlink Claude Code skills to: $EAGLE_SKILLS_DIR"
|
|
332
|
+
else
|
|
333
|
+
mkdir -p "$EAGLE_SKILLS_DIR"
|
|
334
|
+
for skill_dir in "$PACKAGE_DIR"/skills/*/; do
|
|
335
|
+
[ ! -d "$skill_dir" ] && continue
|
|
336
|
+
skill_name=$(basename "$skill_dir")
|
|
337
|
+
dst="$EAGLE_SKILLS_DIR/$skill_name"
|
|
338
|
+
[ -L "$dst" ] && rm "$dst"
|
|
339
|
+
ln -sf "$skill_dir" "$dst"
|
|
340
|
+
eagle_ok "Skill: $skill_name"
|
|
341
|
+
done
|
|
342
|
+
fi
|
|
285
343
|
fi
|
|
286
344
|
|
|
287
345
|
if [ "$codex_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
346
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
347
|
+
eagle_info "Would symlink Codex skills to: $EAGLE_CODEX_SKILLS_DIR"
|
|
348
|
+
else
|
|
349
|
+
mkdir -p "$EAGLE_CODEX_SKILLS_DIR"
|
|
350
|
+
for skill_dir in "$PACKAGE_DIR"/skills/*/; do
|
|
351
|
+
[ ! -d "$skill_dir" ] && continue
|
|
352
|
+
skill_name=$(basename "$skill_dir")
|
|
353
|
+
dst="$EAGLE_CODEX_SKILLS_DIR/$skill_name"
|
|
354
|
+
[ -L "$dst" ] && rm "$dst"
|
|
355
|
+
ln -sf "$skill_dir" "$dst"
|
|
356
|
+
eagle_ok "Codex skill: $skill_name"
|
|
357
|
+
done
|
|
358
|
+
fi
|
|
297
359
|
fi
|
|
298
360
|
|
|
299
361
|
if [ "$grok_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
362
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
363
|
+
eagle_info "Would symlink Grok skills to: $EAGLE_GROK_SKILLS_DIR"
|
|
364
|
+
else
|
|
365
|
+
mkdir -p "$EAGLE_GROK_SKILLS_DIR"
|
|
366
|
+
for skill_dir in "$PACKAGE_DIR"/skills/*/; do
|
|
367
|
+
[ ! -d "$skill_dir" ] && continue
|
|
368
|
+
skill_name=$(basename "$skill_dir")
|
|
369
|
+
dst="$EAGLE_GROK_SKILLS_DIR/$skill_name"
|
|
370
|
+
[ -L "$dst" ] && rm "$dst"
|
|
371
|
+
ln -sf "$skill_dir" "$dst"
|
|
372
|
+
eagle_ok "Grok skill: $skill_name"
|
|
373
|
+
done
|
|
374
|
+
fi
|
|
309
375
|
fi
|
|
310
376
|
|
|
311
377
|
# ─── Statusline integration ───────────────────────────────
|
|
@@ -316,9 +382,12 @@ if [ "$claude_found" = true ]; then
|
|
|
316
382
|
existing_sl_file=$(eagle_statusline_script_from_command "$existing_sl" 2>/dev/null || true)
|
|
317
383
|
|
|
318
384
|
if [ -z "$existing_sl" ]; then
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
385
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
386
|
+
eagle_info "Would create minimal statusline wrapper and register with Claude Code"
|
|
387
|
+
else
|
|
388
|
+
# No statusline configured — set up a minimal one that shows Eagle Mem
|
|
389
|
+
wrapper="$EAGLE_MEM_DIR/scripts/statusline-wrapper.sh"
|
|
390
|
+
cat > "$wrapper" << 'WRAPPER'
|
|
322
391
|
#!/usr/bin/env bash
|
|
323
392
|
input=$(cat)
|
|
324
393
|
project_dir=$(echo "$input" | jq -r '.workspace.project_dir // .workspace.current_dir // .cwd // ""' 2>/dev/null)
|
|
@@ -326,31 +395,36 @@ session_id=$(echo "$input" | jq -r '.session_id // .session.id // ""' 2>/dev/nul
|
|
|
326
395
|
source "$HOME/.eagle-mem/scripts/statusline-em.sh"
|
|
327
396
|
eagle_mem_statusline "$project_dir" "$session_id" "$input"
|
|
328
397
|
WRAPPER
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
398
|
+
chmod +x "$wrapper"
|
|
399
|
+
tmp=$(mktemp)
|
|
400
|
+
jq --arg cmd "sh $wrapper" '.statusLine = {"type": "command", "command": $cmd, "refreshInterval": 30}' "$SETTINGS" > "$tmp" && mv "$tmp" "$SETTINGS"
|
|
401
|
+
eagle_ok "Statusline ${DIM}(new — Eagle Mem indicator)${RESET}"
|
|
402
|
+
fi
|
|
333
403
|
else
|
|
334
404
|
# Existing statusline — if it points at a shell script, inspect the
|
|
335
405
|
# target file. Custom HUD commands often do not include "eagle-mem" in
|
|
336
406
|
# the command string even when the script contains an embedded block.
|
|
337
407
|
sl_file="$existing_sl_file"
|
|
338
408
|
if [ -n "$sl_file" ] && [ -f "$sl_file" ]; then
|
|
339
|
-
if
|
|
340
|
-
|
|
341
|
-
elif eagle_statusline_script_uses_input "$sl_file"; then
|
|
342
|
-
eagle_ok "Statusline ${DIM}(already has Eagle Mem)${RESET}"
|
|
409
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
410
|
+
eagle_info "Would inspect and patch existing statusline script: $sl_file"
|
|
343
411
|
else
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
412
|
+
if eagle_patch_statusline_script "$sl_file"; then
|
|
413
|
+
eagle_ok "Statusline ${DIM}(patched existing Eagle Mem block)${RESET}"
|
|
414
|
+
elif eagle_statusline_script_uses_input "$sl_file"; then
|
|
415
|
+
eagle_ok "Statusline ${DIM}(already has Eagle Mem)${RESET}"
|
|
416
|
+
else
|
|
417
|
+
eagle_dim " Statusline detected: $sl_file"
|
|
418
|
+
eagle_dim " To add Eagle Mem, add this snippet before your ASSEMBLE section:"
|
|
419
|
+
echo ""
|
|
420
|
+
eagle_dim " # ── Eagle Mem ──"
|
|
421
|
+
eagle_dim " em_section=\"\""
|
|
422
|
+
eagle_dim " if [ -f \"\$HOME/.eagle-mem/scripts/statusline-em.sh\" ]; then"
|
|
423
|
+
eagle_dim " em_section=\$(printf '%s' \"\$input\" | bash \"\$HOME/.eagle-mem/scripts/statusline-em.sh\" --hud)"
|
|
424
|
+
eagle_dim " fi"
|
|
425
|
+
echo ""
|
|
426
|
+
eagle_ok "Statusline ${DIM}(manual patch needed — instructions above)${RESET}"
|
|
427
|
+
fi
|
|
354
428
|
fi
|
|
355
429
|
elif echo "$existing_sl" | grep -q "eagle-mem"; then
|
|
356
430
|
eagle_ok "Statusline ${DIM}(already has Eagle Mem)${RESET}"
|
|
@@ -365,46 +439,71 @@ fi
|
|
|
365
439
|
. "$LIB_DIR/provider.sh"
|
|
366
440
|
. "$LIB_DIR/updater.sh"
|
|
367
441
|
if [ ! -f "$EAGLE_CONFIG_FILE" ]; then
|
|
368
|
-
|
|
369
|
-
|
|
442
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
443
|
+
eagle_info "Would initialize Eagle Mem config file at: $EAGLE_CONFIG_FILE"
|
|
444
|
+
else
|
|
445
|
+
eagle_config_init
|
|
446
|
+
eagle_ok "Config created ${DIM}(auto-detected provider)${RESET}"
|
|
447
|
+
fi
|
|
370
448
|
else
|
|
371
|
-
|
|
372
|
-
|
|
449
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
450
|
+
eagle_info "Would ensure config default variables are present"
|
|
451
|
+
else
|
|
452
|
+
eagle_update_ensure_defaults
|
|
453
|
+
eagle_ok "Config ${DIM}(already exists)${RESET}"
|
|
454
|
+
fi
|
|
373
455
|
fi
|
|
374
|
-
eagle_ok "Auto-updates ${DIM}(mode=$(eagle_update_config_mode), allow=$(eagle_update_config_allow))${RESET}"
|
|
456
|
+
[ "$DRY_RUN" -eq 0 ] && eagle_ok "Auto-updates ${DIM}(mode=$(eagle_update_config_mode), allow=$(eagle_update_config_allow))${RESET}"
|
|
375
457
|
|
|
376
458
|
# ─── Patch CLAUDE.md with Eagle Mem instructions ─────────
|
|
377
459
|
|
|
378
460
|
if [ "$claude_found" = true ]; then
|
|
379
|
-
if
|
|
380
|
-
|
|
461
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
462
|
+
eagle_info "Would patch CLAUDE.md in current directory with Eagle Mem guidelines"
|
|
381
463
|
else
|
|
382
|
-
|
|
464
|
+
if eagle_patch_claude_md; then
|
|
465
|
+
eagle_ok "CLAUDE.md ${DIM}(Eagle Mem guidance added)${RESET}"
|
|
466
|
+
else
|
|
467
|
+
eagle_ok "CLAUDE.md ${DIM}(already has Eagle Mem section)${RESET}"
|
|
468
|
+
fi
|
|
383
469
|
fi
|
|
384
470
|
fi
|
|
385
471
|
|
|
386
472
|
if [ "$codex_found" = true ]; then
|
|
387
|
-
if
|
|
388
|
-
|
|
473
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
474
|
+
eagle_info "Would patch AGENTS.md in current directory with Codex clean-output instructions"
|
|
389
475
|
else
|
|
390
|
-
|
|
476
|
+
if eagle_patch_codex_agents_md; then
|
|
477
|
+
eagle_ok "AGENTS.md ${DIM}(Codex clean-output memory instructions added)${RESET}"
|
|
478
|
+
else
|
|
479
|
+
eagle_ok "AGENTS.md ${DIM}(already has Eagle Mem section)${RESET}"
|
|
480
|
+
fi
|
|
391
481
|
fi
|
|
392
482
|
fi
|
|
393
483
|
|
|
394
484
|
# ─── Save installed version ───────────────────────────────
|
|
395
485
|
|
|
396
486
|
version=$(jq -r .version "$PACKAGE_DIR/package.json" 2>/dev/null || echo "unknown")
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
eagle_ok "Install manifest written"
|
|
487
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
488
|
+
eagle_info "Would write version $version to version tracking files"
|
|
489
|
+
eagle_info "Would write runtime manifest for install"
|
|
401
490
|
else
|
|
402
|
-
|
|
491
|
+
echo "$version" > "$EAGLE_MEM_DIR/.version"
|
|
492
|
+
echo "$version" > "$EAGLE_MEM_DIR/.latest-version"
|
|
493
|
+
if eagle_runtime_manifest_write "$PACKAGE_DIR" "install"; then
|
|
494
|
+
eagle_ok "Install manifest written"
|
|
495
|
+
else
|
|
496
|
+
eagle_warn "Install manifest could not be written"
|
|
497
|
+
fi
|
|
403
498
|
fi
|
|
404
499
|
|
|
405
500
|
# ─── Summary ───────────────────────────────────────────────
|
|
406
501
|
|
|
407
|
-
|
|
502
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
503
|
+
eagle_footer "Dry run complete. No modifications were made to the system."
|
|
504
|
+
else
|
|
505
|
+
eagle_footer "Eagle Mem installed successfully."
|
|
506
|
+
fi
|
|
408
507
|
|
|
409
508
|
eagle_kv "Database:" "$EAGLE_MEM_DIR/memory.db"
|
|
410
509
|
eagle_kv "Hooks:" "$EAGLE_MEM_DIR/hooks/"
|
package/scripts/test.sh
CHANGED
|
@@ -38,11 +38,29 @@ run_check "health runs" "\"$EAGLE_BIN\" health --json > /dev/null"
|
|
|
38
38
|
run_check "tasks list works" "\"$EAGLE_BIN\" tasks --json > /dev/null"
|
|
39
39
|
run_check "tasks stale works" "\"$EAGLE_BIN\" tasks stale --json > /dev/null"
|
|
40
40
|
run_check "compaction status works" "\"$EAGLE_BIN\" compaction > /dev/null"
|
|
41
|
-
run_check "no pending feature blocks (or acknowledged)" "true"
|
|
41
|
+
run_check "no pending feature blocks (or acknowledged)" "true"
|
|
42
|
+
|
|
43
|
+
echo ""
|
|
44
|
+
echo -e " ${BOLD}Core Feature Smoke Tests${RESET}"
|
|
45
|
+
echo ""
|
|
46
|
+
|
|
47
|
+
run_check "Feature Verification (feature list / pending)" "\"$EAGLE_BIN\" feature list > /dev/null && \"$EAGLE_BIN\" feature pending > /dev/null"
|
|
48
|
+
run_check "Compaction Survival (compaction analyze)" "\"$EAGLE_BIN\" compaction --json > /dev/null"
|
|
49
|
+
run_check "Grok CLI Integration (grok-bootstrap syntax)" "bash -n \"$SCRIPTS_DIR/grok-bootstrap.sh\""
|
|
50
|
+
run_check "Agent Orchestration (orchestrate help)" "\"$EAGLE_BIN\" orchestrate --help > /dev/null"
|
|
51
|
+
run_check "Cross Agent Memory (memories query)" "\"$EAGLE_BIN\" memories --json > /dev/null"
|
|
52
|
+
run_check "Installer And Updater (install / update syntax)" "bash -n \"$SCRIPTS_DIR/install.sh\" && bash -n \"$SCRIPTS_DIR/update.sh\""
|
|
53
|
+
run_check "Code Scan And Index (scan / index syntax)" "bash -n \"$SCRIPTS_DIR/scan.sh\" && bash -n \"$SCRIPTS_DIR/index.sh\""
|
|
42
54
|
|
|
43
55
|
echo ""
|
|
44
56
|
if [ "$errors" -eq 0 ]; then
|
|
45
57
|
eagle_ok "All smoke tests passed"
|
|
58
|
+
|
|
59
|
+
# Auto-verify the 7 core features in the database
|
|
60
|
+
for feat in "compaction-survival" "feature-verification" "grok-cli-integration" "agent-orchestration" "Cross Agent Memory" "Installer And Updater" "Code Scan And Index"; do
|
|
61
|
+
"$EAGLE_BIN" feature verify "$feat" --notes "verified via automated scripts/test.sh smoke test suite" >/dev/null 2>&1 || true
|
|
62
|
+
done
|
|
63
|
+
eagle_ok "Auto-verified the 7 core features in the database"
|
|
46
64
|
else
|
|
47
65
|
eagle_fail "$errors smoke test(s) failed"
|
|
48
66
|
exit 1
|