eagle-mem 4.6.2 → 4.7.1

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.
Files changed (40) hide show
  1. package/README.md +49 -15
  2. package/db/023_guardrails.sql +3 -2
  3. package/db/024_guardrails_unique.sql +46 -0
  4. package/db/025_pending_feature_verifications.sql +30 -0
  5. package/db/026_agent_source.sql +18 -0
  6. package/db/027_feature_verification_fingerprints.sql +9 -0
  7. package/db/028_agent_artifact_tables.sql +124 -0
  8. package/hooks/post-tool-use.sh +42 -13
  9. package/hooks/pre-tool-use.sh +107 -14
  10. package/hooks/session-end.sh +3 -1
  11. package/hooks/session-start.sh +64 -15
  12. package/hooks/stop.sh +115 -21
  13. package/hooks/user-prompt-submit.sh +14 -5
  14. package/lib/codex-hooks.sh +194 -0
  15. package/lib/common.sh +345 -0
  16. package/lib/db-backfill.sh +3 -3
  17. package/lib/db-features.sh +222 -0
  18. package/lib/db-guardrails.sh +2 -1
  19. package/lib/db-mirrors.sh +79 -43
  20. package/lib/db-observations.sh +3 -2
  21. package/lib/db-sessions.sh +11 -7
  22. package/lib/db-summaries.sh +9 -6
  23. package/lib/hooks-posttool.sh +8 -6
  24. package/lib/provider.sh +190 -4
  25. package/package.json +7 -3
  26. package/scripts/config.sh +2 -0
  27. package/scripts/feature.sh +70 -2
  28. package/scripts/guard.sh +4 -1
  29. package/scripts/health.sh +5 -1
  30. package/scripts/help.sh +13 -8
  31. package/scripts/install.sh +130 -76
  32. package/scripts/memories.sh +71 -45
  33. package/scripts/refresh.sh +3 -3
  34. package/scripts/search.sh +57 -47
  35. package/scripts/statusline-em.sh +1 -1
  36. package/scripts/tasks.sh +186 -15
  37. package/scripts/uninstall.sh +7 -0
  38. package/scripts/update.sh +51 -7
  39. package/skills/eagle-mem-memories/SKILL.md +13 -13
  40. package/skills/eagle-mem-tasks/SKILL.md +21 -15
package/scripts/search.sh CHANGED
@@ -26,7 +26,7 @@ show_help() {
26
26
  echo -e " eagle-mem search ${CYAN}--files${RESET} ${DIM}# frequently modified files${RESET}"
27
27
  echo -e " eagle-mem search ${CYAN}--stats${RESET} ${DIM}# project statistics${RESET}"
28
28
  echo -e " eagle-mem search ${CYAN}--overview${RESET} ${DIM}# project overview${RESET}"
29
- echo -e " eagle-mem search ${CYAN}--memories${RESET} ${DIM}# mirrored Claude memories${RESET}"
29
+ echo -e " eagle-mem search ${CYAN}--memories${RESET} ${DIM}# mirrored agent memories${RESET}"
30
30
  echo -e " eagle-mem search ${CYAN}--tasks${RESET} ${DIM}# in-flight tasks${RESET}"
31
31
  echo ""
32
32
  echo -e " ${BOLD}Options:${RESET}"
@@ -93,7 +93,7 @@ search_keyword() {
93
93
  fi
94
94
 
95
95
  if [ "$json_output" = true ]; then
96
- eagle_db_json "SELECT s.id, s.project, s.request, s.completed, s.learned, s.created_at
96
+ eagle_db_json "SELECT s.id, s.project, s.agent, s.request, s.completed, s.learned, s.created_at
97
97
  FROM summaries s
98
98
  JOIN summaries_fts f ON f.rowid = s.id
99
99
  WHERE summaries_fts MATCH '$q'
@@ -104,7 +104,7 @@ search_keyword() {
104
104
  fi
105
105
 
106
106
  local results
107
- results=$(eagle_db "SELECT s.id, s.project, s.request, s.completed, s.learned, s.created_at
107
+ results=$(eagle_db "SELECT s.id, s.project, s.agent, s.request, s.completed, s.learned, s.created_at
108
108
  FROM summaries s
109
109
  JOIN summaries_fts f ON f.rowid = s.id
110
110
  WHERE summaries_fts MATCH '$q'
@@ -118,12 +118,13 @@ search_keyword() {
118
118
  fi
119
119
 
120
120
  echo ""
121
- while IFS='|' read -r sid sproj req completed learned created_at; do
121
+ while IFS='|' read -r sid sproj sagent req completed learned created_at; do
122
122
  [ -z "$sid" ] && continue
123
123
  echo -e " ${BOLD}#$sid${RESET} ${DIM}$created_at${RESET}"
124
124
  if [ "$cross_project" = true ]; then
125
125
  echo -e " ${DIM}project:${RESET} $sproj"
126
126
  fi
127
+ echo -e " ${DIM}source:${RESET} $(eagle_agent_label "$sagent")"
127
128
  [ -n "$req" ] && echo -e " ${CYAN}Request:${RESET} $req"
128
129
  [ -n "$completed" ] && echo -e " ${GREEN}Done:${RESET} $completed"
129
130
  [ -n "$learned" ] && echo -e " ${YELLOW}Learned:${RESET} $learned"
@@ -137,7 +138,7 @@ search_timeline() {
137
138
  local p; p=$(eagle_sql_escape "$project")
138
139
 
139
140
  if [ "$json_output" = true ]; then
140
- eagle_db_json "SELECT s.id, s.request, s.completed, s.learned, s.next_steps, s.created_at
141
+ eagle_db_json "SELECT s.id, s.agent, s.request, s.completed, s.learned, s.next_steps, s.created_at
141
142
  FROM summaries s
142
143
  WHERE s.project = '$p'
143
144
  ORDER BY s.created_at DESC
@@ -146,7 +147,7 @@ search_timeline() {
146
147
  fi
147
148
 
148
149
  local results
149
- results=$(eagle_db "SELECT s.id, s.request, s.completed, s.learned, s.next_steps, s.created_at
150
+ results=$(eagle_db "SELECT s.id, s.agent, s.request, s.completed, s.learned, s.next_steps, s.created_at
150
151
  FROM summaries s
151
152
  WHERE s.project = '$p'
152
153
  ORDER BY s.created_at DESC
@@ -162,9 +163,10 @@ search_timeline() {
162
163
  echo -e " ${DIM}─────────────────────────────────────${RESET}"
163
164
  echo ""
164
165
 
165
- while IFS='|' read -r sid req completed learned next_steps created_at; do
166
+ while IFS='|' read -r sid sagent req completed learned next_steps created_at; do
166
167
  [ -z "$sid" ] && continue
167
168
  echo -e " ${BOLD}#$sid${RESET} ${DIM}$created_at${RESET}"
169
+ echo -e " ${DIM}source:${RESET} $(eagle_agent_label "$sagent")"
168
170
  [ -n "$req" ] && echo -e " ${CYAN}Request:${RESET} $req"
169
171
  [ -n "$completed" ] && echo -e " ${GREEN}Done:${RESET} $completed"
170
172
  [ -n "$learned" ] && echo -e " ${YELLOW}Learned:${RESET} $learned"
@@ -179,7 +181,7 @@ search_session() {
179
181
  local sid_sql; sid_sql=$(eagle_sql_escape "$session_id")
180
182
 
181
183
  if [ "$json_output" = true ]; then
182
- eagle_db_json "SELECT o.tool_name, o.tool_input_summary, o.files_read, o.files_modified, o.created_at
184
+ eagle_db_json "SELECT o.agent, o.tool_name, o.tool_input_summary, o.files_read, o.files_modified, o.created_at
183
185
  FROM observations o
184
186
  WHERE o.session_id = '$sid_sql'
185
187
  ORDER BY o.created_at ASC;"
@@ -187,7 +189,7 @@ search_session() {
187
189
  fi
188
190
 
189
191
  local results
190
- results=$(eagle_db "SELECT o.tool_name, o.tool_input_summary, o.files_read, o.files_modified, o.created_at
192
+ results=$(eagle_db "SELECT o.agent, o.tool_name, o.tool_input_summary, o.files_read, o.files_modified, o.created_at
191
193
  FROM observations o
192
194
  WHERE o.session_id = '$sid_sql'
193
195
  ORDER BY o.created_at ASC;")
@@ -202,7 +204,7 @@ search_session() {
202
204
  echo -e " ${DIM}─────────────────────────────────────${RESET}"
203
205
  echo ""
204
206
 
205
- while IFS='|' read -r tool_name summary files_r files_m created_at; do
207
+ while IFS='|' read -r oagent tool_name summary files_r files_m created_at; do
206
208
  [ -z "$tool_name" ] && continue
207
209
  local icon="$DOT"
208
210
  case "$tool_name" in
@@ -211,7 +213,7 @@ search_session() {
211
213
  Edit) icon="${YELLOW}E${RESET}" ;;
212
214
  Bash) icon="${BLUE}\$${RESET}" ;;
213
215
  esac
214
- echo -e " ${icon} ${DIM}$created_at${RESET} $summary"
216
+ echo -e " ${icon} ${DIM}$created_at $(eagle_agent_label "$oagent")${RESET} $summary"
215
217
  done <<< "$results"
216
218
  echo ""
217
219
  }
@@ -261,22 +263,26 @@ search_files() {
261
263
  search_stats() {
262
264
  local p; p=$(eagle_sql_escape "$project")
263
265
 
264
- local sessions summaries observations tasks
266
+ local sessions sessions_claude sessions_codex summaries observations tasks
265
267
  sessions=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE project = '$p';")
268
+ sessions_claude=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE project = '$p' AND agent = 'claude-code';")
269
+ sessions_codex=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE project = '$p' AND agent = 'codex';")
266
270
  summaries=$(eagle_db "SELECT COUNT(*) FROM summaries WHERE project = '$p';")
267
271
  observations=$(eagle_db "SELECT COUNT(*) FROM observations o JOIN sessions s ON s.id = o.session_id WHERE s.project = '$p';")
268
- tasks=$(eagle_db "SELECT COUNT(*) FROM claude_tasks WHERE project = '$p';")
272
+ tasks=$(eagle_db "SELECT COUNT(*) FROM agent_tasks WHERE project = '$p';")
269
273
  local chunks
270
274
  chunks=$(eagle_db "SELECT COUNT(*) FROM code_chunks WHERE project = '$p';")
271
275
 
272
276
  if [ "$json_output" = true ]; then
273
277
  jq -nc --arg project "$project" \
274
278
  --argjson sessions "${sessions:-0}" \
279
+ --argjson sessions_claude "${sessions_claude:-0}" \
280
+ --argjson sessions_codex "${sessions_codex:-0}" \
275
281
  --argjson summaries "${summaries:-0}" \
276
282
  --argjson observations "${observations:-0}" \
277
283
  --argjson tasks "${tasks:-0}" \
278
284
  --argjson code_chunks "${chunks:-0}" \
279
- '{project: $project, sessions: $sessions, summaries: $summaries, observations: $observations, tasks: $tasks, code_chunks: $code_chunks}'
285
+ '{project: $project, sessions: $sessions, sessions_claude: $sessions_claude, sessions_codex: $sessions_codex, summaries: $summaries, observations: $observations, tasks: $tasks, code_chunks: $code_chunks}'
280
286
  return
281
287
  fi
282
288
 
@@ -285,6 +291,8 @@ search_stats() {
285
291
  echo -e " ${DIM}─────────────────────────────────────${RESET}"
286
292
  echo ""
287
293
  eagle_kv "Sessions:" "${sessions:-0}"
294
+ eagle_kv "Claude Code:" "${sessions_claude:-0}"
295
+ eagle_kv "Codex:" "${sessions_codex:-0}"
288
296
  eagle_kv "Summaries:" "${summaries:-0}"
289
297
  eagle_kv "Observations:" "${observations:-0}"
290
298
  eagle_kv "Tasks:" "${tasks:-0}"
@@ -341,15 +349,15 @@ search_memories() {
341
349
  exit 1
342
350
  fi
343
351
  local q; q=$(eagle_sql_escape "$sanitized_mq")
344
- local where_match="WHERE claude_memories_fts MATCH '$q'"
352
+ local where_match="WHERE agent_memories_fts MATCH '$q'"
345
353
  if [ "$cross_project" = false ]; then
346
354
  where_match="$where_match AND m.project = '$p'"
347
355
  fi
348
356
 
349
357
  if [ "$json_output" = true ]; then
350
- eagle_db_json "SELECT m.memory_name, m.memory_type, m.description, m.project, m.updated_at
351
- FROM claude_memories m
352
- JOIN claude_memories_fts f ON f.rowid = m.id
358
+ eagle_db_json "SELECT m.memory_name, m.memory_type, m.description, m.project, m.updated_at, m.origin_agent
359
+ FROM agent_memories m
360
+ JOIN agent_memories_fts f ON f.rowid = m.id
353
361
  $where_match
354
362
  ORDER BY rank
355
363
  LIMIT $limit;"
@@ -357,9 +365,9 @@ search_memories() {
357
365
  fi
358
366
 
359
367
  local results
360
- results=$(eagle_db "SELECT m.memory_name, m.memory_type, m.description, m.project, m.updated_at
361
- FROM claude_memories m
362
- JOIN claude_memories_fts f ON f.rowid = m.id
368
+ results=$(eagle_db "SELECT m.memory_name, m.memory_type, m.description, m.project, m.updated_at, m.origin_agent
369
+ FROM agent_memories m
370
+ JOIN agent_memories_fts f ON f.rowid = m.id
363
371
  $where_match
364
372
  ORDER BY rank
365
373
  LIMIT $limit;")
@@ -370,9 +378,9 @@ search_memories() {
370
378
  fi
371
379
 
372
380
  echo ""
373
- while IFS='|' read -r mname mtype mdesc mproj mupdated; do
381
+ while IFS='|' read -r mname mtype mdesc mproj mupdated morigin; do
374
382
  [ -z "$mname" ] && continue
375
- echo -e " ${BOLD}$mname${RESET} ${DIM}[$mtype]${RESET}"
383
+ echo -e " ${BOLD}$mname${RESET} ${DIM}[$mtype][$(eagle_agent_label "$morigin")]${RESET}"
376
384
  [ "$cross_project" = true ] && echo -e " ${DIM}project:${RESET} $mproj"
377
385
  [ -n "$mdesc" ] && echo -e " $mdesc"
378
386
  echo ""
@@ -381,15 +389,15 @@ search_memories() {
381
389
  fi
382
390
 
383
391
  if [ "$json_output" = true ]; then
384
- eagle_db_json "SELECT memory_name, memory_type, description, project, updated_at
385
- FROM claude_memories $where_project
392
+ eagle_db_json "SELECT memory_name, memory_type, description, project, updated_at, origin_agent
393
+ FROM agent_memories $where_project
386
394
  ORDER BY updated_at DESC LIMIT $limit;"
387
395
  return
388
396
  fi
389
397
 
390
398
  local results
391
- results=$(eagle_db "SELECT memory_name, memory_type, description, updated_at
392
- FROM claude_memories $where_project
399
+ results=$(eagle_db "SELECT memory_name, memory_type, description, updated_at, origin_agent
400
+ FROM agent_memories $where_project
393
401
  ORDER BY updated_at DESC LIMIT $limit;")
394
402
 
395
403
  if [ -z "$results" ]; then
@@ -402,9 +410,9 @@ search_memories() {
402
410
  echo -e " ${DIM}─────────────────────────────────────${RESET}"
403
411
  echo ""
404
412
 
405
- while IFS='|' read -r mname mtype mdesc mupdated; do
413
+ while IFS='|' read -r mname mtype mdesc mupdated morigin; do
406
414
  [ -z "$mname" ] && continue
407
- echo -e " ${CYAN}[$mtype]${RESET} ${BOLD}$mname${RESET}"
415
+ echo -e " ${CYAN}[$mtype][$(eagle_agent_label "$morigin")]${RESET} ${BOLD}$mname${RESET}"
408
416
  [ -n "$mdesc" ] && echo -e " ${DIM}$mdesc${RESET}"
409
417
  done <<< "$results"
410
418
  echo ""
@@ -416,8 +424,10 @@ search_tasks() {
416
424
  local p; p=$(eagle_sql_escape "$project")
417
425
 
418
426
  local where_project=""
427
+ local where_task_project=""
419
428
  if [ "$cross_project" = false ]; then
420
429
  where_project="AND project = '$p'"
430
+ where_task_project="AND t.project = '$p'"
421
431
  fi
422
432
 
423
433
  if [ -n "$query" ]; then
@@ -430,22 +440,22 @@ search_tasks() {
430
440
  local q; q=$(eagle_sql_escape "$sanitized_tq")
431
441
 
432
442
  if [ "$json_output" = true ]; then
433
- eagle_db_json "SELECT t.subject, t.status, t.project, t.updated_at
434
- FROM claude_tasks t
435
- JOIN claude_tasks_fts f ON f.rowid = t.id
436
- WHERE claude_tasks_fts MATCH '$q'
437
- ${where_project/AND/AND t.}
443
+ eagle_db_json "SELECT t.subject, t.status, t.project, t.updated_at, t.origin_agent
444
+ FROM agent_tasks t
445
+ JOIN agent_tasks_fts f ON f.rowid = t.id
446
+ WHERE agent_tasks_fts MATCH '$q'
447
+ $where_task_project
438
448
  ORDER BY rank
439
449
  LIMIT $limit;"
440
450
  return
441
451
  fi
442
452
 
443
453
  local results
444
- results=$(eagle_db "SELECT t.subject, t.status, t.project, t.updated_at
445
- FROM claude_tasks t
446
- JOIN claude_tasks_fts f ON f.rowid = t.id
447
- WHERE claude_tasks_fts MATCH '$q'
448
- ${where_project/AND/AND t.}
454
+ results=$(eagle_db "SELECT t.subject, t.status, t.project, t.updated_at, t.origin_agent
455
+ FROM agent_tasks t
456
+ JOIN agent_tasks_fts f ON f.rowid = t.id
457
+ WHERE agent_tasks_fts MATCH '$q'
458
+ $where_task_project
449
459
  ORDER BY rank
450
460
  LIMIT $limit;")
451
461
 
@@ -455,7 +465,7 @@ search_tasks() {
455
465
  fi
456
466
 
457
467
  echo ""
458
- while IFS='|' read -r tsubject tstatus tproj tupdated; do
468
+ while IFS='|' read -r tsubject tstatus tproj tupdated torigin; do
459
469
  [ -z "$tsubject" ] && continue
460
470
  local color="$DIM"
461
471
  case "$tstatus" in
@@ -463,15 +473,15 @@ search_tasks() {
463
473
  completed) color="$GREEN" ;;
464
474
  pending) color="$CYAN" ;;
465
475
  esac
466
- echo -e " ${color}[$tstatus]${RESET} $tsubject"
476
+ echo -e " ${color}[$tstatus][$(eagle_agent_label "$torigin")]${RESET} $tsubject"
467
477
  done <<< "$results"
468
478
  echo ""
469
479
  return
470
480
  fi
471
481
 
472
482
  if [ "$json_output" = true ]; then
473
- eagle_db_json "SELECT subject, status, project, updated_at
474
- FROM claude_tasks
483
+ eagle_db_json "SELECT subject, status, project, updated_at, origin_agent
484
+ FROM agent_tasks
475
485
  WHERE status IN ('in_progress', 'pending')
476
486
  $where_project
477
487
  ORDER BY CASE status WHEN 'in_progress' THEN 0 ELSE 1 END, updated_at DESC
@@ -480,8 +490,8 @@ search_tasks() {
480
490
  fi
481
491
 
482
492
  local results
483
- results=$(eagle_db "SELECT subject, status, updated_at
484
- FROM claude_tasks
493
+ results=$(eagle_db "SELECT subject, status, updated_at, origin_agent
494
+ FROM agent_tasks
485
495
  WHERE status IN ('in_progress', 'pending')
486
496
  $where_project
487
497
  ORDER BY CASE status WHEN 'in_progress' THEN 0 ELSE 1 END, updated_at DESC
@@ -497,14 +507,14 @@ search_tasks() {
497
507
  echo -e " ${DIM}─────────────────────────────────────${RESET}"
498
508
  echo ""
499
509
 
500
- while IFS='|' read -r tsubject tstatus tupdated; do
510
+ while IFS='|' read -r tsubject tstatus tupdated torigin; do
501
511
  [ -z "$tsubject" ] && continue
502
512
  local color="$DIM"
503
513
  case "$tstatus" in
504
514
  in_progress) color="$YELLOW" ;;
505
515
  pending) color="$CYAN" ;;
506
516
  esac
507
- echo -e " ${color}[$tstatus]${RESET} $tsubject"
517
+ echo -e " ${color}[$tstatus][$(eagle_agent_label "$torigin")]${RESET} $tsubject"
508
518
  done <<< "$results"
509
519
  echo ""
510
520
  }
@@ -21,7 +21,7 @@ eagle_mem_statusline() {
21
21
  cnt=$(echo ".headers off
22
22
  SELECT COUNT(*) FROM sessions WHERE project = '${proj}';" | sqlite3 "$em_db" 2>/dev/null | tr -d '[:space:]')
23
23
  mem=$(echo ".headers off
24
- SELECT COUNT(*) FROM claude_memories WHERE project = '${proj}';" | sqlite3 "$em_db" 2>/dev/null | tr -d '[:space:]')
24
+ SELECT COUNT(*) FROM agent_memories WHERE project = '${proj}';" | sqlite3 "$em_db" 2>/dev/null | tr -d '[:space:]')
25
25
  cnt=${cnt:-0}; mem=${mem:-0}
26
26
 
27
27
  local R='\033[0m' CYAN='\033[96m' WHT='\033[97m' DIM='\033[2m'
package/scripts/tasks.sh CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env bash
2
2
  # ═══════════════════════════════════════════════════════════
3
- # Eagle Mem — Tasks (read-only viewer of Claude Code tasks)
4
- # Claude Code manages task state via TaskCreate/TaskUpdate;
5
- # Eagle Mem mirrors it and displays it here.
3
+ # Eagle Mem — Tasks
4
+ # Mirrors native task state and lets agents without native task files persist
5
+ # task handoff records directly.
6
6
  # ═══════════════════════════════════════════════════════════
7
7
  set -euo pipefail
8
8
 
@@ -26,22 +26,29 @@ esac
26
26
 
27
27
  project=""
28
28
  json_output=false
29
+ agent=""
29
30
 
30
31
  show_help() {
31
- echo -e " ${BOLD}eagle-mem tasks${RESET} — View mirrored Claude Code tasks"
32
+ echo -e " ${BOLD}eagle-mem tasks${RESET} — View mirrored agent tasks"
32
33
  echo ""
33
34
  echo -e " ${BOLD}Usage:${RESET}"
34
35
  echo -e " eagle-mem tasks ${DIM}# list pending/in-progress tasks${RESET}"
35
36
  echo -e " eagle-mem tasks ${CYAN}list${RESET} ${DIM}# list all tasks${RESET}"
36
37
  echo -e " eagle-mem tasks ${CYAN}completed${RESET} ${DIM}# list completed tasks${RESET}"
37
38
  echo -e " eagle-mem tasks ${CYAN}search${RESET} <query> ${DIM}# search tasks by keyword${RESET}"
39
+ echo -e " eagle-mem tasks ${CYAN}add${RESET} <subject> ${DIM}# create a persistent task record${RESET}"
40
+ echo -e " eagle-mem tasks ${CYAN}update${RESET} <id> ${DIM}# update subject/description/status${RESET}"
41
+ echo -e " eagle-mem tasks ${CYAN}start${RESET} <id> ${DIM}# mark task in_progress${RESET}"
42
+ echo -e " eagle-mem tasks ${CYAN}complete${RESET} <id> ${DIM}# mark task completed${RESET}"
43
+ echo -e " eagle-mem tasks ${CYAN}cancel${RESET} <id> ${DIM}# mark task cancelled${RESET}"
38
44
  echo ""
39
45
  echo -e " ${BOLD}Options:${RESET}"
40
46
  echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current dir)"
47
+ echo -e " ${CYAN}--agent${RESET} <name> Source agent (codex or claude-code)"
41
48
  echo -e " ${CYAN}-j, --json${RESET} Output as JSON"
42
49
  echo ""
43
- echo -e " ${DIM}Tasks are managed by Claude Code (TaskCreate/TaskUpdate).${RESET}"
44
- echo -e " ${DIM}Eagle Mem automatically mirrors them for cross-session recall.${RESET}"
50
+ echo -e " ${DIM}Claude Code task files are mirrored automatically.${RESET}"
51
+ echo -e " ${DIM}Codex can persist tasks with this CLI when using the eagle-mem-tasks skill.${RESET}"
45
52
  echo ""
46
53
  exit 0
47
54
  }
@@ -50,6 +57,7 @@ args=()
50
57
  while [ $# -gt 0 ]; do
51
58
  case "$1" in
52
59
  --project|-p) project="$2"; shift 2 ;;
60
+ --agent) agent="$2"; shift 2 ;;
53
61
  --json|-j) json_output=true; shift ;;
54
62
  --help|-h) show_help ;;
55
63
  *) args+=("$1"); shift ;;
@@ -58,6 +66,8 @@ done
58
66
 
59
67
  [ -z "$project" ] && project=$(eagle_project_from_cwd "$(pwd)")
60
68
  project_sql=$(eagle_sql_escape "$project")
69
+ [ -z "$agent" ] && agent=$(eagle_agent_source)
70
+ agent_sql=$(eagle_sql_escape "$agent")
61
71
 
62
72
  # ─── List tasks ───────────────────────────────────────────
63
73
 
@@ -73,7 +83,7 @@ tasks_list() {
73
83
 
74
84
  if [ "$json_output" = true ]; then
75
85
  eagle_db_json "SELECT source_task_id, subject, description, status, blocks, blocked_by, updated_at
76
- FROM claude_tasks
86
+ FROM agent_tasks
77
87
  WHERE project = '$project_sql' $where_status
78
88
  ORDER BY updated_at DESC
79
89
  LIMIT 20;"
@@ -82,7 +92,7 @@ tasks_list() {
82
92
 
83
93
  local results
84
94
  results=$(eagle_db "SELECT source_task_id, subject, status, blocked_by, description
85
- FROM claude_tasks
95
+ FROM agent_tasks
86
96
  WHERE project = '$project_sql' $where_status
87
97
  ORDER BY
88
98
  CASE status WHEN 'in_progress' THEN 0 WHEN 'pending' THEN 1 ELSE 2 END,
@@ -106,7 +116,7 @@ tasks_list() {
106
116
  in_progress) icon="${CYAN}>${RESET}"; marker=" ${CYAN}[in_progress]${RESET}" ;;
107
117
  pending) icon="${DIM}o${RESET}"; marker="" ;;
108
118
  completed) icon="${GREEN}+${RESET}"; marker=" ${DIM}[completed]${RESET}" ;;
109
- deleted) icon="${RED}x${RESET}"; marker=" ${RED}[deleted]${RESET}" ;;
119
+ cancelled) icon="${RED}x${RESET}"; marker=" ${RED}[cancelled]${RESET}" ;;
110
120
  *) icon="$DOT"; marker="" ;;
111
121
  esac
112
122
  if [ "$blocked_by" != "[]" ] && [ -n "$blocked_by" ]; then
@@ -137,9 +147,9 @@ tasks_search() {
137
147
  if [ "$json_output" = true ]; then
138
148
  local query_sql; query_sql=$(eagle_sql_escape "$sanitized_query")
139
149
  eagle_db_json "SELECT t.source_task_id, t.subject, t.status, t.description, t.updated_at
140
- FROM claude_tasks t
141
- JOIN claude_tasks_fts f ON f.rowid = t.id
142
- WHERE claude_tasks_fts MATCH '$query_sql'
150
+ FROM agent_tasks t
151
+ JOIN agent_tasks_fts f ON f.rowid = t.id
152
+ WHERE agent_tasks_fts MATCH '$query_sql'
143
153
  AND t.project = '$project_sql'
144
154
  ORDER BY rank
145
155
  LIMIT 10;"
@@ -149,9 +159,9 @@ tasks_search() {
149
159
  local results
150
160
  local query_sql; query_sql=$(eagle_sql_escape "$sanitized_query")
151
161
  results=$(eagle_db "SELECT t.source_task_id, t.subject, t.status, t.description
152
- FROM claude_tasks t
153
- JOIN claude_tasks_fts f ON f.rowid = t.id
154
- WHERE claude_tasks_fts MATCH '$query_sql'
162
+ FROM agent_tasks t
163
+ JOIN agent_tasks_fts f ON f.rowid = t.id
164
+ WHERE agent_tasks_fts MATCH '$query_sql'
155
165
  AND t.project = '$project_sql'
156
166
  ORDER BY rank
157
167
  LIMIT 10;")
@@ -174,6 +184,162 @@ tasks_search() {
174
184
  echo ""
175
185
  }
176
186
 
187
+ # ─── Mutate task records ──────────────────────────────────
188
+
189
+ tasks_add() {
190
+ local subject="${args[0]:-}"
191
+ if [ -z "$subject" ]; then
192
+ eagle_err "Usage: eagle-mem tasks add <subject> [--desc <text>] [--status pending|in_progress]"
193
+ exit 1
194
+ fi
195
+
196
+ local desc=""
197
+ local status="pending"
198
+ local i=1
199
+ while [ "$i" -lt "${#args[@]}" ]; do
200
+ case "${args[$i]}" in
201
+ --desc|-d)
202
+ i=$((i + 1))
203
+ desc="${args[$i]:-}"
204
+ ;;
205
+ --status)
206
+ i=$((i + 1))
207
+ status="${args[$i]:-pending}"
208
+ ;;
209
+ esac
210
+ i=$((i + 1))
211
+ done
212
+
213
+ case "$status" in
214
+ pending|in_progress|completed|cancelled) ;;
215
+ *)
216
+ eagle_err "Invalid status: $status"
217
+ exit 1
218
+ ;;
219
+ esac
220
+
221
+ local task_id file_path content_hash
222
+ task_id="agent-$(date -u +%Y%m%d%H%M%S)-$$"
223
+ file_path="agent-task://$project/$task_id"
224
+ content_hash=$(printf '%s|%s|%s|%s' "$subject" "$desc" "$status" "$agent" | shasum -a 256 | awk '{print $1}')
225
+
226
+ local tid_sql fp_sql subj_sql desc_sql status_sql hash_sql
227
+ tid_sql=$(eagle_sql_escape "$task_id")
228
+ fp_sql=$(eagle_sql_escape "$file_path")
229
+ subj_sql=$(eagle_sql_escape "$subject")
230
+ desc_sql=$(eagle_sql_escape "$desc")
231
+ status_sql=$(eagle_sql_escape "$status")
232
+ hash_sql=$(eagle_sql_escape "$content_hash")
233
+
234
+ eagle_db_pipe <<SQL
235
+ 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)
236
+ VALUES ('$project_sql', 'manual', '$tid_sql', '$fp_sql', '$subj_sql', '$desc_sql', '', '$status_sql', '[]', '[]', '$hash_sql', '$agent_sql');
237
+ SQL
238
+
239
+ if [ "$json_output" = true ]; then
240
+ jq -nc --arg id "$task_id" --arg subject "$subject" --arg status "$status" --arg agent "$agent" \
241
+ '{source_task_id:$id, subject:$subject, status:$status, origin_agent:$agent}'
242
+ else
243
+ eagle_ok "Task '$task_id' created"
244
+ fi
245
+ }
246
+
247
+ tasks_set_status() {
248
+ local task_id="${args[0]:-}"
249
+ local status="$1"
250
+ if [ -z "$task_id" ]; then
251
+ eagle_err "Usage: eagle-mem tasks $action <id>"
252
+ exit 1
253
+ fi
254
+
255
+ local tid_sql status_sql
256
+ tid_sql=$(eagle_sql_escape "$task_id")
257
+ status_sql=$(eagle_sql_escape "$status")
258
+
259
+ changed=$(eagle_db_pipe <<SQL
260
+ UPDATE agent_tasks
261
+ SET status = '$status_sql',
262
+ origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
263
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
264
+ WHERE project = '$project_sql' AND source_task_id = '$tid_sql';
265
+ SELECT changes();
266
+ SQL
267
+ )
268
+
269
+ if [ "${changed:-0}" -gt 0 ] 2>/dev/null; then
270
+ eagle_ok "Task '$task_id' marked $status"
271
+ else
272
+ eagle_err "Task not found: $task_id"
273
+ exit 1
274
+ fi
275
+ }
276
+
277
+ tasks_update() {
278
+ local task_id="${args[0]:-}"
279
+ if [ -z "$task_id" ]; then
280
+ eagle_err "Usage: eagle-mem tasks update <id> [--subject <text>] [--desc <text>] [--status <status>]"
281
+ exit 1
282
+ fi
283
+
284
+ local subject="" desc="" status=""
285
+ local i=1
286
+ while [ "$i" -lt "${#args[@]}" ]; do
287
+ case "${args[$i]}" in
288
+ --subject)
289
+ i=$((i + 1))
290
+ subject="${args[$i]:-}"
291
+ ;;
292
+ --desc|-d)
293
+ i=$((i + 1))
294
+ desc="${args[$i]:-}"
295
+ ;;
296
+ --status)
297
+ i=$((i + 1))
298
+ status="${args[$i]:-}"
299
+ ;;
300
+ esac
301
+ i=$((i + 1))
302
+ done
303
+
304
+ if [ -n "$status" ]; then
305
+ case "$status" in
306
+ pending|in_progress|completed|cancelled) ;;
307
+ *)
308
+ eagle_err "Invalid status: $status"
309
+ exit 1
310
+ ;;
311
+ esac
312
+ fi
313
+
314
+ local tid_sql subj_sql desc_sql status_sql hash_sql
315
+ tid_sql=$(eagle_sql_escape "$task_id")
316
+ subj_sql=$(eagle_sql_escape "$subject")
317
+ desc_sql=$(eagle_sql_escape "$desc")
318
+ status_sql=$(eagle_sql_escape "$status")
319
+ hash_sql=$(printf '%s|%s|%s|%s|%s' "$task_id" "$subject" "$desc" "$status" "$agent" | shasum -a 256 | awk '{print $1}')
320
+ hash_sql=$(eagle_sql_escape "$hash_sql")
321
+
322
+ changed=$(eagle_db_pipe <<SQL
323
+ UPDATE agent_tasks
324
+ SET subject = CASE WHEN '$subj_sql' != '' THEN '$subj_sql' ELSE subject END,
325
+ description = CASE WHEN '$desc_sql' != '' THEN '$desc_sql' ELSE description END,
326
+ status = CASE WHEN '$status_sql' != '' THEN '$status_sql' ELSE status END,
327
+ origin_agent = COALESCE(NULLIF('$agent_sql', ''), origin_agent),
328
+ content_hash = '$hash_sql',
329
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
330
+ WHERE project = '$project_sql' AND source_task_id = '$tid_sql';
331
+ SELECT changes();
332
+ SQL
333
+ )
334
+
335
+ if [ "${changed:-0}" -gt 0 ] 2>/dev/null; then
336
+ eagle_ok "Task '$task_id' updated"
337
+ else
338
+ eagle_err "Task not found: $task_id"
339
+ exit 1
340
+ fi
341
+ }
342
+
177
343
  # ─── Dispatch ─────────────────────────────────────────────
178
344
 
179
345
  case "$action" in
@@ -181,6 +347,11 @@ case "$action" in
181
347
  pending) tasks_list "pending" ;;
182
348
  completed) tasks_list "completed" ;;
183
349
  search) tasks_search ;;
350
+ add) tasks_add ;;
351
+ update) tasks_update ;;
352
+ start) tasks_set_status "in_progress" ;;
353
+ complete) tasks_set_status "completed" ;;
354
+ cancel) tasks_set_status "cancelled" ;;
184
355
  --help|-h) show_help ;;
185
356
  *)
186
357
  eagle_err "Unknown action: $action"
@@ -10,6 +10,7 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
10
10
 
11
11
  . "$SCRIPTS_DIR/style.sh"
12
12
  . "$LIB_DIR/common.sh"
13
+ . "$LIB_DIR/codex-hooks.sh"
13
14
 
14
15
  SETTINGS="$EAGLE_SETTINGS"
15
16
 
@@ -33,6 +34,12 @@ else
33
34
  eagle_warn "Could not patch settings.json (jq not found or file missing)"
34
35
  fi
35
36
 
37
+ if eagle_remove_codex_hooks; then
38
+ eagle_ok "Hooks removed from Codex hooks.json"
39
+ else
40
+ eagle_warn "Could not patch Codex hooks.json (jq not found or file missing)"
41
+ fi
42
+
36
43
  # ─── Remove skill symlinks ────────────────────────────────
37
44
 
38
45
  if [ -d "$EAGLE_SKILLS_DIR" ]; then