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.
@@ -17,13 +17,10 @@ LIB_DIR="$SCRIPT_DIR/../lib"
17
17
  input=$(eagle_read_stdin)
18
18
  [ -z "$input" ] && exit 0
19
19
 
20
- session_id=$(echo "$input" | jq -r '.session_id // empty')
21
- cwd=$(echo "$input" | jq -r '.cwd // empty')
22
- tool_name=$(echo "$input" | jq -r '.tool_name // empty')
20
+ IFS=$'\x1f' read -r session_id cwd tool_name hook_event <<< \
21
+ "$(echo "$input" | jq -r '[.session_id, .cwd, .tool_name, .hook_event_name] | map(. // "") | join("")')"
23
22
  agent=$(eagle_agent_source_from_json "$input")
24
23
 
25
- hook_event=$(echo "$input" | jq -r '.hook_event_name // empty')
26
-
27
24
  if [ -z "$session_id" ]; then exit 0; fi
28
25
 
29
26
  # TaskCreated/TaskCompleted dedicated events — parse top-level fields and exit
@@ -73,7 +70,7 @@ esac
73
70
 
74
71
  # Only track relevant tools
75
72
  case "$tool_name" in
76
- Read|Write|Edit|TaskCreate|TaskUpdate|apply_patch) ;;
73
+ Read|Write|Edit|apply_patch|TaskUpdate) ;;
77
74
  *) eagle_is_shell_tool "$tool_name" || exit 0 ;;
78
75
  esac
79
76
 
@@ -153,9 +150,18 @@ case "$tool_name" in
153
150
  *) command_category="other" ;;
154
151
  esac
155
152
  ;;
156
- TaskCreate|TaskUpdate)
157
- task_subject=$(echo "$input" | jq -r '.tool_input.subject // empty')
158
- tool_summary="$tool_name: $task_subject"
153
+ TaskUpdate)
154
+ task_id=$(echo "$input" | jq -r '.tool_input.taskId // empty')
155
+ task_status=$(echo "$input" | jq -r '.tool_input.status // empty')
156
+ tool_summary="TaskUpdate: ${task_id} → ${task_status}"
157
+ if [ -n "$task_id" ] && [ -n "$task_status" ]; then
158
+ fp_sql=$(eagle_sql_escape "event://${session_id}/${task_id}")
159
+ stat_sql=$(eagle_sql_escape "$task_status")
160
+ eagle_db_pipe <<SQL
161
+ UPDATE agent_tasks SET status = '$stat_sql', updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
162
+ WHERE file_path = '$fp_sql';
163
+ SQL
164
+ fi
159
165
  ;;
160
166
  esac
161
167
 
@@ -199,7 +205,6 @@ esac
199
205
  # ─── Dispatch to extracted responsibilities ───────────────
200
206
 
201
207
  eagle_posttool_mirror_writes "$tool_name" "$fp" "$session_id" "$project" "$agent"
202
- eagle_posttool_mirror_tasks "$tool_name" "$session_id" "$project" "$input" "$agent"
203
208
  eagle_posttool_stale_hint "$tool_name" "$fp" "$project" "$agent"
204
209
  eagle_posttool_decision_surface "$tool_name" "$fp" "$project" "$agent"
205
210
 
@@ -21,7 +21,8 @@ LIB_DIR="$SCRIPT_DIR/../lib"
21
21
  input=$(eagle_read_stdin)
22
22
  [ -z "$input" ] && exit 0
23
23
 
24
- tool_name=$(echo "$input" | jq -r '.tool_name // empty')
24
+ IFS=$'\x1f' read -r tool_name session_id cwd <<< \
25
+ "$(echo "$input" | jq -r '[.tool_name, .session_id, .cwd] | map(. // "") | join("")')"
25
26
  agent=$(eagle_agent_source_from_json "$input")
26
27
 
27
28
  case "$tool_name" in
@@ -30,9 +31,6 @@ case "$tool_name" in
30
31
  esac
31
32
 
32
33
  [ ! -f "$EAGLE_MEM_DB" ] && exit 0
33
-
34
- session_id=$(echo "$input" | jq -r '.session_id // empty')
35
- cwd=$(echo "$input" | jq -r '.cwd // empty')
36
34
  project=$(eagle_project_from_hook_input "$input")
37
35
  [ -z "$project" ] && exit 0
38
36
 
@@ -77,8 +75,6 @@ Pending checks:"
77
75
  done <<< "$pending_rows"
78
76
 
79
77
  jq -nc --arg reason "$block_reason" '{
80
- "decision":"block",
81
- "reason":$reason,
82
78
  "hookSpecificOutput":{
83
79
  "hookEventName":"PreToolUse",
84
80
  "permissionDecision":"deny",
@@ -104,8 +100,6 @@ Recommended compact command:
104
100
  One-off developer bypass:
105
101
  touch $EAGLE_RAW_BASH_UNLOCK"
106
102
  jq -nc --arg reason "$reason" '{
107
- "decision":"block",
108
- "reason":$reason,
109
103
  "hookSpecificOutput":{
110
104
  "hookEventName":"PreToolUse",
111
105
  "permissionDecision":"deny",
@@ -131,8 +125,6 @@ Install RTK or switch enforcement to auto:
131
125
  One-off developer bypass:
132
126
  touch $EAGLE_RAW_BASH_UNLOCK"
133
127
  jq -nc --arg reason "$reason" '{
134
- "decision":"block",
135
- "reason":$reason,
136
128
  "hookSpecificOutput":{
137
129
  "hookEventName":"PreToolUse",
138
130
  "permissionDecision":"deny",
@@ -37,6 +37,12 @@ project=$(eagle_project_from_hook_input "$input")
37
37
  [ -z "$project" ] && exit 0
38
38
 
39
39
  p_esc=$(eagle_sql_escape "$project")
40
+ recall_scope=$(eagle_recall_project_scope_from_cwd "$cwd" "$project")
41
+ [ -z "$recall_scope" ] && recall_scope="$project"
42
+ recall_scope_label=$(eagle_project_scope_label "$recall_scope")
43
+ recall_project_filter=$(eagle_sql_project_scope_condition "project" "$recall_scope")
44
+ recall_memory_filter=$(eagle_sql_project_scope_condition "m.project" "$recall_scope")
45
+ recall_lane_filter=$(eagle_sql_project_scope_condition "l.project" "$recall_scope")
40
46
 
41
47
  eagle_log "INFO" "SessionStart: session=$session_id project=$project source=$source_type agent=$agent"
42
48
 
@@ -96,7 +102,7 @@ while IFS='|' read -r key val; do
96
102
  last_active) stat_last_active="$val" ;;
97
103
  last_summary) stat_last_summary="$val" ;;
98
104
  esac
99
- done <<< "$(eagle_get_project_stats "$project")"
105
+ done <<< "$(eagle_get_project_stats "$recall_scope")"
100
106
 
101
107
  # ─── Build compressed banner (elide zero-value lines) ────
102
108
 
@@ -119,6 +125,10 @@ eagle_banner="======================================
119
125
  Project | $project
120
126
  Agent | $agent_label
121
127
  Sessions | $stat_sessions ($stat_with_summaries with summaries)"
128
+ if [ "$recall_scope_label" != "$project" ]; then
129
+ eagle_banner+="
130
+ Recall Scope | $recall_scope_label"
131
+ fi
122
132
  if [ "$stat_sessions_codex" -gt 0 ] || [ "$stat_sessions_claude" -gt 0 ]; then
123
133
  eagle_banner+="
124
134
  Sources | Claude $stat_sessions_claude, Codex $stat_sessions_codex"
@@ -137,12 +147,18 @@ eagle_banner+="
137
147
  ======================================"
138
148
 
139
149
  context="$eagle_banner
150
+ Eagle Mem recalls: use the project memory below before making design or implementation claims. If it affects your answer, mention the recall in one short attribution line.
140
151
  "
141
152
 
142
153
  if [ "$codex_compact" -eq 1 ]; then
143
154
  codex_context="Eagle Mem Recall Ready
144
155
  Project: $project | Agent: $agent_label
145
156
  Memory: $stat_sessions sessions"
157
+ if [ "$recall_scope_label" != "$project" ]; then
158
+ codex_context="Eagle Mem Recall Ready
159
+ Project: $project | Recall: $recall_scope_label | Agent: $agent_label
160
+ Memory: $stat_sessions sessions"
161
+ fi
146
162
  [ "$stat_with_summaries" -gt 0 ] && codex_context+=", $stat_with_summaries summarized"
147
163
  [ "$stat_memories" -gt 0 ] && codex_context+=", $stat_memories memories"
148
164
  [ "$stat_chunks" -gt 0 ] && codex_context+=", $stat_chunks code chunks"
@@ -172,7 +188,7 @@ Project brief: no overview yet; background scan is running.
172
188
  "
173
189
  fi
174
190
 
175
- recent=$(eagle_get_recent_summaries "$project" 1)
191
+ recent=$(eagle_get_recent_summaries "$recall_scope" 1)
176
192
  if [ -n "$recent" ]; then
177
193
  codex_context+="
178
194
  Relevant now:
@@ -194,16 +210,18 @@ Relevant now:
194
210
  memories=$(eagle_db "SELECT memory_name, memory_type, description, origin_agent,
195
211
  CAST(julianday('now') - julianday(updated_at) AS INTEGER) as days_ago
196
212
  FROM agent_memories
197
- WHERE project = '$p_esc'
213
+ WHERE $recall_project_filter
214
+ AND memory_name NOT IN ('Codex Memory Registry', 'Codex Memory Summary')
198
215
  ORDER BY
199
216
  CASE
200
217
  WHEN lower(memory_name) LIKE 'current status%' THEN 4
201
- WHEN memory_type IN ('feedback', 'user', 'reference') THEN 0
202
- WHEN memory_type = 'project' THEN 1
203
- ELSE 2
218
+ WHEN memory_type = 'project' THEN 0
219
+ WHEN memory_type = 'feedback' THEN 1
220
+ WHEN memory_type IN ('user', 'reference') THEN 2
221
+ ELSE 3
204
222
  END,
205
223
  updated_at DESC
206
- LIMIT 2;")
224
+ LIMIT 4;")
207
225
  if [ -n "$memories" ]; then
208
226
  [ -z "$recent" ] && codex_context+="
209
227
  Relevant now:
@@ -225,7 +243,7 @@ Relevant now:
225
243
  fi
226
244
 
227
245
  synced_tasks=$(eagle_db "SELECT subject, status, blocked_by, origin_agent FROM agent_tasks
228
- WHERE project = '$p_esc'
246
+ WHERE $recall_project_filter
229
247
  AND status IN ('in_progress', 'pending')
230
248
  AND source_session_id != 'orchestration'
231
249
  AND updated_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-7 days')
@@ -234,7 +252,7 @@ Relevant now:
234
252
  orchestration_lanes=$(eagle_db "SELECT o.name, l.lane_key, l.title, l.agent, l.status
235
253
  FROM orchestration_lanes l
236
254
  JOIN orchestrations o ON o.id = l.orchestration_id
237
- WHERE l.project = '$p_esc'
255
+ WHERE $recall_lane_filter
238
256
  AND o.status = 'active'
239
257
  AND l.status IN ('in_progress', 'pending', 'blocked')
240
258
  ORDER BY CASE l.status WHEN 'in_progress' THEN 0 WHEN 'blocked' THEN 1 ELSE 2 END, l.updated_at DESC
@@ -361,7 +379,7 @@ else
361
379
  _summary_limit=2
362
380
  fi
363
381
 
364
- recent=$(eagle_get_recent_summaries "$project" "$_summary_limit")
382
+ recent=$(eagle_get_recent_summaries "$recall_scope" "$_summary_limit")
365
383
 
366
384
  if [ -n "$recent" ]; then
367
385
  context+="
@@ -410,18 +428,20 @@ fi
410
428
 
411
429
  # ─── Memories (skip if none) ─────────────────────────────
412
430
 
413
- memory_limit=3
414
- [ "$codex_compact" -eq 1 ] && memory_limit=2
431
+ memory_limit=5
432
+ [ "$codex_compact" -eq 1 ] && memory_limit=3
415
433
  memories=$(eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_agent,
416
434
  CAST(julianday('now') - julianday(updated_at) AS INTEGER) as days_ago
417
435
  FROM agent_memories
418
- WHERE project = '$p_esc'
436
+ WHERE $recall_project_filter
437
+ AND memory_name NOT IN ('Codex Memory Registry', 'Codex Memory Summary')
419
438
  ORDER BY
420
439
  CASE
421
440
  WHEN lower(memory_name) LIKE 'current status%' THEN 4
422
- WHEN memory_type IN ('feedback', 'user', 'reference') THEN 0
423
- WHEN memory_type = 'project' THEN 1
424
- ELSE 2
441
+ WHEN memory_type = 'project' THEN 0
442
+ WHEN memory_type = 'feedback' THEN 1
443
+ WHEN memory_type IN ('user', 'reference') THEN 2
444
+ ELSE 3
425
445
  END,
426
446
  updated_at DESC
427
447
  LIMIT $memory_limit;")
@@ -450,7 +470,7 @@ fi
450
470
 
451
471
  # ─── Plans (skip if none) ────────────────────────────────
452
472
 
453
- plans=$(eagle_list_agent_plans "$project" 3)
473
+ plans=$(eagle_list_agent_plans "$recall_scope" 3)
454
474
  if [ -n "$plans" ]; then
455
475
  context+="
456
476
  === Eagle Mem: Plans ===
@@ -468,7 +488,7 @@ fi
468
488
  task_limit=5
469
489
  [ "$codex_compact" -eq 1 ] && task_limit=3
470
490
  synced_tasks=$(eagle_db "SELECT subject, status, blocked_by, origin_agent FROM agent_tasks
471
- WHERE project = '$p_esc'
491
+ WHERE $recall_project_filter
472
492
  AND status IN ('in_progress', 'pending')
473
493
  AND source_session_id != 'orchestration'
474
494
  AND updated_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-7 days')
@@ -501,7 +521,7 @@ orchestration_lanes=$(eagle_db "SELECT o.name, o.goal, l.lane_key, l.title,
501
521
  l.agent, l.status, l.validation, l.worktree_path, l.notes
502
522
  FROM orchestration_lanes l
503
523
  JOIN orchestrations o ON o.id = l.orchestration_id
504
- WHERE l.project = '$p_esc'
524
+ WHERE $recall_lane_filter
505
525
  AND o.status = 'active'
506
526
  AND l.status IN ('in_progress', 'pending', 'blocked')
507
527
  ORDER BY
package/hooks/stop.sh CHANGED
@@ -21,16 +21,12 @@ eagle_ensure_db
21
21
  input=$(eagle_read_stdin)
22
22
  [ -z "$input" ] && exit 0
23
23
 
24
- session_id=$(echo "$input" | jq -r '.session_id // empty')
25
- cwd=$(echo "$input" | jq -r '.cwd // empty')
26
- transcript_path=$(echo "$input" | jq -r '.transcript_path // empty')
24
+ IFS=$'\x1f' read -r session_id cwd transcript_path agent_type <<< \
25
+ "$(echo "$input" | jq -r '[.session_id, .cwd, .transcript_path, .agent_type] | map(. // "") | join("")')"
27
26
  last_assistant_message=$(echo "$input" | jq -r '.last_assistant_message // empty')
28
27
  agent=$(eagle_agent_source_from_json "$input")
29
28
 
30
29
  [ -z "$session_id" ] && exit 0
31
-
32
- # Skip subagent contexts
33
- agent_type=$(echo "$input" | jq -r '.agent_type // empty')
34
30
  [ -n "$agent_type" ] && [ "$agent_type" != "main" ] && exit 0
35
31
 
36
32
  project=$(eagle_project_from_hook_input "$input")
@@ -18,14 +18,16 @@ LIB_DIR="$SCRIPT_DIR/../lib"
18
18
  input=$(eagle_read_stdin)
19
19
  [ -z "$input" ] && exit 0
20
20
 
21
- session_id=$(echo "$input" | jq -r '.session_id // empty')
22
- cwd=$(echo "$input" | jq -r '.cwd // empty')
21
+ IFS=$'\x1f' read -r session_id cwd <<< \
22
+ "$(echo "$input" | jq -r '[.session_id, .cwd] | map(. // "") | join("")')"
23
23
  user_prompt=$(echo "$input" | jq -r '.prompt // empty')
24
24
  agent=$(eagle_agent_source_from_json "$input")
25
25
 
26
26
  [ -z "$user_prompt" ] && exit 0
27
27
 
28
28
  project=$(eagle_project_from_hook_input "$input")
29
+ recall_scope=$(eagle_recall_project_scope_from_cwd "$cwd" "$project")
30
+ [ -z "$recall_scope" ] && recall_scope="$project"
29
31
  codex_compact=0
30
32
  [ "$agent" = "codex" ] && codex_compact=1
31
33
 
@@ -93,8 +95,10 @@ fts_query=$(echo "$user_prompt" | tr -cs '[:alnum:]' ' ' | tr '[:upper:]' '[:low
93
95
  split("the a an is are was were be been being have has had do does did will would shall should may might can could of in to for on with at by from", sw, " ")
94
96
  for (i in sw) stop[sw[i]]=1
95
97
  n=0
98
+ split("ad ai ui ux db", keep, " ")
99
+ for (i in keep) short_keep[keep[i]]=1
96
100
  for (i=1; i<=NF && n<6; i++) {
97
- if (length($i) > 2 && !($i in stop)) {
101
+ if ((length($i) > 2 || ($i in short_keep)) && !($i in stop)) {
98
102
  printf "%s%s", (n>0?" OR ":""), $i; n++
99
103
  }
100
104
  }
@@ -137,15 +141,21 @@ if [ "$codex_compact" -eq 1 ]; then
137
141
  code_limit=2
138
142
  fi
139
143
 
140
- results=$(eagle_search_summaries "$fts_query" "$project" "$summary_limit")
144
+ memory_limit=3
145
+ [ "$codex_compact" -eq 1 ] && memory_limit=2
141
146
 
142
- if [ -n "$results" ]; then
147
+ results=$(eagle_search_summaries "$fts_query" "$recall_scope" "$summary_limit")
148
+ memory_results=$(eagle_search_agent_memories "$fts_query" "$recall_scope" "$memory_limit" 2>/dev/null || true)
149
+
150
+ if [ -n "$results" ] || [ -n "$memory_results" ]; then
143
151
  if [ "$codex_compact" -eq 1 ]; then
144
152
  context+="
145
153
  Eagle Mem recalls:
146
154
  "
147
155
  else
148
- context+="=== Eagle Mem: Relevant Recall ===
156
+ context+="Eagle Mem recalls: apply these retrieved project facts before answering. If they are relevant to the user's prompt, start with one short \"Eagle Mem recalls:\" attribution line.
157
+
158
+ === Eagle Mem: Relevant Recall ===
149
159
  "
150
160
  fi
151
161
  while IFS='|' read -r req completed learned _next_steps created_at _proj decisions gotchas key_files summary_agent; do
@@ -186,6 +196,30 @@ Eagle Mem recalls:
186
196
  "
187
197
  fi
188
198
  done <<< "$results"
199
+
200
+ while IFS='|' read -r mname mtype mdesc msnippet _mfile _mupdated morigin; do
201
+ [ -z "$mname" ] && continue
202
+ case "$mname" in
203
+ "Codex Memory Registry"|"Codex Memory Summary") continue ;;
204
+ esac
205
+ origin_label=$(eagle_agent_label "$morigin")
206
+ if [ "$codex_compact" -eq 1 ]; then
207
+ mdesc=$(eagle_trim_text "$mdesc" 120)
208
+ context+="- Memory [$mtype][$origin_label]: $mname"
209
+ [ -n "$mdesc" ] && context+=" — $mdesc"
210
+ context+="
211
+ "
212
+ else
213
+ mdesc=$(eagle_trim_text "$mdesc" 180)
214
+ msnippet=$(eagle_trim_text "$msnippet" 220)
215
+ context+="[Memory][$origin_label][$mtype] $mname"
216
+ [ -n "$mdesc" ] && context+=" — $mdesc"
217
+ [ -n "$msnippet" ] && context+="
218
+ Snippet: $msnippet"
219
+ context+="
220
+ "
221
+ fi
222
+ done <<< "$memory_results"
189
223
  fi
190
224
 
191
225
  # Search indexed code chunks (if any exist for this project)
package/lib/common.sh CHANGED
@@ -362,6 +362,121 @@ eagle_project_from_existing_ancestor() {
362
362
  return 1
363
363
  }
364
364
 
365
+ eagle_project_has_recall_rows() {
366
+ local project="${1:-}"
367
+ [ -n "$project" ] || return 1
368
+
369
+ eagle_project_has_table_row "agent_memories" "$project" \
370
+ || eagle_project_has_table_row "agent_plans" "$project" \
371
+ || eagle_project_has_table_row "agent_tasks" "$project" \
372
+ || eagle_project_has_table_row "summaries" "$project"
373
+ }
374
+
375
+ eagle_recall_ancestor_project_from_cwd() {
376
+ local path="${1:-$(pwd)}"
377
+ local current_project="${2:-}"
378
+ local current key
379
+
380
+ current=$(eagle_normalize_project_path "$path")
381
+ eagle_is_ephemeral_project_path "$current" && return 1
382
+
383
+ [ -d "$current" ] || current=$(dirname "$current")
384
+ current=$(dirname "$current")
385
+
386
+ while [ -n "$current" ] && [ "$current" != "/" ]; do
387
+ if [ "$current" = "$HOME" ]; then
388
+ break
389
+ fi
390
+
391
+ key=$(eagle_project_key_from_target_dir "$current")
392
+ if [ -n "$key" ] && [ "$key" != "$current_project" ]; then
393
+ if eagle_project_has_recall_rows "$key"; then
394
+ printf '%s\n' "$key"
395
+ return 0
396
+ fi
397
+ fi
398
+
399
+ current=$(dirname "$current")
400
+ done
401
+
402
+ return 1
403
+ }
404
+
405
+ eagle_recall_project_scope_from_cwd() {
406
+ local path="${1:-$(pwd)}"
407
+ local project="${2:-}"
408
+ local ancestor
409
+
410
+ [ -z "$project" ] && project=$(eagle_project_from_cwd "$path")
411
+ if ancestor=$(eagle_recall_ancestor_project_from_cwd "$path" "$project"); then
412
+ if [ -n "$project" ] && [ "$ancestor" != "$project" ]; then
413
+ printf '%s|%s\n' "$project" "$ancestor"
414
+ return 0
415
+ fi
416
+ printf '%s\n' "$ancestor"
417
+ return 0
418
+ fi
419
+
420
+ printf '%s\n' "$project"
421
+ }
422
+
423
+ eagle_sql_project_scope_condition() {
424
+ local column="${1:-project}"
425
+ local scope="${2:-}"
426
+ local values="" count=0 item escaped
427
+
428
+ while IFS= read -r item; do
429
+ [ -n "$item" ] || continue
430
+ escaped=$(eagle_sql_escape "$item")
431
+ values="${values},'${escaped}'"
432
+ count=$((count + 1))
433
+ done <<EOF
434
+ $(printf '%s' "$scope" | tr '|' '\n')
435
+ EOF
436
+
437
+ if [ "$count" -eq 0 ]; then
438
+ printf '1 = 0\n'
439
+ elif [ "$count" -eq 1 ]; then
440
+ printf "%s = %s\n" "$column" "${values#,}"
441
+ else
442
+ printf "%s IN (%s)\n" "$column" "${values#,}"
443
+ fi
444
+ }
445
+
446
+ eagle_project_scope_label() {
447
+ local scope="${1:-}"
448
+ printf '%s\n' "$scope" | tr '|' ','
449
+ }
450
+
451
+ eagle_project_scope_contains() {
452
+ local scope="${1:-}"
453
+ local target="${2:-}"
454
+ local item
455
+ [ -n "$target" ] || return 1
456
+
457
+ while IFS= read -r item; do
458
+ [ -z "$item" ] && continue
459
+ [ "$item" = "$target" ] && return 0
460
+ done <<EOF
461
+ $(printf '%s' "$scope" | tr '|' '\n')
462
+ EOF
463
+
464
+ return 1
465
+ }
466
+
467
+ eagle_claude_project_dir_for_key() {
468
+ local project="${1:-}"
469
+ [ -n "$project" ] || return 1
470
+
471
+ local abs slug
472
+ case "$project" in
473
+ /*) abs="$project" ;;
474
+ *) abs="$HOME/$project" ;;
475
+ esac
476
+ slug=$(printf '%s' "$abs" | sed -E 's#[^[:alnum:].]+#-#g')
477
+ printf '%s/%s\n' "$EAGLE_CLAUDE_PROJECTS_DIR" "$slug"
478
+ }
479
+
365
480
  eagle_project_from_workspace_path() {
366
481
  if [ -n "${EAGLE_MEM_PROJECT:-}" ]; then
367
482
  printf '%s\n' "$EAGLE_MEM_PROJECT"
@@ -557,6 +672,15 @@ eagle_project_from_hook_input() {
557
672
  cwd=$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null)
558
673
  transcript_path=$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null)
559
674
 
675
+ if [ -n "${CLAUDE_PROJECT_DIR:-}" ]; then
676
+ project=$(eagle_project_from_workspace_path "$CLAUDE_PROJECT_DIR")
677
+ if [ -n "$project" ]; then
678
+ [ -n "$session_id" ] && eagle_remember_session_project "$session_id" "$project" 1 >/dev/null 2>&1
679
+ printf '%s\n' "$project"
680
+ return
681
+ fi
682
+ fi
683
+
560
684
  workspace_project=$(printf '%s' "$input" | jq -r '.workspace.project_dir // empty' 2>/dev/null)
561
685
  if [ -n "$workspace_project" ]; then
562
686
  project=$(eagle_project_from_workspace_path "$workspace_project")
@@ -728,20 +852,15 @@ eagle_emit_context_for_agent() {
728
852
 
729
853
  [ -z "$context" ] && return 0
730
854
 
731
- if [ "$agent" = "codex" ]; then
732
- jq -cn \
733
- --arg event "$hook_event" \
734
- --arg context "$context" \
735
- '{
736
- hookSpecificOutput: {
737
- hookEventName: $event,
738
- additionalContext: $context
739
- }
740
- }'
741
- return 0
742
- fi
743
-
744
- printf '%s\n' "$context"
855
+ jq -cn \
856
+ --arg event "$hook_event" \
857
+ --arg context "$context" \
858
+ '{
859
+ hookSpecificOutput: {
860
+ hookEventName: $event,
861
+ additionalContext: $context
862
+ }
863
+ }'
745
864
  }
746
865
 
747
866
  eagle_config_get_light() {
@@ -110,16 +110,16 @@ eagle_record_pending_feature_verifications() {
110
110
  local fid; fid=$(eagle_sql_int "$feature_id")
111
111
  local name_esc; name_esc=$(eagle_sql_escape "$feature_name")
112
112
 
113
- if [ -n "$change_fingerprint" ]; then
114
- already_resolved=$(eagle_db "SELECT 1 FROM pending_feature_verifications
115
- WHERE project = '$p_esc'
116
- AND feature_id = $fid
117
- AND file_path = '$fp_esc'
118
- AND change_fingerprint = '$fp_hash_esc'
119
- AND status IN ('verified', 'waived')
120
- LIMIT 1;")
121
- [ -n "$already_resolved" ] && continue
122
- fi
113
+ already_resolved=$(eagle_db "SELECT 1 FROM pending_feature_verifications
114
+ WHERE project = '$p_esc'
115
+ AND feature_id = $fid
116
+ AND file_path = '$fp_esc'
117
+ AND (
118
+ (change_fingerprint = '$fp_hash_esc' AND status = 'verified')
119
+ OR status = 'waived'
120
+ )
121
+ LIMIT 1;")
122
+ [ -n "$already_resolved" ] && continue
123
123
 
124
124
  eagle_db "INSERT INTO pending_feature_verifications
125
125
  (project, feature_id, feature_name, file_path, reason, source_session_id, trigger_tool, change_fingerprint)
package/lib/db-mirrors.sh CHANGED
@@ -103,8 +103,7 @@ eagle_search_agent_memories() {
103
103
 
104
104
  local where_clause=""
105
105
  if [ -n "$project" ]; then
106
- project=$(eagle_sql_escape "$project")
107
- where_clause="AND m.project = '$project'"
106
+ where_clause="AND $(eagle_sql_project_scope_condition "m.project" "$project")"
108
107
  fi
109
108
 
110
109
  eagle_db "SELECT m.memory_name, m.memory_type, m.description,
@@ -124,8 +123,7 @@ eagle_list_agent_memories() {
124
123
 
125
124
  local where_clause=""
126
125
  if [ -n "$project" ]; then
127
- project=$(eagle_sql_escape "$project")
128
- where_clause="WHERE project = '$project'"
126
+ where_clause="WHERE $(eagle_sql_project_scope_condition "project" "$project")"
129
127
  fi
130
128
 
131
129
  eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_agent
@@ -198,8 +196,7 @@ eagle_search_agent_plans() {
198
196
 
199
197
  local where_clause=""
200
198
  if [ -n "$project" ]; then
201
- project=$(eagle_sql_escape "$project")
202
- where_clause="AND p.project = '$project'"
199
+ where_clause="AND $(eagle_sql_project_scope_condition "p.project" "$project")"
203
200
  fi
204
201
 
205
202
  eagle_db "SELECT p.title, p.project,
@@ -219,8 +216,7 @@ eagle_list_agent_plans() {
219
216
 
220
217
  local where_clause=""
221
218
  if [ -n "$project" ]; then
222
- project=$(eagle_sql_escape "$project")
223
- where_clause="WHERE project = '$project'"
219
+ where_clause="WHERE $(eagle_sql_project_scope_condition "project" "$project")"
224
220
  fi
225
221
 
226
222
  eagle_db "SELECT title, project, file_path, updated_at, origin_agent
@@ -303,8 +299,7 @@ eagle_list_agent_tasks() {
303
299
 
304
300
  local where_clause=""
305
301
  if [ -n "$project" ]; then
306
- project=$(eagle_sql_escape "$project")
307
- where_clause="WHERE project = '$project'"
302
+ where_clause="WHERE $(eagle_sql_project_scope_condition "project" "$project")"
308
303
  fi
309
304
 
310
305
  eagle_db "SELECT subject, status, source_session_id, source_task_id, updated_at, origin_agent
@@ -326,8 +321,7 @@ eagle_search_agent_tasks() {
326
321
 
327
322
  local where_clause=""
328
323
  if [ -n "$project" ]; then
329
- project=$(eagle_sql_escape "$project")
330
- where_clause="AND t.project = '$project'"
324
+ where_clause="AND $(eagle_sql_project_scope_condition "t.project" "$project")"
331
325
  fi
332
326
 
333
327
  eagle_db "SELECT t.subject, t.status,