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.
- package/README.md +49 -15
- package/db/023_guardrails.sql +3 -2
- package/db/024_guardrails_unique.sql +46 -0
- package/db/025_pending_feature_verifications.sql +30 -0
- package/db/026_agent_source.sql +18 -0
- package/db/027_feature_verification_fingerprints.sql +9 -0
- package/db/028_agent_artifact_tables.sql +124 -0
- package/hooks/post-tool-use.sh +42 -13
- package/hooks/pre-tool-use.sh +107 -14
- package/hooks/session-end.sh +3 -1
- package/hooks/session-start.sh +64 -15
- package/hooks/stop.sh +115 -21
- package/hooks/user-prompt-submit.sh +14 -5
- package/lib/codex-hooks.sh +194 -0
- package/lib/common.sh +345 -0
- package/lib/db-backfill.sh +3 -3
- package/lib/db-features.sh +222 -0
- package/lib/db-guardrails.sh +2 -1
- package/lib/db-mirrors.sh +79 -43
- package/lib/db-observations.sh +3 -2
- package/lib/db-sessions.sh +11 -7
- package/lib/db-summaries.sh +9 -6
- package/lib/hooks-posttool.sh +8 -6
- package/lib/provider.sh +190 -4
- package/package.json +7 -3
- package/scripts/config.sh +2 -0
- package/scripts/feature.sh +70 -2
- package/scripts/guard.sh +4 -1
- package/scripts/health.sh +5 -1
- package/scripts/help.sh +13 -8
- package/scripts/install.sh +130 -76
- package/scripts/memories.sh +71 -45
- package/scripts/refresh.sh +3 -3
- package/scripts/search.sh +57 -47
- package/scripts/statusline-em.sh +1 -1
- package/scripts/tasks.sh +186 -15
- package/scripts/uninstall.sh +7 -0
- package/scripts/update.sh +51 -7
- package/skills/eagle-mem-memories/SKILL.md +13 -13
- package/skills/eagle-mem-tasks/SKILL.md +21 -15
package/lib/db-features.sh
CHANGED
|
@@ -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")
|
package/lib/db-guardrails.sh
CHANGED
|
@@ -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
|
|
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 —
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
70
|
+
WHERE agent_memories.content_hash != excluded.content_hash;
|
|
53
71
|
SQL
|
|
54
72
|
}
|
|
55
73
|
|
|
56
|
-
|
|
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
|
|
76
|
-
JOIN
|
|
77
|
-
WHERE
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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, ''),
|
|
130
|
-
|
|
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
|
|
153
|
+
WHERE agent_plans.content_hash != excluded.content_hash;
|
|
133
154
|
SQL
|
|
134
155
|
}
|
|
135
156
|
|
|
136
|
-
|
|
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
|
|
156
|
-
JOIN
|
|
157
|
-
WHERE
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
254
|
+
WHERE agent_tasks.content_hash != excluded.content_hash;
|
|
231
255
|
SQL
|
|
232
256
|
}
|
|
233
257
|
|
|
234
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
271
|
-
JOIN
|
|
272
|
-
WHERE
|
|
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 "$@"; }
|
package/lib/db-observations.sh
CHANGED
|
@@ -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'
|
package/lib/db-sessions.sh
CHANGED
|
@@ -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
|
|
49
|
-
SELECT 'plans|' || COUNT(*) FROM
|
|
50
|
-
SELECT 'tasks_pending|' || COUNT(*) FROM
|
|
51
|
-
SELECT 'tasks_progress|' || COUNT(*) FROM
|
|
52
|
-
SELECT 'tasks_done|' || COUNT(*) FROM
|
|
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';
|
package/lib/db-summaries.sh
CHANGED
|
@@ -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
|
|
164
|
-
JOIN
|
|
165
|
-
WHERE
|
|
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
|
}
|