eagle-mem 4.6.2 → 4.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +49 -15
  2. package/db/023_guardrails.sql +3 -2
  3. package/db/024_guardrails_unique.sql +46 -0
  4. package/db/025_pending_feature_verifications.sql +30 -0
  5. package/db/026_agent_source.sql +18 -0
  6. package/db/027_feature_verification_fingerprints.sql +9 -0
  7. package/db/028_agent_artifact_tables.sql +124 -0
  8. package/hooks/post-tool-use.sh +42 -13
  9. package/hooks/pre-tool-use.sh +107 -14
  10. package/hooks/session-end.sh +3 -1
  11. package/hooks/session-start.sh +64 -15
  12. package/hooks/stop.sh +115 -21
  13. package/hooks/user-prompt-submit.sh +14 -5
  14. package/lib/codex-hooks.sh +194 -0
  15. package/lib/common.sh +345 -0
  16. package/lib/db-backfill.sh +3 -3
  17. package/lib/db-features.sh +222 -0
  18. package/lib/db-guardrails.sh +2 -1
  19. package/lib/db-mirrors.sh +79 -43
  20. package/lib/db-observations.sh +3 -2
  21. package/lib/db-sessions.sh +11 -7
  22. package/lib/db-summaries.sh +9 -6
  23. package/lib/hooks-posttool.sh +8 -6
  24. package/lib/provider.sh +190 -4
  25. package/package.json +7 -3
  26. package/scripts/config.sh +2 -0
  27. package/scripts/feature.sh +70 -2
  28. package/scripts/guard.sh +4 -1
  29. package/scripts/health.sh +5 -1
  30. package/scripts/help.sh +13 -8
  31. package/scripts/install.sh +130 -76
  32. package/scripts/memories.sh +71 -45
  33. package/scripts/refresh.sh +3 -3
  34. package/scripts/search.sh +57 -47
  35. package/scripts/statusline-em.sh +1 -1
  36. package/scripts/tasks.sh +186 -15
  37. package/scripts/uninstall.sh +7 -0
  38. package/scripts/update.sh +51 -7
  39. package/skills/eagle-mem-memories/SKILL.md +13 -13
  40. package/skills/eagle-mem-tasks/SKILL.md +21 -15
package/README.md CHANGED
@@ -9,15 +9,18 @@
9
9
 
10
10
  **Context that survives `/compact`.**
11
11
 
12
+ **v4.7.1 hardens first-class Codex support and enforced anti-regression checks.**
13
+ Claude Code and Codex can now share the same local Eagle Mem database, while every captured row records which agent created it.
14
+
12
15
  ## The Problem
13
16
 
14
- Claude Code starts every session with amnesia. It doesn't remember what you built yesterday, what decisions you made, what files matter, or what broke last time. Every `/compact` wipes context. Every new session is a cold start. You waste tokens re-explaining your project, re-reading files, and watching Claude repeat mistakes you already corrected.
17
+ Claude Code and Codex start every session with amnesia. They don't remember what you built yesterday, what decisions you made, what files matter, or what broke last time. Every `/compact` wipes context. Every new session is a cold start. You waste tokens re-explaining your project, re-reading files, and watching agents repeat mistakes you already corrected.
15
18
 
16
19
  The longer you work with Claude Code, the worse this gets. Projects accumulate history — decisions, gotchas, architectural patterns, feature dependencies — and none of it survives across sessions.
17
20
 
18
21
  ## The Solution
19
22
 
20
- Eagle Mem is a recall layer for Claude Code. Every session starts with context from previous sessions — summaries, decisions, memories, tasks, project overviews, and relevant code — injected automatically via hooks. No commands to run, no prompts to write. It just works.
23
+ Eagle Mem is a recall and regression-control layer for Claude Code and Codex. Every session starts with context from previous sessions — summaries, decisions, memories, tasks, project overviews, and relevant code — injected automatically via hooks. Both agents share the same SQLite database at `~/.eagle-mem/memory.db`, and captured rows are source-attributed as `Claude Code` or `Codex`.
21
24
 
22
25
  **Zero per-instance overhead.** No daemon, no vector DB, no MCP server. Just bash scripts, sqlite3 (WAL mode, FTS5 full-text search), and jq.
23
26
 
@@ -41,29 +44,33 @@ npm install -g eagle-mem
41
44
  eagle-mem install
42
45
  ```
43
46
 
44
- That's it. Open Claude Code in any project directory. Eagle Mem activates automatically.
47
+ That's it. Open Claude Code or Codex in any project directory. Eagle Mem activates automatically.
45
48
 
46
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.
47
50
 
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
+
48
53
  ### Prerequisites
49
54
 
50
55
  - `sqlite3` with FTS5 support (ships with macOS; the installer offers to install if missing)
51
56
  - `jq` (the installer offers to install if missing)
52
- - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed (`~/.claude/` must exist)
57
+ - [Claude Code](https://docs.anthropic.com/en/docs/claude-code), Codex, or both installed
53
58
 
54
59
  ## How It Works
55
60
 
56
- Six hooks fire automatically at different points in Claude Code's lifecycle:
61
+ Hooks fire automatically at different points in the agent lifecycle:
57
62
 
58
63
  | Hook | Fires When | What It Does |
59
64
  |------|-----------|--------------|
60
65
  | **SessionStart** | startup, resume, clear, compact | Injects overview, summaries, memories, tasks, core files, working set. Auto-provisions new projects (scan, index). |
61
- | **PreToolUse** | before Bash, Read, Edit, Write | Surfaces guardrails and decisions before edits. Rewrites noisy commands (learned rules). Detects redundant reads, nudges co-edit partners, detects stuck loops. |
66
+ | **PreToolUse** | before Bash/shell, Read, Edit, Write, apply_patch | Surfaces guardrails and decisions before edits. Blocks release-boundary commands while feature verification is pending. Rewrites noisy commands through RTK when available. Detects redundant reads, nudges co-edit partners, detects stuck loops. |
62
67
  | **UserPromptSubmit** | user sends a message | FTS5 search across past sessions and indexed code for relevant context |
63
68
  | **PostToolUse** | after tool calls | Records file touches, mirrors memory/plan/task writes, surfaces decision history and feature impacts on reads, stale memory warnings on edits |
64
- | **Stop** | Claude's turn ends | Extracts `<eagle-summary>` blocks for rich session summaries |
69
+ | **Stop** | agent turn ends | Extracts `<eagle-summary>` blocks for rich session summaries from Claude Code and Codex transcripts |
65
70
  | **SessionEnd** | session closes | Re-syncs tasks, marks session completed |
66
71
 
72
+ Codex shell hooks are registered for `Bash`, `exec_command`, `shell_command`, and `unified_exec` tool names so release-boundary protection works across current Codex shell paths.
73
+
67
74
  ### Background Automation
68
75
 
69
76
  These run automatically via SessionStart — no commands needed:
@@ -92,9 +99,10 @@ Eagle Mem prevents Claude from repeating past mistakes:
92
99
 
93
100
  - **Decision surfacing** — when you edit a file that has past decisions recorded (from `<eagle-summary>` blocks), PreToolUse reminds Claude not to revert without asking
94
101
  - **Guardrails** — file-level rules (manual or curator-discovered) that fire before every Edit/Write
95
- - **Feature verification** — tracks features with smoke tests and dependencies; reminds you to verify on `git push`
102
+ - **Feature verification** — tracks features with smoke tests and dependencies; current git diffs create fingerprinted pending verification records, and release-boundary commands such as `git push`, `gh pr create`, and package publish are blocked until the current fingerprint is verified or waived
96
103
  - **Gotcha surfacing** — past surprises and gotchas are surfaced when editing related files
97
104
  - **Stale memory detection** — warns when edits may contradict stored memories
105
+ - **Token guard** — when `rtk` is installed, raw shell output commands are rewritten or blocked with an RTK equivalent so large output is compacted before it enters agent context
98
106
 
99
107
  ## Commands
100
108
 
@@ -108,7 +116,7 @@ Eagle Mem prevents Claude from repeating past mistakes:
108
116
  | `eagle-mem config` | View or change LLM provider settings |
109
117
  | `eagle-mem guard` | Manage regression guardrails for files |
110
118
  | `eagle-mem overview` | Build or view project overview |
111
- | `eagle-mem memories` | View/sync Claude Code memories |
119
+ | `eagle-mem memories` | View/sync agent memories |
112
120
  | `eagle-mem tasks` | View mirrored tasks |
113
121
  | `eagle-mem curate` | Run curator (co-edits, hot files, guardrails) |
114
122
  | `eagle-mem feature` | Track and verify features |
@@ -122,20 +130,43 @@ Eagle Mem prevents Claude from repeating past mistakes:
122
130
  eagle-mem search "auth bug" # keyword search across summaries
123
131
  eagle-mem search --timeline # recent sessions in chronological order
124
132
  eagle-mem search --overview # project overview
125
- eagle-mem search --memories # mirrored Claude Code memories
133
+ eagle-mem search --memories # mirrored agent memories
126
134
  eagle-mem search --tasks # in-flight tasks (pending/in-progress)
127
135
  eagle-mem search --files # most frequently modified files
128
136
  eagle-mem search --stats # project statistics
129
137
  eagle-mem search --session <id> # full observation trail for one session
130
138
  ```
131
139
 
140
+ ### Feature Verification
141
+
142
+ ```bash
143
+ eagle-mem feature pending
144
+ eagle-mem feature verify "Feature name" --notes "smoke test passed"
145
+ eagle-mem feature waive 12 --reason "docs-only change, no runtime impact"
146
+ ```
147
+
148
+ Verification is tied to the current git diff fingerprint. If the same diff was already verified, release-boundary hooks do not reopen it. If the file changes again, Eagle Mem creates a new pending verification for the new fingerprint.
149
+
150
+ Dry-run validation stays unblocked. For example, `gh pr create --dry-run` and `npm publish --dry-run` are treated as validation. Explicit real commands such as `npm publish --dry-run=false` are treated as release boundaries and will enforce pending feature verification.
151
+
152
+ ### Shared Claude Code + Codex Memory
153
+
154
+ Both agents write to `~/.eagle-mem/memory.db`:
155
+
156
+ - `sessions.agent` records whether a session came from Claude Code or Codex
157
+ - `summaries.agent` records which agent produced the session summary
158
+ - mirrored memories, plans, and tasks include `origin_agent`
159
+ - SessionStart recall labels sources as `Claude Code` or `Codex`
160
+
161
+ That means opening the same project in Claude Code and Codex does not create two isolated memory worlds. They recall the same project history while preserving the source of each memory.
162
+
132
163
  ## Skills (Inside Claude Code)
133
164
 
134
165
  | Skill | What It Does |
135
166
  |-------|-------------|
136
167
  | `/eagle-mem-search` | Search memory and past sessions — Claude interprets results in context |
137
168
  | `/eagle-mem-overview` | Build a rich project briefing from README, entry points, and git history |
138
- | `/eagle-mem-memories` | View and search mirrored Claude Code memories and plans |
169
+ | `/eagle-mem-memories` | View and search mirrored agent memories and plans |
139
170
  | `/eagle-mem-tasks` | TaskAware Compact Loop — break complex work into tasks that survive `/compact` |
140
171
 
141
172
  ## Data
@@ -156,10 +187,11 @@ Single SQLite database at `~/.eagle-mem/memory.db` (WAL mode, FTS5 full-text sea
156
187
  | `feature_files` | Files belonging to each feature |
157
188
  | `feature_dependencies` | Inter-feature dependency relationships |
158
189
  | `feature_smoke_tests` | Smoke test definitions for feature verification |
190
+ | `pending_feature_verifications` | Release blockers created when files tied to features change |
159
191
  | `eagle_meta` | Internal metadata (last scan, last curate, etc.) |
160
- | `claude_memories` | Mirror of Claude Code auto-memories |
161
- | `claude_plans` | Mirror of Claude Code plans |
162
- | `claude_tasks` | Mirror of Claude Code tasks |
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 |
163
195
 
164
196
  ### Project Identity
165
197
 
@@ -175,9 +207,11 @@ Some features (curator auto-enrichment, overview generation) can use an LLM for
175
207
 
176
208
  ```bash
177
209
  eagle-mem config
210
+ eagle-mem config set provider.type agent_cli
211
+ eagle-mem config set agent_cli.preferred current
178
212
  ```
179
213
 
180
- Supported providers: Ollama (auto-detected), Anthropic, OpenAI. Eagle Mem works fully without a provider — LLM features gracefully degrade to heuristic fallbacks.
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.
181
215
 
182
216
  ## License
183
217
 
@@ -11,8 +11,9 @@ CREATE TABLE IF NOT EXISTS guardrails (
11
11
  source TEXT NOT NULL DEFAULT 'manual',
12
12
  active INTEGER NOT NULL DEFAULT 1,
13
13
  created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
14
- updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
15
- UNIQUE(project, source, file_pattern, rule)
14
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
16
15
  );
17
16
 
18
17
  CREATE INDEX IF NOT EXISTS idx_guardrails_project ON guardrails(project, active);
18
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_guardrails_dedup
19
+ ON guardrails(project, COALESCE(file_pattern, ''), rule);
@@ -0,0 +1,46 @@
1
+ -- Migration 024: Normalize guardrail de-duplication
2
+ -- Older installs used UNIQUE(project, source, file_pattern, rule). Runtime
3
+ -- installs already moved to project+file_pattern+rule, so rebuild the table
4
+ -- to make fresh and upgraded databases agree.
5
+
6
+ CREATE TABLE IF NOT EXISTS guardrails_new (
7
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
8
+ project TEXT NOT NULL,
9
+ file_pattern TEXT NOT NULL DEFAULT '',
10
+ rule TEXT NOT NULL,
11
+ source TEXT NOT NULL DEFAULT 'manual',
12
+ active INTEGER NOT NULL DEFAULT 1,
13
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
14
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
15
+ );
16
+
17
+ INSERT OR IGNORE INTO guardrails_new (id, project, file_pattern, rule, source, active, created_at, updated_at)
18
+ SELECT
19
+ MIN(g.id) AS id,
20
+ g.project,
21
+ COALESCE(g.file_pattern, '') AS file_pattern,
22
+ g.rule,
23
+ COALESCE((
24
+ SELECT g2.source
25
+ FROM guardrails g2
26
+ WHERE g2.project = g.project
27
+ AND COALESCE(g2.file_pattern, '') = COALESCE(g.file_pattern, '')
28
+ AND g2.rule = g.rule
29
+ ORDER BY
30
+ CASE g2.source WHEN 'manual' THEN 0 ELSE 1 END,
31
+ g2.updated_at DESC,
32
+ g2.id DESC
33
+ LIMIT 1
34
+ ), 'manual') AS source,
35
+ MAX(g.active) AS active,
36
+ MIN(g.created_at) AS created_at,
37
+ MAX(g.updated_at) AS updated_at
38
+ FROM guardrails g
39
+ GROUP BY g.project, COALESCE(g.file_pattern, ''), g.rule;
40
+
41
+ DROP TABLE guardrails;
42
+ ALTER TABLE guardrails_new RENAME TO guardrails;
43
+
44
+ CREATE INDEX IF NOT EXISTS idx_guardrails_project ON guardrails(project, active);
45
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_guardrails_dedup
46
+ ON guardrails(project, COALESCE(file_pattern, ''), rule);
@@ -0,0 +1,30 @@
1
+ -- Migration 025: Enforced anti-regression verification state
2
+ -- Hooks record affected features here after edits. Release-boundary commands
3
+ -- are blocked while pending rows remain for the project.
4
+
5
+ CREATE TABLE IF NOT EXISTS pending_feature_verifications (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ project TEXT NOT NULL,
8
+ feature_id INTEGER NOT NULL,
9
+ feature_name TEXT NOT NULL,
10
+ file_path TEXT NOT NULL DEFAULT '',
11
+ reason TEXT NOT NULL DEFAULT '',
12
+ source_session_id TEXT,
13
+ trigger_tool TEXT,
14
+ status TEXT NOT NULL DEFAULT 'pending',
15
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
16
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
17
+ resolved_at TEXT,
18
+ notes TEXT,
19
+ FOREIGN KEY (feature_id) REFERENCES features(id) ON DELETE CASCADE
20
+ );
21
+
22
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_pending_feature_verifications_open
23
+ ON pending_feature_verifications(project, feature_id, file_path)
24
+ WHERE status = 'pending';
25
+
26
+ CREATE INDEX IF NOT EXISTS idx_pending_feature_verifications_project
27
+ ON pending_feature_verifications(project, status, updated_at);
28
+
29
+ CREATE INDEX IF NOT EXISTS idx_pending_feature_verifications_feature
30
+ ON pending_feature_verifications(feature_id, status);
@@ -0,0 +1,18 @@
1
+ -- Migration 026: Multi-agent source attribution
2
+ -- Eagle Mem is shared by Claude Code and Codex. Keep lifecycle source
3
+ -- (startup/resume/clear) separate from the agent that wrote each row.
4
+
5
+ ALTER TABLE sessions ADD COLUMN agent TEXT NOT NULL DEFAULT 'claude-code';
6
+ ALTER TABLE observations ADD COLUMN agent TEXT NOT NULL DEFAULT 'claude-code';
7
+ ALTER TABLE summaries ADD COLUMN agent TEXT NOT NULL DEFAULT 'claude-code';
8
+
9
+ ALTER TABLE claude_memories ADD COLUMN origin_agent TEXT NOT NULL DEFAULT 'claude-code';
10
+ ALTER TABLE claude_plans ADD COLUMN origin_agent TEXT NOT NULL DEFAULT 'claude-code';
11
+ ALTER TABLE claude_tasks ADD COLUMN origin_agent TEXT NOT NULL DEFAULT 'claude-code';
12
+
13
+ CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions(agent);
14
+ CREATE INDEX IF NOT EXISTS idx_observations_agent ON observations(agent);
15
+ CREATE INDEX IF NOT EXISTS idx_summaries_agent ON summaries(agent);
16
+ CREATE INDEX IF NOT EXISTS idx_claude_memories_origin_agent ON claude_memories(origin_agent);
17
+ CREATE INDEX IF NOT EXISTS idx_claude_plans_origin_agent ON claude_plans(origin_agent);
18
+ CREATE INDEX IF NOT EXISTS idx_claude_tasks_origin_agent ON claude_tasks(origin_agent);
@@ -0,0 +1,9 @@
1
+ -- Migration 027: Diff-fingerprint feature verification
2
+ -- Verification must attach to the current repository change, not only to a
3
+ -- feature/file pair. This prevents release hooks from reopening already
4
+ -- verified rows when the diff has not changed.
5
+
6
+ ALTER TABLE pending_feature_verifications ADD COLUMN change_fingerprint TEXT NOT NULL DEFAULT '';
7
+
8
+ CREATE INDEX IF NOT EXISTS idx_pending_feature_verifications_fingerprint
9
+ ON pending_feature_verifications(project, feature_id, file_path, change_fingerprint, status);
@@ -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;
@@ -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"
@@ -19,6 +20,7 @@ input=$(eagle_read_stdin)
19
20
  session_id=$(echo "$input" | jq -r '.session_id // empty')
20
21
  cwd=$(echo "$input" | jq -r '.cwd // empty')
21
22
  tool_name=$(echo "$input" | jq -r '.tool_name // empty')
23
+ agent=$(eagle_agent_source_from_json "$input")
22
24
 
23
25
  hook_event=$(echo "$input" | jq -r '.hook_event_name // empty')
24
26
 
@@ -30,7 +32,7 @@ case "$hook_event" in
30
32
  [ ! -f "$EAGLE_MEM_DB" ] && exit 0
31
33
  project=$(eagle_project_from_cwd "$cwd")
32
34
  [ -z "$project" ] && exit 0
33
- eagle_upsert_session "$session_id" "$project" "$cwd" "" ""
35
+ eagle_upsert_session "$session_id" "$project" "$cwd" "" "" "$agent"
34
36
 
35
37
  task_id=$(echo "$input" | jq -r '.task_id // empty')
36
38
  task_subject=$(echo "$input" | jq -r '.task_subject // empty')
@@ -50,14 +52,16 @@ case "$hook_event" in
50
52
  subj_sql=$(eagle_sql_escape "$task_subject")
51
53
  desc_sql=$(eagle_sql_escape "$task_desc")
52
54
  stat_sql=$(eagle_sql_escape "$local_status")
55
+ agent_sql=$(eagle_sql_escape "$agent")
53
56
 
54
57
  eagle_db_pipe <<SQL
55
- INSERT INTO claude_tasks (project, source_session_id, source_task_id, file_path, subject, description, status)
56
- VALUES ('$proj_sql', '$sid_sql', '$tid_sql', '$fp_sql', '$subj_sql', '$desc_sql', '$stat_sql')
58
+ INSERT INTO agent_tasks (project, source_session_id, source_task_id, file_path, subject, description, status, origin_agent)
59
+ VALUES ('$proj_sql', '$sid_sql', '$tid_sql', '$fp_sql', '$subj_sql', '$desc_sql', '$stat_sql', '$agent_sql')
57
60
  ON CONFLICT(file_path) DO UPDATE SET
58
61
  subject = excluded.subject,
59
62
  description = excluded.description,
60
63
  status = excluded.status,
64
+ origin_agent = excluded.origin_agent,
61
65
  updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');
62
66
  SQL
63
67
  fi
@@ -69,8 +73,8 @@ esac
69
73
 
70
74
  # Only track relevant tools
71
75
  case "$tool_name" in
72
- Read|Write|Edit|Bash|TaskCreate|TaskUpdate) ;;
73
- *) exit 0 ;;
76
+ Read|Write|Edit|TaskCreate|TaskUpdate|apply_patch) ;;
77
+ *) eagle_is_shell_tool "$tool_name" || exit 0 ;;
74
78
  esac
75
79
 
76
80
  [ ! -f "$EAGLE_MEM_DB" ] && exit 0
@@ -80,7 +84,7 @@ project=$(eagle_project_from_cwd "$cwd")
80
84
 
81
85
  # Ensure session row exists before inserting observations (FK constraint).
82
86
  # PostToolUse can race SessionStart — the session row might not exist yet.
83
- eagle_upsert_session "$session_id" "$project" "$cwd" "" ""
87
+ eagle_upsert_session "$session_id" "$project" "$cwd" "" "" "$agent"
84
88
 
85
89
  # ─── Extract observation data from tool call ──────────────
86
90
 
@@ -108,10 +112,22 @@ case "$tool_name" in
108
112
  [ -n "$fp" ] && files_modified=$(printf '%s' "$fp" | jq -Rsc '[.]')
109
113
  tool_summary="Edit $fp"
110
114
  ;;
111
- Bash)
112
- cmd=$(echo "$input" | jq -r '.tool_input.command // empty' | cut -c1-200)
115
+ apply_patch)
116
+ patch_cmd=$(echo "$input" | jq -r '.tool_input.command // empty')
117
+ patch_files=$(printf '%s\n' "$patch_cmd" | eagle_extract_apply_patch_files)
118
+ if [ -n "$patch_files" ]; then
119
+ fp=$(printf '%s\n' "$patch_files" | head -1)
120
+ files_modified=$(printf '%s\n' "$patch_files" | jq -Rsc 'split("\n") | map(select(. != ""))')
121
+ patch_count=$(printf '%s\n' "$patch_files" | sed '/^[[:space:]]*$/d' | wc -l | tr -d ' ')
122
+ tool_summary="apply_patch: ${patch_count} file(s)"
123
+ else
124
+ tool_summary="apply_patch"
125
+ fi
126
+ ;;
127
+ Bash|exec_command|shell_command|unified_exec)
128
+ cmd=$(eagle_tool_command_from_json "$input" | cut -c1-200)
113
129
  cmd=$(echo "$cmd" | eagle_redact)
114
- tool_summary="Bash: $cmd"
130
+ tool_summary="${tool_name}: $cmd"
115
131
 
116
132
  tool_output=$(echo "$input" | jq -r '.tool_response.stdout // empty' 2>/dev/null)
117
133
  if [ -n "$tool_output" ]; then
@@ -147,7 +163,7 @@ esac
147
163
 
148
164
  if [ -n "$fp" ] && [ -n "$session_id" ] && eagle_validate_session_id "$session_id"; then
149
165
  case "$tool_name" in
150
- Edit|Write)
166
+ Edit|Write|apply_patch)
151
167
  mod_dir="$EAGLE_MEM_DIR/mod-tracker"
152
168
  mkdir -p "$mod_dir" 2>/dev/null
153
169
  mod_file="$mod_dir/${session_id}"
@@ -167,16 +183,29 @@ if [ -n "$fp" ] && [ -n "$session_id" ] && eagle_validate_session_id "$session_i
167
183
  esac
168
184
  fi
169
185
 
186
+ # ─── Enforced anti-regression: mark affected features pending ──
187
+
188
+ case "$tool_name" in
189
+ Edit|Write|apply_patch)
190
+ if [ -n "$files_modified" ] && [ "$files_modified" != "[]" ]; then
191
+ while IFS= read -r mod_file; do
192
+ [ -z "$mod_file" ] && continue
193
+ eagle_record_current_feature_verifications_for_file "$project" "$cwd" "$mod_file" "$session_id" "$tool_name" "File changed by ${tool_name}" >/dev/null
194
+ done < <(echo "$files_modified" | jq -r '.[]?' 2>/dev/null)
195
+ fi
196
+ ;;
197
+ esac
198
+
170
199
  # ─── Dispatch to extracted responsibilities ───────────────
171
200
 
172
- eagle_posttool_mirror_writes "$tool_name" "$fp" "$session_id" "$project"
173
- eagle_posttool_mirror_tasks "$tool_name" "$session_id" "$project" "$input"
201
+ eagle_posttool_mirror_writes "$tool_name" "$fp" "$session_id" "$project" "$agent"
202
+ eagle_posttool_mirror_tasks "$tool_name" "$session_id" "$project" "$input" "$agent"
174
203
  eagle_posttool_stale_hint "$tool_name" "$fp" "$project"
175
204
  eagle_posttool_decision_surface "$tool_name" "$fp" "$project"
176
205
 
177
206
  # ─── Record observation ──────────────────────────────────
178
207
 
179
- if ! eagle_insert_observation "$session_id" "$project" "$tool_name" "$tool_summary" "$files_read" "$files_modified" "$output_bytes" "$output_lines" "$command_category"; then
208
+ if ! eagle_insert_observation "$session_id" "$project" "$tool_name" "$tool_summary" "$files_read" "$files_modified" "$output_bytes" "$output_lines" "$command_category" "$agent"; then
180
209
  eagle_log "ERROR" "PostToolUse: observation insert failed for session=$session_id tool=$tool_name"
181
210
  fi
182
211