eagle-mem 4.9.2 → 4.9.4

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 CHANGED
@@ -152,6 +152,14 @@ 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.4 Patch
156
+
157
+ 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.
158
+
159
+ ### v4.9.3 Patch
160
+
161
+ Follow-up hardening for the v4.9.2 project-key repair: Claude transcript workspace detection now reads complete early JSONL records instead of a fixed byte slice, so large SessionStart hook context cannot hide the first `cwd`. Metadata-only memory/plan/task repairs also avoid touching FTS-indexed columns, preventing SQLite FTS update triggers from firing during safe project/source rekeys.
162
+
155
163
  ### v4.9.2 Patch
156
164
 
157
165
  Nested-repo Claude Code projects now use one stable project key. When a Claude workspace contains a git repo subdirectory, hooks prefer the Claude transcript workspace root while repo-local CLI commands can still use git-root keys where appropriate. Memory sync and backfill also repair unchanged memory rows whose content hash stayed the same but whose project key was stale. FTS5 update triggers now ignore metadata-only project rekeys, avoiding SQLite virtual-table errors during safe repairs.
@@ -0,0 +1,17 @@
1
+ -- Migration 034: Make feature FTS updates safe for project-key repairs.
2
+ --
3
+ -- Project-key repairs update features.project, but the older trigger fired on
4
+ -- every UPDATE and tried to rewrite the FTS5 row even when searchable text did
5
+ -- not change. Restrict it to the indexed columns so metadata-only rekeys are
6
+ -- safe.
7
+
8
+ DROP TRIGGER IF EXISTS features_au;
9
+
10
+ CREATE TRIGGER features_au
11
+ AFTER UPDATE OF name, description ON features
12
+ BEGIN
13
+ INSERT INTO features_fts(features_fts, rowid, name, description)
14
+ VALUES ('delete', old.id, old.name, old.description);
15
+ INSERT INTO features_fts(rowid, name, description)
16
+ VALUES (new.id, new.name, new.description);
17
+ END;
package/lib/common.sh CHANGED
@@ -210,15 +210,229 @@ eagle_transcript_first_cwd() {
210
210
  local transcript_path="${1:-}"
211
211
  [ -f "$transcript_path" ] || return 1
212
212
 
213
- local sample
214
- sample=$(dd if="$transcript_path" bs=65536 count=4 2>/dev/null)
215
- [ -n "$sample" ] || return 1
213
+ local head_lines
214
+ head_lines=$(sed -n '1,200p' "$transcript_path" 2>/dev/null || true)
215
+ [ -n "$head_lines" ] || return 1
216
+
217
+ local cwd
218
+ cwd=$(printf '%s\n' "$head_lines" \
219
+ | jq -r '
220
+ [
221
+ (.payload? | objects | .workspace? | objects | .project_dir? | strings),
222
+ (.workspace? | objects | .project_dir? | strings),
223
+ (.payload? | objects | .workspace? | objects | .current_dir? | strings),
224
+ (.workspace? | objects | .current_dir? | strings),
225
+ (.cwd? | strings),
226
+ (.payload? | objects | .cwd? | strings),
227
+ (.payload? | objects | .current_dir? | strings)
228
+ ]
229
+ | .[0] // empty
230
+ ' 2>/dev/null \
231
+ | awk 'NF { print; exit }' || true)
232
+
233
+ [ -n "$cwd" ] || return 1
234
+ printf '%s\n' "$cwd"
235
+ }
236
+
237
+ eagle_session_project_marker_file() {
238
+ local session_id="${1:-}"
239
+ eagle_validate_session_id "$session_id" || return 1
240
+ mkdir -p "$EAGLE_MEM_DIR/session-projects" 2>/dev/null || return 1
241
+ printf '%s/session-projects/%s\n' "$EAGLE_MEM_DIR" "$session_id"
242
+ }
243
+
244
+ eagle_get_session_project_marker() {
245
+ local marker
246
+ marker=$(eagle_session_project_marker_file "$1") || return 1
247
+ [ -s "$marker" ] || return 1
248
+ awk 'NF { print; exit }' "$marker"
249
+ }
250
+
251
+ eagle_remember_session_project() {
252
+ local session_id="${1:-}"
253
+ local project="${2:-}"
254
+ local force="${3:-0}"
255
+ [ -n "$project" ] || return 1
256
+
257
+ local marker
258
+ marker=$(eagle_session_project_marker_file "$session_id") || return 1
259
+ if [ "$force" = "1" ] || [ ! -s "$marker" ]; then
260
+ printf '%s\n' "$project" > "$marker" 2>/dev/null || return 1
261
+ fi
262
+ }
263
+
264
+ eagle_get_session_project_light() {
265
+ local session_id="${1:-}"
266
+ eagle_validate_session_id "$session_id" || return 1
267
+ command -v sqlite3 >/dev/null 2>&1 || return 1
268
+ [ -f "$EAGLE_MEM_DB" ] || return 1
269
+
270
+ local sid_sql project
271
+ sid_sql=$(eagle_sql_escape "$session_id")
272
+ project=$(sqlite3 "$EAGLE_MEM_DB" "SELECT project FROM sessions WHERE id = '$sid_sql' AND project != '' LIMIT 1;" 2>/dev/null | awk 'NF { print; exit }')
273
+ [ -n "$project" ] || return 1
274
+ printf '%s\n' "$project"
275
+ }
276
+
277
+ eagle_project_has_table_row() {
278
+ local table="${1:-}"
279
+ local project="${2:-}"
280
+ [ -n "$table" ] && [ -n "$project" ] || return 1
281
+ command -v sqlite3 >/dev/null 2>&1 || return 1
282
+ [ -f "$EAGLE_MEM_DB" ] || return 1
283
+
284
+ local project_sql found
285
+ project_sql=$(eagle_sql_escape "$project")
286
+ found=$(sqlite3 "$EAGLE_MEM_DB" "SELECT 1 FROM $table WHERE project = '$project_sql' LIMIT 1;" 2>/dev/null | awk 'NF { print; exit }')
287
+ [ "$found" = "1" ]
288
+ }
289
+
290
+ eagle_project_from_existing_ancestor() {
291
+ local path="${1:-}"
292
+ [ -n "$path" ] || return 1
293
+
294
+ local current key
295
+ current=$(eagle_normalize_project_path "$path")
296
+ eagle_is_ephemeral_project_path "$current" && return 1
297
+
298
+ while [ -n "$current" ] && [ "$current" != "/" ]; do
299
+ key=$(eagle_project_key_from_target_dir "$current")
300
+ if [ -n "$key" ]; then
301
+ # Prefer ancestors with durable memory/summary content. Session-only
302
+ # rows can be created by the very folder drift this helper repairs.
303
+ if eagle_project_has_table_row "agent_memories" "$key" \
304
+ || eagle_project_has_table_row "summaries" "$key"; then
305
+ printf '%s\n' "$key"
306
+ return 0
307
+ fi
308
+ fi
309
+
310
+ [ "$current" = "$HOME" ] && break
311
+ current=$(dirname "$current")
312
+ done
313
+
314
+ return 1
315
+ }
316
+
317
+ eagle_project_from_workspace_path() {
318
+ if [ -n "${EAGLE_MEM_PROJECT:-}" ]; then
319
+ printf '%s\n' "$EAGLE_MEM_PROJECT"
320
+ return 0
321
+ fi
322
+
323
+ local path="${1:-}"
324
+ [ -n "$path" ] || return 1
325
+
326
+ local resolved worktree_project git_root project
327
+ resolved=$(eagle_normalize_project_path "$path")
328
+ eagle_is_ephemeral_project_path "$resolved" && return 1
329
+
330
+ if worktree_project=$(eagle_project_key_for_worktree_path "$resolved"); then
331
+ printf '%s\n' "$worktree_project"
332
+ return 0
333
+ fi
334
+
335
+ git_root=$(git -C "$resolved" rev-parse --show-toplevel 2>/dev/null)
336
+ if [ -n "$git_root" ]; then
337
+ project=$(eagle_project_key_from_target_dir "$git_root")
338
+ [ -n "$project" ] && { printf '%s\n' "$project"; return 0; }
339
+ fi
340
+
341
+ if project=$(eagle_project_from_existing_ancestor "$path"); then
342
+ printf '%s\n' "$project"
343
+ return 0
344
+ fi
345
+
346
+ project=$(eagle_project_from_path_no_git "$path")
347
+ [ -n "$project" ] || return 1
348
+ printf '%s\n' "$project"
349
+ }
350
+
351
+ eagle_project_from_transcript_start() {
352
+ local transcript_path="${1:-}"
353
+ local cwd="${2:-}"
354
+ [ -f "$transcript_path" ] || return 1
355
+
356
+ local transcript_cwd project
357
+ if project=$(eagle_project_from_claude_transcript "$transcript_path" "$cwd"); then
358
+ printf '%s\n' "$project"
359
+ return 0
360
+ fi
361
+
362
+ transcript_cwd=$(eagle_transcript_first_cwd "$transcript_path") || return 1
363
+ if [ -n "$cwd" ] && ! eagle_path_is_same_or_child "$transcript_cwd" "$cwd"; then
364
+ return 1
365
+ fi
366
+
367
+ project=$(eagle_project_from_workspace_path "$transcript_cwd")
368
+ [ -n "$project" ] || return 1
369
+ printf '%s\n' "$project"
370
+ }
371
+
372
+ eagle_project_from_statusline_input() {
373
+ local input="${1:-}"
374
+ local project_dir="${2:-}"
375
+ local cwd="${3:-}"
376
+ local session_id="${4:-}"
377
+ local project workspace_project_dir transcript_path
378
+
379
+ if [ -n "${EAGLE_MEM_PROJECT:-}" ]; then
380
+ printf '%s\n' "$EAGLE_MEM_PROJECT"
381
+ return
382
+ fi
383
+
384
+ if [ -z "$session_id" ] && [ -n "$input" ]; then
385
+ session_id=$(printf '%s' "$input" | jq -r '.session_id // .session.id // empty' 2>/dev/null)
386
+ fi
387
+
388
+ if [ -n "$input" ]; then
389
+ workspace_project_dir=$(printf '%s' "$input" | jq -r '.workspace.project_dir // empty' 2>/dev/null)
390
+ transcript_path=$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null)
391
+ [ -z "$cwd" ] && cwd=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null)
392
+ fi
393
+
394
+ # Explicit workspace project and transcript start are stronger than older
395
+ # cached session rows because they repair pre-fix sessions that were stored
396
+ # under a nested folder key.
397
+ if [ -n "$workspace_project_dir" ]; then
398
+ project=$(eagle_project_from_workspace_path "$workspace_project_dir")
399
+ [ -n "$project" ] && { printf '%s\n' "$project"; return; }
400
+ fi
401
+
402
+ if [ -n "$transcript_path" ]; then
403
+ if project=$(eagle_project_from_transcript_start "$transcript_path" "${cwd:-$project_dir}"); then
404
+ printf '%s\n' "$project"
405
+ return
406
+ fi
407
+ fi
216
408
 
217
- printf '%s\n' "$sample" | sed -nE '/"cwd"[[:space:]]*:/ {
218
- s/.*"cwd"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/
219
- p
220
- q
221
- }'
409
+ if [ -n "$session_id" ]; then
410
+ if project=$(eagle_get_session_project_light "$session_id"); then
411
+ printf '%s\n' "$project"
412
+ return
413
+ fi
414
+ if project=$(eagle_get_session_project_marker "$session_id"); then
415
+ printf '%s\n' "$project"
416
+ return
417
+ fi
418
+ fi
419
+
420
+ if [ -z "$project_dir" ] && [ -n "$input" ]; then
421
+ project_dir=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null)
422
+ fi
423
+ if [ -n "$project_dir" ]; then
424
+ project=$(eagle_project_from_workspace_path "$project_dir")
425
+ [ -n "$project" ] && { printf '%s\n' "$project"; return; }
426
+ fi
427
+
428
+ if [ -z "$cwd" ] && [ -n "$input" ]; then
429
+ cwd=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null)
430
+ fi
431
+ if [ -n "$cwd" ]; then
432
+ project=$(eagle_project_from_workspace_path "$cwd")
433
+ [ -n "$project" ] && { printf '%s\n' "$project"; return; }
434
+ fi
435
+ eagle_project_from_cwd "$cwd"
222
436
  }
223
437
 
224
438
  eagle_project_from_claude_project_dir() {
@@ -231,7 +445,7 @@ eagle_project_from_claude_project_dir() {
231
445
  [ -f "$jsonl" ] || continue
232
446
  cwd=$(eagle_transcript_first_cwd "$jsonl")
233
447
  [ -z "$cwd" ] && continue
234
- project=$(eagle_project_from_path_no_git "$cwd")
448
+ project=$(eagle_project_from_workspace_path "$cwd")
235
449
  [ -n "$project" ] && { printf '%s\n' "$project"; return 0; }
236
450
  done
237
451
 
@@ -255,7 +469,7 @@ eagle_project_from_claude_transcript() {
255
469
  return 1
256
470
  fi
257
471
 
258
- project=$(eagle_project_from_path_no_git "$transcript_cwd")
472
+ project=$(eagle_project_from_workspace_path "$transcript_cwd")
259
473
  [ -n "$project" ] || return 1
260
474
  printf '%s\n' "$project"
261
475
  }
@@ -268,16 +482,54 @@ eagle_project_from_hook_input() {
268
482
  return
269
483
  fi
270
484
 
271
- local cwd transcript_path project
485
+ local session_id cwd transcript_path workspace_project project
486
+ session_id=$(printf '%s' "$input" | jq -r '.session_id // empty' 2>/dev/null)
272
487
  cwd=$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null)
273
488
  transcript_path=$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null)
274
489
 
490
+ workspace_project=$(printf '%s' "$input" | jq -r '.workspace.project_dir // empty' 2>/dev/null)
491
+ if [ -n "$workspace_project" ]; then
492
+ project=$(eagle_project_from_workspace_path "$workspace_project")
493
+ if [ -n "$project" ]; then
494
+ [ -n "$session_id" ] && eagle_remember_session_project "$session_id" "$project" 1 >/dev/null 2>&1
495
+ printf '%s\n' "$project"
496
+ return
497
+ fi
498
+ fi
499
+
275
500
  if project=$(eagle_project_from_claude_transcript "$transcript_path" "$cwd"); then
501
+ [ -n "$session_id" ] && eagle_remember_session_project "$session_id" "$project" 1 >/dev/null 2>&1
276
502
  printf '%s\n' "$project"
277
503
  return
278
504
  fi
279
505
 
280
- eagle_project_from_cwd "$cwd"
506
+ if [ -n "$transcript_path" ] && [ -f "$transcript_path" ]; then
507
+ local transcript_cwd
508
+ transcript_cwd=$(eagle_transcript_first_cwd "$transcript_path")
509
+ if [ -n "$transcript_cwd" ]; then
510
+ project=$(eagle_project_from_workspace_path "$transcript_cwd")
511
+ if [ -n "$project" ]; then
512
+ [ -n "$session_id" ] && eagle_remember_session_project "$session_id" "$project" 1 >/dev/null 2>&1
513
+ printf '%s\n' "$project"
514
+ return
515
+ fi
516
+ fi
517
+ fi
518
+
519
+ if [ -n "$session_id" ]; then
520
+ if project=$(eagle_get_session_project_light "$session_id"); then
521
+ printf '%s\n' "$project"
522
+ return
523
+ fi
524
+ if project=$(eagle_get_session_project_marker "$session_id"); then
525
+ printf '%s\n' "$project"
526
+ return
527
+ fi
528
+ fi
529
+
530
+ project=$(eagle_project_from_cwd "$cwd")
531
+ [ -n "$session_id" ] && [ -n "$project" ] && eagle_remember_session_project "$session_id" "$project" 0 >/dev/null 2>&1
532
+ printf '%s\n' "$project"
281
533
  }
282
534
 
283
535
  eagle_project_file_path() {
@@ -813,6 +1065,114 @@ eagle_collect_files() {
813
1065
  fi
814
1066
  }
815
1067
 
1068
+ eagle_statusline_script_from_command() {
1069
+ local cmd="${1:-}"
1070
+ [ -z "$cmd" ] && return 1
1071
+
1072
+ cmd="${cmd#sh }"
1073
+ cmd="${cmd#bash }"
1074
+ cmd="${cmd#/bin/sh }"
1075
+ cmd="${cmd#/bin/bash }"
1076
+ cmd="${cmd#zsh }"
1077
+ cmd="${cmd#/bin/zsh }"
1078
+ cmd="${cmd%\"}"
1079
+ cmd="${cmd#\"}"
1080
+ cmd="${cmd%\'}"
1081
+ cmd="${cmd#\'}"
1082
+
1083
+ case "$cmd" in
1084
+ "~/"*) cmd="$HOME/${cmd#\~/}" ;;
1085
+ "\$HOME/"*) cmd="$HOME/${cmd#\$HOME/}" ;;
1086
+ "\${HOME}/"*) cmd="$HOME/${cmd#\$\{HOME\}/}" ;;
1087
+ esac
1088
+
1089
+ if [ -L "$cmd" ]; then
1090
+ local link_target link_dir
1091
+ link_target=$(readlink "$cmd" 2>/dev/null || true)
1092
+ if [ -n "$link_target" ]; then
1093
+ case "$link_target" in
1094
+ /*) cmd="$link_target" ;;
1095
+ *)
1096
+ link_dir=$(cd "$(dirname "$cmd")" && pwd -P) || return 1
1097
+ cmd="$link_dir/$link_target"
1098
+ ;;
1099
+ esac
1100
+ fi
1101
+ fi
1102
+
1103
+ [ -f "$cmd" ] || return 1
1104
+ printf '%s\n' "$cmd"
1105
+ }
1106
+
1107
+ eagle_statusline_script_uses_input() {
1108
+ local sl_file="${1:-}"
1109
+ [ -f "$sl_file" ] || return 1
1110
+ grep -Eq 'eagle_project_from_statusline_input|eagle_mem_statusline.*(\$\{input:-\}|\$input)' "$sl_file"
1111
+ }
1112
+
1113
+ eagle_patch_statusline_script() {
1114
+ local sl_file="${1:-}"
1115
+ [ -f "$sl_file" ] || return 1
1116
+ command -v perl >/dev/null 2>&1 || return 1
1117
+
1118
+ if [ -L "$sl_file" ]; then
1119
+ local link_target link_dir
1120
+ link_target=$(readlink "$sl_file" 2>/dev/null || true)
1121
+ if [ -n "$link_target" ]; then
1122
+ case "$link_target" in
1123
+ /*) sl_file="$link_target" ;;
1124
+ *)
1125
+ link_dir=$(cd "$(dirname "$sl_file")" && pwd -P) || return 1
1126
+ sl_file="$link_dir/$link_target"
1127
+ ;;
1128
+ esac
1129
+ fi
1130
+ fi
1131
+
1132
+ local tmp backup mode
1133
+ tmp=$(mktemp) || return 1
1134
+ cp "$sl_file" "$tmp" || { rm -f "$tmp"; return 1; }
1135
+
1136
+ if ! grep -Eq 'eagle_mem_statusline|eagle_project_from_cwd|claude_memories|agent_memories|\.eagle-mem/scripts/statusline-em' "$tmp" 2>/dev/null; then
1137
+ rm -f "$tmp"
1138
+ return 1
1139
+ fi
1140
+
1141
+ perl -0pi -e '
1142
+ s/(project_dir=\$\(echo "\$input" \| jq -r \x27)\.workspace\.current_dir \/\/ \.cwd/$1.workspace.project_dir \/\/ .workspace.current_dir \/\/ .cwd/g;
1143
+ s/(project_dir=\$\(echo "\$input" \| jq -r ")\.workspace\.current_dir \/\/ \.cwd/$1.workspace.project_dir \/\/ .workspace.current_dir \/\/ .cwd/g;
1144
+ s/eagle_mem_statusline "\$project_dir" "\$session_id" "\$\{input\}"/eagle_mem_statusline "\$project_dir" "\$session_id" "\${input:-}"/g;
1145
+ s/eagle_mem_statusline "\$project_dir" "\$session_id"(?=[\)\n;])/eagle_mem_statusline "\$project_dir" "\$session_id" "\${input:-}"/g;
1146
+ s/eagle_mem_statusline "\$project_dir"(?=[\)\n;])/eagle_mem_statusline "\$project_dir" "\${session_id:-}" "\${input:-}"/g;
1147
+ s/eagle_project_from_cwd "\$cwd"/eagle_project_from_statusline_input "\${input:-}" "\${project_dir:-}" "\${cwd:-}" "\${session_id:-}"/g;
1148
+ s/eagle_project_from_cwd "\$project_dir"/eagle_project_from_statusline_input "\${input:-}" "\${project_dir:-}" "\${cwd:-}" "\${session_id:-}"/g;
1149
+ s/eagle_project_from_cwd "\${project_dir:-\$cwd}"/eagle_project_from_statusline_input "\${input:-}" "\${project_dir:-}" "\${cwd:-}" "\${session_id:-}"/g;
1150
+ s/eagle_project_from_cwd "\${project_dir:-\$(pwd)}"/eagle_project_from_statusline_input "\${input:-}" "\${project_dir:-}" "\${cwd:-}" "\${session_id:-}"/g;
1151
+ s/eagle_project_from_cwd "\${eagle_mem_project_dir:-\${project_dir:-\$cwd}}"/eagle_project_from_statusline_input "\${input:-}" "\${project_dir:-}" "\${cwd:-}" "\${session_id:-}"/g;
1152
+ s/eagle_project_from_cwd\("\$cwd"\)/eagle_project_from_statusline_input "\${input:-}" "\${project_dir:-}" "\${cwd:-}" "\${session_id:-}"/g;
1153
+ s/eagle_project_from_cwd\("\$project_dir"\)/eagle_project_from_statusline_input "\${input:-}" "\${project_dir:-}" "\${cwd:-}" "\${session_id:-}"/g;
1154
+ s/eagle_project_from_cwd\("\${project_dir:-\$cwd}"\)/eagle_project_from_statusline_input "\${input:-}" "\${project_dir:-}" "\${cwd:-}" "\${session_id:-}"/g;
1155
+ s/eagle_project_from_cwd\("\${project_dir:-\$(pwd)}"\)/eagle_project_from_statusline_input "\${input:-}" "\${project_dir:-}" "\${cwd:-}" "\${session_id:-}"/g;
1156
+ s/eagle_project_from_cwd\("\${eagle_mem_project_dir:-\${project_dir:-\$cwd}}"\)/eagle_project_from_statusline_input "\${input:-}" "\${project_dir:-}" "\${cwd:-}" "\${session_id:-}"/g;
1157
+ s/FROM claude_memories WHERE project/FROM agent_memories WHERE project/g;
1158
+ ' "$tmp" || { rm -f "$tmp"; return 1; }
1159
+
1160
+ if cmp -s "$sl_file" "$tmp"; then
1161
+ rm -f "$tmp"
1162
+ return 1
1163
+ fi
1164
+
1165
+ backup="${sl_file}.eagle-mem.bak-$(date -u +%Y%m%dT%H%M%SZ)"
1166
+ cp "$sl_file" "$backup" 2>/dev/null || true
1167
+ mode=$(stat -f %Lp "$sl_file" 2>/dev/null || stat -c %a "$sl_file" 2>/dev/null || echo "")
1168
+ if ! mv "$tmp" "$sl_file"; then
1169
+ rm -f "$tmp"
1170
+ return 1
1171
+ fi
1172
+ [ -n "$mode" ] && chmod "$mode" "$sl_file" 2>/dev/null || chmod +x "$sl_file" 2>/dev/null || true
1173
+ return 0
1174
+ }
1175
+
816
1176
  _eagle_claude_md_section() {
817
1177
  cat << 'EAGLE_MD'
818
1178
 
package/lib/db-mirrors.sh CHANGED
@@ -63,22 +63,32 @@ eagle_capture_agent_memory() {
63
63
  agent_sql=$(eagle_sql_escape "$agent")
64
64
 
65
65
  eagle_db_pipe <<SQL
66
- INSERT INTO agent_memories (project, file_path, memory_name, description, memory_type, content, content_hash, origin_session_id, origin_agent)
67
- VALUES ('$proj_sql', '$fp_sql', '$name_sql', '$desc_sql', '$type_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql')
68
- ON CONFLICT(file_path) DO UPDATE SET
69
- memory_name = excluded.memory_name,
70
- description = excluded.description,
71
- memory_type = excluded.memory_type,
72
- content = excluded.content,
73
- content_hash = excluded.content_hash,
74
- origin_session_id = COALESCE(NULLIF(excluded.origin_session_id, ''), agent_memories.origin_session_id),
75
- origin_agent = COALESCE(NULLIF(excluded.origin_agent, ''), agent_memories.origin_agent),
76
- project = CASE WHEN excluded.project != '' THEN excluded.project ELSE agent_memories.project END,
77
- updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
78
- WHERE agent_memories.content_hash != excluded.content_hash
79
- OR (excluded.project != '' AND agent_memories.project != excluded.project)
80
- OR (excluded.origin_session_id != '' AND agent_memories.origin_session_id != excluded.origin_session_id)
81
- OR (excluded.origin_agent != '' AND agent_memories.origin_agent != excluded.origin_agent);
66
+ INSERT OR IGNORE INTO agent_memories (project, file_path, memory_name, description, memory_type, content, content_hash, origin_session_id, origin_agent)
67
+ VALUES ('$proj_sql', '$fp_sql', '$name_sql', '$desc_sql', '$type_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql');
68
+
69
+ UPDATE agent_memories
70
+ SET memory_name = '$name_sql',
71
+ description = '$desc_sql',
72
+ memory_type = '$type_sql',
73
+ content = '$content_sql',
74
+ content_hash = '$hash_sql',
75
+ origin_session_id = COALESCE(NULLIF('$origin_sql', ''), origin_session_id),
76
+ origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
77
+ project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END,
78
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
79
+ WHERE file_path = '$fp_sql'
80
+ AND content_hash != '$hash_sql';
81
+
82
+ UPDATE agent_memories
83
+ SET origin_session_id = COALESCE(NULLIF('$origin_sql', ''), origin_session_id),
84
+ origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
85
+ project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END,
86
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
87
+ WHERE file_path = '$fp_sql'
88
+ AND content_hash = '$hash_sql'
89
+ AND (('$proj_sql' != '' AND project != '$proj_sql')
90
+ OR ('$origin_sql' != '' AND origin_session_id != '$origin_sql')
91
+ OR ('$agent_sql' != '' AND origin_agent != '$agent_sql'));
82
92
  SQL
83
93
  }
84
94
 
@@ -151,20 +161,30 @@ eagle_capture_agent_plan() {
151
161
  agent_sql=$(eagle_sql_escape "$agent")
152
162
 
153
163
  eagle_db_pipe <<SQL
154
- INSERT INTO agent_plans (project, file_path, title, content, content_hash, origin_session_id, origin_agent)
155
- VALUES ('$proj_sql', '$fp_sql', '$title_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql')
156
- ON CONFLICT(file_path) DO UPDATE SET
157
- title = excluded.title,
158
- content = excluded.content,
159
- content_hash = excluded.content_hash,
160
- origin_session_id = COALESCE(NULLIF(excluded.origin_session_id, ''), agent_plans.origin_session_id),
161
- origin_agent = COALESCE(NULLIF(excluded.origin_agent, ''), agent_plans.origin_agent),
162
- project = CASE WHEN excluded.project != '' THEN excluded.project ELSE agent_plans.project END,
163
- updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
164
- WHERE agent_plans.content_hash != excluded.content_hash
165
- OR (excluded.project != '' AND agent_plans.project != excluded.project)
166
- OR (excluded.origin_session_id != '' AND agent_plans.origin_session_id != excluded.origin_session_id)
167
- OR (excluded.origin_agent != '' AND agent_plans.origin_agent != excluded.origin_agent);
164
+ INSERT OR IGNORE INTO agent_plans (project, file_path, title, content, content_hash, origin_session_id, origin_agent)
165
+ VALUES ('$proj_sql', '$fp_sql', '$title_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql');
166
+
167
+ UPDATE agent_plans
168
+ SET title = '$title_sql',
169
+ content = '$content_sql',
170
+ content_hash = '$hash_sql',
171
+ origin_session_id = COALESCE(NULLIF('$origin_sql', ''), origin_session_id),
172
+ origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
173
+ project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END,
174
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
175
+ WHERE file_path = '$fp_sql'
176
+ AND content_hash != '$hash_sql';
177
+
178
+ UPDATE agent_plans
179
+ SET origin_session_id = COALESCE(NULLIF('$origin_sql', ''), origin_session_id),
180
+ origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
181
+ project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END,
182
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
183
+ WHERE file_path = '$fp_sql'
184
+ AND content_hash = '$hash_sql'
185
+ AND (('$proj_sql' != '' AND project != '$proj_sql')
186
+ OR ('$origin_sql' != '' AND origin_session_id != '$origin_sql')
187
+ OR ('$agent_sql' != '' AND origin_agent != '$agent_sql'));
168
188
  SQL
169
189
  }
170
190
 
@@ -252,22 +272,31 @@ eagle_capture_agent_task() {
252
272
  agent_sql=$(eagle_sql_escape "$agent")
253
273
 
254
274
  eagle_db_pipe <<SQL
255
- INSERT INTO agent_tasks (project, source_session_id, source_task_id, file_path, subject, description, active_form, status, blocks, blocked_by, content_hash, origin_agent)
256
- VALUES ('$proj_sql', '$sid_sql', '$tid_sql', '$fp_sql', '$subj_sql', '$desc_sql', '$af_sql', '$status_sql', '$blocks_sql', '$bb_sql', '$hash_sql', '$agent_sql')
257
- ON CONFLICT(file_path) DO UPDATE SET
258
- subject = excluded.subject,
259
- description = excluded.description,
260
- active_form = excluded.active_form,
261
- status = excluded.status,
262
- blocks = excluded.blocks,
263
- blocked_by = excluded.blocked_by,
264
- content_hash = excluded.content_hash,
265
- origin_agent = excluded.origin_agent,
266
- project = CASE WHEN excluded.project != '' THEN excluded.project ELSE agent_tasks.project END,
267
- updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
268
- WHERE agent_tasks.content_hash != excluded.content_hash
269
- OR (excluded.project != '' AND agent_tasks.project != excluded.project)
270
- OR (excluded.origin_agent != '' AND agent_tasks.origin_agent != excluded.origin_agent);
275
+ INSERT OR IGNORE INTO agent_tasks (project, source_session_id, source_task_id, file_path, subject, description, active_form, status, blocks, blocked_by, content_hash, origin_agent)
276
+ VALUES ('$proj_sql', '$sid_sql', '$tid_sql', '$fp_sql', '$subj_sql', '$desc_sql', '$af_sql', '$status_sql', '$blocks_sql', '$bb_sql', '$hash_sql', '$agent_sql');
277
+
278
+ UPDATE agent_tasks
279
+ SET subject = '$subj_sql',
280
+ description = '$desc_sql',
281
+ active_form = '$af_sql',
282
+ status = '$status_sql',
283
+ blocks = '$blocks_sql',
284
+ blocked_by = '$bb_sql',
285
+ content_hash = '$hash_sql',
286
+ origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
287
+ project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END,
288
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
289
+ WHERE file_path = '$fp_sql'
290
+ AND content_hash != '$hash_sql';
291
+
292
+ UPDATE agent_tasks
293
+ SET origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
294
+ project = CASE WHEN '$proj_sql' != '' THEN '$proj_sql' ELSE project END,
295
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
296
+ WHERE file_path = '$fp_sql'
297
+ AND content_hash = '$hash_sql'
298
+ AND (('$proj_sql' != '' AND project != '$proj_sql')
299
+ OR ('$agent_sql' != '' AND origin_agent != '$agent_sql'));
271
300
  SQL
272
301
  }
273
302
 
@@ -6,12 +6,16 @@
6
6
  _EAGLE_DB_SESSIONS_LOADED=1
7
7
 
8
8
  eagle_upsert_session() {
9
- local session_id; session_id=$(eagle_sql_escape "$1")
10
- local project; project=$(eagle_sql_escape "$2")
9
+ local session_id_raw="${1:-}"
10
+ local project_raw="${2:-}"
11
+ local session_id; session_id=$(eagle_sql_escape "$session_id_raw")
12
+ local project; project=$(eagle_sql_escape "$project_raw")
11
13
  local cwd; cwd=$(eagle_sql_escape "${3:-}")
12
14
  local model; model=$(eagle_sql_escape "${4:-}")
13
15
  local source; source=$(eagle_sql_escape "${5:-}")
14
16
  local agent; agent=$(eagle_sql_escape "${6:-$(eagle_agent_source)}")
17
+ local prior_project
18
+ prior_project=$(eagle_db "SELECT project FROM sessions WHERE id = '$session_id' LIMIT 1;" 2>/dev/null || true)
15
19
 
16
20
  eagle_db "INSERT INTO sessions (id, project, cwd, model, source, agent, last_activity_at)
17
21
  VALUES ('$session_id', '$project', '$cwd', '$model', '$source', '$agent', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
@@ -22,6 +26,127 @@ eagle_upsert_session() {
22
26
  agent = COALESCE(NULLIF(excluded.agent, ''), sessions.agent),
23
27
  status = 'active',
24
28
  last_activity_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');"
29
+
30
+ local needs_project_repair=0
31
+ if [ -n "$project_raw" ]; then
32
+ if [ "$prior_project" != "$project_raw" ]; then
33
+ needs_project_repair=1
34
+ else
35
+ local stale_child
36
+ stale_child=$(eagle_db "SELECT 1 WHERE
37
+ EXISTS (SELECT 1 FROM summaries WHERE session_id = '$session_id' AND project != '$project')
38
+ OR EXISTS (SELECT 1 FROM observations WHERE session_id = '$session_id' AND project != '$project')
39
+ OR EXISTS (SELECT 1 FROM agent_tasks WHERE source_session_id = '$session_id' AND project != '$project')
40
+ OR EXISTS (SELECT 1 FROM agent_memories WHERE origin_session_id = '$session_id' AND project != '$project')
41
+ OR EXISTS (SELECT 1 FROM agent_plans WHERE origin_session_id = '$session_id' AND project != '$project')
42
+ OR EXISTS (SELECT 1 FROM pending_feature_verifications WHERE source_session_id = '$session_id' AND project != '$project')
43
+ OR EXISTS (
44
+ SELECT 1
45
+ FROM pending_feature_verifications p
46
+ JOIN features f ON f.id = p.feature_id
47
+ WHERE p.source_session_id = '$session_id'
48
+ AND f.project != '$project'
49
+ )
50
+ LIMIT 1;" 2>/dev/null || true)
51
+ [ "$stale_child" = "1" ] && needs_project_repair=1
52
+ fi
53
+ fi
54
+
55
+ if [ "$needs_project_repair" = "1" ]; then
56
+ eagle_db_pipe <<SQL >/dev/null 2>&1
57
+ BEGIN;
58
+ UPDATE summaries SET project = '$project' WHERE session_id = '$session_id' AND project != '$project';
59
+ UPDATE observations SET project = '$project' WHERE session_id = '$session_id' AND project != '$project';
60
+ UPDATE agent_tasks SET project = '$project' WHERE source_session_id = '$session_id' AND project != '$project';
61
+ UPDATE agent_memories SET project = '$project' WHERE origin_session_id = '$session_id' AND project != '$project';
62
+ UPDATE agent_plans SET project = '$project' WHERE origin_session_id = '$session_id' AND project != '$project';
63
+ CREATE TEMP TABLE IF NOT EXISTS eagle_feature_repair_map (
64
+ old_feature_id INTEGER PRIMARY KEY,
65
+ new_feature_id INTEGER NOT NULL
66
+ );
67
+ DELETE FROM eagle_feature_repair_map;
68
+ INSERT OR IGNORE INTO features (project, name, description, status, last_verified_at, last_verified_notes)
69
+ SELECT '$project', f_old.name, f_old.description, f_old.status, f_old.last_verified_at, f_old.last_verified_notes
70
+ FROM pending_feature_verifications p
71
+ JOIN features f_old ON f_old.id = p.feature_id
72
+ WHERE p.source_session_id = '$session_id'
73
+ AND (p.project != '$project' OR f_old.project != '$project')
74
+ GROUP BY f_old.name;
75
+ INSERT OR REPLACE INTO eagle_feature_repair_map (old_feature_id, new_feature_id)
76
+ SELECT DISTINCT f_old.id, f_new.id
77
+ FROM pending_feature_verifications p
78
+ JOIN features f_old ON f_old.id = p.feature_id
79
+ JOIN features f_new ON f_new.project = '$project' AND f_new.name = f_old.name
80
+ WHERE p.source_session_id = '$session_id'
81
+ AND (p.project != '$project' OR f_old.project != '$project');
82
+ INSERT OR IGNORE INTO feature_files (feature_id, file_path, role)
83
+ SELECT f_new.id, ff.file_path, ff.role
84
+ FROM pending_feature_verifications p
85
+ JOIN features f_old ON f_old.id = p.feature_id
86
+ JOIN eagle_feature_repair_map m ON m.old_feature_id = f_old.id
87
+ JOIN features f_new ON f_new.id = m.new_feature_id
88
+ JOIN feature_files ff ON ff.feature_id = f_old.id
89
+ WHERE p.source_session_id = '$session_id'
90
+ AND (p.project != '$project' OR f_old.project != '$project');
91
+ INSERT OR IGNORE INTO feature_dependencies (feature_id, kind, target, name, notes)
92
+ SELECT f_new.id, fd.kind, fd.target, fd.name, fd.notes
93
+ FROM pending_feature_verifications p
94
+ JOIN features f_old ON f_old.id = p.feature_id
95
+ JOIN eagle_feature_repair_map m ON m.old_feature_id = f_old.id
96
+ JOIN features f_new ON f_new.id = m.new_feature_id
97
+ JOIN feature_dependencies fd ON fd.feature_id = f_old.id
98
+ WHERE p.source_session_id = '$session_id'
99
+ AND (p.project != '$project' OR f_old.project != '$project');
100
+ INSERT OR IGNORE INTO feature_smoke_tests (feature_id, command, description)
101
+ SELECT f_new.id, fst.command, fst.description
102
+ FROM pending_feature_verifications p
103
+ JOIN features f_old ON f_old.id = p.feature_id
104
+ JOIN eagle_feature_repair_map m ON m.old_feature_id = f_old.id
105
+ JOIN features f_new ON f_new.id = m.new_feature_id
106
+ JOIN feature_smoke_tests fst ON fst.feature_id = f_old.id
107
+ WHERE p.source_session_id = '$session_id'
108
+ AND (p.project != '$project' OR f_old.project != '$project');
109
+ DELETE FROM pending_feature_verifications
110
+ WHERE source_session_id = '$session_id'
111
+ AND status = 'pending'
112
+ AND id NOT IN (
113
+ SELECT MIN(p.id)
114
+ FROM pending_feature_verifications p
115
+ LEFT JOIN eagle_feature_repair_map m ON m.old_feature_id = p.feature_id
116
+ WHERE p.source_session_id = '$session_id'
117
+ AND p.status = 'pending'
118
+ GROUP BY
119
+ CASE WHEN m.new_feature_id IS NOT NULL THEN '$project' ELSE p.project END,
120
+ COALESCE(m.new_feature_id, p.feature_id),
121
+ p.file_path
122
+ )
123
+ AND EXISTS (
124
+ SELECT 1
125
+ FROM eagle_feature_repair_map m
126
+ WHERE m.old_feature_id = pending_feature_verifications.feature_id
127
+ );
128
+ UPDATE pending_feature_verifications
129
+ SET feature_id = COALESCE((SELECT new_feature_id FROM eagle_feature_repair_map WHERE old_feature_id = feature_id), feature_id)
130
+ WHERE source_session_id = '$session_id'
131
+ AND EXISTS (SELECT 1 FROM eagle_feature_repair_map WHERE old_feature_id = pending_feature_verifications.feature_id);
132
+ DELETE FROM pending_feature_verifications
133
+ WHERE source_session_id = '$session_id'
134
+ AND project != '$project'
135
+ AND status = 'pending'
136
+ AND EXISTS (
137
+ SELECT 1
138
+ FROM pending_feature_verifications p2
139
+ WHERE p2.project = '$project'
140
+ AND p2.feature_id = pending_feature_verifications.feature_id
141
+ AND p2.file_path = pending_feature_verifications.file_path
142
+ AND p2.status = 'pending'
143
+ AND p2.id != pending_feature_verifications.id
144
+ );
145
+ UPDATE pending_feature_verifications SET project = '$project' WHERE source_session_id = '$session_id' AND project != '$project';
146
+ UPDATE sessions SET project = '$project' WHERE id = '$session_id' AND project != '$project';
147
+ COMMIT;
148
+ SQL
149
+ fi
25
150
  }
26
151
 
27
152
  eagle_end_session() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eagle-mem",
3
- "version": "4.9.2",
3
+ "version": "4.9.4",
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"
@@ -299,6 +299,7 @@ fi
299
299
  if [ "$claude_found" = true ]; then
300
300
  EM_STATUSLINE="$EAGLE_MEM_DIR/scripts/statusline-em.sh"
301
301
  existing_sl=$(jq -r '.statusLine.command // empty' "$SETTINGS" 2>/dev/null)
302
+ existing_sl_file=$(eagle_statusline_script_from_command "$existing_sl" 2>/dev/null || true)
302
303
 
303
304
  if [ -z "$existing_sl" ]; then
304
305
  # No statusline configured — set up a minimal one that shows Eagle Mem
@@ -309,29 +310,37 @@ input=$(cat)
309
310
  project_dir=$(echo "$input" | jq -r '.workspace.project_dir // .workspace.current_dir // .cwd // ""' 2>/dev/null)
310
311
  session_id=$(echo "$input" | jq -r '.session_id // .session.id // ""' 2>/dev/null)
311
312
  source "$HOME/.eagle-mem/scripts/statusline-em.sh"
312
- eagle_mem_statusline "$project_dir" "$session_id"
313
+ eagle_mem_statusline "$project_dir" "$session_id" "$input"
313
314
  WRAPPER
314
315
  chmod +x "$wrapper"
315
316
  tmp=$(mktemp)
316
317
  jq --arg cmd "sh $wrapper" '.statusLine = {"type": "command", "command": $cmd, "refreshInterval": 30}' "$SETTINGS" > "$tmp" && mv "$tmp" "$SETTINGS"
317
318
  eagle_ok "Statusline ${DIM}(new — Eagle Mem indicator)${RESET}"
318
- elif echo "$existing_sl" | grep -q "eagle-mem"; then
319
- eagle_ok "Statusline ${DIM}(already has Eagle Mem)${RESET}"
320
319
  else
321
- # Existing statusline — check if it's a .sh file we can patch
322
- sl_file=$(echo "$existing_sl" | sed 's/^sh //')
323
- if [ -f "$sl_file" ] && ! grep -q "eagle-mem" "$sl_file"; then
324
- eagle_dim " Statusline detected: $sl_file"
325
- eagle_dim " To add Eagle Mem, add this snippet before your ASSEMBLE section:"
326
- echo ""
327
- eagle_dim " # ── Eagle Mem ──"
328
- eagle_dim " em_section=\"\""
329
- eagle_dim " if [ -f \"\$HOME/.eagle-mem/scripts/statusline-em.sh\" ]; then"
330
- eagle_dim " source \"\$HOME/.eagle-mem/scripts/statusline-em.sh\""
331
- eagle_dim " em_section=\$(eagle_mem_statusline \"\$project_dir\" \"\$session_id\")"
332
- eagle_dim " fi"
333
- echo ""
334
- eagle_ok "Statusline ${DIM}(manual patch needed — instructions above)${RESET}"
320
+ # Existing statusline — if it points at a shell script, inspect the
321
+ # target file. Custom HUD commands often do not include "eagle-mem" in
322
+ # the command string even when the script contains an embedded block.
323
+ sl_file="$existing_sl_file"
324
+ if [ -n "$sl_file" ] && [ -f "$sl_file" ]; then
325
+ if eagle_patch_statusline_script "$sl_file"; then
326
+ eagle_ok "Statusline ${DIM}(patched existing Eagle Mem block)${RESET}"
327
+ elif eagle_statusline_script_uses_input "$sl_file"; then
328
+ eagle_ok "Statusline ${DIM}(already has Eagle Mem)${RESET}"
329
+ else
330
+ eagle_dim " Statusline detected: $sl_file"
331
+ eagle_dim " To add Eagle Mem, add this snippet before your ASSEMBLE section:"
332
+ echo ""
333
+ eagle_dim " # ── Eagle Mem ──"
334
+ eagle_dim " em_section=\"\""
335
+ eagle_dim " if [ -f \"\$HOME/.eagle-mem/scripts/statusline-em.sh\" ]; then"
336
+ eagle_dim " source \"\$HOME/.eagle-mem/scripts/statusline-em.sh\""
337
+ eagle_dim " em_section=\$(eagle_mem_statusline \"\$project_dir\" \"\$session_id\" \"\$input\")"
338
+ eagle_dim " fi"
339
+ echo ""
340
+ eagle_ok "Statusline ${DIM}(manual patch needed — instructions above)${RESET}"
341
+ fi
342
+ elif echo "$existing_sl" | grep -q "eagle-mem"; then
343
+ eagle_ok "Statusline ${DIM}(already has Eagle Mem)${RESET}"
335
344
  else
336
345
  eagle_ok "Statusline ${DIM}(existing — cannot auto-patch; add Eagle Mem manually)${RESET}"
337
346
  fi
@@ -6,6 +6,7 @@
6
6
  eagle_mem_statusline() {
7
7
  local project_dir="${1:-}"
8
8
  local session_id="${2:-}"
9
+ local statusline_input="${3:-}"
9
10
  local em_db="$HOME/.eagle-mem/memory.db"
10
11
  [ -f "$em_db" ] || return
11
12
 
@@ -14,7 +15,7 @@ eagle_mem_statusline() {
14
15
 
15
16
  local proj
16
17
  [ -z "$project_dir" ] && project_dir="$(pwd)"
17
- proj=$(eagle_project_from_cwd "$project_dir")
18
+ proj=$(eagle_project_from_statusline_input "$statusline_input" "$project_dir" "$project_dir" "$session_id")
18
19
  [ -z "$proj" ] && return
19
20
 
20
21
  proj=$(eagle_sql_escape "$proj")
@@ -57,5 +58,5 @@ if [ "${BASH_SOURCE[0]}" = "$0" ]; then
57
58
  fi
58
59
  project_dir=$(echo "$input" | jq -r '.workspace.project_dir // .workspace.current_dir // .cwd // empty' 2>/dev/null)
59
60
  session_id=$(echo "$input" | jq -r '.session_id // .session.id // empty' 2>/dev/null)
60
- eagle_mem_statusline "${project_dir:-$(pwd)}" "$session_id"
61
+ eagle_mem_statusline "${project_dir:-$(pwd)}" "$session_id" "$input"
61
62
  fi
package/scripts/update.sh CHANGED
@@ -153,12 +153,22 @@ input=$(cat)
153
153
  project_dir=$(echo "$input" | jq -r '.workspace.project_dir // .workspace.current_dir // .cwd // ""' 2>/dev/null)
154
154
  session_id=$(echo "$input" | jq -r '.session_id // .session.id // ""' 2>/dev/null)
155
155
  source "$HOME/.eagle-mem/scripts/statusline-em.sh"
156
- eagle_mem_statusline "$project_dir" "$session_id"
156
+ eagle_mem_statusline "$project_dir" "$session_id" "$input"
157
157
  WRAPPER
158
158
  chmod +x "$statusline_wrapper"
159
159
  eagle_ok "Statusline wrapper updated"
160
160
  fi
161
161
 
162
+ if [ "$claude_found" = true ] && [ -f "$SETTINGS" ] && command -v jq >/dev/null 2>&1; then
163
+ existing_sl=$(jq -r '.statusLine.command // empty' "$SETTINGS" 2>/dev/null)
164
+ existing_sl_file=$(eagle_statusline_script_from_command "$existing_sl" 2>/dev/null || true)
165
+ if eagle_patch_statusline_script "$existing_sl_file"; then
166
+ eagle_ok "Statusline custom Eagle Mem block patched"
167
+ elif [ -n "$existing_sl_file" ] && [ -f "$existing_sl_file" ] && grep -q "eagle-mem" "$existing_sl_file" && ! eagle_statusline_script_uses_input "$existing_sl_file"; then
168
+ eagle_warn "Statusline custom Eagle Mem block needs manual input-aware update"
169
+ fi
170
+ fi
171
+
162
172
  # ─── Backfill project names ───────────────────────────────
163
173
 
164
174
  backfilled=$(eagle_backfill_projects 2>/dev/null)