eagle-mem 4.7.0 → 4.8.0
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 +56 -12
- package/bin/eagle-mem +1 -0
- package/db/028_agent_artifact_tables.sql +124 -0
- package/db/029_orchestration_lanes.sql +45 -0
- package/db/030_orchestration_lane_scope.sql +88 -0
- package/db/031_orchestration_workers.sql +20 -0
- package/db/032_orchestration_run_keys.sql +9 -0
- package/hooks/post-tool-use.sh +2 -1
- package/hooks/pre-tool-use.sh +25 -1
- package/hooks/session-end.sh +2 -1
- package/hooks/session-start.sh +103 -13
- package/hooks/stop.sh +15 -13
- package/hooks/user-prompt-submit.sh +71 -12
- package/lib/common.sh +173 -2
- package/lib/db-backfill.sh +3 -3
- package/lib/db-mirrors.sh +59 -32
- package/lib/db-observations.sh +7 -0
- package/lib/db-sessions.sh +12 -6
- package/lib/db-summaries.sh +9 -5
- package/lib/hooks-posttool.sh +4 -4
- package/lib/provider.sh +224 -4
- package/package.json +3 -1
- package/scripts/config.sh +32 -0
- package/scripts/health.sh +71 -1
- package/scripts/help.sh +18 -7
- package/scripts/install.sh +12 -0
- package/scripts/memories.sh +50 -27
- package/scripts/orchestrate.sh +1268 -0
- package/scripts/refresh.sh +3 -3
- package/scripts/search.sh +21 -19
- package/scripts/statusline-em.sh +1 -1
- package/scripts/tasks.sh +186 -15
- package/scripts/update.sh +20 -1
- package/skills/eagle-mem-memories/SKILL.md +13 -13
- package/skills/eagle-mem-orchestrate/SKILL.md +149 -0
- package/skills/eagle-mem-tasks/SKILL.md +23 -15
package/lib/db-mirrors.sh
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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:-}"
|
|
@@ -19,6 +19,9 @@ eagle_capture_claude_memory() {
|
|
|
19
19
|
local fm body
|
|
20
20
|
fm=$(awk '/^---$/{c++; next} c==1' "$file_path")
|
|
21
21
|
body=$(awk '/^---$/{c++; next} c>=2' "$file_path")
|
|
22
|
+
if [ -z "$body" ] && [ -z "$fm" ]; then
|
|
23
|
+
body=$(cat "$file_path")
|
|
24
|
+
fi
|
|
22
25
|
|
|
23
26
|
_fm_field() { printf '%s\n' "$fm" | awk -F': *' -v k="$1" '$1==k{sub(/^[^:]+: */,""); gsub(/^"|"$/,""); print; exit}'; }
|
|
24
27
|
|
|
@@ -29,6 +32,18 @@ eagle_capture_claude_memory() {
|
|
|
29
32
|
morigin=$(_fm_field "originSessionId")
|
|
30
33
|
[ -z "$morigin" ] && morigin="$session_id"
|
|
31
34
|
|
|
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
|
+
|
|
32
47
|
local fp_sql proj_sql name_sql desc_sql type_sql content_sql hash_sql origin_sql agent_sql
|
|
33
48
|
fp_sql=$(eagle_sql_escape "$file_path")
|
|
34
49
|
proj_sql=$(eagle_sql_escape "$project")
|
|
@@ -41,7 +56,7 @@ eagle_capture_claude_memory() {
|
|
|
41
56
|
agent_sql=$(eagle_sql_escape "$agent")
|
|
42
57
|
|
|
43
58
|
eagle_db_pipe <<SQL
|
|
44
|
-
INSERT INTO
|
|
59
|
+
INSERT INTO agent_memories (project, file_path, memory_name, description, memory_type, content, content_hash, origin_session_id, origin_agent)
|
|
45
60
|
VALUES ('$proj_sql', '$fp_sql', '$name_sql', '$desc_sql', '$type_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql')
|
|
46
61
|
ON CONFLICT(file_path) DO UPDATE SET
|
|
47
62
|
memory_name = excluded.memory_name,
|
|
@@ -52,11 +67,11 @@ ON CONFLICT(file_path) DO UPDATE SET
|
|
|
52
67
|
origin_session_id = excluded.origin_session_id,
|
|
53
68
|
origin_agent = excluded.origin_agent,
|
|
54
69
|
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
55
|
-
WHERE
|
|
70
|
+
WHERE agent_memories.content_hash != excluded.content_hash;
|
|
56
71
|
SQL
|
|
57
72
|
}
|
|
58
73
|
|
|
59
|
-
|
|
74
|
+
eagle_search_agent_memories() {
|
|
60
75
|
local query; query=$(eagle_fts_sanitize "$1")
|
|
61
76
|
if [ -z "$query" ]; then
|
|
62
77
|
echo "Search query is empty after sanitization. Try a different search term." >&2
|
|
@@ -75,15 +90,15 @@ eagle_search_claude_memories() {
|
|
|
75
90
|
eagle_db "SELECT m.memory_name, m.memory_type, m.description,
|
|
76
91
|
replace(substr(m.content, 1, 200), char(10), ' '),
|
|
77
92
|
m.file_path, m.updated_at, m.origin_agent
|
|
78
|
-
FROM
|
|
79
|
-
JOIN
|
|
80
|
-
WHERE
|
|
93
|
+
FROM agent_memories m
|
|
94
|
+
JOIN agent_memories_fts f ON f.rowid = m.id
|
|
95
|
+
WHERE agent_memories_fts MATCH '$query'
|
|
81
96
|
$where_clause
|
|
82
97
|
ORDER BY rank
|
|
83
98
|
LIMIT $limit;"
|
|
84
99
|
}
|
|
85
100
|
|
|
86
|
-
|
|
101
|
+
eagle_list_agent_memories() {
|
|
87
102
|
local project="${1:-}"
|
|
88
103
|
local limit; limit=$(eagle_sql_int "${2:-20}")
|
|
89
104
|
|
|
@@ -94,13 +109,13 @@ eagle_list_claude_memories() {
|
|
|
94
109
|
fi
|
|
95
110
|
|
|
96
111
|
eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_agent
|
|
97
|
-
FROM
|
|
112
|
+
FROM agent_memories
|
|
98
113
|
$where_clause
|
|
99
114
|
ORDER BY updated_at DESC
|
|
100
115
|
LIMIT $limit;"
|
|
101
116
|
}
|
|
102
117
|
|
|
103
|
-
|
|
118
|
+
eagle_capture_agent_plan() {
|
|
104
119
|
local file_path="$1"
|
|
105
120
|
local session_id="${2:-}"
|
|
106
121
|
local project="${3:-}"
|
|
@@ -125,21 +140,21 @@ eagle_capture_claude_plan() {
|
|
|
125
140
|
agent_sql=$(eagle_sql_escape "$agent")
|
|
126
141
|
|
|
127
142
|
eagle_db_pipe <<SQL
|
|
128
|
-
INSERT INTO
|
|
143
|
+
INSERT INTO agent_plans (project, file_path, title, content, content_hash, origin_session_id, origin_agent)
|
|
129
144
|
VALUES ('$proj_sql', '$fp_sql', '$title_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql')
|
|
130
145
|
ON CONFLICT(file_path) DO UPDATE SET
|
|
131
146
|
title = excluded.title,
|
|
132
147
|
content = excluded.content,
|
|
133
148
|
content_hash = excluded.content_hash,
|
|
134
|
-
origin_session_id = COALESCE(NULLIF(excluded.origin_session_id, ''),
|
|
135
|
-
origin_agent = COALESCE(NULLIF(excluded.origin_agent, ''),
|
|
136
|
-
project = CASE WHEN excluded.project != '' THEN excluded.project ELSE
|
|
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,
|
|
137
152
|
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
138
|
-
WHERE
|
|
153
|
+
WHERE agent_plans.content_hash != excluded.content_hash;
|
|
139
154
|
SQL
|
|
140
155
|
}
|
|
141
156
|
|
|
142
|
-
|
|
157
|
+
eagle_search_agent_plans() {
|
|
143
158
|
local query; query=$(eagle_fts_sanitize "$1")
|
|
144
159
|
if [ -z "$query" ]; then
|
|
145
160
|
echo "Search query is empty after sanitization. Try a different search term." >&2
|
|
@@ -158,15 +173,15 @@ eagle_search_claude_plans() {
|
|
|
158
173
|
eagle_db "SELECT p.title, p.project,
|
|
159
174
|
replace(substr(p.content, 1, 200), char(10), ' '),
|
|
160
175
|
p.file_path, p.updated_at, p.origin_agent
|
|
161
|
-
FROM
|
|
162
|
-
JOIN
|
|
163
|
-
WHERE
|
|
176
|
+
FROM agent_plans p
|
|
177
|
+
JOIN agent_plans_fts f ON f.rowid = p.id
|
|
178
|
+
WHERE agent_plans_fts MATCH '$query'
|
|
164
179
|
$where_clause
|
|
165
180
|
ORDER BY rank
|
|
166
181
|
LIMIT $limit;"
|
|
167
182
|
}
|
|
168
183
|
|
|
169
|
-
|
|
184
|
+
eagle_list_agent_plans() {
|
|
170
185
|
local project="${1:-}"
|
|
171
186
|
local limit; limit=$(eagle_sql_int "${2:-20}")
|
|
172
187
|
|
|
@@ -177,13 +192,13 @@ eagle_list_claude_plans() {
|
|
|
177
192
|
fi
|
|
178
193
|
|
|
179
194
|
eagle_db "SELECT title, project, file_path, updated_at, origin_agent
|
|
180
|
-
FROM
|
|
195
|
+
FROM agent_plans
|
|
181
196
|
$where_clause
|
|
182
197
|
ORDER BY updated_at DESC
|
|
183
198
|
LIMIT $limit;"
|
|
184
199
|
}
|
|
185
200
|
|
|
186
|
-
|
|
201
|
+
eagle_capture_agent_task() {
|
|
187
202
|
local file_path="$1"
|
|
188
203
|
local session_id="${2:-}"
|
|
189
204
|
local project="${3:-}"
|
|
@@ -223,7 +238,7 @@ eagle_capture_claude_task() {
|
|
|
223
238
|
agent_sql=$(eagle_sql_escape "$agent")
|
|
224
239
|
|
|
225
240
|
eagle_db_pipe <<SQL
|
|
226
|
-
INSERT INTO
|
|
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)
|
|
227
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')
|
|
228
243
|
ON CONFLICT(file_path) DO UPDATE SET
|
|
229
244
|
subject = excluded.subject,
|
|
@@ -234,13 +249,13 @@ ON CONFLICT(file_path) DO UPDATE SET
|
|
|
234
249
|
blocked_by = excluded.blocked_by,
|
|
235
250
|
content_hash = excluded.content_hash,
|
|
236
251
|
origin_agent = excluded.origin_agent,
|
|
237
|
-
project = CASE WHEN excluded.project != '' THEN excluded.project ELSE
|
|
252
|
+
project = CASE WHEN excluded.project != '' THEN excluded.project ELSE agent_tasks.project END,
|
|
238
253
|
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
239
|
-
WHERE
|
|
254
|
+
WHERE agent_tasks.content_hash != excluded.content_hash;
|
|
240
255
|
SQL
|
|
241
256
|
}
|
|
242
257
|
|
|
243
|
-
|
|
258
|
+
eagle_list_agent_tasks() {
|
|
244
259
|
local project="${1:-}"
|
|
245
260
|
local limit; limit=$(eagle_sql_int "${2:-20}")
|
|
246
261
|
|
|
@@ -251,13 +266,13 @@ eagle_list_claude_tasks() {
|
|
|
251
266
|
fi
|
|
252
267
|
|
|
253
268
|
eagle_db "SELECT subject, status, source_session_id, source_task_id, updated_at, origin_agent
|
|
254
|
-
FROM
|
|
269
|
+
FROM agent_tasks
|
|
255
270
|
$where_clause
|
|
256
271
|
ORDER BY updated_at DESC
|
|
257
272
|
LIMIT $limit;"
|
|
258
273
|
}
|
|
259
274
|
|
|
260
|
-
|
|
275
|
+
eagle_search_agent_tasks() {
|
|
261
276
|
local query; query=$(eagle_fts_sanitize "$1")
|
|
262
277
|
if [ -z "$query" ]; then
|
|
263
278
|
echo "Search query is empty after sanitization. Try a different search term." >&2
|
|
@@ -276,10 +291,22 @@ eagle_search_claude_tasks() {
|
|
|
276
291
|
eagle_db "SELECT t.subject, t.status,
|
|
277
292
|
replace(substr(t.description, 1, 200), char(10), ' '),
|
|
278
293
|
t.source_session_id, t.source_task_id, t.updated_at, t.origin_agent
|
|
279
|
-
FROM
|
|
280
|
-
JOIN
|
|
281
|
-
WHERE
|
|
294
|
+
FROM agent_tasks t
|
|
295
|
+
JOIN agent_tasks_fts f ON f.rowid = t.id
|
|
296
|
+
WHERE agent_tasks_fts MATCH '$query'
|
|
282
297
|
$where_clause
|
|
283
298
|
ORDER BY rank
|
|
284
299
|
LIMIT $limit;"
|
|
285
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
|
@@ -70,6 +70,13 @@ eagle_search_code_chunks() {
|
|
|
70
70
|
JOIN code_chunks_fts f ON f.rowid = c.id
|
|
71
71
|
WHERE code_chunks_fts MATCH '$query'
|
|
72
72
|
AND c.project = '$project'
|
|
73
|
+
AND c.file_path NOT LIKE '.playwright-mcp/%'
|
|
74
|
+
AND c.file_path NOT LIKE '.eagle-worktrees/%'
|
|
75
|
+
AND c.file_path NOT LIKE '.git/%'
|
|
76
|
+
AND c.file_path NOT LIKE 'node_modules/%'
|
|
77
|
+
AND c.file_path NOT LIKE 'dist/%'
|
|
78
|
+
AND c.file_path NOT LIKE 'build/%'
|
|
79
|
+
AND c.file_path NOT LIKE 'coverage/%'
|
|
73
80
|
ORDER BY rank
|
|
74
81
|
LIMIT $limit;"
|
|
75
82
|
}
|
package/lib/db-sessions.sh
CHANGED
|
@@ -49,15 +49,21 @@ SELECT 'sessions_claude|' || COUNT(*) FROM sessions WHERE project = '$project' A
|
|
|
49
49
|
SELECT 'sessions_codex|' || COUNT(*) FROM sessions WHERE project = '$project' AND agent = 'codex';
|
|
50
50
|
SELECT 'summaries|' || COUNT(*) FROM summaries WHERE project = '$project';
|
|
51
51
|
SELECT 'with_summaries|' || COUNT(*) FROM summaries WHERE project = '$project' AND request IS NOT NULL AND request != '';
|
|
52
|
-
SELECT 'memories|' || COUNT(*) FROM
|
|
53
|
-
SELECT 'plans|' || COUNT(*) FROM
|
|
54
|
-
SELECT 'tasks_pending|' || COUNT(*) FROM
|
|
55
|
-
SELECT 'tasks_progress|' || COUNT(*) FROM
|
|
56
|
-
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';
|
|
57
57
|
SELECT 'chunks|' || COUNT(*) FROM code_chunks WHERE project = '$project';
|
|
58
58
|
SELECT 'observations|' || COUNT(*) FROM observations WHERE session_id IN (SELECT id FROM sessions WHERE project = '$project');
|
|
59
59
|
SELECT 'last_active|' || COALESCE(MAX(date(COALESCE(last_activity_at, started_at))), 'never') FROM sessions WHERE project = '$project';
|
|
60
|
-
SELECT 'last_summary|' || COALESCE((SELECT substr(request, 1, 60)
|
|
60
|
+
SELECT 'last_summary|' || COALESCE((SELECT substr(request, 1, 60)
|
|
61
|
+
FROM summaries
|
|
62
|
+
WHERE project = '$project'
|
|
63
|
+
AND COALESCE(request, '') NOT LIKE '# AGENTS.md instructions%'
|
|
64
|
+
AND COALESCE(request, '') NOT LIKE '<environment_context>%'
|
|
65
|
+
ORDER BY created_at DESC
|
|
66
|
+
LIMIT 1), '');
|
|
61
67
|
SQL
|
|
62
68
|
}
|
|
63
69
|
|
package/lib/db-summaries.sh
CHANGED
|
@@ -63,7 +63,9 @@ eagle_get_recent_summaries() {
|
|
|
63
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
|
|
64
64
|
FROM summaries s
|
|
65
65
|
WHERE s.project = '$project'
|
|
66
|
-
AND s.request NOT LIKE '%<local-command-caveat>%'
|
|
66
|
+
AND COALESCE(s.request, '') NOT LIKE '%<local-command-caveat>%'
|
|
67
|
+
AND COALESCE(s.request, '') NOT LIKE '# AGENTS.md instructions%'
|
|
68
|
+
AND COALESCE(s.request, '') NOT LIKE '<environment_context>%'
|
|
67
69
|
ORDER BY s.created_at DESC
|
|
68
70
|
LIMIT $limit;"
|
|
69
71
|
}
|
|
@@ -84,7 +86,9 @@ eagle_search_summaries() {
|
|
|
84
86
|
FROM summaries s
|
|
85
87
|
JOIN summaries_fts f ON f.rowid = s.id
|
|
86
88
|
WHERE summaries_fts MATCH '$query'
|
|
87
|
-
AND s.request NOT LIKE '%<local-command-caveat>%'
|
|
89
|
+
AND COALESCE(s.request, '') NOT LIKE '%<local-command-caveat>%'
|
|
90
|
+
AND COALESCE(s.request, '') NOT LIKE '# AGENTS.md instructions%'
|
|
91
|
+
AND COALESCE(s.request, '') NOT LIKE '<environment_context>%'
|
|
88
92
|
$where_clause
|
|
89
93
|
ORDER BY rank
|
|
90
94
|
LIMIT $limit;"
|
|
@@ -163,9 +167,9 @@ eagle_search_stale_memories() {
|
|
|
163
167
|
local project; project=$(eagle_sql_escape "$1")
|
|
164
168
|
local fts_query; fts_query=$(eagle_sql_escape "$2")
|
|
165
169
|
eagle_db "SELECT m.memory_name
|
|
166
|
-
FROM
|
|
167
|
-
JOIN
|
|
168
|
-
WHERE
|
|
170
|
+
FROM agent_memories m
|
|
171
|
+
JOIN agent_memories_fts f ON f.rowid = m.id
|
|
172
|
+
WHERE agent_memories_fts MATCH '$fts_query'
|
|
169
173
|
AND m.project = '$project'
|
|
170
174
|
LIMIT 1;"
|
|
171
175
|
}
|
package/lib/hooks-posttool.sh
CHANGED
|
@@ -19,12 +19,12 @@ eagle_posttool_mirror_writes() {
|
|
|
19
19
|
local mem_base
|
|
20
20
|
mem_base=$(basename "$fp")
|
|
21
21
|
if [ "$mem_base" != "MEMORY.md" ] && [ -f "$fp" ]; then
|
|
22
|
-
|
|
22
|
+
eagle_capture_agent_memory "$fp" "$session_id" "$project" "$agent"
|
|
23
23
|
fi
|
|
24
24
|
;;
|
|
25
25
|
"$EAGLE_CLAUDE_PLANS_DIR/"*.md)
|
|
26
26
|
if [ -f "$fp" ]; then
|
|
27
|
-
|
|
27
|
+
eagle_capture_agent_plan "$fp" "$session_id" "$project" "$agent"
|
|
28
28
|
fi
|
|
29
29
|
;;
|
|
30
30
|
esac
|
|
@@ -47,10 +47,10 @@ eagle_posttool_mirror_tasks() {
|
|
|
47
47
|
if [ -z "$task_id" ]; then
|
|
48
48
|
local newest
|
|
49
49
|
newest=$(ls -t "$task_dir"/*.json 2>/dev/null | head -1)
|
|
50
|
-
[ -n "$newest" ] && [ -f "$newest" ] &&
|
|
50
|
+
[ -n "$newest" ] && [ -f "$newest" ] && eagle_capture_agent_task "$newest" "$session_id" "$project" "$agent"
|
|
51
51
|
elif eagle_validate_session_id "$task_id"; then
|
|
52
52
|
local task_json="$task_dir/$task_id.json"
|
|
53
|
-
[ -f "$task_json" ] &&
|
|
53
|
+
[ -f "$task_json" ] && eagle_capture_agent_task "$task_json" "$session_id" "$project" "$agent"
|
|
54
54
|
fi
|
|
55
55
|
fi
|
|
56
56
|
fi
|
package/lib/provider.sh
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ═══════════════════════════════════════════════════════════
|
|
3
3
|
# Eagle Mem — LLM Provider Abstraction
|
|
4
|
-
# Config parsing + unified eagle_llm_call for Ollama/
|
|
4
|
+
# Config parsing + unified eagle_llm_call for Ollama/agent CLI/API providers
|
|
5
5
|
# ═══════════════════════════════════════════════════════════
|
|
6
6
|
|
|
7
7
|
EAGLE_CONFIG_FILE="${EAGLE_MEM_DIR}/config.toml"
|
|
@@ -121,12 +121,17 @@ eagle_config_init() {
|
|
|
121
121
|
local ollama_url="$EAGLE_DEFAULT_OLLAMA_URL"
|
|
122
122
|
local provider="none"
|
|
123
123
|
local model=""
|
|
124
|
+
local ollama_model="mistral"
|
|
124
125
|
|
|
125
126
|
local ollama_response
|
|
126
127
|
ollama_response=$(eagle_detect_ollama "$ollama_url")
|
|
127
128
|
if [ -n "$ollama_response" ]; then
|
|
128
129
|
provider="ollama"
|
|
129
130
|
model=$(eagle_ollama_best_model "$ollama_url")
|
|
131
|
+
ollama_model="$model"
|
|
132
|
+
elif command -v codex &>/dev/null || command -v claude &>/dev/null; then
|
|
133
|
+
provider="agent_cli"
|
|
134
|
+
model="native"
|
|
130
135
|
elif [ -n "${ANTHROPIC_API_KEY:-}" ]; then
|
|
131
136
|
provider="anthropic"
|
|
132
137
|
model="claude-haiku-4-5-20251001"
|
|
@@ -138,18 +143,38 @@ eagle_config_init() {
|
|
|
138
143
|
# Create config with restrictive permissions from the start (no TOCTOU window)
|
|
139
144
|
(
|
|
140
145
|
umask 077
|
|
146
|
+
mkdir -p "$EAGLE_MEM_DIR"
|
|
141
147
|
cat > "$EAGLE_CONFIG_FILE" << TOML
|
|
142
148
|
# Eagle Mem configuration
|
|
143
149
|
# Docs: https://github.com/eagleisbatman/eagle-mem
|
|
144
150
|
|
|
145
151
|
[provider]
|
|
146
152
|
# Which LLM provider to use for the curator and analysis features
|
|
147
|
-
# Options: "ollama" (free, local), "anthropic", "openai"
|
|
153
|
+
# Options: "ollama" (free, local), "agent_cli" (Codex/Claude CLI auth), "anthropic", "openai"
|
|
148
154
|
type = "$provider"
|
|
149
155
|
|
|
150
156
|
[ollama]
|
|
151
157
|
url = "$ollama_url"
|
|
152
|
-
model = "$
|
|
158
|
+
model = "$ollama_model"
|
|
159
|
+
|
|
160
|
+
[agent_cli]
|
|
161
|
+
# Uses the already-installed Codex/Claude CLI instead of direct API keys.
|
|
162
|
+
# preferred = "current" uses EAGLE_AGENT_SOURCE when hooks invoke Eagle Mem.
|
|
163
|
+
# If run manually with no agent source, it prefers Codex when available.
|
|
164
|
+
preferred = "current"
|
|
165
|
+
codex_model = ""
|
|
166
|
+
claude_model = ""
|
|
167
|
+
|
|
168
|
+
[orchestration]
|
|
169
|
+
# route = "opposite" means Codex coordinates Claude workers and Claude
|
|
170
|
+
# coordinates Codex workers by default.
|
|
171
|
+
route = "opposite"
|
|
172
|
+
auto_worktree = "true"
|
|
173
|
+
worktree_root = ""
|
|
174
|
+
codex_worker_model = "gpt-5.5"
|
|
175
|
+
codex_worker_effort = "xhigh"
|
|
176
|
+
claude_worker_model = "claude-opus-4-7"
|
|
177
|
+
claude_worker_effort = "xhigh"
|
|
153
178
|
|
|
154
179
|
[anthropic]
|
|
155
180
|
# Uses ANTHROPIC_API_KEY env var for authentication
|
|
@@ -164,11 +189,20 @@ model = "gpt-4o-mini"
|
|
|
164
189
|
schedule = "auto"
|
|
165
190
|
min_sessions = 5
|
|
166
191
|
|
|
192
|
+
[token_guard]
|
|
193
|
+
# rtk: "off" disables RTK help, "auto" uses RTK when found,
|
|
194
|
+
# "enforce" blocks known raw-output shell commands when RTK is unavailable.
|
|
195
|
+
rtk = "auto"
|
|
196
|
+
# raw_bash: "block" blocks raw Codex shell output when an RTK rewrite exists.
|
|
197
|
+
# "allow" keeps RTK advisory only.
|
|
198
|
+
raw_bash = "block"
|
|
199
|
+
|
|
167
200
|
[redaction]
|
|
168
201
|
# Additional secret patterns (regex) beyond built-in defaults
|
|
169
202
|
# extra_patterns = ["MY_CUSTOM_SECRET_.*"]
|
|
170
203
|
TOML
|
|
171
204
|
)
|
|
205
|
+
chmod 700 "$EAGLE_MEM_DIR" 2>/dev/null || true
|
|
172
206
|
eagle_log "INFO" "Config initialized: provider=$provider model=$model"
|
|
173
207
|
}
|
|
174
208
|
|
|
@@ -184,6 +218,7 @@ eagle_llm_call() {
|
|
|
184
218
|
|
|
185
219
|
case "$provider" in
|
|
186
220
|
ollama) _eagle_call_ollama "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
221
|
+
agent_cli) _eagle_call_agent_cli "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
187
222
|
anthropic) _eagle_call_anthropic "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
188
223
|
openai) _eagle_call_openai "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
189
224
|
none)
|
|
@@ -235,6 +270,170 @@ _eagle_call_ollama() {
|
|
|
235
270
|
echo "$response" | jq -r '.message.content // empty'
|
|
236
271
|
}
|
|
237
272
|
|
|
273
|
+
_eagle_agent_cli_target() {
|
|
274
|
+
local preferred
|
|
275
|
+
preferred=$(eagle_config_get "agent_cli" "preferred" "current")
|
|
276
|
+
|
|
277
|
+
case "$preferred" in
|
|
278
|
+
codex|openai-codex) echo "codex"; return 0 ;;
|
|
279
|
+
claude|claude-code|cloud-code) echo "claude-code"; return 0 ;;
|
|
280
|
+
auto)
|
|
281
|
+
if [ -n "${EAGLE_AGENT_SOURCE:-${EAGLE_AGENT:-}}" ]; then
|
|
282
|
+
eagle_agent_source
|
|
283
|
+
elif command -v codex &>/dev/null; then
|
|
284
|
+
echo "codex"
|
|
285
|
+
elif command -v claude &>/dev/null; then
|
|
286
|
+
echo "claude-code"
|
|
287
|
+
else
|
|
288
|
+
echo "none"
|
|
289
|
+
fi
|
|
290
|
+
;;
|
|
291
|
+
current|*)
|
|
292
|
+
if [ -n "${EAGLE_AGENT_SOURCE:-${EAGLE_AGENT:-}}" ]; then
|
|
293
|
+
eagle_agent_source
|
|
294
|
+
elif command -v codex &>/dev/null; then
|
|
295
|
+
echo "codex"
|
|
296
|
+
elif command -v claude &>/dev/null; then
|
|
297
|
+
echo "claude-code"
|
|
298
|
+
else
|
|
299
|
+
echo "none"
|
|
300
|
+
fi
|
|
301
|
+
;;
|
|
302
|
+
esac
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
_eagle_agent_cli_prompt_file() {
|
|
306
|
+
local prompt="$1" system="$2" max_tokens="$3" file="$4"
|
|
307
|
+
{
|
|
308
|
+
printf 'System instruction:\n%s\n\n' "$system"
|
|
309
|
+
printf 'Task:\n%s\n\n' "$prompt"
|
|
310
|
+
printf 'Output contract:\n'
|
|
311
|
+
printf -- '- Return only the requested curator text.\n'
|
|
312
|
+
printf -- '- Do not use markdown fences unless the task explicitly asks for them.\n'
|
|
313
|
+
printf -- '- Do not edit files, run project commands, or inspect the repository; all needed data is in this prompt.\n'
|
|
314
|
+
printf -- '- Keep the response within roughly %s tokens.\n' "$max_tokens"
|
|
315
|
+
} > "$file"
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
_eagle_call_agent_cli() {
|
|
319
|
+
local prompt="$1" system="$2" max_tokens="$3"
|
|
320
|
+
local target
|
|
321
|
+
target=$(_eagle_agent_cli_target)
|
|
322
|
+
|
|
323
|
+
case "$target" in
|
|
324
|
+
codex) _eagle_call_codex_cli "$prompt" "$system" "$max_tokens" ;;
|
|
325
|
+
claude-code) _eagle_call_claude_cli "$prompt" "$system" "$max_tokens" ;;
|
|
326
|
+
*)
|
|
327
|
+
eagle_log "ERROR" "agent_cli provider unavailable: no Codex or Claude CLI found"
|
|
328
|
+
return 1
|
|
329
|
+
;;
|
|
330
|
+
esac
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
_eagle_call_codex_cli() {
|
|
334
|
+
local prompt="$1" system="$2" max_tokens="$3"
|
|
335
|
+
command -v codex &>/dev/null || {
|
|
336
|
+
eagle_log "ERROR" "agent_cli provider selected Codex, but codex command was not found"
|
|
337
|
+
return 1
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
mkdir -p "$EAGLE_MEM_DIR/tmp"
|
|
341
|
+
local prompt_file out_file
|
|
342
|
+
prompt_file=$(mktemp "$EAGLE_MEM_DIR/tmp/codex-provider-prompt.XXXXXX")
|
|
343
|
+
out_file=$(mktemp "$EAGLE_MEM_DIR/tmp/codex-provider-output.XXXXXX")
|
|
344
|
+
_eagle_agent_cli_prompt_file "$prompt" "$system" "$max_tokens" "$prompt_file"
|
|
345
|
+
|
|
346
|
+
local model
|
|
347
|
+
model=$(eagle_config_get "agent_cli" "codex_model" "")
|
|
348
|
+
|
|
349
|
+
local rc _had_errexit=0
|
|
350
|
+
case "$-" in *e*) _had_errexit=1; set +e ;; esac
|
|
351
|
+
if [ -n "$model" ]; then
|
|
352
|
+
EAGLE_MEM_DISABLE_HOOKS=1 codex exec \
|
|
353
|
+
--ephemeral \
|
|
354
|
+
--skip-git-repo-check \
|
|
355
|
+
--ignore-rules \
|
|
356
|
+
-c features.codex_hooks=false \
|
|
357
|
+
--sandbox read-only \
|
|
358
|
+
--cd "${EAGLE_AGENT_CWD:-$(pwd)}" \
|
|
359
|
+
--model "$model" \
|
|
360
|
+
--output-last-message "$out_file" \
|
|
361
|
+
- < "$prompt_file" >> "$EAGLE_MEM_LOG" 2>&1
|
|
362
|
+
rc=$?
|
|
363
|
+
else
|
|
364
|
+
EAGLE_MEM_DISABLE_HOOKS=1 codex exec \
|
|
365
|
+
--ephemeral \
|
|
366
|
+
--skip-git-repo-check \
|
|
367
|
+
--ignore-rules \
|
|
368
|
+
-c features.codex_hooks=false \
|
|
369
|
+
--sandbox read-only \
|
|
370
|
+
--cd "${EAGLE_AGENT_CWD:-$(pwd)}" \
|
|
371
|
+
--output-last-message "$out_file" \
|
|
372
|
+
- < "$prompt_file" >> "$EAGLE_MEM_LOG" 2>&1
|
|
373
|
+
rc=$?
|
|
374
|
+
fi
|
|
375
|
+
[ "$_had_errexit" -eq 1 ] && set -e
|
|
376
|
+
|
|
377
|
+
rm -f "$prompt_file"
|
|
378
|
+
if [ "$rc" -ne 0 ] || [ ! -s "$out_file" ]; then
|
|
379
|
+
rm -f "$out_file"
|
|
380
|
+
eagle_log "ERROR" "Codex agent_cli provider call failed"
|
|
381
|
+
return 1
|
|
382
|
+
fi
|
|
383
|
+
|
|
384
|
+
cat "$out_file"
|
|
385
|
+
rm -f "$out_file"
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
_eagle_call_claude_cli() {
|
|
389
|
+
local prompt="$1" system="$2" max_tokens="$3"
|
|
390
|
+
command -v claude &>/dev/null || {
|
|
391
|
+
eagle_log "ERROR" "agent_cli provider selected Claude Code, but claude command was not found"
|
|
392
|
+
return 1
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
mkdir -p "$EAGLE_MEM_DIR/tmp"
|
|
396
|
+
local prompt_file out_file model rc
|
|
397
|
+
prompt_file=$(mktemp "$EAGLE_MEM_DIR/tmp/claude-provider-prompt.XXXXXX")
|
|
398
|
+
out_file=$(mktemp "$EAGLE_MEM_DIR/tmp/claude-provider-output.XXXXXX")
|
|
399
|
+
_eagle_agent_cli_prompt_file "$prompt" "$system" "$max_tokens" "$prompt_file"
|
|
400
|
+
model=$(eagle_config_get "agent_cli" "claude_model" "")
|
|
401
|
+
|
|
402
|
+
local _had_errexit=0
|
|
403
|
+
case "$-" in *e*) _had_errexit=1; set +e ;; esac
|
|
404
|
+
if [ -n "$model" ]; then
|
|
405
|
+
EAGLE_MEM_DISABLE_HOOKS=1 CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 claude -p \
|
|
406
|
+
--no-session-persistence \
|
|
407
|
+
--disable-slash-commands \
|
|
408
|
+
--permission-mode dontAsk \
|
|
409
|
+
--tools "" \
|
|
410
|
+
--output-format text \
|
|
411
|
+
--model "$model" \
|
|
412
|
+
"$(cat "$prompt_file")" > "$out_file" 2>> "$EAGLE_MEM_LOG"
|
|
413
|
+
rc=$?
|
|
414
|
+
else
|
|
415
|
+
EAGLE_MEM_DISABLE_HOOKS=1 CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 claude -p \
|
|
416
|
+
--no-session-persistence \
|
|
417
|
+
--disable-slash-commands \
|
|
418
|
+
--permission-mode dontAsk \
|
|
419
|
+
--tools "" \
|
|
420
|
+
--output-format text \
|
|
421
|
+
"$(cat "$prompt_file")" > "$out_file" 2>> "$EAGLE_MEM_LOG"
|
|
422
|
+
rc=$?
|
|
423
|
+
fi
|
|
424
|
+
[ "$_had_errexit" -eq 1 ] && set -e
|
|
425
|
+
|
|
426
|
+
rm -f "$prompt_file"
|
|
427
|
+
if [ "$rc" -ne 0 ] || [ ! -s "$out_file" ]; then
|
|
428
|
+
rm -f "$out_file"
|
|
429
|
+
eagle_log "ERROR" "Claude agent_cli provider call failed"
|
|
430
|
+
return 1
|
|
431
|
+
fi
|
|
432
|
+
|
|
433
|
+
cat "$out_file"
|
|
434
|
+
rm -f "$out_file"
|
|
435
|
+
}
|
|
436
|
+
|
|
238
437
|
_eagle_call_anthropic() {
|
|
239
438
|
local prompt="$1" system="$2" max_tokens="$3"
|
|
240
439
|
local model api_key
|
|
@@ -332,7 +531,11 @@ eagle_show_config() {
|
|
|
332
531
|
|
|
333
532
|
local provider model
|
|
334
533
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
335
|
-
|
|
534
|
+
if [ "$provider" = "agent_cli" ]; then
|
|
535
|
+
model=$(_eagle_agent_cli_target)
|
|
536
|
+
else
|
|
537
|
+
model=$(eagle_config_get "$provider" "model" "unknown")
|
|
538
|
+
fi
|
|
336
539
|
|
|
337
540
|
echo "Provider: $provider"
|
|
338
541
|
echo "Model: $model"
|
|
@@ -349,8 +552,25 @@ eagle_show_config() {
|
|
|
349
552
|
else
|
|
350
553
|
echo "Status: not running"
|
|
351
554
|
fi
|
|
555
|
+
elif [ "$provider" = "agent_cli" ]; then
|
|
556
|
+
echo "Preferred: $(eagle_config_get "agent_cli" "preferred" "current")"
|
|
557
|
+
echo "Codex: $(command -v codex 2>/dev/null || echo "not found")"
|
|
558
|
+
echo "Claude: $(command -v claude 2>/dev/null || echo "not found")"
|
|
352
559
|
fi
|
|
353
560
|
|
|
561
|
+
echo ""
|
|
562
|
+
echo "Orchestration:"
|
|
563
|
+
echo " Route: $(eagle_config_get "orchestration" "route" "opposite")"
|
|
564
|
+
echo " Worktrees: $(eagle_config_get "orchestration" "auto_worktree" "true")"
|
|
565
|
+
echo " Codex: $(eagle_config_get "orchestration" "codex_worker_model" "gpt-5.5") / $(eagle_config_get "orchestration" "codex_worker_effort" "xhigh")"
|
|
566
|
+
echo " Claude: $(eagle_config_get "orchestration" "claude_worker_model" "claude-opus-4-7") / $(eagle_config_get "orchestration" "claude_worker_effort" "xhigh")"
|
|
567
|
+
|
|
568
|
+
echo ""
|
|
569
|
+
echo "Token guard:"
|
|
570
|
+
echo " RTK mode: $(eagle_config_get "token_guard" "rtk" "auto")"
|
|
571
|
+
echo " Raw bash: $(eagle_config_get "token_guard" "raw_bash" "block")"
|
|
572
|
+
echo " RTK bin: $(command -v rtk 2>/dev/null || echo "not found")"
|
|
573
|
+
|
|
354
574
|
echo ""
|
|
355
575
|
echo "Config: $EAGLE_CONFIG_FILE"
|
|
356
576
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eagle-mem",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"description": "Context that survives /compact for Claude Code and Codex — SQLite + FTS5, no daemon, no bloat",
|
|
5
5
|
"bin": {
|
|
6
6
|
"eagle-mem": "bin/eagle-mem"
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"sqlite",
|
|
21
21
|
"fts5",
|
|
22
22
|
"hooks",
|
|
23
|
+
"orchestration",
|
|
24
|
+
"rtk",
|
|
23
25
|
"persistent-memory"
|
|
24
26
|
],
|
|
25
27
|
"author": "eagleisbatman",
|