eagle-mem 4.7.0 → 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 +11 -9
- package/db/028_agent_artifact_tables.sql +124 -0
- package/hooks/post-tool-use.sh +2 -1
- package/hooks/pre-tool-use.sh +1 -0
- package/hooks/session-end.sh +2 -1
- package/hooks/session-start.sh +11 -3
- package/hooks/stop.sh +1 -0
- package/hooks/user-prompt-submit.sh +1 -0
- package/lib/common.sh +4 -0
- package/lib/db-backfill.sh +3 -3
- package/lib/db-mirrors.sh +59 -32
- package/lib/db-sessions.sh +5 -5
- package/lib/db-summaries.sh +3 -3
- package/lib/hooks-posttool.sh +4 -4
- package/lib/provider.sh +190 -4
- package/package.json +1 -1
- package/scripts/config.sh +2 -0
- package/scripts/health.sh +5 -1
- package/scripts/help.sh +6 -6
- package/scripts/install.sh +12 -0
- package/scripts/memories.sh +50 -27
- 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-tasks/SKILL.md +21 -15
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
**Context that survives `/compact`.**
|
|
11
11
|
|
|
12
|
-
**v4.7.
|
|
12
|
+
**v4.7.1 hardens first-class Codex support and enforced anti-regression checks.**
|
|
13
13
|
Claude Code and Codex can now share the same local Eagle Mem database, while every captured row records which agent created it.
|
|
14
14
|
|
|
15
15
|
## The Problem
|
|
@@ -48,7 +48,7 @@ That's it. Open Claude Code or Codex in any project directory. Eagle Mem activat
|
|
|
48
48
|
|
|
49
49
|
Everything is automatic from here. Eagle Mem scans your codebase, indexes source files, captures session summaries, mirrors Claude's memories and tasks, learns which commands are noisy, and prunes stale data — all in the background via hooks.
|
|
50
50
|
|
|
51
|
-
For Codex, the installer enables `codex_hooks` in `~/.codex/config.toml`, registers hooks in `~/.codex/hooks.json`, and patches `~/.codex/AGENTS.md` with the Eagle Mem summary contract. For Claude Code, it keeps using `~/.claude/settings.json`, `CLAUDE.md`, and the existing Claude memory/task locations.
|
|
51
|
+
For Codex, the installer enables `codex_hooks` in `~/.codex/config.toml`, registers hooks in `~/.codex/hooks.json`, symlinks Eagle Mem skills into `~/.codex/skills`, and patches `~/.codex/AGENTS.md` with the Eagle Mem summary contract. For Claude Code, it keeps using `~/.claude/settings.json`, `CLAUDE.md`, `~/.claude/skills`, and the existing Claude memory/task locations.
|
|
52
52
|
|
|
53
53
|
### Prerequisites
|
|
54
54
|
|
|
@@ -116,7 +116,7 @@ Eagle Mem prevents Claude from repeating past mistakes:
|
|
|
116
116
|
| `eagle-mem config` | View or change LLM provider settings |
|
|
117
117
|
| `eagle-mem guard` | Manage regression guardrails for files |
|
|
118
118
|
| `eagle-mem overview` | Build or view project overview |
|
|
119
|
-
| `eagle-mem memories` | View/sync
|
|
119
|
+
| `eagle-mem memories` | View/sync agent memories |
|
|
120
120
|
| `eagle-mem tasks` | View mirrored tasks |
|
|
121
121
|
| `eagle-mem curate` | Run curator (co-edits, hot files, guardrails) |
|
|
122
122
|
| `eagle-mem feature` | Track and verify features |
|
|
@@ -130,7 +130,7 @@ Eagle Mem prevents Claude from repeating past mistakes:
|
|
|
130
130
|
eagle-mem search "auth bug" # keyword search across summaries
|
|
131
131
|
eagle-mem search --timeline # recent sessions in chronological order
|
|
132
132
|
eagle-mem search --overview # project overview
|
|
133
|
-
eagle-mem search --memories # mirrored
|
|
133
|
+
eagle-mem search --memories # mirrored agent memories
|
|
134
134
|
eagle-mem search --tasks # in-flight tasks (pending/in-progress)
|
|
135
135
|
eagle-mem search --files # most frequently modified files
|
|
136
136
|
eagle-mem search --stats # project statistics
|
|
@@ -166,7 +166,7 @@ That means opening the same project in Claude Code and Codex does not create two
|
|
|
166
166
|
|-------|-------------|
|
|
167
167
|
| `/eagle-mem-search` | Search memory and past sessions — Claude interprets results in context |
|
|
168
168
|
| `/eagle-mem-overview` | Build a rich project briefing from README, entry points, and git history |
|
|
169
|
-
| `/eagle-mem-memories` | View and search mirrored
|
|
169
|
+
| `/eagle-mem-memories` | View and search mirrored agent memories and plans |
|
|
170
170
|
| `/eagle-mem-tasks` | TaskAware Compact Loop — break complex work into tasks that survive `/compact` |
|
|
171
171
|
|
|
172
172
|
## Data
|
|
@@ -189,9 +189,9 @@ Single SQLite database at `~/.eagle-mem/memory.db` (WAL mode, FTS5 full-text sea
|
|
|
189
189
|
| `feature_smoke_tests` | Smoke test definitions for feature verification |
|
|
190
190
|
| `pending_feature_verifications` | Release blockers created when files tied to features change |
|
|
191
191
|
| `eagle_meta` | Internal metadata (last scan, last curate, etc.) |
|
|
192
|
-
| `
|
|
193
|
-
| `
|
|
194
|
-
| `
|
|
192
|
+
| `agent_memories` | Mirror of agent memory files, with source attribution |
|
|
193
|
+
| `agent_plans` | Mirror of agent plan files, with source attribution |
|
|
194
|
+
| `agent_tasks` | Mirror of agent task records, with source attribution |
|
|
195
195
|
|
|
196
196
|
### Project Identity
|
|
197
197
|
|
|
@@ -207,9 +207,11 @@ Some features (curator auto-enrichment, overview generation) can use an LLM for
|
|
|
207
207
|
|
|
208
208
|
```bash
|
|
209
209
|
eagle-mem config
|
|
210
|
+
eagle-mem config set provider.type agent_cli
|
|
211
|
+
eagle-mem config set agent_cli.preferred current
|
|
210
212
|
```
|
|
211
213
|
|
|
212
|
-
|
|
214
|
+
Provider preference is local-first: Ollama is auto-detected when running, then Eagle Mem can use the installed Codex/Claude CLI via `agent_cli` before falling back to explicit Anthropic/OpenAI API providers. Eagle Mem works fully without a provider — LLM features gracefully degrade to heuristic fallbacks.
|
|
213
215
|
|
|
214
216
|
## License
|
|
215
217
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
-- Migration 028: Promote mirrored artifact tables from Claude-specific names
|
|
2
|
+
-- to agent-generic names. The original names were correct when Eagle Mem only
|
|
3
|
+
-- mirrored Claude Code artifacts; Codex support made the stored data model
|
|
4
|
+
-- multi-agent. Keep read-only legacy views so older ad-hoc SELECT queries do
|
|
5
|
+
-- not break immediately.
|
|
6
|
+
|
|
7
|
+
DROP TRIGGER IF EXISTS claude_memories_ai;
|
|
8
|
+
DROP TRIGGER IF EXISTS claude_memories_ad;
|
|
9
|
+
DROP TRIGGER IF EXISTS claude_memories_au;
|
|
10
|
+
DROP TRIGGER IF EXISTS claude_plans_ai;
|
|
11
|
+
DROP TRIGGER IF EXISTS claude_plans_ad;
|
|
12
|
+
DROP TRIGGER IF EXISTS claude_plans_au;
|
|
13
|
+
DROP TRIGGER IF EXISTS claude_tasks_ai;
|
|
14
|
+
DROP TRIGGER IF EXISTS claude_tasks_ad;
|
|
15
|
+
DROP TRIGGER IF EXISTS claude_tasks_au;
|
|
16
|
+
|
|
17
|
+
DROP TABLE IF EXISTS claude_memories_fts;
|
|
18
|
+
DROP TABLE IF EXISTS claude_plans_fts;
|
|
19
|
+
DROP TABLE IF EXISTS claude_tasks_fts;
|
|
20
|
+
|
|
21
|
+
ALTER TABLE claude_memories RENAME TO agent_memories;
|
|
22
|
+
ALTER TABLE claude_plans RENAME TO agent_plans;
|
|
23
|
+
ALTER TABLE claude_tasks RENAME TO agent_tasks;
|
|
24
|
+
|
|
25
|
+
DROP INDEX IF EXISTS idx_claude_memories_project;
|
|
26
|
+
DROP INDEX IF EXISTS idx_claude_memories_type;
|
|
27
|
+
DROP INDEX IF EXISTS idx_claude_memories_origin_agent;
|
|
28
|
+
DROP INDEX IF EXISTS idx_claude_plans_project;
|
|
29
|
+
DROP INDEX IF EXISTS idx_claude_plans_origin_agent;
|
|
30
|
+
DROP INDEX IF EXISTS idx_claude_tasks_project;
|
|
31
|
+
DROP INDEX IF EXISTS idx_claude_tasks_session;
|
|
32
|
+
DROP INDEX IF EXISTS idx_claude_tasks_status;
|
|
33
|
+
DROP INDEX IF EXISTS idx_claude_tasks_origin_agent;
|
|
34
|
+
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_agent_memories_project ON agent_memories(project);
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_agent_memories_type ON agent_memories(memory_type);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_agent_memories_origin_agent ON agent_memories(origin_agent);
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_agent_plans_project ON agent_plans(project);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_agent_plans_origin_agent ON agent_plans(origin_agent);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_agent_tasks_project ON agent_tasks(project);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_agent_tasks_session ON agent_tasks(source_session_id);
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_agent_tasks_status ON agent_tasks(status);
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_agent_tasks_origin_agent ON agent_tasks(origin_agent);
|
|
44
|
+
|
|
45
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS agent_memories_fts USING fts5(
|
|
46
|
+
memory_name,
|
|
47
|
+
description,
|
|
48
|
+
content,
|
|
49
|
+
content='agent_memories',
|
|
50
|
+
content_rowid='id'
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS agent_plans_fts USING fts5(
|
|
54
|
+
title,
|
|
55
|
+
content,
|
|
56
|
+
content='agent_plans',
|
|
57
|
+
content_rowid='id'
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS agent_tasks_fts USING fts5(
|
|
61
|
+
subject,
|
|
62
|
+
description,
|
|
63
|
+
content='agent_tasks',
|
|
64
|
+
content_rowid='id'
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
INSERT INTO agent_memories_fts(agent_memories_fts) VALUES('rebuild');
|
|
68
|
+
INSERT INTO agent_plans_fts(agent_plans_fts) VALUES('rebuild');
|
|
69
|
+
INSERT INTO agent_tasks_fts(agent_tasks_fts) VALUES('rebuild');
|
|
70
|
+
|
|
71
|
+
CREATE TRIGGER IF NOT EXISTS agent_memories_ai AFTER INSERT ON agent_memories BEGIN
|
|
72
|
+
INSERT INTO agent_memories_fts(rowid, memory_name, description, content)
|
|
73
|
+
VALUES (new.id, new.memory_name, new.description, new.content);
|
|
74
|
+
END;
|
|
75
|
+
|
|
76
|
+
CREATE TRIGGER IF NOT EXISTS agent_memories_ad AFTER DELETE ON agent_memories BEGIN
|
|
77
|
+
INSERT INTO agent_memories_fts(agent_memories_fts, rowid, memory_name, description, content)
|
|
78
|
+
VALUES ('delete', old.id, old.memory_name, old.description, old.content);
|
|
79
|
+
END;
|
|
80
|
+
|
|
81
|
+
CREATE TRIGGER IF NOT EXISTS agent_memories_au AFTER UPDATE ON agent_memories BEGIN
|
|
82
|
+
INSERT INTO agent_memories_fts(agent_memories_fts, rowid, memory_name, description, content)
|
|
83
|
+
VALUES ('delete', old.id, old.memory_name, old.description, old.content);
|
|
84
|
+
INSERT INTO agent_memories_fts(rowid, memory_name, description, content)
|
|
85
|
+
VALUES (new.id, new.memory_name, new.description, new.content);
|
|
86
|
+
END;
|
|
87
|
+
|
|
88
|
+
CREATE TRIGGER IF NOT EXISTS agent_plans_ai AFTER INSERT ON agent_plans BEGIN
|
|
89
|
+
INSERT INTO agent_plans_fts(rowid, title, content)
|
|
90
|
+
VALUES (new.id, new.title, new.content);
|
|
91
|
+
END;
|
|
92
|
+
|
|
93
|
+
CREATE TRIGGER IF NOT EXISTS agent_plans_ad AFTER DELETE ON agent_plans BEGIN
|
|
94
|
+
INSERT INTO agent_plans_fts(agent_plans_fts, rowid, title, content)
|
|
95
|
+
VALUES ('delete', old.id, old.title, old.content);
|
|
96
|
+
END;
|
|
97
|
+
|
|
98
|
+
CREATE TRIGGER IF NOT EXISTS agent_plans_au AFTER UPDATE ON agent_plans BEGIN
|
|
99
|
+
INSERT INTO agent_plans_fts(agent_plans_fts, rowid, title, content)
|
|
100
|
+
VALUES ('delete', old.id, old.title, old.content);
|
|
101
|
+
INSERT INTO agent_plans_fts(rowid, title, content)
|
|
102
|
+
VALUES (new.id, new.title, new.content);
|
|
103
|
+
END;
|
|
104
|
+
|
|
105
|
+
CREATE TRIGGER IF NOT EXISTS agent_tasks_ai AFTER INSERT ON agent_tasks BEGIN
|
|
106
|
+
INSERT INTO agent_tasks_fts(rowid, subject, description)
|
|
107
|
+
VALUES (new.id, new.subject, new.description);
|
|
108
|
+
END;
|
|
109
|
+
|
|
110
|
+
CREATE TRIGGER IF NOT EXISTS agent_tasks_ad AFTER DELETE ON agent_tasks BEGIN
|
|
111
|
+
INSERT INTO agent_tasks_fts(agent_tasks_fts, rowid, subject, description)
|
|
112
|
+
VALUES ('delete', old.id, old.subject, old.description);
|
|
113
|
+
END;
|
|
114
|
+
|
|
115
|
+
CREATE TRIGGER IF NOT EXISTS agent_tasks_au AFTER UPDATE ON agent_tasks BEGIN
|
|
116
|
+
INSERT INTO agent_tasks_fts(agent_tasks_fts, rowid, subject, description)
|
|
117
|
+
VALUES ('delete', old.id, old.subject, old.description);
|
|
118
|
+
INSERT INTO agent_tasks_fts(rowid, subject, description)
|
|
119
|
+
VALUES (new.id, new.subject, new.description);
|
|
120
|
+
END;
|
|
121
|
+
|
|
122
|
+
CREATE VIEW IF NOT EXISTS claude_memories AS SELECT * FROM agent_memories;
|
|
123
|
+
CREATE VIEW IF NOT EXISTS claude_plans AS SELECT * FROM agent_plans;
|
|
124
|
+
CREATE VIEW IF NOT EXISTS claude_tasks AS SELECT * FROM agent_tasks;
|
package/hooks/post-tool-use.sh
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# Captures observations + dispatches to extracted responsibilities
|
|
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"
|
|
@@ -54,7 +55,7 @@ case "$hook_event" in
|
|
|
54
55
|
agent_sql=$(eagle_sql_escape "$agent")
|
|
55
56
|
|
|
56
57
|
eagle_db_pipe <<SQL
|
|
57
|
-
INSERT INTO
|
|
58
|
+
INSERT INTO agent_tasks (project, source_session_id, source_task_id, file_path, subject, description, status, origin_agent)
|
|
58
59
|
VALUES ('$proj_sql', '$sid_sql', '$tid_sql', '$fp_sql', '$subj_sql', '$desc_sql', '$stat_sql', '$agent_sql')
|
|
59
60
|
ON CONFLICT(file_path) DO UPDATE SET
|
|
60
61
|
subject = excluded.subject,
|
package/hooks/pre-tool-use.sh
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
# 6. Stuck loop detection (repeated edits to same file)
|
|
11
11
|
# ═══════════════════════════════════════════════════════════
|
|
12
12
|
set +e
|
|
13
|
+
[ "${EAGLE_MEM_DISABLE_HOOKS:-}" = "1" ] && exit 0
|
|
13
14
|
|
|
14
15
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
15
16
|
LIB_DIR="$SCRIPT_DIR/../lib"
|
package/hooks/session-end.sh
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# Marks the session as completed
|
|
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"
|
|
@@ -31,7 +32,7 @@ if eagle_validate_session_id "$session_id"; then
|
|
|
31
32
|
if [ -d "$task_dir" ]; then
|
|
32
33
|
for task_file in "$task_dir"/*.json; do
|
|
33
34
|
[ ! -f "$task_file" ] && continue
|
|
34
|
-
|
|
35
|
+
eagle_capture_agent_task "$task_file" "$session_id" "$project" "$agent"
|
|
35
36
|
done
|
|
36
37
|
eagle_log "INFO" "SessionEnd: re-synced tasks from $task_dir"
|
|
37
38
|
fi
|
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"
|
|
@@ -166,6 +167,13 @@ $update_notice
|
|
|
166
167
|
"
|
|
167
168
|
fi
|
|
168
169
|
|
|
170
|
+
if [ "$agent" = "codex" ] && [ "${stat_with_summaries:-0}" -eq 0 ] 2>/dev/null; then
|
|
171
|
+
context+="
|
|
172
|
+
=== Eagle Mem: Codex Capture Warming Up ===
|
|
173
|
+
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.
|
|
174
|
+
"
|
|
175
|
+
fi
|
|
176
|
+
|
|
169
177
|
# ─── Project overview (capped at 500 chars) ──────────────
|
|
170
178
|
|
|
171
179
|
overview=$(eagle_get_overview "$project")
|
|
@@ -228,7 +236,7 @@ fi
|
|
|
228
236
|
|
|
229
237
|
memories=$(eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_agent,
|
|
230
238
|
CAST(julianday('now') - julianday(updated_at) AS INTEGER) as days_ago
|
|
231
|
-
FROM
|
|
239
|
+
FROM agent_memories
|
|
232
240
|
WHERE project = '$p_esc'
|
|
233
241
|
ORDER BY updated_at DESC
|
|
234
242
|
LIMIT 5;")
|
|
@@ -256,7 +264,7 @@ fi
|
|
|
256
264
|
|
|
257
265
|
# ─── Plans (skip if none) ────────────────────────────────
|
|
258
266
|
|
|
259
|
-
plans=$(
|
|
267
|
+
plans=$(eagle_list_agent_plans "$project" 3)
|
|
260
268
|
if [ -n "$plans" ]; then
|
|
261
269
|
context+="
|
|
262
270
|
=== Eagle Mem: Plans ===
|
|
@@ -271,7 +279,7 @@ fi
|
|
|
271
279
|
|
|
272
280
|
# ─── Tasks (skip if none) ────────────────────────────────
|
|
273
281
|
|
|
274
|
-
synced_tasks=$(eagle_db "SELECT subject, status, blocked_by, origin_agent FROM
|
|
282
|
+
synced_tasks=$(eagle_db "SELECT subject, status, blocked_by, origin_agent FROM agent_tasks
|
|
275
283
|
WHERE project = '$p_esc'
|
|
276
284
|
AND status IN ('in_progress', 'pending')
|
|
277
285
|
AND updated_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-7 days')
|
package/hooks/stop.sh
CHANGED
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() {
|
|
@@ -556,6 +558,8 @@ regression_risks: [risk, ...]
|
|
|
556
558
|
|
|
557
559
|
**How to apply:**
|
|
558
560
|
- Attribute recalled context as "Eagle Mem recalls:" when it is injected
|
|
561
|
+
- Use the Eagle Mem skills when relevant: `eagle-mem-search`, `eagle-mem-overview`, `eagle-mem-memories`, and `eagle-mem-tasks`
|
|
562
|
+
- 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
563
|
- Do not revert Eagle Mem-surfaced decisions without asking the user
|
|
560
564
|
- If Eagle Mem reports pending feature verification, verify or waive it before push/PR/publish
|
|
561
565
|
- Never put raw secrets in summaries
|
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();
|
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-sessions.sh
CHANGED
|
@@ -49,11 +49,11 @@ 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';
|
package/lib/db-summaries.sh
CHANGED
|
@@ -163,9 +163,9 @@ eagle_search_stale_memories() {
|
|
|
163
163
|
local project; project=$(eagle_sql_escape "$1")
|
|
164
164
|
local fts_query; fts_query=$(eagle_sql_escape "$2")
|
|
165
165
|
eagle_db "SELECT m.memory_name
|
|
166
|
-
FROM
|
|
167
|
-
JOIN
|
|
168
|
-
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'
|
|
169
169
|
AND m.project = '$project'
|
|
170
170
|
LIMIT 1;"
|
|
171
171
|
}
|
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
|