eagle-mem 3.2.0 → 4.0.0

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.
@@ -13,6 +13,7 @@ SCRIPTS_DIR="$SCRIPT_DIR/../scripts"
13
13
  . "$LIB_DIR/common.sh"
14
14
  . "$LIB_DIR/db.sh"
15
15
  . "$LIB_DIR/provider.sh"
16
+ . "$LIB_DIR/hooks-sessionstart.sh"
16
17
 
17
18
  eagle_ensure_db
18
19
 
@@ -27,8 +28,6 @@ model=$(echo "$input" | jq -r '.model // empty')
27
28
  [ -z "$session_id" ] && exit 0
28
29
 
29
30
  project=$(eagle_project_from_cwd "$cwd")
30
-
31
- # Skip ephemeral directories (tmp, Downloads, etc.) — no tracking
32
31
  [ -z "$project" ] && exit 0
33
32
 
34
33
  p_esc=$(eagle_sql_escape "$project")
@@ -36,32 +35,18 @@ p_esc=$(eagle_sql_escape "$project")
36
35
  eagle_log "INFO" "SessionStart: session=$session_id project=$project source=$source_type"
37
36
 
38
37
  eagle_upsert_session "$session_id" "$project" "$cwd" "$model" "$source_type"
39
-
40
- # ─── Sweep stuck sessions (no activity for 7 days) ─────────
41
- # Uses last_activity_at (updated by trigger on every observation insert)
42
- # so long-lived sessions with regular compactions aren't falsely abandoned
43
38
  eagle_abandon_stale_sessions "$session_id"
44
39
 
45
- # ─── Auto-curate trigger (background, non-blocking) ──────────
46
- # Moved here from SessionEnd because SessionEnd rarely fires in long-lived sessions.
47
- # SessionStart fires on every new session, resume, and compaction — reliable trigger.
48
- curator_schedule=$(eagle_config_get "curator" "schedule" "manual")
49
- if [ "$curator_schedule" = "auto" ]; then
50
- _curator_provider=$(eagle_config_get "provider" "type" "none")
51
- if [ "$_curator_provider" != "none" ]; then
52
- _min_sessions=$(eagle_config_get "curator" "min_sessions" "5")
53
- _min_sessions=$(eagle_sql_int "$_min_sessions")
54
- _last_curated=$(eagle_meta_get "last_curated_at" "$project")
55
- _since="${_last_curated:-1970-01-01T00:00:00Z}"
56
- _sessions_since=$(eagle_count_sessions_since "$project" "$_since")
57
- if [ "${_sessions_since:-0}" -ge "$_min_sessions" ]; then
58
- eagle_log "INFO" "SessionStart: auto-curate triggered (${_sessions_since} sessions since last curate)"
59
- nohup bash "$SCRIPTS_DIR/curate.sh" -p "$project" >> "$EAGLE_MEM_LOG" 2>&1 &
60
- fi
61
- fi
62
- fi
40
+ # ─── Background automation (non-blocking) ────────────────
63
41
 
64
- # ─── Version check (non-blocking) ────────────────────────────
42
+ eagle_sessionstart_auto_provision "$project" "$cwd" "$SCRIPTS_DIR"
43
+ eagle_sessionstart_auto_prune "$project" "$SCRIPTS_DIR" "$(eagle_db "SELECT COUNT(*) FROM observations WHERE session_id IN (SELECT id FROM sessions WHERE project='$p_esc');")"
44
+ eagle_sessionstart_auto_curate "$project" "$SCRIPTS_DIR"
45
+
46
+ find "$EAGLE_MEM_DIR/read-tracker" -type f -mtime +1 -delete 2>/dev/null &
47
+ find "$EAGLE_MEM_DIR/mod-tracker" -type f -mtime +1 -delete 2>/dev/null &
48
+
49
+ # ─── Version check (non-blocking) ────────────────────────
65
50
 
66
51
  update_notice=""
67
52
  version_file="$EAGLE_MEM_DIR/.version"
@@ -89,7 +74,7 @@ if [ -f "$version_file" ] && [ -s "$version_file" ]; then
89
74
  fi
90
75
  fi
91
76
 
92
- # ─── Gather stats ───────────────────────────────────────────
77
+ # ─── Gather stats ────────────────────────────────────────
93
78
 
94
79
  stat_sessions=0; stat_summaries=0; stat_with_summaries=0; stat_memories=0
95
80
  stat_tasks_pending=0; stat_tasks_progress=0; stat_tasks_done=0
@@ -113,7 +98,8 @@ while IFS='|' read -r key val; do
113
98
  esac
114
99
  done <<< "$(eagle_get_project_stats "$project")"
115
100
 
116
- # Build task summary line
101
+ # ─── Build compressed banner (elide zero-value lines) ────
102
+
117
103
  task_parts=""
118
104
  [ "$stat_tasks_progress" -gt 0 ] && task_parts="${stat_tasks_progress} in progress"
119
105
  if [ "$stat_tasks_pending" -gt 0 ]; then
@@ -124,27 +110,26 @@ if [ "$stat_tasks_done" -gt 0 ]; then
124
110
  [ -n "$task_parts" ] && task_parts+=", "
125
111
  task_parts+="${stat_tasks_done} completed"
126
112
  fi
127
- [ -z "$task_parts" ] && task_parts="none"
128
113
 
129
- # Truncate last summary for display
130
114
  stat_last_display="${stat_last_summary:0:60}"
131
115
  [ ${#stat_last_summary} -gt 60 ] && stat_last_display+="..."
132
- [ -z "$stat_last_display" ] && stat_last_display="(no sessions yet)"
133
-
134
- # ─── Build context injection ────────────────────────────────
135
116
 
136
117
  eagle_banner="======================================
137
118
  Eagle Mem Loaded
138
119
  ======================================
139
120
  Project | $project
140
- Sessions | $stat_sessions total ($stat_with_summaries with summaries)
141
- Memories | $stat_memories stored
142
- Plans | $stat_plans saved
143
- Tasks | $task_parts
144
- Code Index | $stat_chunks chunks indexed
145
- Observations | $stat_observations captured
146
- Last Active | $stat_last_active
147
- Last Work | $stat_last_display
121
+ Sessions | $stat_sessions ($stat_with_summaries with summaries)"
122
+ [ "$stat_memories" -gt 0 ] && eagle_banner+="
123
+ Memories | $stat_memories stored"
124
+ [ "$stat_plans" -gt 0 ] && eagle_banner+="
125
+ Plans | $stat_plans saved"
126
+ [ -n "$task_parts" ] && eagle_banner+="
127
+ Tasks | $task_parts"
128
+ [ "$stat_chunks" -gt 0 ] && eagle_banner+="
129
+ Code Index | $stat_chunks chunks"
130
+ [ -n "$stat_last_display" ] && eagle_banner+="
131
+ Last Work | $stat_last_display"
132
+ eagle_banner+="
148
133
  ======================================"
149
134
 
150
135
  context="$eagle_banner
@@ -156,22 +141,33 @@ if [ -n "$update_notice" ]; then
156
141
  "
157
142
  fi
158
143
 
159
- # Project overview
144
+ # ─── Project overview (capped at 500 chars) ──────────────
145
+
160
146
  overview=$(eagle_get_overview "$project")
161
147
  if [ -n "$overview" ]; then
148
+ if [ ${#overview} -gt 500 ]; then
149
+ overview="${overview:0:497}..."
150
+ fi
162
151
  context+="
163
- === Project Overview ===
152
+ === Overview ===
164
153
  $overview
165
154
  "
166
155
  else
167
156
  context+="
168
- === Action Required ===
169
- No overview exists for '$project'. Run /eagle-mem-overview to build one.
157
+ === New Project ===
158
+ No overview yet auto-scan is running. Run /eagle-mem-overview for a richer briefing.
170
159
  "
171
160
  fi
172
161
 
173
- # Recent summaries for this project (last 5 sessions)
174
- recent=$(eagle_get_recent_summaries "$project" 5)
162
+ # ─── Recent sessions (1 on compact, 3 on startup) ────────
163
+
164
+ if [ "$source_type" = "compact" ] || [ "$source_type" = "clear" ]; then
165
+ _summary_limit=1
166
+ else
167
+ _summary_limit=3
168
+ fi
169
+
170
+ recent=$(eagle_get_recent_summaries "$project" "$_summary_limit")
175
171
 
176
172
  if [ -n "$recent" ]; then
177
173
  context+="
@@ -200,7 +196,7 @@ Next steps: $next_steps"
200
196
  "
201
197
  fi
202
198
 
203
- # ─── Mirrored Claude memories (with age) ─────────────────
199
+ # ─── Memories (skip if none) ─────────────────────────────
204
200
 
205
201
  memories=$(eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at,
206
202
  CAST(julianday('now') - julianday(updated_at) AS INTEGER) as days_ago
@@ -229,7 +225,7 @@ if [ -n "$memories" ]; then
229
225
  done <<< "$memories"
230
226
  fi
231
227
 
232
- # ─── Mirrored Claude plans ────────────────────────────────
228
+ # ─── Plans (skip if none) ────────────────────────────────
233
229
 
234
230
  plans=$(eagle_list_claude_plans "$project" 3)
235
231
  if [ -n "$plans" ]; then
@@ -243,7 +239,7 @@ if [ -n "$plans" ]; then
243
239
  done <<< "$plans"
244
240
  fi
245
241
 
246
- # ─── Claude Code tasks ───────────────────────────────────
242
+ # ─── Tasks (skip if none) ────────────────────────────────
247
243
 
248
244
  synced_tasks=$(eagle_db "SELECT subject, status, blocked_by FROM claude_tasks
249
245
  WHERE project = '$p_esc'
@@ -268,39 +264,27 @@ if [ -n "$synced_tasks" ]; then
268
264
  done <<< "$synced_tasks"
269
265
  fi
270
266
 
271
- # ─── Instructions (full on startup, minimal on compact) ──
267
+ # ─── Instructions (compressed) ───────────────────────────
272
268
 
273
269
  if [ "$source_type" = "compact" ] || [ "$source_type" = "clear" ]; then
274
270
  context+="
275
- === Eagle Mem (compact reload) ===
276
- Persistent memory active. Attribute recalled context to Eagle Mem. Do not revert past decisions surfaced by PostToolUse without asking the user. Emit <eagle-summary> before your final response.
271
+ === Eagle Mem ===
272
+ Memory active. Attribute recalled context to Eagle Mem. Do not revert PostToolUse-surfaced decisions without asking. Emit <eagle-summary> before final response.
277
273
  "
278
274
  else
279
275
  context+="
280
276
  === Eagle Mem ===
281
- Persistent memory active for '$project'. Attribute recalled context: \"Eagle Mem recalls:\" When PostToolUse surfaces past decisions about a file, do not revert without explicit user request. Never include raw secrets in eagle-summary fields. If you change something that contradicts a loaded memory, update that memory file.
282
-
283
- Emit an <eagle-summary> block before your FINAL response:
277
+ Memory active for '$project'. Scan, index, prune, and self-learning run automatically — never ask the user to run these. Attribute recalled context: \"Eagle Mem recalls:\" Do not revert PostToolUse-surfaced decisions without user request. No raw secrets in summaries. If you contradict a loaded memory, update the memory file.
284
278
 
279
+ Before your final response, emit:
285
280
  <eagle-summary>
286
- request: [what the user asked for]
287
- investigated: [file paths read or explored]
288
- learned: [non-obvious discoveries a future session couldn't guess from code]
289
- completed: [what shipped — be specific]
290
- next_steps: [concrete actions for next session]
291
- decisions:
292
- - [choice] Why: [reason]
293
- gotchas:
294
- - [what failed or surprised — \"X doesn't work because Y\"]
295
- key_files:
296
- - [path] — [role in this work]
297
- files_read: [file1, file2]
298
- files_modified: [file1, file2]
281
+ request: [what user asked] | completed: [what shipped] | learned: [non-obvious discoveries]
282
+ next_steps: [concrete actions] | decisions: [choice — why] | gotchas: [what surprised]
283
+ key_files: [path role] | files_read: [...] | files_modified: [...]
299
284
  </eagle-summary>
300
285
  "
301
286
  fi
302
287
 
303
- # Output context (plain text stdout = additionalContext for SessionStart)
304
288
  if [ -n "$context" ]; then
305
289
  echo "$context"
306
290
  fi
@@ -52,7 +52,8 @@ eagle_get_command_rule() {
52
52
  WHERE enabled = 1
53
53
  AND (project = '$project' OR project = '')
54
54
  AND ('$cmd' LIKE pattern OR '$cmd' = pattern)
55
- ORDER BY CASE WHEN project != '' THEN 0 ELSE 1 END
55
+ ORDER BY CASE WHEN project != '' THEN 0 ELSE 1 END,
56
+ LENGTH(pattern) DESC
56
57
  LIMIT 1;"
57
58
  }
58
59
 
@@ -15,9 +15,9 @@ eagle_upsert_session() {
15
15
  eagle_db "INSERT INTO sessions (id, project, cwd, model, source, last_activity_at)
16
16
  VALUES ('$session_id', '$project', '$cwd', '$model', '$source', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
17
17
  ON CONFLICT(id) DO UPDATE SET
18
- cwd = COALESCE(excluded.cwd, sessions.cwd),
19
- model = COALESCE(excluded.model, sessions.model),
20
- source = COALESCE(excluded.source, sessions.source),
18
+ cwd = COALESCE(NULLIF(excluded.cwd, ''), sessions.cwd),
19
+ model = COALESCE(NULLIF(excluded.model, ''), sessions.model),
20
+ source = COALESCE(NULLIF(excluded.source, ''), sessions.source),
21
21
  status = 'active',
22
22
  last_activity_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');"
23
23
  }
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Eagle Mem — SessionStart extracted automation
4
+ # Source from hooks/session-start.sh after common.sh + db.sh
5
+ # ═══════════════════════════════════════════════════════════
6
+ [ -n "${_EAGLE_HOOKS_SESSIONSTART_LOADED:-}" ] && return 0
7
+ _EAGLE_HOOKS_SESSIONSTART_LOADED=1
8
+
9
+ _state_dir="$EAGLE_MEM_DIR/state"
10
+
11
+ _eagle_state_fresh() {
12
+ local key="$1" project="$2" max_age_days="${3:-1}"
13
+ local state_file="$_state_dir/${key}-${project}"
14
+ [ -f "$state_file" ] && [ -z "$(find "$state_file" -mtime +${max_age_days} 2>/dev/null)" ]
15
+ }
16
+
17
+ _eagle_state_touch() {
18
+ local key="$1" project="$2"
19
+ mkdir -p "$_state_dir" 2>/dev/null
20
+ touch "$_state_dir/${key}-${project}"
21
+ }
22
+
23
+ eagle_sessionstart_auto_provision() {
24
+ local project="$1" cwd="$2" scripts_dir="$3"
25
+ local needs_scan=false needs_index=false
26
+
27
+ # Auto-scan: no overview exists
28
+ local overview
29
+ overview=$(eagle_get_overview "$project")
30
+ if [ -z "$overview" ] && ! _eagle_state_fresh "scan" "$project" 1; then
31
+ needs_scan=true
32
+ fi
33
+
34
+ # Auto-index: 0 chunks or stale > 7 days
35
+ local chunk_count
36
+ chunk_count=$(eagle_db "SELECT COUNT(*) FROM code_chunks WHERE project = '$(eagle_sql_escape "$project")';" 2>/dev/null)
37
+ chunk_count=${chunk_count:-0}
38
+ if [ "$chunk_count" -eq 0 ] && ! _eagle_state_fresh "index" "$project" 1; then
39
+ needs_index=true
40
+ elif [ "$chunk_count" -gt 0 ] && ! _eagle_state_fresh "index" "$project" 7; then
41
+ needs_index=true
42
+ fi
43
+
44
+ if [ "$needs_scan" = true ] && [ "$needs_index" = true ]; then
45
+ eagle_log "INFO" "SessionStart: first-session provision — scan then index"
46
+ _eagle_state_touch "scan" "$project"
47
+ _eagle_state_touch "index" "$project"
48
+ nohup bash -c "bash '$scripts_dir/scan.sh' '$cwd' >> '$EAGLE_MEM_LOG' 2>&1; bash '$scripts_dir/index.sh' '$cwd' >> '$EAGLE_MEM_LOG' 2>&1" &
49
+ elif [ "$needs_scan" = true ]; then
50
+ eagle_log "INFO" "SessionStart: auto-scan triggered"
51
+ _eagle_state_touch "scan" "$project"
52
+ nohup bash "$scripts_dir/scan.sh" "$cwd" >> "$EAGLE_MEM_LOG" 2>&1 &
53
+ elif [ "$needs_index" = true ]; then
54
+ eagle_log "INFO" "SessionStart: auto-index triggered"
55
+ _eagle_state_touch "index" "$project"
56
+ nohup bash "$scripts_dir/index.sh" "$cwd" >> "$EAGLE_MEM_LOG" 2>&1 &
57
+ fi
58
+ }
59
+
60
+ eagle_sessionstart_auto_prune() {
61
+ local project="$1" scripts_dir="$2" observation_count="$3"
62
+ if [ "${observation_count:-0}" -gt 10000 ] && ! _eagle_state_fresh "prune" "$project" 1; then
63
+ eagle_log "INFO" "SessionStart: auto-prune triggered (${observation_count} observations)"
64
+ _eagle_state_touch "prune" "$project"
65
+ nohup bash "$scripts_dir/prune.sh" -p "$project" >> "$EAGLE_MEM_LOG" 2>&1 &
66
+ fi
67
+ }
68
+
69
+ eagle_sessionstart_auto_curate() {
70
+ local project="$1" scripts_dir="$2"
71
+ local curator_schedule
72
+ curator_schedule=$(eagle_config_get "curator" "schedule" "manual")
73
+ if [ "$curator_schedule" = "auto" ]; then
74
+ local _provider
75
+ _provider=$(eagle_config_get "provider" "type" "none")
76
+ if [ "$_provider" != "none" ]; then
77
+ local _min_sessions _last_curated _since _sessions_since
78
+ _min_sessions=$(eagle_config_get "curator" "min_sessions" "5")
79
+ _min_sessions=$(eagle_sql_int "$_min_sessions")
80
+ _last_curated=$(eagle_meta_get "last_curated_at" "$project")
81
+ _since="${_last_curated:-1970-01-01T00:00:00Z}"
82
+ _sessions_since=$(eagle_count_sessions_since "$project" "$_since")
83
+ if [ "${_sessions_since:-0}" -ge "$_min_sessions" ]; then
84
+ eagle_log "INFO" "SessionStart: auto-curate triggered (${_sessions_since} sessions since last curate)"
85
+ nohup bash "$scripts_dir/curate.sh" -p "$project" >> "$EAGLE_MEM_LOG" 2>&1 &
86
+ fi
87
+ fi
88
+ fi
89
+ }
package/lib/hooks.sh CHANGED
@@ -11,7 +11,15 @@ eagle_patch_hook() {
11
11
  local command="$4"
12
12
  local description="${5:-}"
13
13
 
14
- if jq -e ".hooks.${event}[]? | select(.hooks[]?.command == \"$command\")" "$settings" &>/dev/null; then
14
+ # Check both command AND matcher to avoid skipping entries with different matchers
15
+ # (e.g. PreToolUse with "Bash" vs "Read" matcher using the same script)
16
+ local match_query
17
+ if [ -n "$matcher" ]; then
18
+ match_query=".hooks.${event}[]? | select(.matcher == \"$matcher\" and (.hooks[]?.command == \"$command\"))"
19
+ else
20
+ match_query=".hooks.${event}[]? | select(.matcher == null and (.hooks[]?.command == \"$command\"))"
21
+ fi
22
+ if jq -e "$match_query" "$settings" &>/dev/null; then
15
23
  [ -n "$description" ] && eagle_ok "$description ${DIM}(already registered)${RESET}"
16
24
  return 0
17
25
  fi
package/lib/provider.sh CHANGED
@@ -151,8 +151,8 @@ model = "claude-haiku-4-5-20251001"
151
151
  model = "gpt-4o-mini"
152
152
 
153
153
  [curator]
154
- # "auto" = triggers at session end after min_sessions; "manual" = CLI only
155
- schedule = "manual"
154
+ # "auto" = triggers at session start after min_sessions; "manual" = CLI only
155
+ schedule = "auto"
156
156
  min_sessions = 5
157
157
 
158
158
  [redaction]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eagle-mem",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "description": "Persistent memory for Claude Code — SQLite + FTS5, no daemon, no bloat",
5
5
  "bin": {
6
6
  "eagle-mem": "bin/eagle-mem"
package/scripts/curate.sh CHANGED
@@ -222,6 +222,13 @@ If no rules needed, output: NONE"
222
222
  eagle_log "WARN" "Curator: skipping RULE with invalid strategy '$strategy'"
223
223
  continue
224
224
  ;; esac
225
+ # Guard: reject dangerous LLM-generated patterns that match everything
226
+ # Require at least 2 literal characters (not just wildcards)
227
+ _literal_chars=$(printf '%s' "$pattern" | sed 's/[%_]//g')
228
+ if [ ${#_literal_chars} -lt 2 ]; then
229
+ eagle_log "WARN" "Curator: skipping overly broad pattern '$pattern' (needs >=2 literal chars)"
230
+ continue
231
+ fi
225
232
 
226
233
  [ "$max_lines" = "-" ] && max_lines=""
227
234
 
package/scripts/help.sh CHANGED
@@ -16,38 +16,26 @@ echo -e " ${BOLD}Eagle Mem${RESET} ${DIM}v${version}${RESET}"
16
16
  echo -e " ${DIM}Persistent memory for Claude Code${RESET}"
17
17
  echo ""
18
18
  echo -e " ${BOLD}Commands:${RESET}"
19
- echo -e " ${CYAN}search${RESET} Search past sessions, memories, and code"
20
- echo -e " ${CYAN}overview${RESET} View or set project overview"
21
- echo -e " ${CYAN}scan${RESET} Analyze codebase structure"
22
- echo -e " ${CYAN}index${RESET} Index source files for FTS5 code search"
23
- echo -e " ${CYAN}memories${RESET} View/sync mirrored Claude Code memories"
24
- echo -e " ${CYAN}tasks${RESET} View mirrored Claude Code tasks"
25
- echo -e " ${CYAN}refresh${RESET} Full project sync: scan (if needed) + index + memories"
26
- echo -e " ${CYAN}prune${RESET} Remove old observations and orphaned chunks"
27
- echo -e " ${CYAN}config${RESET} View or change LLM provider settings"
28
- echo -e " ${CYAN}curate${RESET} Run the self-learning curator (LLM-powered analysis)"
29
- echo -e " ${CYAN}feature${RESET} Manage feature graph (list/show/verify/add)"
30
- echo -e " ${CYAN}health${RESET} Diagnose self-learning pipeline health"
31
19
  echo -e " ${CYAN}install${RESET} First-time setup: hooks, database, skills"
32
20
  echo -e " ${CYAN}update${RESET} Re-deploy hooks and run migrations"
33
21
  echo -e " ${CYAN}uninstall${RESET} Remove hooks and optionally delete data"
22
+ echo -e " ${CYAN}search${RESET} Search past sessions, memories, and code"
23
+ echo -e " ${CYAN}health${RESET} Diagnose pipeline health and background automation"
24
+ echo -e " ${CYAN}config${RESET} View or change LLM provider settings"
34
25
  echo ""
35
26
  echo -e " ${BOLD}Examples:${RESET}"
36
- echo -e " ${DIM}\$${RESET} eagle-mem search \"auth bug\" ${DIM}# keyword search${RESET}"
37
- echo -e " ${DIM}\$${RESET} eagle-mem search --timeline ${DIM}# recent sessions${RESET}"
38
- echo -e " ${DIM}\$${RESET} eagle-mem refresh ${DIM}# full project sync${RESET}"
39
- echo -e " ${DIM}\$${RESET} eagle-mem overview ${DIM}# view overview${RESET}"
40
- echo -e " ${DIM}\$${RESET} eagle-mem prune --dry-run ${DIM}# preview cleanup${RESET}"
41
27
  echo -e " ${DIM}\$${RESET} eagle-mem install ${DIM}# first-time setup${RESET}"
28
+ echo -e " ${DIM}\$${RESET} eagle-mem search \"auth bug\" ${DIM}# keyword search${RESET}"
29
+ echo -e " ${DIM}\$${RESET} eagle-mem health ${DIM}# check pipeline${RESET}"
42
30
  echo ""
43
31
  echo -e " ${BOLD}Skills${RESET} ${DIM}(inside Claude Code sessions):${RESET}"
44
32
  echo -e " ${CYAN}/eagle-mem-search${RESET} Search memory and past sessions"
45
33
  echo -e " ${CYAN}/eagle-mem-overview${RESET} Build or update project overview"
46
- echo -e " ${CYAN}/eagle-mem-scan${RESET} Analyze codebase structure"
47
- echo -e " ${CYAN}/eagle-mem-index${RESET} Index source files for code search"
48
34
  echo -e " ${CYAN}/eagle-mem-memories${RESET} View/sync Claude Code memories"
49
35
  echo -e " ${CYAN}/eagle-mem-tasks${RESET} TaskAware Compact Loop for multi-step work"
50
- echo -e " ${CYAN}/eagle-mem-prune${RESET} Clean up stale data"
36
+ echo ""
37
+ echo -e " ${DIM}Everything else is automatic — scan, index, prune, and${RESET}"
38
+ echo -e " ${DIM}curator all run in the background via hooks.${RESET}"
51
39
  echo ""
52
40
  echo -e " ${DIM}https://github.com/eagleisbatman/eagle-mem${RESET}"
53
41
  echo ""
@@ -186,7 +186,11 @@ eagle_patch_hook "$SETTINGS" "UserPromptSubmit" "" \
186
186
 
187
187
  eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash" \
188
188
  "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
189
- "PreToolUse hook"
189
+ "PreToolUse hook (Bash)"
190
+
191
+ eagle_patch_hook "$SETTINGS" "PreToolUse" "Read" \
192
+ "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
193
+ "PreToolUse hook (Read)"
190
194
 
191
195
  # ─── Install skills ────────────────────────────────────────
192
196
 
package/scripts/update.sh CHANGED
@@ -76,6 +76,7 @@ if [ -f "$SETTINGS" ] && command -v jq &>/dev/null; then
76
76
  eagle_patch_hook "$SETTINGS" "SessionEnd" "" "$EAGLE_MEM_DIR/hooks/session-end.sh"
77
77
  eagle_patch_hook "$SETTINGS" "UserPromptSubmit" "" "$EAGLE_MEM_DIR/hooks/user-prompt-submit.sh"
78
78
  eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
79
+ eagle_patch_hook "$SETTINGS" "PreToolUse" "Read" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
79
80
 
80
81
  eagle_ok "Hooks registered"
81
82
  fi
@@ -17,11 +17,12 @@ description: >
17
17
  ## Judgment
18
18
 
19
19
  **Build an overview when:**
20
- - SessionStart says "No overview exists"build on the user's first prompt
21
- - The current overview reads like scan metadata (file counts, directory listings) — upgrade it
20
+ - SessionStart shows a scan-generated overview (file counts, directory listings) upgrade it to a rich briefing
22
21
  - The user asks: "update overview", "summarize this project", "what is this project"
23
22
  - The project has changed significantly since the last overview (new major feature, architecture shift)
24
23
 
24
+ Note: New projects are auto-scanned at SessionStart. The scan produces structural metadata. This skill upgrades that into a briefing with intent, architecture, and current state.
25
+
25
26
  **Don't rebuild when:**
26
27
  - The overview is already rich and current — just use it
27
28
  - The user needs help with a specific task — do the task first, update overview after if needed
@@ -64,11 +65,7 @@ If the output is empty or doesn't match what you wrote, the save failed. Retry o
64
65
 
65
66
  ### 4. Fallback for empty repos
66
67
 
67
- If no readable source files exist (fresh repo, no README), run a structural scan as a starting point:
68
- ```bash
69
- eagle-mem scan .
70
- ```
71
- Then tell the user: "I've generated a structural overview from scan. Once you add a README or source code, re-run `/eagle-mem-overview` for a richer briefing."
68
+ If no readable source files exist (fresh repo, no README), Eagle Mem's auto-scan has already generated a structural overview in the background. Tell the user: "Auto-scan has captured the project structure. Once you add a README or source code, re-run `/eagle-mem-overview` for a richer briefing."
72
69
 
73
70
  ## What makes a good overview
74
71
 
@@ -86,6 +83,10 @@ A good overview lets a fresh Claude Code context window give useful answers with
86
83
  **Bad:**
87
84
  > eagle-mem: Node.js project (42 files, ~5k lines). Structure: bin/ (1), db/ (10), hooks/ (5), lib/ (3), scripts/ (13), skills/ (7). Entry: bin/eagle-mem. No tests detected.
88
85
 
86
+ ## How automation works
87
+
88
+ Eagle Mem v4.0 auto-scans new projects at SessionStart — no user action needed. This skill exists to *upgrade* that scan into a rich, multi-paragraph briefing that captures intent, architecture, and current state.
89
+
89
90
  ## Reference
90
91
 
91
92
  ```bash
@@ -93,5 +94,4 @@ eagle-mem overview # view current overview
93
94
  eagle-mem overview set "..." # save new overview
94
95
  eagle-mem overview list # all projects with overviews
95
96
  eagle-mem overview delete # remove current project's overview
96
- eagle-mem scan . # structural scan (fallback only)
97
97
  ```
@@ -1,120 +0,0 @@
1
- ---
2
- name: eagle-mem-index
3
- description: >
4
- Index source files into FTS5-searchable chunks. Use when: 'eagle index', 'index this project',
5
- 'make code searchable', 'reindex', 'update index', 'index after pull',
6
- 'code search setup'. Uses the eagle-mem CLI.
7
- ---
8
-
9
- # Eagle Mem — Index
10
-
11
- ## Purpose
12
-
13
- **For the user:** Claude Code can find code they wrote weeks ago -- across sessions, without needing to remember which file it was in. Every prompt automatically surfaces relevant code chunks from the index, so past work stays accessible.
14
-
15
- **For you (Claude Code):** The UserPromptSubmit hook searches `code_chunks` on every user prompt and injects matching file references into your context. You don't call a search command for this -- it happens automatically. But the index must exist and be current for it to work. That's what this skill manages.
16
-
17
- ## Judgment
18
-
19
- **Index when:**
20
- - First time setting up Eagle Mem on a project (`eagle-mem index .`)
21
- - After a `git pull` or branch switch that brought in significant changes
22
- - After a major refactor (renamed files, moved directories, new modules)
23
- - The user says "index this project" or "make code searchable"
24
- - You notice the UserPromptSubmit hook isn't surfacing relevant code -- the index may be stale
25
-
26
- **Don't index when:**
27
- - You just need to read a specific file -- use Read directly
28
- - The project was recently indexed and no files changed (mtime dedup handles this, but don't waste time running it)
29
- - You're in the middle of a task -- index at session boundaries, not mid-work
30
-
31
- **Decision rule:** Index is a maintenance task, not a search tool. Run it to *prepare* the database, then rely on the automatic hook to *use* it. If the hook isn't surfacing what you expect, the index is stale -- re-run.
32
-
33
- ## Steps
34
-
35
- ### 1. Run the indexer
36
-
37
- ```bash
38
- eagle-mem index .
39
- ```
40
- Indexes the current directory. The script:
41
- - Collects source files (respects `.gitignore`, skips binaries/lockfiles)
42
- - Filters to known source extensions (sh, js, ts, py, go, rs, java, etc.)
43
- - Skips files over 1MB
44
- - Compares mtime against stored mtime -- only re-indexes changed files
45
- - Chunks each file into segments (default 80 lines) and inserts into `code_chunks` with FTS5
46
-
47
- The mtime check makes re-running cheap. On a project with 200 files where 5 changed, it processes only those 5.
48
-
49
- ### 2. Understand chunk size
50
-
51
- Default: 80 lines per chunk. Override with:
52
- ```bash
53
- EAGLE_MEM_CHUNK_SIZE=40 eagle-mem index .
54
- ```
55
-
56
- **Smaller chunks (30-50):** More precise search matches. Better for codebases with many small, distinct functions. Produces more chunks -- larger DB.
57
-
58
- **Larger chunks (100-150):** More context per match. Better for files with long, cohesive blocks. Fewer chunks -- smaller DB.
59
-
60
- **When to change:** If the hook is surfacing irrelevant matches (chunks too big, mixing unrelated code), shrink. If matches lack context (cutting functions in half), grow. The default of 80 works well for most projects.
61
-
62
- ### 3. Know what gets indexed
63
-
64
- The indexer processes files matching these extensions:
65
- `sh bash zsh js jsx mjs cjs ts tsx mts py rb go rs java kt kts swift c h cpp cc cxx hpp cs php sql html htm css scss vue svelte dart ex exs zig lua r scala yaml yml toml json md`
66
-
67
- Silently skipped: images, binaries, lockfiles (`package-lock.json` etc. are over 1MB), `.git/` contents, anything in `.gitignore`.
68
-
69
- ### 4. Understand how indexed code reaches the user
70
-
71
- The flow is:
72
- 1. You run `eagle-mem index .` -- code is chunked and stored in `code_chunks` table
73
- 2. User submits a prompt in any future session
74
- 3. UserPromptSubmit hook extracts keywords from the prompt
75
- 4. Hook searches `code_chunks` FTS5 index for matches
76
- 5. Matching file paths + line ranges are injected into your context as "EAGLE MEM — Relevant Code"
77
-
78
- There is no manual "search code" CLI command. The index feeds the hook, and the hook feeds you. Your job is to keep the index current.
79
-
80
- ### 5. Verify the index
81
-
82
- After indexing, confirm it worked:
83
- ```bash
84
- eagle-mem search --stats
85
- ```
86
- Check the "Code chunks" count. For a typical project:
87
- - 50-file project ~ 200-500 chunks
88
- - 200-file project ~ 800-2000 chunks
89
- - 0 chunks after indexing = something went wrong (check file extensions, directory path)
90
-
91
- Also verify the hook is using it: on the next user prompt with 3+ words, you should see "EAGLE MEM — Relevant Code" injected if any chunks match.
92
-
93
- ### 6. When indexing gets stale
94
-
95
- The index reflects the state of files *when you last ran it*. It goes stale when:
96
- - `git pull` brings in changes from others
97
- - `git checkout` switches branches (different file contents, possibly different files)
98
- - A major refactor renames or moves files (old paths stay in the index, new paths are missing)
99
- - Dependencies are updated and source files regenerate
100
-
101
- **After any of these, re-run `eagle-mem index .`** The mtime check ensures only changed files are re-processed. Old chunks for unchanged files are kept. Chunks for changed files are atomically replaced (DELETE + INSERT in a transaction).
102
-
103
- ## What makes a good indexing practice
104
-
105
- **Good:**
106
- > Indexed the project after pulling the team's changes from main. Stats show 1,247 chunks across 189 files. The UserPromptSubmit hook should now surface the new API routes the team added.
107
-
108
- **Bad:**
109
- > Indexed once when Eagle Mem was installed and never again. The hook keeps surfacing stale code from deleted files, and misses the 30 new files added over the past month.
110
-
111
- ## Reference
112
-
113
- | Command / Flag | What it does |
114
- |---|---|
115
- | `eagle-mem index .` | Index current directory (incremental via mtime) |
116
- | `eagle-mem index /path/to/dir` | Index a specific directory |
117
- | `EAGLE_MEM_CHUNK_SIZE=N` | Override chunk size (default: 80 lines) |
118
- | `eagle-mem search --stats` | Verify chunk count after indexing |
119
- | Max file size | 1MB (larger files silently skipped) |
120
- | Dedup | mtime-based -- re-running is cheap and safe |