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
@@ -58,6 +58,228 @@ eagle_verify_feature() {
58
58
  WHERE project = '$project' AND name = '$name';"
59
59
  }
60
60
 
61
+ eagle_find_feature_impacts_for_file() {
62
+ local project; project=$(eagle_sql_escape "$1")
63
+ local file_path="$2"
64
+ local fname; fname=$(basename "$file_path")
65
+ local file_esc; file_esc=$(eagle_sql_escape "$file_path")
66
+ local fname_esc; fname_esc=$(eagle_sql_escape "$fname")
67
+ local file_like; file_like=$(eagle_like_escape "$file_esc")
68
+ local fname_like; fname_like=$(eagle_like_escape "$fname_esc")
69
+
70
+ eagle_db "SELECT DISTINCT f.id, f.name, f.description, f.last_verified_at,
71
+ ff.file_path,
72
+ (SELECT GROUP_CONCAT(fst.command, '; ')
73
+ FROM feature_smoke_tests fst WHERE fst.feature_id = f.id) as smoke_tests
74
+ FROM features f
75
+ JOIN feature_files ff ON ff.feature_id = f.id
76
+ WHERE f.project = '$project'
77
+ AND f.status = 'active'
78
+ AND (
79
+ ff.file_path = '$file_esc'
80
+ OR ff.file_path LIKE '%/$file_like' ESCAPE '\\'
81
+ OR '$file_esc' LIKE '%' || ff.file_path ESCAPE '\\'
82
+ OR ff.file_path LIKE '%$fname_like' ESCAPE '\\'
83
+ OR ff.file_path LIKE '%$fname_like%' ESCAPE '\\'
84
+ )
85
+ ORDER BY f.updated_at DESC
86
+ LIMIT 10;"
87
+ }
88
+
89
+ eagle_record_pending_feature_verifications() {
90
+ local project="$1"
91
+ local file_path="$2"
92
+ local session_id="${3:-}"
93
+ local trigger_tool="${4:-}"
94
+ local reason="${5:-File changed}"
95
+ local change_fingerprint="${6:-}"
96
+
97
+ local impacts
98
+ impacts=$(eagle_find_feature_impacts_for_file "$project" "$file_path")
99
+ [ -z "$impacts" ] && return 0
100
+
101
+ local p_esc; p_esc=$(eagle_sql_escape "$project")
102
+ local fp_esc; fp_esc=$(eagle_sql_escape "$file_path")
103
+ local sid_esc; sid_esc=$(eagle_sql_escape "$session_id")
104
+ local tool_esc; tool_esc=$(eagle_sql_escape "$trigger_tool")
105
+ local reason_esc; reason_esc=$(eagle_sql_escape "$reason")
106
+ local fp_hash_esc; fp_hash_esc=$(eagle_sql_escape "$change_fingerprint")
107
+
108
+ while IFS='|' read -r feature_id feature_name _desc _verified _matched_file _smoke; do
109
+ [ -z "$feature_id" ] && continue
110
+ local fid; fid=$(eagle_sql_int "$feature_id")
111
+ local name_esc; name_esc=$(eagle_sql_escape "$feature_name")
112
+
113
+ if [ -n "$change_fingerprint" ]; then
114
+ already_resolved=$(eagle_db "SELECT 1 FROM pending_feature_verifications
115
+ WHERE project = '$p_esc'
116
+ AND feature_id = $fid
117
+ AND file_path = '$fp_esc'
118
+ AND change_fingerprint = '$fp_hash_esc'
119
+ AND status IN ('verified', 'waived')
120
+ LIMIT 1;")
121
+ [ -n "$already_resolved" ] && continue
122
+ fi
123
+
124
+ eagle_db "INSERT INTO pending_feature_verifications
125
+ (project, feature_id, feature_name, file_path, reason, source_session_id, trigger_tool, change_fingerprint)
126
+ VALUES ('$p_esc', $fid, '$name_esc', '$fp_esc', '$reason_esc', '$sid_esc', '$tool_esc', '$fp_hash_esc')
127
+ ON CONFLICT(project, feature_id, file_path) WHERE status = 'pending' DO UPDATE SET
128
+ feature_name = excluded.feature_name,
129
+ reason = excluded.reason,
130
+ source_session_id = excluded.source_session_id,
131
+ trigger_tool = excluded.trigger_tool,
132
+ change_fingerprint = excluded.change_fingerprint,
133
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');" >/dev/null
134
+ done <<< "$impacts"
135
+
136
+ printf '%s\n' "$impacts"
137
+ }
138
+
139
+ eagle_record_current_feature_verifications_for_file() {
140
+ local project="$1"
141
+ local cwd="$2"
142
+ local file_path="$3"
143
+ local session_id="${4:-}"
144
+ local trigger_tool="${5:-}"
145
+ local reason="${6:-File changed}"
146
+
147
+ local norm_file
148
+ norm_file=$(eagle_project_file_path "$cwd" "$file_path")
149
+ [ -z "$norm_file" ] && return 0
150
+
151
+ local fingerprint
152
+ fingerprint=$(eagle_change_fingerprint_for_file "$cwd" "$norm_file")
153
+ eagle_record_pending_feature_verifications "$project" "$norm_file" "$session_id" "$trigger_tool" "$reason" "$fingerprint"
154
+ }
155
+
156
+ eagle_reconcile_current_feature_verifications() {
157
+ local project="$1"
158
+ local cwd="$2"
159
+ local session_id="${3:-}"
160
+ local trigger_tool="${4:-}"
161
+ local reason="${5:-Repository change detected}"
162
+ local changed_files="${6:-}"
163
+
164
+ [ -z "$changed_files" ] && return 0
165
+ while IFS= read -r changed_file; do
166
+ [ -z "$changed_file" ] && continue
167
+ eagle_record_current_feature_verifications_for_file "$project" "$cwd" "$changed_file" "$session_id" "$trigger_tool" "$reason" >/dev/null
168
+ done <<< "$changed_files"
169
+ }
170
+
171
+ eagle_list_current_pending_feature_verifications() {
172
+ local project="$1"
173
+ local cwd="$2"
174
+ local changed_files="${3:-}"
175
+ local limit; limit=$(eagle_sql_int "${4:-20}")
176
+ [ "$limit" -eq 0 ] && limit=20
177
+
178
+ [ -z "$changed_files" ] && return 0
179
+
180
+ local p_esc; p_esc=$(eagle_sql_escape "$project")
181
+ local emitted=0
182
+ local seen="|"
183
+
184
+ while IFS= read -r changed_file; do
185
+ [ -z "$changed_file" ] && continue
186
+ [ "$emitted" -ge "$limit" ] && break
187
+
188
+ local norm_file fingerprint impacts fp_esc fp_hash_esc
189
+ norm_file=$(eagle_project_file_path "$cwd" "$changed_file")
190
+ [ -z "$norm_file" ] && continue
191
+ fingerprint=$(eagle_change_fingerprint_for_file "$cwd" "$norm_file")
192
+ impacts=$(eagle_find_feature_impacts_for_file "$project" "$norm_file")
193
+ [ -z "$impacts" ] && continue
194
+
195
+ fp_esc=$(eagle_sql_escape "$norm_file")
196
+ fp_hash_esc=$(eagle_sql_escape "$fingerprint")
197
+
198
+ while IFS='|' read -r feature_id _feature_name _desc _verified _matched_file _smoke; do
199
+ [ -z "$feature_id" ] && continue
200
+ [ "$emitted" -ge "$limit" ] && break
201
+
202
+ local fid row row_id
203
+ fid=$(eagle_sql_int "$feature_id")
204
+ row=$(eagle_db "SELECT p.id, p.feature_name, p.file_path, p.reason, p.trigger_tool, p.created_at,
205
+ COALESCE((SELECT GROUP_CONCAT(fst.command, '; ')
206
+ FROM feature_smoke_tests fst WHERE fst.feature_id = p.feature_id), '') as smoke_tests,
207
+ substr(p.change_fingerprint, 1, 12) as fingerprint
208
+ FROM pending_feature_verifications p
209
+ WHERE p.project = '$p_esc'
210
+ AND p.feature_id = $fid
211
+ AND p.file_path = '$fp_esc'
212
+ AND p.change_fingerprint = '$fp_hash_esc'
213
+ AND p.status = 'pending'
214
+ ORDER BY p.updated_at DESC, p.id DESC
215
+ LIMIT 1;")
216
+ [ -z "$row" ] && continue
217
+ row_id=${row%%|*}
218
+ case "$seen" in *"|$row_id|"*) continue ;; esac
219
+ seen+="$row_id|"
220
+ printf '%s\n' "$row"
221
+ emitted=$((emitted + 1))
222
+ done <<< "$impacts"
223
+ done <<< "$changed_files"
224
+ }
225
+
226
+ eagle_count_pending_feature_verifications() {
227
+ local project; project=$(eagle_sql_escape "$1")
228
+ eagle_db "SELECT COUNT(*) FROM pending_feature_verifications
229
+ WHERE project = '$project' AND status = 'pending';"
230
+ }
231
+
232
+ eagle_list_pending_feature_verifications() {
233
+ local project; project=$(eagle_sql_escape "$1")
234
+ local limit; limit=$(eagle_sql_int "${2:-20}")
235
+
236
+ eagle_db "SELECT p.id, p.feature_name, p.file_path, p.reason, p.trigger_tool, p.created_at,
237
+ COALESCE((SELECT GROUP_CONCAT(fst.command, '; ')
238
+ FROM feature_smoke_tests fst WHERE fst.feature_id = p.feature_id), '') as smoke_tests,
239
+ substr(p.change_fingerprint, 1, 12) as fingerprint
240
+ FROM pending_feature_verifications p
241
+ WHERE p.project = '$project' AND p.status = 'pending'
242
+ ORDER BY p.updated_at DESC, p.id DESC
243
+ LIMIT $limit;"
244
+ }
245
+
246
+ eagle_resolve_pending_feature_verifications() {
247
+ local project; project=$(eagle_sql_escape "$1")
248
+ local name; name=$(eagle_sql_escape "$2")
249
+ local status; status=$(eagle_sql_escape "${3:-verified}")
250
+ local notes; notes=$(eagle_sql_escape "${4:-}")
251
+
252
+ eagle_db_pipe <<SQL
253
+ UPDATE pending_feature_verifications
254
+ SET status = '$status',
255
+ notes = '$notes',
256
+ resolved_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
257
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
258
+ WHERE project = '$project'
259
+ AND feature_name = '$name'
260
+ AND status = 'pending';
261
+ SELECT changes();
262
+ SQL
263
+ }
264
+
265
+ eagle_waive_pending_feature_verification() {
266
+ local project; project=$(eagle_sql_escape "$1")
267
+ local id; id=$(eagle_sql_int "$2")
268
+ local notes; notes=$(eagle_sql_escape "${3:-}")
269
+
270
+ eagle_db_pipe <<SQL
271
+ UPDATE pending_feature_verifications
272
+ SET status = 'waived',
273
+ notes = '$notes',
274
+ resolved_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
275
+ updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
276
+ WHERE project = '$project'
277
+ AND id = $id
278
+ AND status = 'pending';
279
+ SELECT changes();
280
+ SQL
281
+ }
282
+
61
283
  eagle_get_feature_id() {
62
284
  local project; project=$(eagle_sql_escape "$1")
63
285
  local name; name=$(eagle_sql_escape "$2")
@@ -21,7 +21,8 @@ eagle_add_guardrail() {
21
21
  file_pattern=$(eagle_sql_escape "$file_pattern")
22
22
  eagle_db "INSERT INTO guardrails (project, file_pattern, rule, source)
23
23
  VALUES ('$project', '$file_pattern', '$rule', '$source')
24
- ON CONFLICT(project, source, file_pattern, rule) DO UPDATE SET
24
+ ON CONFLICT DO UPDATE SET
25
+ source = excluded.source,
25
26
  updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');"
26
27
  }
27
28
 
package/lib/db-mirrors.sh CHANGED
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env bash
2
2
  # ═══════════════════════════════════════════════════════════
3
- # Eagle Mem — Claude Code memory/plan/task mirror helpers
3
+ # Eagle Mem — Agent memory/plan/task mirror helpers
4
4
  # ═══════════════════════════════════════════════════════════
5
5
  [ -n "${_EAGLE_DB_MIRRORS_LOADED:-}" ] && return 0
6
6
  _EAGLE_DB_MIRRORS_LOADED=1
7
7
 
8
- eagle_capture_claude_memory() {
8
+ eagle_capture_agent_memory() {
9
9
  local file_path="$1"
10
10
  local session_id="${2:-}"
11
11
  local project="${3:-}"
12
+ local agent="${4:-$(eagle_agent_source)}"
12
13
 
13
14
  [ ! -f "$file_path" ] && return 0
14
15
 
@@ -18,6 +19,9 @@ eagle_capture_claude_memory() {
18
19
  local fm body
19
20
  fm=$(awk '/^---$/{c++; next} c==1' "$file_path")
20
21
  body=$(awk '/^---$/{c++; next} c>=2' "$file_path")
22
+ if [ -z "$body" ] && [ -z "$fm" ]; then
23
+ body=$(cat "$file_path")
24
+ fi
21
25
 
22
26
  _fm_field() { printf '%s\n' "$fm" | awk -F': *' -v k="$1" '$1==k{sub(/^[^:]+: */,""); gsub(/^"|"$/,""); print; exit}'; }
23
27
 
@@ -28,7 +32,19 @@ eagle_capture_claude_memory() {
28
32
  morigin=$(_fm_field "originSessionId")
29
33
  [ -z "$morigin" ] && morigin="$session_id"
30
34
 
31
- local fp_sql proj_sql name_sql desc_sql type_sql content_sql hash_sql origin_sql
35
+ if [ -z "$mname" ]; then
36
+ case "$(basename "$file_path")" in
37
+ MEMORY.md) mname="Codex Memory Registry" ;;
38
+ memory_summary.md) mname="Codex Memory Summary" ;;
39
+ *) mname=$(basename "$file_path" .md) ;;
40
+ esac
41
+ fi
42
+ [ -z "$mtype" ] && mtype="$agent"
43
+ if [ -z "$mdesc" ]; then
44
+ mdesc=$(printf '%s\n' "$body" | sed '/^[[:space:]]*$/d' | head -1 | cut -c1-200)
45
+ fi
46
+
47
+ local fp_sql proj_sql name_sql desc_sql type_sql content_sql hash_sql origin_sql agent_sql
32
48
  fp_sql=$(eagle_sql_escape "$file_path")
33
49
  proj_sql=$(eagle_sql_escape "$project")
34
50
  name_sql=$(eagle_sql_escape "$mname")
@@ -37,10 +53,11 @@ eagle_capture_claude_memory() {
37
53
  content_sql=$(eagle_sql_escape "$body")
38
54
  hash_sql=$(eagle_sql_escape "$chash")
39
55
  origin_sql=$(eagle_sql_escape "$morigin")
56
+ agent_sql=$(eagle_sql_escape "$agent")
40
57
 
41
58
  eagle_db_pipe <<SQL
42
- INSERT INTO claude_memories (project, file_path, memory_name, description, memory_type, content, content_hash, origin_session_id)
43
- VALUES ('$proj_sql', '$fp_sql', '$name_sql', '$desc_sql', '$type_sql', '$content_sql', '$hash_sql', '$origin_sql')
59
+ INSERT INTO agent_memories (project, file_path, memory_name, description, memory_type, content, content_hash, origin_session_id, origin_agent)
60
+ VALUES ('$proj_sql', '$fp_sql', '$name_sql', '$desc_sql', '$type_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql')
44
61
  ON CONFLICT(file_path) DO UPDATE SET
45
62
  memory_name = excluded.memory_name,
46
63
  description = excluded.description,
@@ -48,12 +65,13 @@ ON CONFLICT(file_path) DO UPDATE SET
48
65
  content = excluded.content,
49
66
  content_hash = excluded.content_hash,
50
67
  origin_session_id = excluded.origin_session_id,
68
+ origin_agent = excluded.origin_agent,
51
69
  updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
52
- WHERE claude_memories.content_hash != excluded.content_hash;
70
+ WHERE agent_memories.content_hash != excluded.content_hash;
53
71
  SQL
54
72
  }
55
73
 
56
- eagle_search_claude_memories() {
74
+ eagle_search_agent_memories() {
57
75
  local query; query=$(eagle_fts_sanitize "$1")
58
76
  if [ -z "$query" ]; then
59
77
  echo "Search query is empty after sanitization. Try a different search term." >&2
@@ -71,16 +89,16 @@ eagle_search_claude_memories() {
71
89
 
72
90
  eagle_db "SELECT m.memory_name, m.memory_type, m.description,
73
91
  replace(substr(m.content, 1, 200), char(10), ' '),
74
- m.file_path, m.updated_at
75
- FROM claude_memories m
76
- JOIN claude_memories_fts f ON f.rowid = m.id
77
- WHERE claude_memories_fts MATCH '$query'
92
+ m.file_path, m.updated_at, m.origin_agent
93
+ FROM agent_memories m
94
+ JOIN agent_memories_fts f ON f.rowid = m.id
95
+ WHERE agent_memories_fts MATCH '$query'
78
96
  $where_clause
79
97
  ORDER BY rank
80
98
  LIMIT $limit;"
81
99
  }
82
100
 
83
- eagle_list_claude_memories() {
101
+ eagle_list_agent_memories() {
84
102
  local project="${1:-}"
85
103
  local limit; limit=$(eagle_sql_int "${2:-20}")
86
104
 
@@ -90,17 +108,18 @@ eagle_list_claude_memories() {
90
108
  where_clause="WHERE project = '$project'"
91
109
  fi
92
110
 
93
- eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at
94
- FROM claude_memories
111
+ eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_agent
112
+ FROM agent_memories
95
113
  $where_clause
96
114
  ORDER BY updated_at DESC
97
115
  LIMIT $limit;"
98
116
  }
99
117
 
100
- eagle_capture_claude_plan() {
118
+ eagle_capture_agent_plan() {
101
119
  local file_path="$1"
102
120
  local session_id="${2:-}"
103
121
  local project="${3:-}"
122
+ local agent="${4:-$(eagle_agent_source)}"
104
123
 
105
124
  [ ! -f "$file_path" ] && return 0
106
125
 
@@ -111,29 +130,31 @@ eagle_capture_claude_plan() {
111
130
  title=$(awk '/^# /{print; exit}' "$file_path" | sed 's/^# //')
112
131
  content=$(cat "$file_path")
113
132
 
114
- local fp_sql proj_sql title_sql content_sql hash_sql origin_sql
133
+ local fp_sql proj_sql title_sql content_sql hash_sql origin_sql agent_sql
115
134
  fp_sql=$(eagle_sql_escape "$file_path")
116
135
  proj_sql=$(eagle_sql_escape "$project")
117
136
  title_sql=$(eagle_sql_escape "$title")
118
137
  content_sql=$(eagle_sql_escape "$content")
119
138
  hash_sql=$(eagle_sql_escape "$chash")
120
139
  origin_sql=$(eagle_sql_escape "$session_id")
140
+ agent_sql=$(eagle_sql_escape "$agent")
121
141
 
122
142
  eagle_db_pipe <<SQL
123
- INSERT INTO claude_plans (project, file_path, title, content, content_hash, origin_session_id)
124
- VALUES ('$proj_sql', '$fp_sql', '$title_sql', '$content_sql', '$hash_sql', '$origin_sql')
143
+ INSERT INTO agent_plans (project, file_path, title, content, content_hash, origin_session_id, origin_agent)
144
+ VALUES ('$proj_sql', '$fp_sql', '$title_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql')
125
145
  ON CONFLICT(file_path) DO UPDATE SET
126
146
  title = excluded.title,
127
147
  content = excluded.content,
128
148
  content_hash = excluded.content_hash,
129
- origin_session_id = COALESCE(NULLIF(excluded.origin_session_id, ''), claude_plans.origin_session_id),
130
- project = CASE WHEN excluded.project != '' THEN excluded.project ELSE claude_plans.project END,
149
+ origin_session_id = COALESCE(NULLIF(excluded.origin_session_id, ''), agent_plans.origin_session_id),
150
+ origin_agent = COALESCE(NULLIF(excluded.origin_agent, ''), agent_plans.origin_agent),
151
+ project = CASE WHEN excluded.project != '' THEN excluded.project ELSE agent_plans.project END,
131
152
  updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
132
- WHERE claude_plans.content_hash != excluded.content_hash;
153
+ WHERE agent_plans.content_hash != excluded.content_hash;
133
154
  SQL
134
155
  }
135
156
 
136
- eagle_search_claude_plans() {
157
+ eagle_search_agent_plans() {
137
158
  local query; query=$(eagle_fts_sanitize "$1")
138
159
  if [ -z "$query" ]; then
139
160
  echo "Search query is empty after sanitization. Try a different search term." >&2
@@ -151,16 +172,16 @@ eagle_search_claude_plans() {
151
172
 
152
173
  eagle_db "SELECT p.title, p.project,
153
174
  replace(substr(p.content, 1, 200), char(10), ' '),
154
- p.file_path, p.updated_at
155
- FROM claude_plans p
156
- JOIN claude_plans_fts f ON f.rowid = p.id
157
- WHERE claude_plans_fts MATCH '$query'
175
+ p.file_path, p.updated_at, p.origin_agent
176
+ FROM agent_plans p
177
+ JOIN agent_plans_fts f ON f.rowid = p.id
178
+ WHERE agent_plans_fts MATCH '$query'
158
179
  $where_clause
159
180
  ORDER BY rank
160
181
  LIMIT $limit;"
161
182
  }
162
183
 
163
- eagle_list_claude_plans() {
184
+ eagle_list_agent_plans() {
164
185
  local project="${1:-}"
165
186
  local limit; limit=$(eagle_sql_int "${2:-20}")
166
187
 
@@ -170,17 +191,18 @@ eagle_list_claude_plans() {
170
191
  where_clause="WHERE project = '$project'"
171
192
  fi
172
193
 
173
- eagle_db "SELECT title, project, file_path, updated_at
174
- FROM claude_plans
194
+ eagle_db "SELECT title, project, file_path, updated_at, origin_agent
195
+ FROM agent_plans
175
196
  $where_clause
176
197
  ORDER BY updated_at DESC
177
198
  LIMIT $limit;"
178
199
  }
179
200
 
180
- eagle_capture_claude_task() {
201
+ eagle_capture_agent_task() {
181
202
  local file_path="$1"
182
203
  local session_id="${2:-}"
183
204
  local project="${3:-}"
205
+ local agent="${4:-$(eagle_agent_source)}"
184
206
 
185
207
  [ ! -f "$file_path" ] && return 0
186
208
 
@@ -201,7 +223,7 @@ eagle_capture_claude_task() {
201
223
 
202
224
  [ -z "$task_id" ] && return 0
203
225
 
204
- local fp_sql proj_sql sid_sql tid_sql subj_sql desc_sql af_sql status_sql blocks_sql bb_sql hash_sql
226
+ local fp_sql proj_sql sid_sql tid_sql subj_sql desc_sql af_sql status_sql blocks_sql bb_sql hash_sql agent_sql
205
227
  fp_sql=$(eagle_sql_escape "$file_path")
206
228
  proj_sql=$(eagle_sql_escape "$project")
207
229
  sid_sql=$(eagle_sql_escape "$session_id")
@@ -213,10 +235,11 @@ eagle_capture_claude_task() {
213
235
  blocks_sql=$(eagle_sql_escape "$blocks")
214
236
  bb_sql=$(eagle_sql_escape "$blocked_by")
215
237
  hash_sql=$(eagle_sql_escape "$chash")
238
+ agent_sql=$(eagle_sql_escape "$agent")
216
239
 
217
240
  eagle_db_pipe <<SQL
218
- INSERT INTO claude_tasks (project, source_session_id, source_task_id, file_path, subject, description, active_form, status, blocks, blocked_by, content_hash)
219
- VALUES ('$proj_sql', '$sid_sql', '$tid_sql', '$fp_sql', '$subj_sql', '$desc_sql', '$af_sql', '$status_sql', '$blocks_sql', '$bb_sql', '$hash_sql')
241
+ 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)
242
+ 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')
220
243
  ON CONFLICT(file_path) DO UPDATE SET
221
244
  subject = excluded.subject,
222
245
  description = excluded.description,
@@ -225,13 +248,14 @@ ON CONFLICT(file_path) DO UPDATE SET
225
248
  blocks = excluded.blocks,
226
249
  blocked_by = excluded.blocked_by,
227
250
  content_hash = excluded.content_hash,
228
- project = CASE WHEN excluded.project != '' THEN excluded.project ELSE claude_tasks.project END,
251
+ origin_agent = excluded.origin_agent,
252
+ project = CASE WHEN excluded.project != '' THEN excluded.project ELSE agent_tasks.project END,
229
253
  updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
230
- WHERE claude_tasks.content_hash != excluded.content_hash;
254
+ WHERE agent_tasks.content_hash != excluded.content_hash;
231
255
  SQL
232
256
  }
233
257
 
234
- eagle_list_claude_tasks() {
258
+ eagle_list_agent_tasks() {
235
259
  local project="${1:-}"
236
260
  local limit; limit=$(eagle_sql_int "${2:-20}")
237
261
 
@@ -241,14 +265,14 @@ eagle_list_claude_tasks() {
241
265
  where_clause="WHERE project = '$project'"
242
266
  fi
243
267
 
244
- eagle_db "SELECT subject, status, source_session_id, source_task_id, updated_at
245
- FROM claude_tasks
268
+ eagle_db "SELECT subject, status, source_session_id, source_task_id, updated_at, origin_agent
269
+ FROM agent_tasks
246
270
  $where_clause
247
271
  ORDER BY updated_at DESC
248
272
  LIMIT $limit;"
249
273
  }
250
274
 
251
- eagle_search_claude_tasks() {
275
+ eagle_search_agent_tasks() {
252
276
  local query; query=$(eagle_fts_sanitize "$1")
253
277
  if [ -z "$query" ]; then
254
278
  echo "Search query is empty after sanitization. Try a different search term." >&2
@@ -266,11 +290,23 @@ eagle_search_claude_tasks() {
266
290
 
267
291
  eagle_db "SELECT t.subject, t.status,
268
292
  replace(substr(t.description, 1, 200), char(10), ' '),
269
- t.source_session_id, t.source_task_id, t.updated_at
270
- FROM claude_tasks t
271
- JOIN claude_tasks_fts f ON f.rowid = t.id
272
- WHERE claude_tasks_fts MATCH '$query'
293
+ t.source_session_id, t.source_task_id, t.updated_at, t.origin_agent
294
+ FROM agent_tasks t
295
+ JOIN agent_tasks_fts f ON f.rowid = t.id
296
+ WHERE agent_tasks_fts MATCH '$query'
273
297
  $where_clause
274
298
  ORDER BY rank
275
299
  LIMIT $limit;"
276
300
  }
301
+
302
+ # Backward-compatible helper names for older installed hooks/scripts that source
303
+ # this library during an update window. Runtime code should use agent_* helpers.
304
+ eagle_capture_claude_memory() { eagle_capture_agent_memory "$@"; }
305
+ eagle_search_claude_memories() { eagle_search_agent_memories "$@"; }
306
+ eagle_list_claude_memories() { eagle_list_agent_memories "$@"; }
307
+ eagle_capture_claude_plan() { eagle_capture_agent_plan "$@"; }
308
+ eagle_search_claude_plans() { eagle_search_agent_plans "$@"; }
309
+ eagle_list_claude_plans() { eagle_list_agent_plans "$@"; }
310
+ eagle_capture_claude_task() { eagle_capture_agent_task "$@"; }
311
+ eagle_list_claude_tasks() { eagle_list_agent_tasks "$@"; }
312
+ eagle_search_claude_tasks() { eagle_search_agent_tasks "$@"; }
@@ -15,6 +15,7 @@ eagle_insert_observation() {
15
15
  local output_bytes="${7:-}"
16
16
  local output_lines="${8:-}"
17
17
  local command_category; command_category=$(eagle_sql_escape "${9:-}")
18
+ local agent; agent=$(eagle_sql_escape "${10:-$(eagle_agent_source)}")
18
19
 
19
20
  local extra_cols=""
20
21
  local extra_vals=""
@@ -23,8 +24,8 @@ eagle_insert_observation() {
23
24
  extra_vals=", $(eagle_sql_int "$output_bytes"), $(eagle_sql_int "$output_lines"), '$command_category'"
24
25
  fi
25
26
 
26
- eagle_db "INSERT INTO observations (session_id, project, tool_name, tool_input_summary, files_read, files_modified${extra_cols})
27
- SELECT '$session_id', '$project', '$tool_name', '$tool_input_summary', '$files_read', '$files_modified'${extra_vals}
27
+ eagle_db "INSERT INTO observations (session_id, project, agent, tool_name, tool_input_summary, files_read, files_modified${extra_cols})
28
+ SELECT '$session_id', '$project', '$agent', '$tool_name', '$tool_input_summary', '$files_read', '$files_modified'${extra_vals}
28
29
  WHERE NOT EXISTS (
29
30
  SELECT 1 FROM observations
30
31
  WHERE session_id = '$session_id'
@@ -11,13 +11,15 @@ eagle_upsert_session() {
11
11
  local cwd; cwd=$(eagle_sql_escape "${3:-}")
12
12
  local model; model=$(eagle_sql_escape "${4:-}")
13
13
  local source; source=$(eagle_sql_escape "${5:-}")
14
+ local agent; agent=$(eagle_sql_escape "${6:-$(eagle_agent_source)}")
14
15
 
15
- eagle_db "INSERT INTO sessions (id, project, cwd, model, source, last_activity_at)
16
- VALUES ('$session_id', '$project', '$cwd', '$model', '$source', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
16
+ eagle_db "INSERT INTO sessions (id, project, cwd, model, source, agent, last_activity_at)
17
+ VALUES ('$session_id', '$project', '$cwd', '$model', '$source', '$agent', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
17
18
  ON CONFLICT(id) DO UPDATE SET
18
19
  cwd = COALESCE(NULLIF(excluded.cwd, ''), sessions.cwd),
19
20
  model = COALESCE(NULLIF(excluded.model, ''), sessions.model),
20
21
  source = COALESCE(NULLIF(excluded.source, ''), sessions.source),
22
+ agent = COALESCE(NULLIF(excluded.agent, ''), sessions.agent),
21
23
  status = 'active',
22
24
  last_activity_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');"
23
25
  }
@@ -43,13 +45,15 @@ eagle_get_project_stats() {
43
45
  local project; project=$(eagle_sql_escape "$1")
44
46
  eagle_db_pipe <<SQL
45
47
  SELECT 'sessions|' || COUNT(*) FROM sessions WHERE project = '$project';
48
+ SELECT 'sessions_claude|' || COUNT(*) FROM sessions WHERE project = '$project' AND agent = 'claude-code';
49
+ SELECT 'sessions_codex|' || COUNT(*) FROM sessions WHERE project = '$project' AND agent = 'codex';
46
50
  SELECT 'summaries|' || COUNT(*) FROM summaries WHERE project = '$project';
47
51
  SELECT 'with_summaries|' || COUNT(*) FROM summaries WHERE project = '$project' AND request IS NOT NULL AND request != '';
48
- SELECT 'memories|' || COUNT(*) FROM claude_memories WHERE project = '$project';
49
- SELECT 'plans|' || COUNT(*) FROM claude_plans WHERE project = '$project';
50
- SELECT 'tasks_pending|' || COUNT(*) FROM claude_tasks WHERE project = '$project' AND status = 'pending';
51
- SELECT 'tasks_progress|' || COUNT(*) FROM claude_tasks WHERE project = '$project' AND status = 'in_progress';
52
- SELECT 'tasks_done|' || COUNT(*) FROM claude_tasks WHERE project = '$project' AND status = 'completed';
52
+ SELECT 'memories|' || COUNT(*) FROM agent_memories WHERE project = '$project';
53
+ SELECT 'plans|' || COUNT(*) FROM agent_plans WHERE project = '$project';
54
+ SELECT 'tasks_pending|' || COUNT(*) FROM agent_tasks WHERE project = '$project' AND status = 'pending';
55
+ SELECT 'tasks_progress|' || COUNT(*) FROM agent_tasks WHERE project = '$project' AND status = 'in_progress';
56
+ SELECT 'tasks_done|' || COUNT(*) FROM agent_tasks WHERE project = '$project' AND status = 'completed';
53
57
  SELECT 'chunks|' || COUNT(*) FROM code_chunks WHERE project = '$project';
54
58
  SELECT 'observations|' || COUNT(*) FROM observations WHERE session_id IN (SELECT id FROM sessions WHERE project = '$project');
55
59
  SELECT 'last_active|' || COALESCE(MAX(date(COALESCE(last_activity_at, started_at))), 'never') FROM sessions WHERE project = '$project';
@@ -19,12 +19,14 @@ eagle_insert_summary() {
19
19
  local decisions; decisions=$(eagle_sql_escape "${11:-}")
20
20
  local gotchas; gotchas=$(eagle_sql_escape "${12:-}")
21
21
  local key_files; key_files=$(eagle_sql_escape "${13:-}")
22
+ local agent; agent=$(eagle_sql_escape "${14:-$(eagle_agent_source)}")
22
23
 
23
24
  eagle_db_pipe <<SQL
24
- INSERT INTO summaries (session_id, project, request, investigated, learned, completed, next_steps, files_read, files_modified, notes, decisions, gotchas, key_files)
25
+ INSERT INTO summaries (session_id, project, agent, request, investigated, learned, completed, next_steps, files_read, files_modified, notes, decisions, gotchas, key_files)
25
26
  VALUES (
26
27
  '$session_id',
27
28
  '$project',
29
+ '$agent',
28
30
  '$request',
29
31
  '$investigated',
30
32
  '$learned',
@@ -39,6 +41,7 @@ VALUES (
39
41
  )
40
42
  ON CONFLICT(session_id) DO UPDATE SET
41
43
  project = excluded.project,
44
+ agent = COALESCE(NULLIF(excluded.agent, ''), summaries.agent),
42
45
  request = COALESCE(NULLIF(excluded.request, ''), summaries.request),
43
46
  investigated = COALESCE(NULLIF(excluded.investigated, ''), summaries.investigated),
44
47
  learned = COALESCE(NULLIF(excluded.learned, ''), summaries.learned),
@@ -57,7 +60,7 @@ eagle_get_recent_summaries() {
57
60
  local project; project=$(eagle_sql_escape "$1")
58
61
  local limit; limit=$(eagle_sql_int "${2:-5}")
59
62
 
60
- eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at, s.decisions, s.gotchas, s.key_files
63
+ eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at, s.decisions, s.gotchas, s.key_files, s.agent
61
64
  FROM summaries s
62
65
  WHERE s.project = '$project'
63
66
  AND s.request NOT LIKE '%<local-command-caveat>%'
@@ -77,7 +80,7 @@ eagle_search_summaries() {
77
80
  where_clause="AND s.project = '$project'"
78
81
  fi
79
82
 
80
- eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at, s.project, s.decisions, s.gotchas, s.key_files
83
+ eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at, s.project, s.decisions, s.gotchas, s.key_files, s.agent
81
84
  FROM summaries s
82
85
  JOIN summaries_fts f ON f.rowid = s.id
83
86
  WHERE summaries_fts MATCH '$query'
@@ -160,9 +163,9 @@ eagle_search_stale_memories() {
160
163
  local project; project=$(eagle_sql_escape "$1")
161
164
  local fts_query; fts_query=$(eagle_sql_escape "$2")
162
165
  eagle_db "SELECT m.memory_name
163
- FROM claude_memories m
164
- JOIN claude_memories_fts f ON f.rowid = m.id
165
- WHERE claude_memories_fts MATCH '$fts_query'
166
+ FROM agent_memories m
167
+ JOIN agent_memories_fts f ON f.rowid = m.id
168
+ WHERE agent_memories_fts MATCH '$fts_query'
166
169
  AND m.project = '$project'
167
170
  LIMIT 1;"
168
171
  }