eagle-mem 4.9.4 → 4.9.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/README.md +7 -3
- package/db/migrate.sh +8 -7
- package/hooks/stop.sh +30 -1
- package/lib/codex-hooks.sh +1 -1
- package/lib/common.sh +45 -6
- package/lib/db-core.sh +12 -3
- package/lib/updater.sh +4 -2
- package/package.json +1 -1
- package/scripts/enrich-summary.sh +98 -0
- package/scripts/install.sh +12 -11
- package/scripts/statusline-em.sh +5 -2
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 and Codex the same local memory, 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.
|
|
14
|
+
**v4.9.5 hardens hooks and SQLite:** Stop hooks save immediately and queue LLM enrichment in the background instead of launching nested agents during turn shutdown. SQLite calls also resolve through one FTS5-capable binary selector, so Android SDK or other PATH shims do not accidentally break Eagle Mem.
|
|
15
15
|
|
|
16
16
|
**Website:** [Product](https://eagleisbatman.github.io/eagle-mem/) |
|
|
17
17
|
[Architecture](https://eagleisbatman.github.io/eagle-mem/architecture.html) |
|
|
@@ -73,7 +73,7 @@ For Codex, the installer enables `codex_hooks` in `~/.codex/config.toml`, regist
|
|
|
73
73
|
|
|
74
74
|
### Prerequisites
|
|
75
75
|
|
|
76
|
-
- `sqlite3` with FTS5 support (ships with macOS;
|
|
76
|
+
- `sqlite3` with FTS5 support (ships with macOS; Eagle Mem prefers known system/Homebrew SQLite binaries before PATH shims)
|
|
77
77
|
- `jq` (the installer offers to install if missing)
|
|
78
78
|
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code), Codex, or both installed
|
|
79
79
|
|
|
@@ -87,7 +87,7 @@ Hooks fire automatically at different points in the agent lifecycle:
|
|
|
87
87
|
| **PreToolUse** | before Bash/shell, Read, Edit, Write, apply_patch | Surfaces guardrails and decisions before edits. Blocks release-boundary commands while feature verification is pending. Rewrites noisy commands through RTK when available. Detects redundant reads, nudges co-edit partners, detects stuck loops. |
|
|
88
88
|
| **UserPromptSubmit** | user sends a message | FTS5 search across past sessions and indexed code for relevant context |
|
|
89
89
|
| **PostToolUse** | after tool calls | Records file touches, mirrors memory/plan/task writes, surfaces decision history and feature impacts on reads, stale memory warnings on edits |
|
|
90
|
-
| **Stop** | agent turn ends |
|
|
90
|
+
| **Stop** | agent turn ends | Saves fast heuristic summaries and extracts `<eagle-summary>` blocks when present. LLM enrichment runs later in the background so the agent lifecycle is not blocked. |
|
|
91
91
|
| **SessionEnd** | session closes | Re-syncs tasks, marks session completed |
|
|
92
92
|
|
|
93
93
|
Codex shell hooks are registered for `Bash`, `exec_command`, `shell_command`, and `unified_exec` tool names so release-boundary protection works across current Codex shell paths.
|
|
@@ -152,6 +152,10 @@ Eagle Mem prevents Claude from repeating past mistakes:
|
|
|
152
152
|
| `eagle-mem scan` | Scan codebase and generate overview |
|
|
153
153
|
| `eagle-mem index` | Index source files for FTS5 code search |
|
|
154
154
|
|
|
155
|
+
### v4.9.5 Patch
|
|
156
|
+
|
|
157
|
+
Stop hooks now use a fast path: they save heuristic summaries immediately, extract explicit summary blocks when present, and queue LLM enrichment in the background so Codex/Claude lifecycle hooks do not time out. SQLite access now goes through a shared FTS5-capable binary resolver used by migrations, DB helpers, updater backups, install checks, and the statusline, avoiding Android SDK or other PATH shims that shadow working SQLite builds.
|
|
158
|
+
|
|
155
159
|
### v4.9.4 Patch
|
|
156
160
|
|
|
157
161
|
Project-key hardening for agents that move between folders: hooks now keep a per-session project identity instead of recalculating from every new cwd, and statuslines prefer the stored session project before falling back to folder paths. Install/update also repairs older embedded Eagle Mem statusline blocks so nested-repo projects stop showing `Memories: 0` when the session belongs to the parent workspace.
|
package/db/migrate.sh
CHANGED
|
@@ -13,11 +13,12 @@ COMMON_SH="$SCRIPT_DIR/../lib/common.sh"
|
|
|
13
13
|
if [ -f "$COMMON_SH" ]; then
|
|
14
14
|
. "$COMMON_SH"
|
|
15
15
|
eagle_require_sqlite_fts5 || exit 1
|
|
16
|
+
SQLITE_BIN=$(eagle_sqlite_path)
|
|
16
17
|
else
|
|
17
|
-
|
|
18
|
-
if [ -z "$
|
|
18
|
+
SQLITE_BIN=$(command -v sqlite3 2>/dev/null || true)
|
|
19
|
+
if [ -z "$SQLITE_BIN" ] || ! "$SQLITE_BIN" :memory: "CREATE VIRTUAL TABLE eagle_mem_fts5_probe USING fts5(value);" >/dev/null 2>&1; then
|
|
19
20
|
echo "Eagle Mem requires SQLite FTS5, but the active sqlite3 does not support it." >&2
|
|
20
|
-
[ -n "$
|
|
21
|
+
[ -n "$SQLITE_BIN" ] && echo "Detected sqlite3: $SQLITE_BIN" >&2
|
|
21
22
|
echo "Fix PATH so an FTS5-capable sqlite3 is first, then re-run the command." >&2
|
|
22
23
|
exit 1
|
|
23
24
|
fi
|
|
@@ -33,7 +34,7 @@ run_migration() {
|
|
|
33
34
|
local file="$2"
|
|
34
35
|
|
|
35
36
|
local already_applied
|
|
36
|
-
already_applied=$(
|
|
37
|
+
already_applied=$("$SQLITE_BIN" "$DB" "SELECT COUNT(*) FROM _migrations WHERE name = '$name';" 2>/dev/null || echo "0")
|
|
37
38
|
|
|
38
39
|
if [ "$already_applied" = "0" ]; then
|
|
39
40
|
# Strip PRAGMAs from migration body (they can't run inside transactions
|
|
@@ -43,20 +44,20 @@ run_migration() {
|
|
|
43
44
|
|
|
44
45
|
# Set connection PRAGMAs, then run migration body + tracking insert atomically
|
|
45
46
|
# .bail on ensures sqlite3 stops on the first error instead of continuing
|
|
46
|
-
{ echo ".bail on"; echo "PRAGMA trusted_schema=ON;"; echo "PRAGMA foreign_keys=ON;"; echo "PRAGMA busy_timeout=5000;"; echo "BEGIN;"; echo "$body"; echo "INSERT INTO _migrations (name) VALUES ('$name');"; echo "COMMIT;"; } |
|
|
47
|
+
{ echo ".bail on"; echo "PRAGMA trusted_schema=ON;"; echo "PRAGMA foreign_keys=ON;"; echo "PRAGMA busy_timeout=5000;"; echo "BEGIN;"; echo "$body"; echo "INSERT INTO _migrations (name) VALUES ('$name');"; echo "COMMIT;"; } | "$SQLITE_BIN" "$DB"
|
|
47
48
|
echo " applied: $name"
|
|
48
49
|
fi
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
# Ensure _migrations table exists (bootstrap)
|
|
52
|
-
|
|
53
|
+
"$SQLITE_BIN" "$DB" "CREATE TABLE IF NOT EXISTS _migrations (
|
|
53
54
|
id INTEGER PRIMARY KEY,
|
|
54
55
|
name TEXT NOT NULL UNIQUE,
|
|
55
56
|
applied_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
56
57
|
);"
|
|
57
58
|
|
|
58
59
|
# Set PRAGMAs (these must be set on every connection)
|
|
59
|
-
|
|
60
|
+
"$SQLITE_BIN" "$DB" "PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA busy_timeout = 5000; PRAGMA foreign_keys = ON;"
|
|
60
61
|
|
|
61
62
|
# ─── Migration 001: Initial schema (special name) ──────────
|
|
62
63
|
run_migration "001_initial_schema" "$SCRIPT_DIR/schema.sql"
|
package/hooks/stop.sh
CHANGED
|
@@ -223,6 +223,7 @@ if echo "$request" | grep -qE '<(local-command-caveat|system-reminder|command-na
|
|
|
223
223
|
fi
|
|
224
224
|
|
|
225
225
|
needs_enrichment=0
|
|
226
|
+
defer_enrichment=0
|
|
226
227
|
if [ "$has_rich_data" -eq 0 ]; then
|
|
227
228
|
needs_enrichment=1
|
|
228
229
|
elif [ "$context_pressure" -eq 1 ]; then
|
|
@@ -235,7 +236,13 @@ fi
|
|
|
235
236
|
|
|
236
237
|
if [ "$needs_enrichment" -eq 1 ]; then
|
|
237
238
|
provider=$(eagle_config_get "provider" "type" "none" 2>/dev/null)
|
|
238
|
-
|
|
239
|
+
# Stop hooks must stay fast. Expensive LLM enrichment belongs in curate or
|
|
240
|
+
# another background path; nested agent_cli calls can exceed Codex/Claude
|
|
241
|
+
# lifecycle timeouts and make the hook look broken to users.
|
|
242
|
+
if [ "${EAGLE_MEM_STOP_ENRICH:-0}" != "1" ]; then
|
|
243
|
+
defer_enrichment=1
|
|
244
|
+
eagle_log "INFO" "Stop: LLM enrichment skipped — fast hook path (provider=$provider)"
|
|
245
|
+
elif [ "$provider" != "none" ] && [ -n "$text_content" ]; then
|
|
239
246
|
excerpt=$(echo "$text_content" | tail -c 3000)
|
|
240
247
|
|
|
241
248
|
enrich_prompt="Extract facts from this AI coding session. Only include items with clear evidence in the session text. Do NOT invent or repeat example content.
|
|
@@ -401,4 +408,26 @@ if [ -n "$request" ] || [ -n "$completed" ] || [ -n "$learned" ]; then
|
|
|
401
408
|
fi
|
|
402
409
|
fi
|
|
403
410
|
|
|
411
|
+
if [ "$defer_enrichment" -eq 1 ] && [ "${EAGLE_MEM_STOP_BACKGROUND_ENRICH:-1}" = "1" ] && [ -n "$text_content" ]; then
|
|
412
|
+
mkdir -p "$EAGLE_MEM_DIR/tmp" 2>/dev/null || true
|
|
413
|
+
enrich_job=$(mktemp "$EAGLE_MEM_DIR/tmp/summary-enrich.XXXXXX.json" 2>/dev/null)
|
|
414
|
+
if [ -n "$enrich_job" ]; then
|
|
415
|
+
jq -cn \
|
|
416
|
+
--arg session_id "$session_id" \
|
|
417
|
+
--arg project "$project" \
|
|
418
|
+
--arg agent "$agent" \
|
|
419
|
+
--arg text "$text_content" \
|
|
420
|
+
'{session_id:$session_id, project:$project, agent:$agent, text:$text}' > "$enrich_job"
|
|
421
|
+
|
|
422
|
+
enrich_script="$SCRIPT_DIR/../scripts/enrich-summary.sh"
|
|
423
|
+
if [ -x "$enrich_script" ]; then
|
|
424
|
+
nohup env EAGLE_MEM_DISABLE_HOOKS=1 EAGLE_AGENT_SOURCE="$agent" EAGLE_AGENT_CWD="$cwd" bash "$enrich_script" "$enrich_job" >/dev/null 2>&1 &
|
|
425
|
+
eagle_log "INFO" "Stop: queued background summary enrichment for session=$session_id"
|
|
426
|
+
else
|
|
427
|
+
rm -f "$enrich_job" 2>/dev/null || true
|
|
428
|
+
eagle_log "WARN" "Stop: background enrichment script missing: $enrich_script"
|
|
429
|
+
fi
|
|
430
|
+
fi
|
|
431
|
+
fi
|
|
432
|
+
|
|
404
433
|
exit 0
|
package/lib/codex-hooks.sh
CHANGED
package/lib/common.sh
CHANGED
|
@@ -20,24 +20,63 @@ EAGLE_CODEX_SKILLS_DIR="${EAGLE_CODEX_SKILLS_DIR:-$EAGLE_CODEX_DIR/skills}"
|
|
|
20
20
|
EAGLE_CODEX_MEMORIES_DIR="${EAGLE_CODEX_MEMORIES_DIR:-$EAGLE_CODEX_DIR/memories}"
|
|
21
21
|
EAGLE_RAW_BASH_UNLOCK="${EAGLE_RAW_BASH_UNLOCK:-/tmp/eagle-mem-raw-bash-unlock}"
|
|
22
22
|
|
|
23
|
+
_eagle_sqlite_candidate_paths() {
|
|
24
|
+
[ -n "${EAGLE_SQLITE_BIN:-}" ] && printf '%s\n' "$EAGLE_SQLITE_BIN"
|
|
25
|
+
[ -f "$EAGLE_MEM_DIR/.sqlite-bin" ] && sed -n '1p' "$EAGLE_MEM_DIR/.sqlite-bin" 2>/dev/null
|
|
26
|
+
printf '%s\n' \
|
|
27
|
+
"/opt/homebrew/opt/sqlite/bin/sqlite3" \
|
|
28
|
+
"/usr/local/opt/sqlite/bin/sqlite3" \
|
|
29
|
+
"/usr/bin/sqlite3" \
|
|
30
|
+
"/opt/homebrew/bin/sqlite3" \
|
|
31
|
+
"/usr/local/bin/sqlite3"
|
|
32
|
+
command -v sqlite3 2>/dev/null || true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_eagle_sqlite_bin_supports_fts5() {
|
|
36
|
+
local bin="$1"
|
|
37
|
+
[ -n "$bin" ] && [ -x "$bin" ] || return 1
|
|
38
|
+
"$bin" :memory: "CREATE VIRTUAL TABLE eagle_mem_fts5_probe USING fts5(value);" >/dev/null 2>&1
|
|
39
|
+
}
|
|
40
|
+
|
|
23
41
|
eagle_sqlite_path() {
|
|
42
|
+
local seen="" candidate
|
|
43
|
+
while IFS= read -r candidate; do
|
|
44
|
+
[ -n "$candidate" ] || continue
|
|
45
|
+
case "|$seen|" in *"|$candidate|"*) continue ;; esac
|
|
46
|
+
seen="${seen}|${candidate}"
|
|
47
|
+
if _eagle_sqlite_bin_supports_fts5 "$candidate"; then
|
|
48
|
+
printf '%s\n' "$candidate"
|
|
49
|
+
return 0
|
|
50
|
+
fi
|
|
51
|
+
done <<EOF
|
|
52
|
+
$(_eagle_sqlite_candidate_paths)
|
|
53
|
+
EOF
|
|
54
|
+
|
|
24
55
|
command -v sqlite3 2>/dev/null || true
|
|
25
56
|
}
|
|
26
57
|
|
|
27
58
|
eagle_sqlite_version() {
|
|
28
|
-
|
|
59
|
+
local sqlite_bin
|
|
60
|
+
sqlite_bin=$(eagle_sqlite_path)
|
|
61
|
+
[ -n "$sqlite_bin" ] || return 0
|
|
62
|
+
"$sqlite_bin" --version 2>/dev/null | awk '{print $1}'
|
|
29
63
|
}
|
|
30
64
|
|
|
31
65
|
eagle_sqlite_supports_fts5() {
|
|
32
|
-
|
|
33
|
-
|
|
66
|
+
local sqlite_bin
|
|
67
|
+
sqlite_bin=$(eagle_sqlite_path)
|
|
68
|
+
_eagle_sqlite_bin_supports_fts5 "$sqlite_bin"
|
|
34
69
|
}
|
|
35
70
|
|
|
36
71
|
eagle_print_sqlite_fts5_error() {
|
|
37
72
|
local sqlite_path sqlite_version probe_error
|
|
38
73
|
sqlite_path=$(eagle_sqlite_path)
|
|
39
74
|
sqlite_version=$(eagle_sqlite_version)
|
|
40
|
-
|
|
75
|
+
if [ -n "$sqlite_path" ]; then
|
|
76
|
+
probe_error=$("$sqlite_path" :memory: "CREATE VIRTUAL TABLE eagle_mem_fts5_probe USING fts5(value);" 2>&1 >/dev/null || true)
|
|
77
|
+
else
|
|
78
|
+
probe_error=""
|
|
79
|
+
fi
|
|
41
80
|
|
|
42
81
|
printf '%s\n' "Eagle Mem requires SQLite FTS5, but the active sqlite3 does not support it." >&2
|
|
43
82
|
if [ -n "$sqlite_path" ]; then
|
|
@@ -47,8 +86,8 @@ eagle_print_sqlite_fts5_error() {
|
|
|
47
86
|
fi
|
|
48
87
|
[ -n "$sqlite_version" ] && printf '%s\n' "SQLite version: $sqlite_version" >&2
|
|
49
88
|
[ -n "$probe_error" ] && printf '%s\n' "SQLite error: $probe_error" >&2
|
|
50
|
-
printf '%s\n' "Fix:
|
|
51
|
-
printf '%s\n' "macOS:
|
|
89
|
+
printf '%s\n' "Fix: install an FTS5-capable sqlite3, or set EAGLE_SQLITE_BIN to its absolute path." >&2
|
|
90
|
+
printf '%s\n' "macOS: /usr/bin/sqlite3 usually has FTS5. Eagle Mem now prefers known system/Homebrew sqlite3 paths over Android SDK shims." >&2
|
|
52
91
|
printf '%s\n' "Homebrew: install sqlite and prepend its bin directory, for example: export PATH=\"/opt/homebrew/opt/sqlite/bin:\$PATH\"" >&2
|
|
53
92
|
printf '%s\n' "Linux: install a sqlite3 package compiled with ENABLE_FTS5." >&2
|
|
54
93
|
}
|
package/lib/db-core.sh
CHANGED
|
@@ -15,10 +15,13 @@ PRAGMA trusted_schema=ON;
|
|
|
15
15
|
.output stdout"
|
|
16
16
|
|
|
17
17
|
eagle_db() {
|
|
18
|
+
local _eagle_sqlite_bin
|
|
19
|
+
_eagle_sqlite_bin=$(eagle_sqlite_path)
|
|
20
|
+
[ -n "$_eagle_sqlite_bin" ] || return 1
|
|
18
21
|
local _eagle_db_err
|
|
19
22
|
_eagle_db_err=$(mktemp 2>/dev/null || echo "/tmp/_eagle_db_err.$$")
|
|
20
23
|
local _eagle_db_out
|
|
21
|
-
_eagle_db_out=$({ echo "$EAGLE_DB_SETUP"; echo "$*"; } |
|
|
24
|
+
_eagle_db_out=$({ echo "$EAGLE_DB_SETUP"; echo "$*"; } | "$_eagle_sqlite_bin" "$EAGLE_MEM_DB" 2>"$_eagle_db_err")
|
|
22
25
|
local _eagle_db_rc=$?
|
|
23
26
|
if [ -s "$_eagle_db_err" ]; then
|
|
24
27
|
cat "$_eagle_db_err" >> "$EAGLE_MEM_LOG" 2>/dev/null
|
|
@@ -29,10 +32,13 @@ eagle_db() {
|
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
eagle_db_pipe() {
|
|
35
|
+
local _eagle_sqlite_bin
|
|
36
|
+
_eagle_sqlite_bin=$(eagle_sqlite_path)
|
|
37
|
+
[ -n "$_eagle_sqlite_bin" ] || return 1
|
|
32
38
|
local _eagle_db_err
|
|
33
39
|
_eagle_db_err=$(mktemp 2>/dev/null || echo "/tmp/_eagle_db_pipe_err.$$")
|
|
34
40
|
local _eagle_db_out
|
|
35
|
-
_eagle_db_out=$({ echo "$EAGLE_DB_SETUP"; echo ".bail on"; cat; } |
|
|
41
|
+
_eagle_db_out=$({ echo "$EAGLE_DB_SETUP"; echo ".bail on"; cat; } | "$_eagle_sqlite_bin" "$EAGLE_MEM_DB" 2>"$_eagle_db_err")
|
|
36
42
|
local _eagle_db_rc=$?
|
|
37
43
|
if [ -s "$_eagle_db_err" ]; then
|
|
38
44
|
cat "$_eagle_db_err" >> "$EAGLE_MEM_LOG" 2>/dev/null
|
|
@@ -43,10 +49,13 @@ eagle_db_pipe() {
|
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
eagle_db_json() {
|
|
52
|
+
local _eagle_sqlite_bin
|
|
53
|
+
_eagle_sqlite_bin=$(eagle_sqlite_path)
|
|
54
|
+
[ -n "$_eagle_sqlite_bin" ] || return 1
|
|
46
55
|
local _eagle_db_err
|
|
47
56
|
_eagle_db_err=$(mktemp 2>/dev/null || echo "/tmp/_eagle_db_json_err.$$")
|
|
48
57
|
local _eagle_db_out
|
|
49
|
-
_eagle_db_out=$({ echo "$EAGLE_DB_SETUP"; echo ".mode json"; echo "$*"; } |
|
|
58
|
+
_eagle_db_out=$({ echo "$EAGLE_DB_SETUP"; echo ".mode json"; echo "$*"; } | "$_eagle_sqlite_bin" "$EAGLE_MEM_DB" 2>"$_eagle_db_err")
|
|
50
59
|
local _eagle_db_rc=$?
|
|
51
60
|
if [ -s "$_eagle_db_err" ]; then
|
|
52
61
|
cat "$_eagle_db_err" >> "$EAGLE_MEM_LOG" 2>/dev/null
|
package/lib/updater.sh
CHANGED
|
@@ -186,8 +186,10 @@ eagle_update_backup_runtime() {
|
|
|
186
186
|
done
|
|
187
187
|
|
|
188
188
|
if [ -f "$EAGLE_MEM_DB" ]; then
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
local sqlite_bin
|
|
190
|
+
sqlite_bin=$(eagle_sqlite_path)
|
|
191
|
+
if [ -n "$sqlite_bin" ]; then
|
|
192
|
+
"$sqlite_bin" "$EAGLE_MEM_DB" ".backup '$backup_dir/memory.db'" >/dev/null 2>&1 || cp "$EAGLE_MEM_DB" "$backup_dir/memory.db" 2>/dev/null || true
|
|
191
193
|
else
|
|
192
194
|
cp "$EAGLE_MEM_DB" "$backup_dir/memory.db" 2>/dev/null || true
|
|
193
195
|
fi
|
package/package.json
CHANGED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Eagle Mem — background summary enrichment
|
|
3
|
+
# Runs outside lifecycle hook timeouts. Input is a JSON job file created by Stop.
|
|
4
|
+
set +e
|
|
5
|
+
[ "${EAGLE_MEM_DISABLE_BACKGROUND_ENRICH:-}" = "1" ] && exit 0
|
|
6
|
+
|
|
7
|
+
job_file="${1:-}"
|
|
8
|
+
[ -n "$job_file" ] && [ -f "$job_file" ] || exit 0
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
11
|
+
LIB_DIR="$SCRIPT_DIR/../lib"
|
|
12
|
+
|
|
13
|
+
. "$LIB_DIR/common.sh"
|
|
14
|
+
. "$LIB_DIR/db.sh"
|
|
15
|
+
. "$LIB_DIR/provider.sh"
|
|
16
|
+
|
|
17
|
+
eagle_ensure_db || exit 0
|
|
18
|
+
|
|
19
|
+
session_id=$(jq -r '.session_id // empty' "$job_file" 2>/dev/null)
|
|
20
|
+
project=$(jq -r '.project // empty' "$job_file" 2>/dev/null)
|
|
21
|
+
agent=$(jq -r '.agent // empty' "$job_file" 2>/dev/null)
|
|
22
|
+
text_content=$(jq -r '.text // empty' "$job_file" 2>/dev/null)
|
|
23
|
+
|
|
24
|
+
if [ -z "$session_id" ] || [ -z "$project" ] || [ -z "$text_content" ]; then
|
|
25
|
+
rm -f "$job_file" 2>/dev/null || true
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
provider=$(eagle_config_get "provider" "type" "none" 2>/dev/null)
|
|
30
|
+
if [ "$provider" = "none" ]; then
|
|
31
|
+
eagle_log "INFO" "Summary enrichment skipped: no provider for session=$session_id"
|
|
32
|
+
rm -f "$job_file" 2>/dev/null || true
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
excerpt=$(printf '%s' "$text_content" | tail -c 3000)
|
|
37
|
+
|
|
38
|
+
enrich_prompt="Extract facts from this AI coding session. Only include items with clear evidence in the session text. Do NOT invent or repeat example content.
|
|
39
|
+
|
|
40
|
+
Respond with EXACTLY these sections (omit sections with no evidence):
|
|
41
|
+
|
|
42
|
+
REQUEST:
|
|
43
|
+
One-line summary of what the user asked for. No system tags or XML.
|
|
44
|
+
|
|
45
|
+
COMPLETED:
|
|
46
|
+
What was actually accomplished. Be specific about changes made.
|
|
47
|
+
|
|
48
|
+
LEARNED:
|
|
49
|
+
Non-obvious discoveries or insights from the session.
|
|
50
|
+
|
|
51
|
+
DECISIONS:
|
|
52
|
+
Each as: <what was decided> — why: <reason>
|
|
53
|
+
|
|
54
|
+
GOTCHAS:
|
|
55
|
+
Each as: <surprising finding or pitfall>
|
|
56
|
+
|
|
57
|
+
KEY_FILES:
|
|
58
|
+
Each as: <filepath>
|
|
59
|
+
|
|
60
|
+
SESSION TEXT:
|
|
61
|
+
$excerpt"
|
|
62
|
+
|
|
63
|
+
enrich_system="You extract structured facts from Claude Code and Codex development sessions. Output format for decisions: '- Did X — why: Y'. Output format for gotchas: '- Gotcha description'. Be concise. Only include items with clear evidence in the session text. Never fabricate content."
|
|
64
|
+
enrich_result=$(eagle_llm_call "$enrich_prompt" "$enrich_system" 768 2>/dev/null)
|
|
65
|
+
llm_rc=$?
|
|
66
|
+
|
|
67
|
+
if [ $llm_rc -ne 0 ] || [ -z "$enrich_result" ]; then
|
|
68
|
+
eagle_log "WARN" "Summary enrichment failed (rc=$llm_rc) for session=$session_id provider=$provider"
|
|
69
|
+
rm -f "$job_file" 2>/dev/null || true
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
extract_section() {
|
|
74
|
+
local result="$1" header="$2"
|
|
75
|
+
printf '%s\n' "$result" | awk -v h="$header:" '
|
|
76
|
+
$0 == h || $0 ~ "^"h { found=1; next }
|
|
77
|
+
found && /^[A-Z_]+:/ { exit }
|
|
78
|
+
found && /^[[:space:]]*$/ { next }
|
|
79
|
+
found && /^- / { sub(/^- /, ""); lines[++n] = $0; next }
|
|
80
|
+
found { lines[++n] = $0 }
|
|
81
|
+
END { for (i=1; i<=n; i++) { printf "%s", lines[i]; if (i<n) printf "; " } }
|
|
82
|
+
'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
request=$(extract_section "$enrich_result" "REQUEST" | eagle_redact)
|
|
86
|
+
completed=$(extract_section "$enrich_result" "COMPLETED" | eagle_redact)
|
|
87
|
+
learned=$(extract_section "$enrich_result" "LEARNED" | eagle_redact)
|
|
88
|
+
decisions=$(extract_section "$enrich_result" "DECISIONS" | eagle_redact)
|
|
89
|
+
gotchas=$(extract_section "$enrich_result" "GOTCHAS" | eagle_redact)
|
|
90
|
+
key_files=$(extract_section "$enrich_result" "KEY_FILES" | eagle_redact)
|
|
91
|
+
|
|
92
|
+
if [ -n "$request" ] || [ -n "$completed" ] || [ -n "$learned" ] || [ -n "$decisions" ] || [ -n "$gotchas" ] || [ -n "$key_files" ]; then
|
|
93
|
+
eagle_insert_summary "$session_id" "$project" "$request" "" "$learned" "$completed" "" "[]" "[]" "" "$decisions" "$gotchas" "$key_files" "$agent"
|
|
94
|
+
eagle_log "INFO" "Summary enrichment saved for session=$session_id provider=$provider"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
rm -f "$job_file" 2>/dev/null || true
|
|
98
|
+
exit 0
|
package/scripts/install.sh
CHANGED
|
@@ -80,16 +80,18 @@ echo ""
|
|
|
80
80
|
prereqs_ok=true
|
|
81
81
|
|
|
82
82
|
# sqlite3
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
sqlite_path=$(eagle_sqlite_path)
|
|
84
|
+
if [ -n "$sqlite_path" ]; then
|
|
85
|
+
sqlite_version=$(eagle_sqlite_version)
|
|
86
|
+
eagle_ok "sqlite3 ${DIM}($sqlite_version at $sqlite_path)${RESET}"
|
|
86
87
|
else
|
|
87
88
|
eagle_fail "sqlite3 not found"
|
|
88
89
|
if eagle_confirm "Install sqlite3?"; then
|
|
89
90
|
install_package sqlite3
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
sqlite_path=$(eagle_sqlite_path)
|
|
92
|
+
if [ -n "$sqlite_path" ]; then
|
|
93
|
+
sqlite_version=$("$sqlite_path" --version 2>/dev/null | awk '{print $1}')
|
|
94
|
+
eagle_ok "sqlite3 installed ${DIM}($sqlite_version at $sqlite_path)${RESET}"
|
|
93
95
|
else
|
|
94
96
|
eagle_fail "sqlite3 installation failed"
|
|
95
97
|
prereqs_ok=false
|
|
@@ -100,8 +102,7 @@ else
|
|
|
100
102
|
fi
|
|
101
103
|
|
|
102
104
|
# FTS5 support
|
|
103
|
-
if
|
|
104
|
-
sqlite_path=$(eagle_sqlite_path)
|
|
105
|
+
if [ -n "$sqlite_path" ]; then
|
|
105
106
|
if eagle_sqlite_supports_fts5; then
|
|
106
107
|
eagle_ok "FTS5 support ${DIM}($sqlite_path)${RESET}"
|
|
107
108
|
else
|
|
@@ -109,9 +110,9 @@ if command -v sqlite3 &>/dev/null; then
|
|
|
109
110
|
eagle_dim "Detected sqlite3: $sqlite_path"
|
|
110
111
|
sqlite_version=$(eagle_sqlite_version)
|
|
111
112
|
[ -n "$sqlite_version" ] && eagle_dim "SQLite version: $sqlite_version"
|
|
112
|
-
eagle_dim "Run:
|
|
113
|
-
eagle_dim "
|
|
114
|
-
eagle_dim "macOS: /usr/bin/sqlite3 usually has FTS5;
|
|
113
|
+
eagle_dim "Run: eagle-mem health, or set EAGLE_SQLITE_BIN=/absolute/path/to/sqlite3."
|
|
114
|
+
eagle_dim "Eagle Mem prefers known system/Homebrew SQLite paths before PATH shims."
|
|
115
|
+
eagle_dim "macOS: /usr/bin/sqlite3 usually has FTS5; Android SDK sqlite3 shims are ignored when a better binary exists."
|
|
115
116
|
eagle_dim "Homebrew: brew install sqlite, then prepend /opt/homebrew/opt/sqlite/bin or /usr/local/opt/sqlite/bin."
|
|
116
117
|
eagle_dim "Linux: install a sqlite3 package compiled with ENABLE_FTS5."
|
|
117
118
|
prereqs_ok=false
|
package/scripts/statusline-em.sh
CHANGED
|
@@ -12,6 +12,9 @@ eagle_mem_statusline() {
|
|
|
12
12
|
|
|
13
13
|
local SCRIPT_DIR; SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
14
|
. "$SCRIPT_DIR/../lib/common.sh"
|
|
15
|
+
local sqlite_bin
|
|
16
|
+
sqlite_bin=$(eagle_sqlite_path)
|
|
17
|
+
[ -n "$sqlite_bin" ] || return
|
|
15
18
|
|
|
16
19
|
local proj
|
|
17
20
|
[ -z "$project_dir" ] && project_dir="$(pwd)"
|
|
@@ -24,9 +27,9 @@ eagle_mem_statusline() {
|
|
|
24
27
|
version=$(tr -d '[:space:]' < "$HOME/.eagle-mem/.version" 2>/dev/null)
|
|
25
28
|
[ -z "$version" ] && version="?"
|
|
26
29
|
cnt=$(echo ".headers off
|
|
27
|
-
SELECT COUNT(*) FROM sessions WHERE project = '${proj}';" |
|
|
30
|
+
SELECT COUNT(*) FROM sessions WHERE project = '${proj}';" | "$sqlite_bin" "$em_db" 2>/dev/null | tr -d '[:space:]')
|
|
28
31
|
mem=$(echo ".headers off
|
|
29
|
-
SELECT COUNT(*) FROM agent_memories WHERE project = '${proj}';" |
|
|
32
|
+
SELECT COUNT(*) FROM agent_memories WHERE project = '${proj}';" | "$sqlite_bin" "$em_db" 2>/dev/null | tr -d '[:space:]')
|
|
30
33
|
if [ -n "$session_id" ] && [ -f "$HOME/.eagle-mem/.turn-counter.${session_id}" ]; then
|
|
31
34
|
turns=$(tr -d '[:space:]' < "$HOME/.eagle-mem/.turn-counter.${session_id}" 2>/dev/null)
|
|
32
35
|
else
|