eagle-mem 4.9.7 → 4.9.9

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.
@@ -167,24 +167,31 @@ eagle_abandon_stale_sessions() {
167
167
  }
168
168
 
169
169
  eagle_get_project_stats() {
170
- local project; project=$(eagle_sql_escape "$1")
170
+ local project_scope="${1:-}"
171
+ local session_filter memory_filter plan_filter task_filter chunk_filter observation_filter
172
+ session_filter=$(eagle_sql_project_scope_condition "project" "$project_scope")
173
+ memory_filter=$(eagle_sql_project_scope_condition "project" "$project_scope")
174
+ plan_filter=$(eagle_sql_project_scope_condition "project" "$project_scope")
175
+ task_filter=$(eagle_sql_project_scope_condition "project" "$project_scope")
176
+ chunk_filter=$(eagle_sql_project_scope_condition "project" "$project_scope")
177
+ observation_filter=$(eagle_sql_project_scope_condition "project" "$project_scope")
171
178
  eagle_db_pipe <<SQL
172
- SELECT 'sessions|' || COUNT(*) FROM sessions WHERE project = '$project';
173
- SELECT 'sessions_claude|' || COUNT(*) FROM sessions WHERE project = '$project' AND agent = 'claude-code';
174
- SELECT 'sessions_codex|' || COUNT(*) FROM sessions WHERE project = '$project' AND agent = 'codex';
175
- SELECT 'summaries|' || COUNT(*) FROM summaries WHERE project = '$project';
176
- SELECT 'with_summaries|' || COUNT(*) FROM summaries WHERE project = '$project' AND request IS NOT NULL AND request != '';
177
- SELECT 'memories|' || COUNT(*) FROM agent_memories WHERE project = '$project';
178
- SELECT 'plans|' || COUNT(*) FROM agent_plans WHERE project = '$project';
179
- SELECT 'tasks_pending|' || COUNT(*) FROM agent_tasks WHERE project = '$project' AND status = 'pending';
180
- SELECT 'tasks_progress|' || COUNT(*) FROM agent_tasks WHERE project = '$project' AND status = 'in_progress';
181
- SELECT 'tasks_done|' || COUNT(*) FROM agent_tasks WHERE project = '$project' AND status = 'completed';
182
- SELECT 'chunks|' || COUNT(*) FROM code_chunks WHERE project = '$project';
183
- SELECT 'observations|' || COUNT(*) FROM observations WHERE session_id IN (SELECT id FROM sessions WHERE project = '$project');
184
- SELECT 'last_active|' || COALESCE(MAX(date(COALESCE(last_activity_at, started_at))), 'never') FROM sessions WHERE project = '$project';
179
+ SELECT 'sessions|' || COUNT(*) FROM sessions WHERE $session_filter;
180
+ SELECT 'sessions_claude|' || COUNT(*) FROM sessions WHERE $session_filter AND agent = 'claude-code';
181
+ SELECT 'sessions_codex|' || COUNT(*) FROM sessions WHERE $session_filter AND agent = 'codex';
182
+ SELECT 'summaries|' || COUNT(*) FROM summaries WHERE $session_filter;
183
+ SELECT 'with_summaries|' || COUNT(*) FROM summaries WHERE $session_filter AND request IS NOT NULL AND request != '';
184
+ SELECT 'memories|' || COUNT(*) FROM agent_memories WHERE $memory_filter;
185
+ SELECT 'plans|' || COUNT(*) FROM agent_plans WHERE $plan_filter;
186
+ SELECT 'tasks_pending|' || COUNT(*) FROM agent_tasks WHERE $task_filter AND status = 'pending';
187
+ SELECT 'tasks_progress|' || COUNT(*) FROM agent_tasks WHERE $task_filter AND status = 'in_progress';
188
+ SELECT 'tasks_done|' || COUNT(*) FROM agent_tasks WHERE $task_filter AND status = 'completed';
189
+ SELECT 'chunks|' || COUNT(*) FROM code_chunks WHERE $chunk_filter;
190
+ SELECT 'observations|' || COUNT(*) FROM observations WHERE $observation_filter;
191
+ SELECT 'last_active|' || COALESCE(MAX(date(COALESCE(last_activity_at, started_at))), 'never') FROM sessions WHERE $session_filter;
185
192
  SELECT 'last_summary|' || COALESCE((SELECT substr(request, 1, 60)
186
193
  FROM summaries
187
- WHERE project = '$project'
194
+ WHERE $session_filter
188
195
  AND COALESCE(request, '') NOT LIKE '# AGENTS.md instructions%'
189
196
  AND COALESCE(request, '') NOT LIKE '<environment_context>%'
190
197
  ORDER BY created_at DESC
@@ -57,12 +57,16 @@ SQL
57
57
  }
58
58
 
59
59
  eagle_get_recent_summaries() {
60
- local project; project=$(eagle_sql_escape "$1")
60
+ local project_scope="${1:-}"
61
61
  local limit; limit=$(eagle_sql_int "${2:-5}")
62
+ local project_filter="1 = 1"
63
+ if [ -n "$project_scope" ]; then
64
+ project_filter=$(eagle_sql_project_scope_condition "s.project" "$project_scope")
65
+ fi
62
66
 
63
67
  eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at, s.decisions, s.gotchas, s.key_files, s.agent
64
68
  FROM summaries s
65
- WHERE s.project = '$project'
69
+ WHERE $project_filter
66
70
  AND COALESCE(s.request, '') NOT LIKE '%<local-command-caveat>%'
67
71
  AND COALESCE(s.request, '') NOT LIKE '# AGENTS.md instructions%'
68
72
  AND COALESCE(s.request, '') NOT LIKE '<environment_context>%'
@@ -73,13 +77,12 @@ eagle_get_recent_summaries() {
73
77
  eagle_search_summaries() {
74
78
  local query; query=$(eagle_fts_sanitize "$1")
75
79
  query=$(eagle_sql_escape "$query")
76
- local project="${2:-}"
80
+ local project_scope="${2:-}"
77
81
  local limit; limit=$(eagle_sql_int "${3:-10}")
78
82
 
79
83
  local where_clause=""
80
- if [ -n "$project" ]; then
81
- project=$(eagle_sql_escape "$project")
82
- where_clause="AND s.project = '$project'"
84
+ if [ -n "$project_scope" ]; then
85
+ where_clause="AND $(eagle_sql_project_scope_condition "s.project" "$project_scope")"
83
86
  fi
84
87
 
85
88
  eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at, s.project, s.decisions, s.gotchas, s.key_files, s.agent
@@ -33,31 +33,6 @@ eagle_posttool_mirror_writes() {
33
33
  esac
34
34
  }
35
35
 
36
- eagle_posttool_mirror_tasks() {
37
- local tool_name="$1" session_id="$2" project="$3" input="$4"
38
- local agent="${5:-$(eagle_agent_source)}"
39
-
40
- case "$tool_name" in
41
- TaskCreate|TaskUpdate)
42
- if eagle_validate_session_id "$session_id"; then
43
- local task_dir="$EAGLE_CLAUDE_TASKS_DIR/$session_id"
44
- if [ -d "$task_dir" ]; then
45
- local task_id
46
- task_id=$(echo "$input" | jq -r '.tool_input.id // empty')
47
- if [ -z "$task_id" ]; then
48
- local newest
49
- newest=$(ls -t "$task_dir"/*.json 2>/dev/null | head -1)
50
- [ -n "$newest" ] && [ -f "$newest" ] && eagle_capture_agent_task "$newest" "$session_id" "$project" "$agent"
51
- elif eagle_validate_session_id "$task_id"; then
52
- local task_json="$task_dir/$task_id.json"
53
- [ -f "$task_json" ] && eagle_capture_agent_task "$task_json" "$session_id" "$project" "$agent"
54
- fi
55
- fi
56
- fi
57
- ;;
58
- esac
59
- }
60
-
61
36
  eagle_posttool_stale_hint() {
62
37
  local tool_name="$1" fp="$2" project="$3"
63
38
  local agent="${4:-$(eagle_agent_source)}"
package/lib/hooks.sh CHANGED
@@ -4,6 +4,18 @@
4
4
  # Shared by install.sh and update.sh
5
5
  # ═══════════════════════════════════════════════════════════
6
6
 
7
+ eagle_clean_hook_entries() {
8
+ local settings="$1"
9
+ local event="$2"
10
+ local command="$3"
11
+
12
+ local tmp
13
+ tmp=$(mktemp)
14
+ jq --arg cmd "$command" \
15
+ ".hooks.${event} = ((.hooks.${event} // []) | map(select(.hooks | all(.command != \$cmd))))" \
16
+ "$settings" > "$tmp" && mv "$tmp" "$settings"
17
+ }
18
+
7
19
  eagle_patch_hook() {
8
20
  local settings="$1"
9
21
  local event="$2"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eagle-mem",
3
- "version": "4.9.7",
3
+ "version": "4.9.9",
4
4
  "description": "Shared memory, release guardrails, RTK token protection, and worker lanes for Claude Code and Codex",
5
5
  "bin": {
6
6
  "eagle-mem": "bin/eagle-mem"
@@ -125,13 +125,7 @@ case "$subcommand" in
125
125
 
126
126
  waive)
127
127
  id="${1:-}"
128
- [ -z "$id" ] && { eagle_err "Usage: eagle-mem feature waive <id> --reason <text>"; exit 1; }
129
- case "$id" in
130
- *[!0-9]*)
131
- eagle_err "Invalid ID: '$id' (must be numeric)"
132
- exit 1
133
- ;;
134
- esac
128
+ [ -z "$id" ] && { eagle_err "Usage: eagle-mem feature waive <id|name> --reason <text>"; exit 1; }
135
129
  shift
136
130
  reason=""
137
131
  while [ $# -gt 0 ]; do
@@ -147,14 +141,27 @@ case "$subcommand" in
147
141
  *) reason="$1"; shift ;;
148
142
  esac
149
143
  done
150
- [ -z "$reason" ] && { eagle_err "Usage: eagle-mem feature waive <id> --reason <text>"; exit 1; }
151
- waived=$(eagle_waive_pending_feature_verification "$project" "$id" "$reason" | tail -1)
152
- if [ "${waived:-0}" -gt 0 ] 2>/dev/null; then
153
- eagle_ok "Pending verification #$id waived"
154
- else
155
- eagle_err "No pending verification found with ID $id"
156
- exit 1
157
- fi
144
+ [ -z "$reason" ] && { eagle_err "Usage: eagle-mem feature waive <id|name> --reason <text>"; exit 1; }
145
+ case "$id" in
146
+ *[!0-9]*)
147
+ waived=$(eagle_resolve_pending_feature_verifications "$project" "$id" "waived" "$reason" | tail -1)
148
+ if [ "${waived:-0}" -gt 0 ] 2>/dev/null; then
149
+ eagle_ok "Waived $waived pending verification(s) for '$id'"
150
+ else
151
+ eagle_err "No pending verifications found for feature '$id'"
152
+ exit 1
153
+ fi
154
+ ;;
155
+ *)
156
+ waived=$(eagle_waive_pending_feature_verification "$project" "$id" "$reason" | tail -1)
157
+ if [ "${waived:-0}" -gt 0 ] 2>/dev/null; then
158
+ eagle_ok "Pending verification #$id waived"
159
+ else
160
+ eagle_err "No pending verification found with ID $id"
161
+ exit 1
162
+ fi
163
+ ;;
164
+ esac
158
165
  ;;
159
166
 
160
167
  add)
@@ -226,7 +226,11 @@ if [ "$claude_found" = true ]; then
226
226
  "$EAGLE_MEM_DIR/hooks/stop.sh" \
227
227
  "Stop hook"
228
228
 
229
- eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskCreate|TaskUpdate" \
229
+ # Clean old registrations before re-registering (handles matcher changes across versions)
230
+ eagle_clean_hook_entries "$SETTINGS" "PostToolUse" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
231
+ eagle_clean_hook_entries "$SETTINGS" "PreToolUse" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
232
+
233
+ eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskUpdate" \
230
234
  "$EAGLE_MEM_DIR/hooks/post-tool-use.sh" \
231
235
  "PostToolUse hook"
232
236
 
@@ -246,21 +250,9 @@ if [ "$claude_found" = true ]; then
246
250
  "$EAGLE_MEM_DIR/hooks/user-prompt-submit.sh" \
247
251
  "UserPromptSubmit hook"
248
252
 
249
- eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash" \
250
- "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
251
- "PreToolUse hook (Bash)"
252
-
253
- eagle_patch_hook "$SETTINGS" "PreToolUse" "Read" \
254
- "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
255
- "PreToolUse hook (Read)"
256
-
257
- eagle_patch_hook "$SETTINGS" "PreToolUse" "Edit" \
258
- "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
259
- "PreToolUse hook (Edit)"
260
-
261
- eagle_patch_hook "$SETTINGS" "PreToolUse" "Write" \
253
+ eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash|Read|Edit|Write" \
262
254
  "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
263
- "PreToolUse hook (Write)"
255
+ "PreToolUse hook"
264
256
  else
265
257
  eagle_info "Claude hooks skipped ${DIM}(Claude Code not detected)${RESET}"
266
258
  fi
@@ -24,6 +24,7 @@ case "${1:-}" in
24
24
  esac
25
25
 
26
26
  project=""
27
+ project_was_explicit=false
27
28
  limit=20
28
29
  query=""
29
30
  raw_output=false
@@ -76,7 +77,7 @@ esac
76
77
 
77
78
  while [ $# -gt 0 ]; do
78
79
  case "$1" in
79
- --project|-p) project="$2"; shift 2 ;;
80
+ --project|-p) project="$2"; project_was_explicit=true; shift 2 ;;
80
81
  --limit|-l) limit="$2"; shift 2 ;;
81
82
  --all|-a) cross_project=true; shift ;;
82
83
  --raw|--debug) raw_output=true; shift ;;
@@ -95,6 +96,9 @@ done
95
96
  if [ -z "$project" ] && [ "$cross_project" = false ]; then
96
97
  project=$(eagle_project_from_cwd "$(pwd)")
97
98
  fi
99
+ if [ "$cross_project" = false ] && [ "$project_was_explicit" = false ]; then
100
+ project=$(eagle_recall_project_scope_from_cwd "$(pwd)" "$project")
101
+ fi
98
102
 
99
103
  eagle_age_label() {
100
104
  local updated="${1:-}"
@@ -230,7 +234,7 @@ memories_show() {
230
234
  query_sql=$(eagle_sql_escape "$query")
231
235
  project_filter=""
232
236
  if [ -n "$project" ]; then
233
- project_filter="AND project = '$(eagle_sql_escape "$project")'"
237
+ project_filter="AND $(eagle_sql_project_scope_condition "project" "$project")"
234
238
  fi
235
239
 
236
240
  meta_json=$(eagle_db_json "SELECT memory_name, memory_type, description, content, file_path, updated_at, origin_session_id, origin_agent
@@ -370,7 +374,7 @@ plans_show() {
370
374
  query_sql=$(eagle_sql_escape "$query")
371
375
  project_filter=""
372
376
  if [ -n "$project" ]; then
373
- project_filter="AND project = '$(eagle_sql_escape "$project")'"
377
+ project_filter="AND $(eagle_sql_project_scope_condition "project" "$project")"
374
378
  fi
375
379
  meta_json=$(eagle_db_json "SELECT title, project, content, file_path, updated_at, origin_session_id, origin_agent
376
380
  FROM agent_plans
@@ -517,7 +521,7 @@ tasks_show() {
517
521
  query_sql=$(eagle_sql_escape "$query")
518
522
  project_filter=""
519
523
  if [ -n "$project" ]; then
520
- project_filter="AND project = '$(eagle_sql_escape "$project")'"
524
+ project_filter="AND $(eagle_sql_project_scope_condition "project" "$project")"
521
525
  fi
522
526
  meta_json=$(eagle_db_json "SELECT subject, status, description, active_form, source_session_id, source_task_id, file_path, updated_at, origin_agent
523
527
  FROM agent_tasks
@@ -575,6 +579,33 @@ tasks_show() {
575
579
  memories_sync() {
576
580
  eagle_header "Memory, Plan & Task Sync"
577
581
 
582
+ local lock_root="$EAGLE_MEM_DIR/locks"
583
+ local lock_dir="$lock_root/memories-sync.lock"
584
+ mkdir -p "$lock_root" 2>/dev/null || true
585
+ if mkdir "$lock_dir" 2>/dev/null; then
586
+ printf '%s\n' "$$" > "$lock_dir/pid" 2>/dev/null || true
587
+ _EAGLE_MEMORIES_SYNC_LOCK_DIR="$lock_dir"
588
+ trap 'rm -rf "${_EAGLE_MEMORIES_SYNC_LOCK_DIR:-}" 2>/dev/null || true' EXIT
589
+ else
590
+ local lock_pid=""
591
+ [ -f "$lock_dir/pid" ] && lock_pid=$(cat "$lock_dir/pid" 2>/dev/null | tr -cd '0-9')
592
+ if [ -n "$lock_pid" ] && kill -0 "$lock_pid" 2>/dev/null; then
593
+ eagle_info "Sync already running (pid $lock_pid). Skipping this duplicate run."
594
+ echo ""
595
+ eagle_footer "Sync skipped."
596
+ return 0
597
+ fi
598
+ rm -rf "$lock_dir" 2>/dev/null || true
599
+ if mkdir "$lock_dir" 2>/dev/null; then
600
+ printf '%s\n' "$$" > "$lock_dir/pid" 2>/dev/null || true
601
+ _EAGLE_MEMORIES_SYNC_LOCK_DIR="$lock_dir"
602
+ trap 'rm -rf "${_EAGLE_MEMORIES_SYNC_LOCK_DIR:-}" 2>/dev/null || true' EXIT
603
+ else
604
+ eagle_err "Could not acquire memories sync lock."
605
+ return 1
606
+ fi
607
+ fi
608
+
578
609
  # ─── Sync memories ───────────────────────────────────
579
610
  eagle_info "Scanning for agent memory files..."
580
611
  echo ""
@@ -583,15 +614,21 @@ memories_sync() {
583
614
  local mem_synced=0
584
615
  local mem_skipped=0
585
616
 
586
- if [ -d "$claude_mem_root" ]; then
587
- while IFS= read -r -d '' memfile; do
617
+ sync_one_memory_file() {
618
+ local memfile="$1"
619
+ [ -f "$memfile" ] || return 0
620
+
588
621
  local base
589
622
  base=$(basename "$memfile")
590
- [ "$base" = "MEMORY.md" ] && continue
623
+ [ "$base" = "MEMORY.md" ] && return 0
591
624
 
592
625
  local mem_project mem_project_dir existing_row existing_hash existing_project
593
626
  mem_project_dir="${memfile%/memory/*}"
594
627
  mem_project=$(eagle_project_from_claude_project_dir "$mem_project_dir" 2>/dev/null || true)
628
+ if [ "$cross_project" = false ] && [ -n "$project" ]; then
629
+ eagle_project_scope_contains "$project" "$mem_project" || return 0
630
+ fi
631
+
595
632
  existing_row=$(eagle_db "SELECT content_hash, project FROM agent_memories WHERE file_path = '$(eagle_sql_escape "$memfile")';")
596
633
  IFS='|' read -r existing_hash existing_project <<< "$existing_row"
597
634
  local new_hash
@@ -605,7 +642,25 @@ memories_sync() {
605
642
  eagle_capture_agent_memory "$memfile" "" "$mem_project" "claude-code"
606
643
  mem_synced=$((mem_synced + 1))
607
644
  eagle_ok "Memory: $base"
608
- done < <(find "$claude_mem_root" -path "*/memory/*.md" -print0 2>/dev/null)
645
+ }
646
+
647
+ if [ -d "$claude_mem_root" ]; then
648
+ if [ "$cross_project" = true ] || [ -z "$project" ]; then
649
+ while IFS= read -r -d '' memfile; do
650
+ sync_one_memory_file "$memfile"
651
+ done < <(find "$claude_mem_root" -path "*/memory/*.md" -print0 2>/dev/null)
652
+ else
653
+ while IFS= read -r scope_project; do
654
+ [ -z "$scope_project" ] && continue
655
+ scope_dir=$(eagle_claude_project_dir_for_key "$scope_project")
656
+ [ -d "$scope_dir/memory" ] || continue
657
+ while IFS= read -r -d '' memfile; do
658
+ sync_one_memory_file "$memfile"
659
+ done < <(find "$scope_dir/memory" -maxdepth 1 -type f -name "*.md" -print0 2>/dev/null)
660
+ done <<EOF
661
+ $(printf '%s' "$project" | tr '|' '\n')
662
+ EOF
663
+ fi
609
664
  fi
610
665
 
611
666
  local codex_mem_root="$EAGLE_CODEX_MEMORIES_DIR"
@@ -710,12 +765,16 @@ memories_sync() {
710
765
  # ─── Backfill project names ──────────────────────────
711
766
  eagle_info "Resolving project names from agent transcripts..."
712
767
 
713
- local backfilled
714
- backfilled=$(eagle_backfill_projects)
715
- if [ "${backfilled:-0}" -gt 0 ]; then
716
- eagle_ok "$backfilled rows updated with correct project names"
768
+ if [ "$cross_project" = true ]; then
769
+ local backfilled
770
+ backfilled=$(eagle_backfill_projects)
771
+ if [ "${backfilled:-0}" -gt 0 ]; then
772
+ eagle_ok "$backfilled rows updated with correct project names"
773
+ else
774
+ eagle_ok "All project names up to date"
775
+ fi
717
776
  else
718
- eagle_ok "All project names up to date"
777
+ eagle_ok "Scoped sync complete; use --all for global transcript backfill"
719
778
  fi
720
779
 
721
780
  eagle_footer "Sync complete."
package/scripts/search.sh CHANGED
@@ -43,6 +43,9 @@ show_help() {
43
43
 
44
44
  mode="keyword"
45
45
  project=""
46
+ project_was_explicit=false
47
+ project_scope=""
48
+ project_label=""
46
49
  limit=10
47
50
  session_id=""
48
51
  json_output=false
@@ -59,7 +62,7 @@ while [ $# -gt 0 ]; do
59
62
  --overview|-o) mode="overview"; shift ;;
60
63
  --memories|-m) mode="memories"; shift ;;
61
64
  --tasks) mode="tasks"; shift ;;
62
- --project|-p) project="$2"; shift 2 ;;
65
+ --project|-p) project="$2"; project_was_explicit=true; shift 2 ;;
63
66
  --limit|-n) limit="$2"; shift 2 ;;
64
67
  --all|-a) cross_project=true; shift ;;
65
68
  --json|-j) json_output=true; shift ;;
@@ -75,6 +78,12 @@ while [ $# -gt 0 ]; do
75
78
  done
76
79
 
77
80
  [ -z "$project" ] && project=$(eagle_project_from_cwd "$(pwd)")
81
+ project_scope="$project"
82
+ if [ "$cross_project" = false ] && [ "$project_was_explicit" = false ]; then
83
+ project_scope=$(eagle_recall_project_scope_from_cwd "$(pwd)" "$project")
84
+ fi
85
+ project_label=$(eagle_project_scope_label "$project_scope")
86
+ [ -z "$project_label" ] && project_label="$project"
78
87
  limit=$(eagle_sql_int "$limit")
79
88
  [ "$limit" -eq 0 ] && limit=10
80
89
 
@@ -88,11 +97,12 @@ search_keyword() {
88
97
  exit 1
89
98
  fi
90
99
  local q; q=$(eagle_sql_escape "$sanitized_q")
91
- local p; p=$(eagle_sql_escape "$project")
100
+ local project_condition
101
+ project_condition=$(eagle_sql_project_scope_condition "s.project" "$project_scope")
92
102
 
93
103
  local where_project=""
94
104
  if [ "$cross_project" = false ]; then
95
- where_project="AND s.project = '$p'"
105
+ where_project="AND $project_condition"
96
106
  fi
97
107
 
98
108
  if [ "$json_output" = true ]; then
@@ -173,12 +183,13 @@ search_keyword() {
173
183
  # ─── Timeline ────────────────────────────────────────────
174
184
 
175
185
  search_timeline() {
176
- local p; p=$(eagle_sql_escape "$project")
186
+ local project_condition
187
+ project_condition=$(eagle_sql_project_scope_condition "s.project" "$project_scope")
177
188
 
178
189
  if [ "$json_output" = true ]; then
179
190
  eagle_db_json "SELECT s.id, s.agent, s.request, s.completed, s.learned, s.next_steps, s.created_at
180
191
  FROM summaries s
181
- WHERE s.project = '$p'
192
+ WHERE $project_condition
182
193
  ORDER BY s.created_at DESC
183
194
  LIMIT $limit;"
184
195
  return
@@ -187,7 +198,7 @@ search_timeline() {
187
198
  local results
188
199
  results=$(eagle_db "SELECT s.id, s.agent, s.request, s.completed, s.learned, s.next_steps, s.created_at
189
200
  FROM summaries s
190
- WHERE s.project = '$p'
201
+ WHERE $project_condition
191
202
  ORDER BY s.created_at DESC
192
203
  LIMIT $limit;")
193
204
 
@@ -197,7 +208,7 @@ search_timeline() {
197
208
  fi
198
209
 
199
210
  echo ""
200
- echo -e " ${BOLD}Recent sessions${RESET} ${DIM}($project)${RESET}"
211
+ echo -e " ${BOLD}Recent sessions${RESET} ${DIM}($project_label)${RESET}"
201
212
  echo -e " ${DIM}─────────────────────────────────────${RESET}"
202
213
  echo ""
203
214
 
@@ -401,20 +412,25 @@ search_files() {
401
412
  # ─── Stats ────────────────────────────────────────────────
402
413
 
403
414
  search_stats() {
404
- local p; p=$(eagle_sql_escape "$project")
415
+ local sessions_condition summaries_condition observations_condition tasks_condition chunks_condition
416
+ sessions_condition=$(eagle_sql_project_scope_condition "project" "$project_scope")
417
+ summaries_condition=$(eagle_sql_project_scope_condition "project" "$project_scope")
418
+ observations_condition=$(eagle_sql_project_scope_condition "s.project" "$project_scope")
419
+ tasks_condition=$(eagle_sql_project_scope_condition "project" "$project_scope")
420
+ chunks_condition=$(eagle_sql_project_scope_condition "project" "$project_scope")
405
421
 
406
422
  local sessions sessions_claude sessions_codex summaries observations tasks
407
- sessions=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE project = '$p';")
408
- sessions_claude=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE project = '$p' AND agent = 'claude-code';")
409
- sessions_codex=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE project = '$p' AND agent = 'codex';")
410
- summaries=$(eagle_db "SELECT COUNT(*) FROM summaries WHERE project = '$p';")
411
- observations=$(eagle_db "SELECT COUNT(*) FROM observations o JOIN sessions s ON s.id = o.session_id WHERE s.project = '$p';")
412
- tasks=$(eagle_db "SELECT COUNT(*) FROM agent_tasks WHERE project = '$p';")
423
+ sessions=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE $sessions_condition;")
424
+ sessions_claude=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE $sessions_condition AND agent = 'claude-code';")
425
+ sessions_codex=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE $sessions_condition AND agent = 'codex';")
426
+ summaries=$(eagle_db "SELECT COUNT(*) FROM summaries WHERE $summaries_condition;")
427
+ observations=$(eagle_db "SELECT COUNT(*) FROM observations o JOIN sessions s ON s.id = o.session_id WHERE $observations_condition;")
428
+ tasks=$(eagle_db "SELECT COUNT(*) FROM agent_tasks WHERE $tasks_condition;")
413
429
  local chunks
414
- chunks=$(eagle_db "SELECT COUNT(*) FROM code_chunks WHERE project = '$p';")
430
+ chunks=$(eagle_db "SELECT COUNT(*) FROM code_chunks WHERE $chunks_condition;")
415
431
 
416
432
  if [ "$json_output" = true ]; then
417
- jq -nc --arg project "$project" \
433
+ jq -nc --arg project "$project_label" \
418
434
  --argjson sessions "${sessions:-0}" \
419
435
  --argjson sessions_claude "${sessions_claude:-0}" \
420
436
  --argjson sessions_codex "${sessions_codex:-0}" \
@@ -427,7 +443,7 @@ search_stats() {
427
443
  fi
428
444
 
429
445
  echo ""
430
- echo -e " ${BOLD}Stats${RESET} ${DIM}($project)${RESET}"
446
+ echo -e " ${BOLD}Stats${RESET} ${DIM}($project_label)${RESET}"
431
447
  echo -e " ${DIM}─────────────────────────────────────${RESET}"
432
448
  echo ""
433
449
  eagle_kv "Sessions:" "${sessions:-0}"
@@ -474,11 +490,13 @@ search_overview() {
474
490
  # ─── Memories ────────────────────────────────────────────
475
491
 
476
492
  search_memories() {
477
- local p; p=$(eagle_sql_escape "$project")
493
+ local project_condition memory_condition
494
+ project_condition=$(eagle_sql_project_scope_condition "project" "$project_scope")
495
+ memory_condition=$(eagle_sql_project_scope_condition "m.project" "$project_scope")
478
496
 
479
497
  local where_project=""
480
498
  if [ "$cross_project" = false ]; then
481
- where_project="WHERE project = '$p'"
499
+ where_project="WHERE $project_condition"
482
500
  fi
483
501
 
484
502
  if [ -n "$query" ]; then
@@ -491,7 +509,7 @@ search_memories() {
491
509
  local q; q=$(eagle_sql_escape "$sanitized_mq")
492
510
  local where_match="WHERE agent_memories_fts MATCH '$q'"
493
511
  if [ "$cross_project" = false ]; then
494
- where_match="$where_match AND m.project = '$p'"
512
+ where_match="$where_match AND $memory_condition"
495
513
  fi
496
514
 
497
515
  if [ "$json_output" = true ]; then
@@ -570,7 +588,7 @@ search_memories() {
570
588
  fi
571
589
 
572
590
  echo ""
573
- echo -e " ${BOLD}Memories${RESET} ${DIM}($project)${RESET}"
591
+ echo -e " ${BOLD}Memories${RESET} ${DIM}($project_label)${RESET}"
574
592
  echo -e " ${DIM}─────────────────────────────────────${RESET}"
575
593
  echo ""
576
594
 
@@ -585,13 +603,15 @@ search_memories() {
585
603
  # ─── Tasks ───────────────────────────────────────────────
586
604
 
587
605
  search_tasks() {
588
- local p; p=$(eagle_sql_escape "$project")
606
+ local task_condition bare_task_condition
607
+ task_condition=$(eagle_sql_project_scope_condition "t.project" "$project_scope")
608
+ bare_task_condition=$(eagle_sql_project_scope_condition "project" "$project_scope")
589
609
 
590
610
  local where_project=""
591
611
  local where_task_project=""
592
612
  if [ "$cross_project" = false ]; then
593
- where_project="AND project = '$p'"
594
- where_task_project="AND t.project = '$p'"
613
+ where_project="AND $bare_task_condition"
614
+ where_task_project="AND $task_condition"
595
615
  fi
596
616
 
597
617
  if [ -n "$query" ]; then
@@ -662,7 +682,7 @@ search_tasks() {
662
682
  LIMIT $limit;")
663
683
 
664
684
  if [ -z "$results" ]; then
665
- eagle_dim "No active tasks for project '$project'"
685
+ eagle_dim "No active tasks for project '$project_label'"
666
686
  return
667
687
  fi
668
688
 
@@ -58,17 +58,18 @@ eagle_mem_statusline_stats() {
58
58
  [ -z "$project_dir" ] && project_dir="$(pwd)"
59
59
  [ -z "$current_dir" ] && current_dir="$project_dir"
60
60
 
61
- local project_key project_sql stats sessions memories last_raw turns version latest
61
+ local project_key project_scope project_condition stats sessions memories last_raw turns version latest
62
62
  project_key=$(eagle_project_from_statusline_input "$statusline_input" "$project_dir" "$current_dir" "$session_id")
63
63
  [ -n "$project_key" ] || return
64
- project_sql=$(eagle_sql_escape "$project_key")
64
+ project_scope=$(eagle_recall_project_scope_from_cwd "${current_dir:-$project_dir}" "$project_key")
65
+ project_condition=$(eagle_sql_project_scope_condition "project" "$project_scope")
65
66
 
66
67
  stats=$("$sqlite_bin" "$em_db" "SELECT
67
68
  COUNT(*) || '|' ||
68
- (SELECT COUNT(*) FROM agent_memories WHERE project = '$project_sql') || '|' ||
69
+ (SELECT COUNT(*) FROM agent_memories WHERE $project_condition) || '|' ||
69
70
  COALESCE(MAX(COALESCE(last_activity_at, started_at)), 'never')
70
71
  FROM sessions
71
- WHERE project = '$project_sql';" 2>/dev/null)
72
+ WHERE $project_condition;" 2>/dev/null)
72
73
  IFS='|' read -r sessions memories last_raw <<< "${stats:-0|0|never}"
73
74
  sessions=${sessions:-0}
74
75
  memories=${memories:-0}
package/scripts/update.sh CHANGED
@@ -76,23 +76,18 @@ fi
76
76
  # ─── Re-register hooks (idempotent) ───────────────────────
77
77
 
78
78
  if [ "$claude_found" = true ] && [ -f "$SETTINGS" ] && command -v jq &>/dev/null; then
79
- # Update PostToolUse matcher if it has the old value (pre-v1.3.0)
80
- if jq -e '.hooks.PostToolUse[]? | select(.matcher == "Read|Write|Edit|Bash")' "$SETTINGS" &>/dev/null; then
81
- _tmp=$(mktemp)
82
- jq '(.hooks.PostToolUse[] | select(.matcher == "Read|Write|Edit|Bash")).matcher = "Read|Write|Edit|Bash|TaskCreate|TaskUpdate"' "$SETTINGS" > "$_tmp" && mv "$_tmp" "$SETTINGS"
83
- fi
79
+ # Clean old registrations before re-registering (handles matcher changes across versions)
80
+ eagle_clean_hook_entries "$SETTINGS" "PostToolUse" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
81
+ eagle_clean_hook_entries "$SETTINGS" "PreToolUse" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
84
82
 
85
83
  eagle_patch_hook "$SETTINGS" "SessionStart" "" "$EAGLE_MEM_DIR/hooks/session-start.sh"
86
84
  eagle_patch_hook "$SETTINGS" "Stop" "" "$EAGLE_MEM_DIR/hooks/stop.sh"
87
- eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskCreate|TaskUpdate" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
85
+ eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskUpdate" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
88
86
  eagle_patch_hook "$SETTINGS" "TaskCreated" "" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
89
87
  eagle_patch_hook "$SETTINGS" "TaskCompleted" "" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
90
88
  eagle_patch_hook "$SETTINGS" "SessionEnd" "" "$EAGLE_MEM_DIR/hooks/session-end.sh"
91
89
  eagle_patch_hook "$SETTINGS" "UserPromptSubmit" "" "$EAGLE_MEM_DIR/hooks/user-prompt-submit.sh"
92
- eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
93
- eagle_patch_hook "$SETTINGS" "PreToolUse" "Read" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
94
- eagle_patch_hook "$SETTINGS" "PreToolUse" "Edit" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
95
- eagle_patch_hook "$SETTINGS" "PreToolUse" "Write" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
90
+ eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash|Read|Edit|Write" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
96
91
 
97
92
  eagle_ok "Hooks registered"
98
93
  fi