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/hooks/session-start.sh
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# Injects project memory + pending tasks into Claude's context
|
|
6
6
|
# ═══════════════════════════════════════════════════════════
|
|
7
7
|
set +e
|
|
8
|
+
[ "${EAGLE_MEM_DISABLE_HOOKS:-}" = "1" ] && exit 0
|
|
8
9
|
|
|
9
10
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
11
|
LIB_DIR="$SCRIPT_DIR/../lib"
|
|
@@ -26,6 +27,8 @@ source_type=$(echo "$input" | jq -r '.source // empty')
|
|
|
26
27
|
model=$(echo "$input" | jq -r '.model // empty')
|
|
27
28
|
agent=$(eagle_agent_source_from_json "$input")
|
|
28
29
|
agent_label=$(eagle_agent_label "$agent")
|
|
30
|
+
codex_compact=0
|
|
31
|
+
[ "$agent" = "codex" ] && codex_compact=1
|
|
29
32
|
|
|
30
33
|
[ -z "$session_id" ] && exit 0
|
|
31
34
|
|
|
@@ -130,8 +133,7 @@ if [ "$stat_tasks_done" -gt 0 ]; then
|
|
|
130
133
|
task_parts+="${stat_tasks_done} completed"
|
|
131
134
|
fi
|
|
132
135
|
|
|
133
|
-
stat_last_display
|
|
134
|
-
[ ${#stat_last_summary} -gt 60 ] && stat_last_display+="..."
|
|
136
|
+
stat_last_display=$(eagle_trim_text "$stat_last_summary" 60)
|
|
135
137
|
|
|
136
138
|
eagle_banner="======================================
|
|
137
139
|
Eagle Mem Recall Ready
|
|
@@ -166,13 +168,20 @@ $update_notice
|
|
|
166
168
|
"
|
|
167
169
|
fi
|
|
168
170
|
|
|
171
|
+
if [ "$agent" = "codex" ] && [ "${stat_with_summaries:-0}" -eq 0 ] 2>/dev/null; then
|
|
172
|
+
context+="
|
|
173
|
+
=== Eagle Mem: Codex Capture Warming Up ===
|
|
174
|
+
Codex hooks are active. End important turns with an <eagle-summary> block so future Claude Code and Codex sessions can recall decisions, gotchas, key files, and next steps from this project.
|
|
175
|
+
"
|
|
176
|
+
fi
|
|
177
|
+
|
|
169
178
|
# ─── Project overview (capped at 500 chars) ──────────────
|
|
170
179
|
|
|
171
180
|
overview=$(eagle_get_overview "$project")
|
|
172
181
|
if [ -n "$overview" ]; then
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
182
|
+
overview_limit=500
|
|
183
|
+
[ "$codex_compact" -eq 1 ] && overview_limit=320
|
|
184
|
+
overview=$(eagle_trim_text "$overview" "$overview_limit")
|
|
176
185
|
context+="
|
|
177
186
|
=== Eagle Mem: Project Overview ===
|
|
178
187
|
$overview
|
|
@@ -188,8 +197,10 @@ fi
|
|
|
188
197
|
|
|
189
198
|
if [ "$source_type" = "compact" ] || [ "$source_type" = "clear" ]; then
|
|
190
199
|
_summary_limit=1
|
|
200
|
+
elif [ "$codex_compact" -eq 1 ]; then
|
|
201
|
+
_summary_limit=1
|
|
191
202
|
else
|
|
192
|
-
_summary_limit=
|
|
203
|
+
_summary_limit=2
|
|
193
204
|
fi
|
|
194
205
|
|
|
195
206
|
recent=$(eagle_get_recent_summaries "$project" "$_summary_limit")
|
|
@@ -201,6 +212,21 @@ if [ -n "$recent" ]; then
|
|
|
201
212
|
while IFS='|' read -r request completed learned next_steps created_at decisions gotchas key_files summary_agent; do
|
|
202
213
|
[ -z "$request" ] && [ -z "$completed" ] && continue
|
|
203
214
|
summary_agent_label=$(eagle_agent_label "$summary_agent")
|
|
215
|
+
request=$(eagle_trim_text "$request" 160)
|
|
216
|
+
completed=$(eagle_trim_text "$completed" 220)
|
|
217
|
+
learned=$(eagle_trim_text "$learned" 180)
|
|
218
|
+
decisions=$(eagle_trim_text "$decisions" 160)
|
|
219
|
+
gotchas=$(eagle_trim_text "$gotchas" 160)
|
|
220
|
+
key_files=$(eagle_trim_text "$key_files" 160)
|
|
221
|
+
next_steps=$(eagle_trim_text "$next_steps" 160)
|
|
222
|
+
if [ "$codex_compact" -eq 1 ]; then
|
|
223
|
+
request=$(eagle_trim_text "$request" 120)
|
|
224
|
+
completed=$(eagle_trim_text "$completed" 160)
|
|
225
|
+
learned=$(eagle_trim_text "$learned" 120)
|
|
226
|
+
decisions=""
|
|
227
|
+
key_files=""
|
|
228
|
+
next_steps=""
|
|
229
|
+
fi
|
|
204
230
|
context+="
|
|
205
231
|
--- $created_at ---"
|
|
206
232
|
[ -n "$summary_agent" ] && context+="
|
|
@@ -226,12 +252,14 @@ fi
|
|
|
226
252
|
|
|
227
253
|
# ─── Memories (skip if none) ─────────────────────────────
|
|
228
254
|
|
|
255
|
+
memory_limit=3
|
|
256
|
+
[ "$codex_compact" -eq 1 ] && memory_limit=2
|
|
229
257
|
memories=$(eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_agent,
|
|
230
258
|
CAST(julianday('now') - julianday(updated_at) AS INTEGER) as days_ago
|
|
231
|
-
FROM
|
|
259
|
+
FROM agent_memories
|
|
232
260
|
WHERE project = '$p_esc'
|
|
233
261
|
ORDER BY updated_at DESC
|
|
234
|
-
LIMIT
|
|
262
|
+
LIMIT $memory_limit;")
|
|
235
263
|
if [ -n "$memories" ]; then
|
|
236
264
|
context+="
|
|
237
265
|
=== Eagle Mem: Stored Memories ===
|
|
@@ -239,6 +267,7 @@ if [ -n "$memories" ]; then
|
|
|
239
267
|
while IFS='|' read -r mname mtype mdesc _fpath _updated morigin days_ago; do
|
|
240
268
|
[ -z "$mname" ] && continue
|
|
241
269
|
origin_label=$(eagle_agent_label "$morigin")
|
|
270
|
+
mdesc=$(eagle_trim_text "$mdesc" 180)
|
|
242
271
|
age_label=""
|
|
243
272
|
if [ -n "$days_ago" ] && [ "$days_ago" -gt 0 ] 2>/dev/null; then
|
|
244
273
|
if [ "$days_ago" -eq 1 ]; then
|
|
@@ -256,7 +285,7 @@ fi
|
|
|
256
285
|
|
|
257
286
|
# ─── Plans (skip if none) ────────────────────────────────
|
|
258
287
|
|
|
259
|
-
plans=$(
|
|
288
|
+
plans=$(eagle_list_agent_plans "$project" 3)
|
|
260
289
|
if [ -n "$plans" ]; then
|
|
261
290
|
context+="
|
|
262
291
|
=== Eagle Mem: Plans ===
|
|
@@ -271,14 +300,17 @@ fi
|
|
|
271
300
|
|
|
272
301
|
# ─── Tasks (skip if none) ────────────────────────────────
|
|
273
302
|
|
|
274
|
-
|
|
303
|
+
task_limit=5
|
|
304
|
+
[ "$codex_compact" -eq 1 ] && task_limit=3
|
|
305
|
+
synced_tasks=$(eagle_db "SELECT subject, status, blocked_by, origin_agent FROM agent_tasks
|
|
275
306
|
WHERE project = '$p_esc'
|
|
276
307
|
AND status IN ('in_progress', 'pending')
|
|
308
|
+
AND source_session_id != 'orchestration'
|
|
277
309
|
AND updated_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-7 days')
|
|
278
310
|
ORDER BY
|
|
279
311
|
CASE status WHEN 'in_progress' THEN 0 ELSE 1 END,
|
|
280
312
|
updated_at DESC
|
|
281
|
-
LIMIT
|
|
313
|
+
LIMIT $task_limit;")
|
|
282
314
|
if [ -n "$synced_tasks" ]; then
|
|
283
315
|
context+="
|
|
284
316
|
=== Eagle Mem: Tasks ===
|
|
@@ -295,10 +327,54 @@ if [ -n "$synced_tasks" ]; then
|
|
|
295
327
|
done <<< "$synced_tasks"
|
|
296
328
|
fi
|
|
297
329
|
|
|
330
|
+
# ─── Orchestration lanes (skip if none) ───────────────────
|
|
331
|
+
|
|
332
|
+
lane_limit=8
|
|
333
|
+
[ "$codex_compact" -eq 1 ] && lane_limit=4
|
|
334
|
+
orchestration_lanes=$(eagle_db "SELECT o.name, o.goal, l.lane_key, l.title,
|
|
335
|
+
COALESCE(REPLACE(REPLACE(l.description, char(10), ' '), '|', '/'), ''),
|
|
336
|
+
l.agent, l.status, l.validation, l.worktree_path, l.notes
|
|
337
|
+
FROM orchestration_lanes l
|
|
338
|
+
JOIN orchestrations o ON o.id = l.orchestration_id
|
|
339
|
+
WHERE l.project = '$p_esc'
|
|
340
|
+
AND o.status = 'active'
|
|
341
|
+
AND l.status IN ('in_progress', 'pending', 'blocked')
|
|
342
|
+
ORDER BY
|
|
343
|
+
CASE l.status WHEN 'in_progress' THEN 0 WHEN 'blocked' THEN 1 ELSE 2 END,
|
|
344
|
+
l.updated_at DESC
|
|
345
|
+
LIMIT $lane_limit;" 2>/dev/null)
|
|
346
|
+
if [ -n "$orchestration_lanes" ]; then
|
|
347
|
+
context+="
|
|
348
|
+
=== Eagle Mem: Orchestration Lanes ===
|
|
349
|
+
"
|
|
350
|
+
while IFS='|' read -r oname ogoal lkey ltitle ldesc lagent lstatus lvalidation lworktree lnotes; do
|
|
351
|
+
[ -z "$lkey" ] && continue
|
|
352
|
+
origin_label=$(eagle_agent_label "$lagent")
|
|
353
|
+
context+=" - [$lstatus][$origin_label] $lkey: $ltitle"
|
|
354
|
+
if [ -n "$ldesc" ]; then
|
|
355
|
+
desc_limit=220
|
|
356
|
+
[ "$codex_compact" -eq 1 ] && desc_limit=120
|
|
357
|
+
context+=" | scope: $(eagle_trim_text "$ldesc" "$desc_limit")"
|
|
358
|
+
fi
|
|
359
|
+
[ -n "$oname" ] && context+=" | plan: $oname"
|
|
360
|
+
[ -n "$lvalidation" ] && context+=" | validate: $lvalidation"
|
|
361
|
+
[ -n "$lworktree" ] && context+=" | worktree: $lworktree"
|
|
362
|
+
[ -n "$lnotes" ] && context+=" | notes: $lnotes"
|
|
363
|
+
context+="
|
|
364
|
+
"
|
|
365
|
+
done <<< "$orchestration_lanes"
|
|
366
|
+
context+="You, the active agent, must run 'eagle-mem orchestrate' before taking lane work. Do not ask the user to run these commands. Update lane status when work starts, blocks, or completes.
|
|
367
|
+
"
|
|
368
|
+
fi
|
|
369
|
+
|
|
298
370
|
# ─── Pending feature verifications ───────────────────────
|
|
299
371
|
|
|
300
|
-
|
|
372
|
+
pending_limit=5
|
|
373
|
+
[ "$codex_compact" -eq 1 ] && pending_limit=3
|
|
374
|
+
pending_features=$(eagle_list_pending_feature_verifications "$project" "$pending_limit" 2>/dev/null)
|
|
301
375
|
if [ -n "$pending_features" ]; then
|
|
376
|
+
pending_total=$(eagle_db "SELECT COUNT(*) FROM pending_feature_verifications WHERE project = '$p_esc' AND status = 'pending';" 2>/dev/null)
|
|
377
|
+
pending_total=${pending_total:-0}
|
|
302
378
|
context+="
|
|
303
379
|
=== Eagle Mem: Pending Feature Verification ===
|
|
304
380
|
Release-boundary commands are blocked until these are verified or waived.
|
|
@@ -313,6 +389,10 @@ Release-boundary commands are blocked until these are verified or waived.
|
|
|
313
389
|
context+="
|
|
314
390
|
"
|
|
315
391
|
done <<< "$pending_features"
|
|
392
|
+
if [ "$pending_total" -gt "$pending_limit" ] 2>/dev/null; then
|
|
393
|
+
context+=" - ... $((pending_total - pending_limit)) more pending; run: eagle-mem feature pending
|
|
394
|
+
"
|
|
395
|
+
fi
|
|
316
396
|
context+="Resolve with: eagle-mem feature verify <name> --notes \"what passed\"; or eagle-mem feature waive <id> --reason \"why safe\".
|
|
317
397
|
"
|
|
318
398
|
fi
|
|
@@ -326,9 +406,14 @@ if [ -n "$hot_files" ]; then
|
|
|
326
406
|
Frequently read — re-read sparingly if unchanged.
|
|
327
407
|
"
|
|
328
408
|
IFS=',' read -ra hf_arr <<< "$hot_files"
|
|
409
|
+
hf_count=0
|
|
410
|
+
hot_file_limit=8
|
|
411
|
+
[ "$codex_compact" -eq 1 ] && hot_file_limit=5
|
|
329
412
|
for hf in "${hf_arr[@]}"; do
|
|
413
|
+
[ "$hf_count" -ge "$hot_file_limit" ] && break
|
|
330
414
|
[ -n "$hf" ] && context+=" - $(basename "$hf")
|
|
331
415
|
"
|
|
416
|
+
hf_count=$((hf_count + 1))
|
|
332
417
|
done
|
|
333
418
|
fi
|
|
334
419
|
|
|
@@ -351,7 +436,12 @@ fi
|
|
|
351
436
|
|
|
352
437
|
# ─── Instructions (compressed) ───────────────────────────
|
|
353
438
|
|
|
354
|
-
if [ "$
|
|
439
|
+
if [ "$agent" = "codex" ]; then
|
|
440
|
+
context+="
|
|
441
|
+
=== Eagle Mem: Active ===
|
|
442
|
+
Memory active for '$project'. Emit <eagle-summary> in final responses with request, completed, learned, decisions, gotchas, next_steps, key_files, files_read, files_modified, affected_features, verified_features, and regression_risks.
|
|
443
|
+
"
|
|
444
|
+
elif [ "$source_type" = "compact" ] || [ "$source_type" = "clear" ]; then
|
|
355
445
|
context+="
|
|
356
446
|
=== Eagle Mem: Active ===
|
|
357
447
|
Memory active. Attribute recalled context to Eagle Mem. Do not revert PostToolUse-surfaced decisions without asking. Emit <eagle-summary> before final response.
|
package/hooks/stop.sh
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
# LLM enrichment fills in decisions/gotchas/key_files
|
|
8
8
|
# ═══════════════════════════════════════════════════════════
|
|
9
9
|
set +e
|
|
10
|
+
[ "${EAGLE_MEM_DISABLE_HOOKS:-}" = "1" ] && exit 0
|
|
10
11
|
|
|
11
12
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
12
13
|
LIB_DIR="$SCRIPT_DIR/../lib"
|
|
@@ -56,14 +57,22 @@ request=""
|
|
|
56
57
|
heuristic_reads=""
|
|
57
58
|
heuristic_writes=""
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
| grep -v '<local-command-caveat>' \
|
|
60
|
+
eagle_clean_request_candidates() {
|
|
61
|
+
grep -v '<local-command-caveat>' \
|
|
62
62
|
| grep -v '<system-reminder>' \
|
|
63
63
|
| grep -v '<command-name>' \
|
|
64
64
|
| grep -v '<command-message>' \
|
|
65
65
|
| grep -v '^\[{' \
|
|
66
|
-
|
|
|
66
|
+
| grep -v '^# AGENTS.md instructions' \
|
|
67
|
+
| grep -v '^<environment_context>' \
|
|
68
|
+
| awk 'NF' \
|
|
69
|
+
| tail -1 \
|
|
70
|
+
| cut -c1-500
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if [ -n "$transcript_path" ] && [ -f "$transcript_path" ]; then
|
|
74
|
+
request=$(jq -r 'select(.type == "user") | .message.content | if type == "string" then . elif type == "array" then [.[] | select(.type == "text") | .text] | join(" ") else "" end' "$transcript_path" 2>/dev/null \
|
|
75
|
+
| eagle_clean_request_candidates)
|
|
67
76
|
|
|
68
77
|
if [ -z "$request" ]; then
|
|
69
78
|
request=$(jq -r '
|
|
@@ -73,19 +82,12 @@ if [ -n "$transcript_path" ] && [ -f "$transcript_path" ]; then
|
|
|
73
82
|
elif type == "array" then [.[]? | select(.type == "input_text" or .type == "text") | .text] | join(" ")
|
|
74
83
|
else "" end
|
|
75
84
|
' "$transcript_path" 2>/dev/null \
|
|
76
|
-
|
|
|
77
|
-
| grep -v '<system-reminder>' \
|
|
78
|
-
| grep -v '<command-name>' \
|
|
79
|
-
| grep -v '<command-message>' \
|
|
80
|
-
| grep -v '^\[{' \
|
|
81
|
-
| head -1 | cut -c1-500)
|
|
85
|
+
| eagle_clean_request_candidates)
|
|
82
86
|
fi
|
|
83
87
|
|
|
84
88
|
if [ -z "$request" ]; then
|
|
85
89
|
request=$(jq -r 'select(.type == "event_msg" and .payload.type == "user_message") | .payload.message // empty' "$transcript_path" 2>/dev/null \
|
|
86
|
-
|
|
|
87
|
-
| grep -v '<system-reminder>' \
|
|
88
|
-
| head -1 | cut -c1-500)
|
|
90
|
+
| eagle_clean_request_candidates)
|
|
89
91
|
fi
|
|
90
92
|
|
|
91
93
|
heuristic_reads=$(jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | select(.name == "Read") | .input.file_path // empty' "$transcript_path" 2>/dev/null | sort -u | head -20)
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# Searches memory for relevant context and injects it
|
|
6
6
|
# ═══════════════════════════════════════════════════════════
|
|
7
7
|
set +e
|
|
8
|
+
[ "${EAGLE_MEM_DISABLE_HOOKS:-}" = "1" ] && exit 0
|
|
8
9
|
|
|
9
10
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
11
|
LIB_DIR="$SCRIPT_DIR/../lib"
|
|
@@ -25,6 +26,8 @@ agent=$(eagle_agent_source_from_json "$input")
|
|
|
25
26
|
[ -z "$user_prompt" ] && exit 0
|
|
26
27
|
|
|
27
28
|
project=$(eagle_project_from_cwd "$cwd")
|
|
29
|
+
codex_compact=0
|
|
30
|
+
[ "$agent" = "codex" ] && codex_compact=1
|
|
28
31
|
|
|
29
32
|
# ─── Context pressure detection (turn counter since last compact) ──
|
|
30
33
|
# Must run before any early exits so every prompt is counted
|
|
@@ -84,8 +87,37 @@ if [ -z "$fts_query" ]; then
|
|
|
84
87
|
exit 0
|
|
85
88
|
fi
|
|
86
89
|
|
|
90
|
+
# ─── Agent-run orchestration nudge ────────────────────────
|
|
91
|
+
|
|
92
|
+
lower_prompt=$(printf '%s' "$user_prompt" | tr '[:upper:]' '[:lower:]')
|
|
93
|
+
if printf '%s\n' "$lower_prompt" | grep -Eq '(orchestrat|worker|parallel|multi-agent|multi agent|split|lane|scope out|plan and get started|broad|full codebase|release|publish|ship)'; then
|
|
94
|
+
if [ "$codex_compact" -eq 1 ]; then
|
|
95
|
+
context+="=== Eagle Mem: Orchestration Protocol ===
|
|
96
|
+
For broad work, you run eagle-mem orchestrate yourself. Use durable lanes, opposite-agent workers, and concise user-visible status.
|
|
97
|
+
"
|
|
98
|
+
else
|
|
99
|
+
context+="=== Eagle Mem: Orchestration Protocol ===
|
|
100
|
+
If this request is broad enough to split into worker lanes, YOU run the orchestration commands. Do not ask the user to run them.
|
|
101
|
+
|
|
102
|
+
Use:
|
|
103
|
+
eagle-mem orchestrate init \"<goal>\"
|
|
104
|
+
eagle-mem orchestrate lane add <key> --agent codex|claude-code --desc \"<self-contained scope>\" --validate \"<command>\"
|
|
105
|
+
eagle-mem orchestrate lane start|block|complete <key>
|
|
106
|
+
|
|
107
|
+
Keep this mostly invisible to the user; surface only concise status or handoff when useful.
|
|
108
|
+
"
|
|
109
|
+
fi
|
|
110
|
+
fi
|
|
111
|
+
|
|
87
112
|
# Search for relevant past summaries (cross-session)
|
|
88
|
-
|
|
113
|
+
summary_limit=2
|
|
114
|
+
code_limit=3
|
|
115
|
+
if [ "$codex_compact" -eq 1 ]; then
|
|
116
|
+
summary_limit=1
|
|
117
|
+
code_limit=2
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
results=$(eagle_search_summaries "$fts_query" "$project" "$summary_limit")
|
|
89
121
|
|
|
90
122
|
if [ -n "$results" ]; then
|
|
91
123
|
context+="=== Eagle Mem: Relevant Recall ===
|
|
@@ -93,25 +125,46 @@ if [ -n "$results" ]; then
|
|
|
93
125
|
while IFS='|' read -r req completed learned _next_steps created_at _proj decisions gotchas key_files summary_agent; do
|
|
94
126
|
[ -z "$req" ] && [ -z "$completed" ] && continue
|
|
95
127
|
origin_label=$(eagle_agent_label "$summary_agent")
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
128
|
+
if [ "$codex_compact" -eq 1 ]; then
|
|
129
|
+
req=$(eagle_trim_text "$req" 90)
|
|
130
|
+
completed=$(eagle_trim_text "$completed" 150)
|
|
131
|
+
learned=$(eagle_trim_text "$learned" 110)
|
|
132
|
+
context+="- [$origin_label] "
|
|
133
|
+
if [ -n "$completed" ]; then
|
|
134
|
+
context+="$completed"
|
|
135
|
+
else
|
|
136
|
+
context+="$req"
|
|
137
|
+
fi
|
|
138
|
+
[ -n "$learned" ] && context+=" | learned: $learned"
|
|
139
|
+
context+="
|
|
140
|
+
"
|
|
141
|
+
else
|
|
142
|
+
req=$(eagle_trim_text "$req" 160)
|
|
143
|
+
completed=$(eagle_trim_text "$completed" 220)
|
|
144
|
+
learned=$(eagle_trim_text "$learned" 180)
|
|
145
|
+
decisions=$(eagle_trim_text "$decisions" 160)
|
|
146
|
+
gotchas=$(eagle_trim_text "$gotchas" 160)
|
|
147
|
+
key_files=$(eagle_trim_text "$key_files" 160)
|
|
148
|
+
context+="[$created_at][$origin_label] "
|
|
149
|
+
[ -n "$req" ] && context+="$req"
|
|
150
|
+
[ -n "$completed" ] && context+=" → $completed"
|
|
151
|
+
[ -n "$learned" ] && context+=" (Learned: $learned)"
|
|
152
|
+
[ -n "$decisions" ] && context+="
|
|
101
153
|
Decisions: $decisions"
|
|
102
|
-
|
|
154
|
+
[ -n "$gotchas" ] && context+="
|
|
103
155
|
Gotchas: $gotchas"
|
|
104
|
-
|
|
156
|
+
[ -n "$key_files" ] && context+="
|
|
105
157
|
Key files: $key_files"
|
|
106
|
-
|
|
158
|
+
context+="
|
|
107
159
|
"
|
|
160
|
+
fi
|
|
108
161
|
done <<< "$results"
|
|
109
162
|
fi
|
|
110
163
|
|
|
111
164
|
# Search indexed code chunks (if any exist for this project)
|
|
112
165
|
has_chunks=$(eagle_count_code_chunks "$project")
|
|
113
166
|
if [ "${has_chunks:-0}" -gt 0 ]; then
|
|
114
|
-
code_results=$(eagle_search_code_chunks "$fts_query" "$project"
|
|
167
|
+
code_results=$(eagle_search_code_chunks "$fts_query" "$project" "$code_limit")
|
|
115
168
|
|
|
116
169
|
if [ -n "$code_results" ]; then
|
|
117
170
|
context+="=== Eagle Mem: Relevant Code ===
|
|
@@ -128,11 +181,17 @@ fi
|
|
|
128
181
|
|
|
129
182
|
[ -z "$context" ] && exit 0
|
|
130
183
|
|
|
131
|
-
|
|
132
|
-
|
|
184
|
+
if [ "$codex_compact" -eq 1 ]; then
|
|
185
|
+
context+="
|
|
186
|
+
Use only if directly useful. If you mention it to the user, keep Eagle Mem attribution to one short line.
|
|
187
|
+
"
|
|
188
|
+
else
|
|
189
|
+
context+="
|
|
190
|
+
IMPORTANT: If directly useful, start with one short Eagle Mem attribution line, then proceed.
|
|
133
191
|
|
|
134
192
|
=== Eagle Mem: Persistent Memory ===
|
|
135
193
|
"
|
|
194
|
+
fi
|
|
136
195
|
|
|
137
196
|
eagle_emit_context_for_agent "$agent" "UserPromptSubmit" "$context"
|
|
138
197
|
exit 0
|
package/lib/common.sh
CHANGED
|
@@ -16,6 +16,8 @@ EAGLE_CODEX_DIR="${EAGLE_CODEX_DIR:-$HOME/.codex}"
|
|
|
16
16
|
EAGLE_CODEX_CONFIG="${EAGLE_CODEX_CONFIG:-$EAGLE_CODEX_DIR/config.toml}"
|
|
17
17
|
EAGLE_CODEX_HOOKS="${EAGLE_CODEX_HOOKS:-$EAGLE_CODEX_DIR/hooks.json}"
|
|
18
18
|
EAGLE_CODEX_AGENTS_MD="${EAGLE_CODEX_AGENTS_MD:-$EAGLE_CODEX_DIR/AGENTS.md}"
|
|
19
|
+
EAGLE_CODEX_SKILLS_DIR="${EAGLE_CODEX_SKILLS_DIR:-$EAGLE_CODEX_DIR/skills}"
|
|
20
|
+
EAGLE_CODEX_MEMORIES_DIR="${EAGLE_CODEX_MEMORIES_DIR:-$EAGLE_CODEX_DIR/memories}"
|
|
19
21
|
EAGLE_RAW_BASH_UNLOCK="${EAGLE_RAW_BASH_UNLOCK:-/tmp/eagle-mem-raw-bash-unlock}"
|
|
20
22
|
|
|
21
23
|
eagle_log() {
|
|
@@ -29,6 +31,11 @@ eagle_log() {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
eagle_project_from_cwd() {
|
|
34
|
+
if [ -n "${EAGLE_MEM_PROJECT:-}" ]; then
|
|
35
|
+
echo "$EAGLE_MEM_PROJECT"
|
|
36
|
+
return
|
|
37
|
+
fi
|
|
38
|
+
|
|
32
39
|
local cwd="${1:-$(pwd)}"
|
|
33
40
|
local resolved="$cwd"
|
|
34
41
|
|
|
@@ -111,7 +118,13 @@ eagle_agent_source() {
|
|
|
111
118
|
case "$agent" in
|
|
112
119
|
codex|openai-codex) echo "codex" ;;
|
|
113
120
|
claude|claude-code|cloud-code) echo "claude-code" ;;
|
|
114
|
-
*)
|
|
121
|
+
*)
|
|
122
|
+
if [ -n "${CODEX_THREAD_ID:-}" ] || [ -n "${CODEX_CI:-}" ] || [ -n "${CODEX_MANAGED_BY_NPM:-}" ]; then
|
|
123
|
+
echo "codex"
|
|
124
|
+
else
|
|
125
|
+
echo "claude-code"
|
|
126
|
+
fi
|
|
127
|
+
;;
|
|
115
128
|
esac
|
|
116
129
|
}
|
|
117
130
|
|
|
@@ -145,6 +158,22 @@ eagle_agent_label() {
|
|
|
145
158
|
esac
|
|
146
159
|
}
|
|
147
160
|
|
|
161
|
+
eagle_trim_text() {
|
|
162
|
+
local text="${1:-}"
|
|
163
|
+
local max="${2:-240}"
|
|
164
|
+
|
|
165
|
+
text=$(printf '%s' "$text" | tr '\n' ' ' | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//')
|
|
166
|
+
if [ "${#text}" -gt "$max" ] 2>/dev/null; then
|
|
167
|
+
if [ "$max" -gt 3 ] 2>/dev/null; then
|
|
168
|
+
printf '%s...' "${text:0:$((max - 3))}"
|
|
169
|
+
else
|
|
170
|
+
printf '%s' "${text:0:$max}"
|
|
171
|
+
fi
|
|
172
|
+
else
|
|
173
|
+
printf '%s' "$text"
|
|
174
|
+
fi
|
|
175
|
+
}
|
|
176
|
+
|
|
148
177
|
eagle_is_shell_tool() {
|
|
149
178
|
case "${1:-}" in
|
|
150
179
|
Bash|exec_command|shell_command|unified_exec) return 0 ;;
|
|
@@ -188,8 +217,124 @@ eagle_emit_context_for_agent() {
|
|
|
188
217
|
printf '%s\n' "$context"
|
|
189
218
|
}
|
|
190
219
|
|
|
220
|
+
eagle_config_get_light() {
|
|
221
|
+
local section="$1"
|
|
222
|
+
local key="$2"
|
|
223
|
+
local default="${3:-}"
|
|
224
|
+
local cfg="${EAGLE_CONFIG_FILE:-$EAGLE_MEM_DIR/config.toml}"
|
|
225
|
+
|
|
226
|
+
if [ ! -f "$cfg" ]; then
|
|
227
|
+
echo "$default"
|
|
228
|
+
return
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
local value
|
|
232
|
+
value=$(awk -v section="$section" -v key="$key" '
|
|
233
|
+
/^[[:space:]]*\[/ {
|
|
234
|
+
gsub(/[\[\][:space:]]/, "")
|
|
235
|
+
current = $0
|
|
236
|
+
}
|
|
237
|
+
current == section && /^[[:space:]]*[^#\[]/ {
|
|
238
|
+
split($0, parts, "=")
|
|
239
|
+
gsub(/^[[:space:]]+|[[:space:]]+$/, "", parts[1])
|
|
240
|
+
if (parts[1] == key) {
|
|
241
|
+
val = substr($0, index($0, "=") + 1)
|
|
242
|
+
gsub(/^[[:space:]]+|[[:space:]]+$/, "", val)
|
|
243
|
+
gsub(/^["'"'"']|["'"'"']$/, "", val)
|
|
244
|
+
print val
|
|
245
|
+
exit
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
' "$cfg")
|
|
249
|
+
|
|
250
|
+
if [ -n "$value" ]; then
|
|
251
|
+
echo "$value"
|
|
252
|
+
else
|
|
253
|
+
echo "$default"
|
|
254
|
+
fi
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
eagle_token_guard_rtk_mode() {
|
|
258
|
+
if declare -F eagle_config_get >/dev/null 2>&1; then
|
|
259
|
+
eagle_config_get "token_guard" "rtk" "auto"
|
|
260
|
+
else
|
|
261
|
+
eagle_config_get_light "token_guard" "rtk" "auto"
|
|
262
|
+
fi
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
eagle_token_guard_raw_bash_mode() {
|
|
266
|
+
if declare -F eagle_config_get >/dev/null 2>&1; then
|
|
267
|
+
eagle_config_get "token_guard" "raw_bash" "block"
|
|
268
|
+
else
|
|
269
|
+
eagle_config_get_light "token_guard" "raw_bash" "block"
|
|
270
|
+
fi
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
eagle_raw_output_command_needs_guard() {
|
|
274
|
+
local cmd="$1"
|
|
275
|
+
local first
|
|
276
|
+
first=$(printf '%s\n' "$cmd" | awk 'NR == 1 {print $1}' | sed 's|.*/||')
|
|
277
|
+
|
|
278
|
+
case "$first" in
|
|
279
|
+
cat|head|tail|find|grep|rg|wc) return 0 ;;
|
|
280
|
+
esac
|
|
281
|
+
|
|
282
|
+
if printf '%s\n' "$cmd" \
|
|
283
|
+
| tr '\n' ';' \
|
|
284
|
+
| sed -E 's/(&&|[|][|]|;)/\
|
|
285
|
+
/g' \
|
|
286
|
+
| awk '
|
|
287
|
+
{
|
|
288
|
+
for (i = 1; i <= NF; i++) {
|
|
289
|
+
token = $i
|
|
290
|
+
sub(/^.*\//, "", token)
|
|
291
|
+
if (token ~ /^(cat|head|tail|find|grep|rg|wc)$/) found = 1
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
END { exit(found ? 0 : 1) }
|
|
295
|
+
'
|
|
296
|
+
then
|
|
297
|
+
return 0
|
|
298
|
+
fi
|
|
299
|
+
|
|
300
|
+
if printf '%s\n' "$cmd" \
|
|
301
|
+
| tr '\n' ';' \
|
|
302
|
+
| sed -E 's/(&&|[|][|]|;)/\
|
|
303
|
+
/g' \
|
|
304
|
+
| awk '
|
|
305
|
+
{
|
|
306
|
+
for (i = 1; i <= NF; i++) {
|
|
307
|
+
if ($i !~ /(^|\/)git$/) continue
|
|
308
|
+
j = i + 1
|
|
309
|
+
while (j <= NF && $j ~ /^-/) {
|
|
310
|
+
opt = $j
|
|
311
|
+
j++
|
|
312
|
+
if (opt == "-C" || opt == "-c" ||
|
|
313
|
+
opt == "--git-dir" || opt == "--work-tree" ||
|
|
314
|
+
opt == "--namespace" || opt == "--exec-path" ||
|
|
315
|
+
opt == "--config-env" || opt == "--super-prefix") {
|
|
316
|
+
j++
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if ($j ~ /^(diff|show|log|blame|grep)$/) found = 1
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
END { exit(found ? 0 : 1) }
|
|
323
|
+
'
|
|
324
|
+
then
|
|
325
|
+
return 0
|
|
326
|
+
fi
|
|
327
|
+
|
|
328
|
+
if printf '%s\n' "$cmd" | grep -qE '\|\s*(head|tail|grep|rg|wc)\b'; then
|
|
329
|
+
return 0
|
|
330
|
+
fi
|
|
331
|
+
|
|
332
|
+
return 1
|
|
333
|
+
}
|
|
334
|
+
|
|
191
335
|
eagle_rtk_rewrite_command() {
|
|
192
336
|
local cmd="$1"
|
|
337
|
+
[ "$(eagle_token_guard_rtk_mode)" = "off" ] && return 1
|
|
193
338
|
command -v rtk >/dev/null 2>&1 || return 1
|
|
194
339
|
|
|
195
340
|
case "$cmd" in
|
|
@@ -462,7 +607,7 @@ _eagle_claude_md_section() {
|
|
|
462
607
|
|
|
463
608
|
## Eagle Mem — Persistent Memory
|
|
464
609
|
|
|
465
|
-
Eagle Mem hooks are active in every project. SessionStart injects context (overview, recent sessions, memories, tasks, core files). Stop captures session summaries. PostToolUse mirrors memories/plans/tasks.
|
|
610
|
+
Eagle Mem hooks are active in every project. SessionStart injects context (overview, recent sessions, memories, tasks, orchestration lanes, core files). Stop captures session summaries. PostToolUse mirrors memories/plans/tasks.
|
|
466
611
|
|
|
467
612
|
**Rule:** Before your final response in every session, emit an `<eagle-summary>` block so the Stop hook can capture a rich summary instead of just heuristics.
|
|
468
613
|
|
|
@@ -490,6 +635,7 @@ regression_risks: [risk, ...]
|
|
|
490
635
|
- When Eagle Mem injects context at SessionStart, attribute it: "Eagle Mem recalls:"
|
|
491
636
|
- Do not revert decisions surfaced by PostToolUse without asking the user
|
|
492
637
|
- If Eagle Mem reports pending feature verification, verify or waive it before push/PR/publish
|
|
638
|
+
- For broad multi-agent work, YOU run `eagle-mem orchestrate`; do not ask the user to run these commands
|
|
493
639
|
- Never put raw secrets in the summary — Eagle Mem redacts but defense in depth
|
|
494
640
|
- If you contradict a loaded memory, update the memory file
|
|
495
641
|
EAGLE_MD
|
|
@@ -520,6 +666,17 @@ eagle_patch_claude_md() {
|
|
|
520
666
|
_eagle_claude_md_section >> "$claude_md"
|
|
521
667
|
return 0
|
|
522
668
|
fi
|
|
669
|
+
if ! grep -qF 'eagle-mem orchestrate' "$claude_md" 2>/dev/null; then
|
|
670
|
+
local tmp_md
|
|
671
|
+
tmp_md=$(mktemp)
|
|
672
|
+
awk '
|
|
673
|
+
{ print }
|
|
674
|
+
/pending feature verification/ {
|
|
675
|
+
print "- For broad multi-agent work, YOU run `eagle-mem orchestrate`; do not ask the user to run these commands"
|
|
676
|
+
}
|
|
677
|
+
' "$claude_md" > "$tmp_md" && mv "$tmp_md" "$claude_md"
|
|
678
|
+
return 0
|
|
679
|
+
fi
|
|
523
680
|
return 1
|
|
524
681
|
fi
|
|
525
682
|
|
|
@@ -556,6 +713,9 @@ regression_risks: [risk, ...]
|
|
|
556
713
|
|
|
557
714
|
**How to apply:**
|
|
558
715
|
- Attribute recalled context as "Eagle Mem recalls:" when it is injected
|
|
716
|
+
- Use the Eagle Mem skills when relevant: `eagle-mem-search`, `eagle-mem-overview`, `eagle-mem-memories`, `eagle-mem-tasks`, and `eagle-mem-orchestrate`
|
|
717
|
+
- For broad multi-agent work, YOU run `eagle-mem orchestrate`; do not ask the user to run these commands
|
|
718
|
+
- For important decisions, preferences, gotchas, or durable project facts, explicitly include them in the `<eagle-summary>` block so Codex-originated memories become available to future Claude Code and Codex sessions
|
|
559
719
|
- Do not revert Eagle Mem-surfaced decisions without asking the user
|
|
560
720
|
- If Eagle Mem reports pending feature verification, verify or waive it before push/PR/publish
|
|
561
721
|
- Never put raw secrets in summaries
|
|
@@ -569,6 +729,17 @@ eagle_patch_codex_agents_md() {
|
|
|
569
729
|
mkdir -p "$(dirname "$agents_md")"
|
|
570
730
|
|
|
571
731
|
if [ -f "$agents_md" ] && grep -qF "$marker" "$agents_md" 2>/dev/null; then
|
|
732
|
+
if ! grep -qF 'eagle-mem orchestrate' "$agents_md" 2>/dev/null; then
|
|
733
|
+
local tmp_md
|
|
734
|
+
tmp_md=$(mktemp)
|
|
735
|
+
awk '
|
|
736
|
+
{ print }
|
|
737
|
+
/eagle-mem-tasks/ {
|
|
738
|
+
print "- Use the `eagle-mem-orchestrate` skill for broad multi-agent work. YOU run `eagle-mem orchestrate`; do not ask the user to run these commands"
|
|
739
|
+
}
|
|
740
|
+
' "$agents_md" > "$tmp_md" && mv "$tmp_md" "$agents_md"
|
|
741
|
+
return 0
|
|
742
|
+
fi
|
|
572
743
|
return 1
|
|
573
744
|
fi
|
|
574
745
|
|
package/lib/db-backfill.sh
CHANGED
|
@@ -65,9 +65,9 @@ eagle_backfill_projects() {
|
|
|
65
65
|
ch=$(eagle_db_pipe <<SQL
|
|
66
66
|
BEGIN;
|
|
67
67
|
UPDATE sessions SET project = '$proj_sql' WHERE id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
68
|
-
UPDATE
|
|
69
|
-
UPDATE
|
|
70
|
-
UPDATE
|
|
68
|
+
UPDATE agent_tasks SET project = '$proj_sql' WHERE source_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
69
|
+
UPDATE agent_memories SET project = '$proj_sql' WHERE origin_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
70
|
+
UPDATE agent_plans SET project = '$proj_sql' WHERE origin_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
71
71
|
UPDATE summaries SET project = '$proj_sql' WHERE session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
72
72
|
UPDATE observations SET project = '$proj_sql' WHERE session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
73
73
|
SELECT total_changes();
|