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 +8 -0
- package/db/034_safe_feature_rekey_trigger.sql +17 -0
- package/lib/common.sh +372 -12
- package/lib/db-mirrors.sh +75 -46
- package/lib/db-sessions.sh +127 -2
- package/package.json +1 -1
- package/scripts/install.sh +26 -17
- package/scripts/statusline-em.sh +3 -2
- package/scripts/update.sh +11 -1
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
|
|
214
|
-
|
|
215
|
-
[ -n "$
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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=$(
|
|
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=$(
|
|
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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
|
package/lib/db-sessions.sh
CHANGED
|
@@ -6,12 +6,16 @@
|
|
|
6
6
|
_EAGLE_DB_SESSIONS_LOADED=1
|
|
7
7
|
|
|
8
8
|
eagle_upsert_session() {
|
|
9
|
-
local
|
|
10
|
-
local
|
|
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
package/scripts/install.sh
CHANGED
|
@@ -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 —
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
package/scripts/statusline-em.sh
CHANGED
|
@@ -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=$(
|
|
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)
|