eagle-mem 4.10.0 → 4.10.2
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 +48 -18
- package/architecture.html +40 -19
- package/hooks/pre-tool-use.sh +2 -6
- package/integrations/__pycache__/google_antigravity_hook.cpython-314.pyc +0 -0
- package/integrations/google_antigravity_hook.py +416 -0
- package/lib/codex-hooks.sh +17 -0
- package/lib/common.sh +8 -1
- package/lib/hooks-posttool.sh +10 -0
- package/lib/hooks.sh +17 -0
- package/package.json +4 -3
- package/scripts/grok-bootstrap.sh +26 -4
- package/scripts/install.sh +9 -1
- package/scripts/memories.sh +79 -1
- package/scripts/session.sh +2 -1
- package/scripts/uninstall.sh +7 -0
package/README.md
CHANGED
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
# Eagle Mem
|
|
9
9
|
|
|
10
|
-
**Shared memory, release guardrails, and worker lanes for Claude Code, Codex, and
|
|
10
|
+
**Shared memory, release guardrails, and worker lanes for Claude Code, Codex, Grok, and Google Antigravity.**
|
|
11
11
|
|
|
12
|
-
Eagle Mem turns AI coding sessions into compounding project knowledge. It gives Claude Code and
|
|
12
|
+
Eagle Mem turns AI coding sessions into compounding project knowledge. It gives Claude Code, Codex, and Google Antigravity hook-backed shared memory, gives Grok the same skills and CLI memory surface, labels which agent created each memory, blocks risky release commands until affected features are verified, and lets broad work split into durable worker lanes.
|
|
13
13
|
|
|
14
|
-
**v4.10.0 focuses on Grok support and Compaction Survival:** Grok users
|
|
14
|
+
**v4.10.0 and onward focuses on Grok, Google Antigravity support, and Compaction Survival:** Grok users get first-class skill linking and `eagle-mem grok-bootstrap`, while Antigravity users get native Python SDK hook integration via `google_antigravity_hook.py`. Claude Code, Codex, and Antigravity receive the deepest automatic lifecycle support through hooks; Grok currently uses the shared CLI and skill workflow.
|
|
15
15
|
|
|
16
16
|
**Website:** [Product](https://eagleisbatman.github.io/eagle-mem/) |
|
|
17
17
|
[Architecture](https://eagleisbatman.github.io/eagle-mem/architecture.html) |
|
|
@@ -22,12 +22,12 @@ Eagle Mem turns AI coding sessions into compounding project knowledge. It gives
|
|
|
22
22
|
- **Start warmer** - every new session can recall project overviews, decisions, gotchas, summaries, hot files, mirrored memories, plans, and tasks.
|
|
23
23
|
- **Ship safer** - feature-mapped changes create pending verification records, and release-boundary commands stay blocked until the current diff is verified or waived.
|
|
24
24
|
- **Waste fewer tokens** - Eagle Mem injects compact context, nudges duplicate reads, and can route noisy shell output through RTK.
|
|
25
|
-
- **Coordinate agents** - Codex and
|
|
25
|
+
- **Coordinate agents** - Claude, Codex, Grok, and Antigravity can share one project memory while worker lanes record owner, model, effort, worktree, logs, validation, and handoff.
|
|
26
26
|
- **Stay local** - no daemon, no hosted memory service, no vector database. The core is hooks plus SQLite/FTS5.
|
|
27
27
|
|
|
28
28
|
## The Problem
|
|
29
29
|
|
|
30
|
-
Claude Code and
|
|
30
|
+
Claude Code, Codex, Grok, and Google Antigravity 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.
|
|
31
31
|
|
|
32
32
|
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.
|
|
33
33
|
|
|
@@ -41,9 +41,9 @@ Eagle Mem is a local runtime layer for AI coding agents. It adds three things th
|
|
|
41
41
|
| **Guardrails** | "The agent cannot casually undo known decisions or push unverified feature changes." | Surfaces decisions before edits and enforces feature verification on push, PR, and publish boundaries. |
|
|
42
42
|
| **Lanes** | "A big task can survive compaction and split across agents." | Persists orchestrations, worker lanes, worktrees, logs, validation commands, and handoffs. |
|
|
43
43
|
|
|
44
|
-
Claude Code and
|
|
44
|
+
Claude Code, Codex, and Google Antigravity share the same SQLite database at `~/.eagle-mem/memory.db`, and captured rows are source-attributed as `Claude Code`, `Codex`, or `Antigravity`. Grok uses the same database through skills and CLI commands.
|
|
45
45
|
|
|
46
|
-
**Zero per-instance overhead.** No daemon, no vector DB, no MCP server. Just bash
|
|
46
|
+
**Zero per-instance overhead.** No daemon, no vector DB, no MCP server. Just bash/python hooks, sqlite3 (WAL mode, FTS5 full-text search), and jq.
|
|
47
47
|
|
|
48
48
|
```
|
|
49
49
|
======================================
|
|
@@ -66,17 +66,17 @@ eagle-mem install
|
|
|
66
66
|
eagle-mem doctor
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
That's it. `doctor` should report a healthy install. Open Claude Code or Codex in
|
|
69
|
+
That's it. `doctor` should report a healthy install. Open Claude Code or Codex, or import the Antigravity hook in your agent config, and Eagle Mem activates automatically. For Grok, run `eagle-mem grok-bootstrap` after install to confirm the linked skills and CLI workflow.
|
|
70
70
|
|
|
71
|
-
For Claude Code and
|
|
71
|
+
For Claude Code, Codex, and Google Antigravity, everything is automatic. Eagle Mem scans your codebase, indexes source files, captures session summaries, mirrors memories and tasks (including planning-mode artifacts like `implementation_plan.md`, `task.md`, and `walkthrough.md`), learns which commands are noisy, and prunes stale data — all in the background via hooks.
|
|
72
72
|
|
|
73
|
-
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`. For Claude Code, it integrates with `~/.claude/settings.json`, `CLAUDE.md`, and `~/.claude/skills`. Grok users get skill symlinks into `~/.grok/skills/` and can run `eagle-mem grok-bootstrap` for setup guidance and self-linking.
|
|
73
|
+
For Google Antigravity, the installer copies the native Python integration to `~/.eagle-mem/integrations/google_antigravity_hook.py`. 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`. For Claude Code, it integrates with `~/.claude/settings.json`, `CLAUDE.md`, and `~/.claude/skills`. Grok users get skill symlinks into `~/.grok/skills/` and can run `eagle-mem grok-bootstrap` for setup guidance and self-linking.
|
|
74
74
|
|
|
75
75
|
### Prerequisites
|
|
76
76
|
|
|
77
77
|
- `sqlite3` with FTS5 support (ships with macOS; Eagle Mem prefers known system/Homebrew SQLite binaries before PATH shims)
|
|
78
78
|
- `jq` (the installer offers to install if missing)
|
|
79
|
-
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code), Codex, or a Grok environment (`~/.grok/`)
|
|
79
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code), Codex, Google Antigravity SDK, or a Grok environment (`~/.grok/`)
|
|
80
80
|
|
|
81
81
|
## How It Works
|
|
82
82
|
|
|
@@ -110,7 +110,7 @@ Eagle Mem actively reduces token consumption:
|
|
|
110
110
|
|
|
111
111
|
### Compaction Survival
|
|
112
112
|
|
|
113
|
-
One of the core promises of Eagle Mem is protecting against `/compact` and session amnesia. Compaction Survival is project-level
|
|
113
|
+
One of the core promises of Eagle Mem is protecting against `/compact` and session amnesia. Compaction Survival is project-level: it reads shared Eagle Mem state such as durable tasks, enriched summaries, stale work, and orchestration lanes. Claude Code, Codex, and Google Antigravity get automatic hook-backed recovery through `SessionStart`, `Stop`/`post_turn`, and context-pressure nudges. Grok can inspect and use the same state through `eagle-mem compaction`, `eagle-mem tasks`, and the linked skills.
|
|
114
114
|
|
|
115
115
|
Run `eagle-mem compaction` anytime to check readiness.
|
|
116
116
|
|
|
@@ -134,7 +134,7 @@ Eagle Mem prevents Claude from repeating past mistakes:
|
|
|
134
134
|
- **Gotcha surfacing** — past surprises and gotchas are surfaced when editing related files
|
|
135
135
|
- **Stale memory detection** — warns when edits may contradict stored memories
|
|
136
136
|
- **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
|
|
137
|
-
- **Orchestration lanes** — long-running work can be split into durable worker lanes with owners, validation commands, worktree paths, status notes, and handoff output shared
|
|
137
|
+
- **Orchestration lanes** — long-running work can be split into durable worker lanes with owners, validation commands, worktree paths, status notes, and handoff output shared across Claude Code, Codex, Grok, and Google Antigravity
|
|
138
138
|
|
|
139
139
|
## Commands
|
|
140
140
|
|
|
@@ -175,7 +175,37 @@ eagle-mem uninstall --dry-run
|
|
|
175
175
|
|
|
176
176
|
Install and update print the files/configs they intend to touch before they change the runtime. The installed runtime writes `~/.eagle-mem/install-manifest.json` with file sizes, modes, and SHA-256 hashes, so `doctor` can tell whether hooks, scripts, libraries, and database helpers still match the package that installed them.
|
|
177
177
|
|
|
178
|
-
Uninstall removes Claude Code and
|
|
178
|
+
Uninstall removes Claude Code, Codex, Grok, and Google Antigravity hook registrations, Eagle Mem instruction blocks, custom Claude statusline integration, and skill links. It backs up user config files before editing them and keeps `~/.eagle-mem/memory.db` unless you explicitly confirm data deletion.
|
|
179
|
+
|
|
180
|
+
### Download Counts, Privacy, and Telemetry
|
|
181
|
+
|
|
182
|
+
npm download graphs are public registry aggregates, not user analytics. A download count means the npm registry served package tarballs. It does not tell package authors who downloaded the package, which project installed it, whether it was a human, or whether it became an active user.
|
|
183
|
+
|
|
184
|
+
Large single-day spikes can come from normal registry behavior:
|
|
185
|
+
|
|
186
|
+
- package mirrors and caches fetching a newly published version
|
|
187
|
+
- CI jobs, disposable containers, and build farms reinstalling dependencies
|
|
188
|
+
- bots and scanners that analyze public npm packages
|
|
189
|
+
- repeated installs during local testing or release verification
|
|
190
|
+
|
|
191
|
+
Eagle Mem does not add phone-home telemetry, install tracking, analytics beacons, or a hosted usage service. Runtime memory stays local in `~/.eagle-mem/memory.db` unless you explicitly choose to move or publish it yourself.
|
|
192
|
+
|
|
193
|
+
You can inspect the same public aggregate counts shown by npm with:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
curl https://api.npmjs.org/downloads/range/last-week/eagle-mem
|
|
197
|
+
curl https://api.npmjs.org/downloads/range/last-month/eagle-mem
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Use these numbers as directional popularity signals only. They are useful for spotting broad distribution patterns, but they are not a reliable count of users, installs, projects, or companies.
|
|
201
|
+
|
|
202
|
+
### v4.10.1 Patch
|
|
203
|
+
|
|
204
|
+
This documentation patch clarifies npm download-count behavior and Eagle Mem privacy expectations:
|
|
205
|
+
|
|
206
|
+
- npm download counts are aggregate tarball-serving statistics.
|
|
207
|
+
- Package maintainers cannot identify who downloaded the package from npm's public stats.
|
|
208
|
+
- Eagle Mem remains local-first and does not include install telemetry or phone-home analytics.
|
|
179
209
|
|
|
180
210
|
### v4.10.0 Minor Release
|
|
181
211
|
|
|
@@ -309,12 +339,12 @@ Each lane is stored in `orchestration_lanes` and mirrored into `agent_tasks`, so
|
|
|
309
339
|
|
|
310
340
|
Both agents write to `~/.eagle-mem/memory.db`:
|
|
311
341
|
|
|
312
|
-
- `sessions.agent` records whether a session came from Claude Code or
|
|
342
|
+
- `sessions.agent` records whether a session came from Claude Code, Codex, or Antigravity
|
|
313
343
|
- `summaries.agent` records which agent produced the session summary
|
|
314
344
|
- mirrored memories, plans, and tasks include `origin_agent`
|
|
315
|
-
- SessionStart recall labels sources as `Claude Code` or `
|
|
345
|
+
- SessionStart recall labels sources as `Claude Code`, `Codex`, or `Antigravity`
|
|
316
346
|
|
|
317
|
-
That means opening the same project in Claude Code and
|
|
347
|
+
That means opening the same project in Claude Code, Codex, and Google Antigravity does not create isolated memory worlds. They recall the same project history while preserving the source of each memory. Grok can search, inspect, and update that same project memory through the linked skills and CLI commands.
|
|
318
348
|
|
|
319
349
|
## Skills (Inside Claude Code, Codex, and Grok)
|
|
320
350
|
|
|
@@ -324,7 +354,7 @@ That means opening the same project in Claude Code and Codex does not create two
|
|
|
324
354
|
| `/eagle-mem-overview` | Build a rich project briefing from README, entry points, and git history |
|
|
325
355
|
| `/eagle-mem-memories` | View and search mirrored agent memories and plans |
|
|
326
356
|
| `/eagle-mem-tasks` | TaskAware Compact Loop — break complex work into tasks that survive `/compact` |
|
|
327
|
-
| `/eagle-mem-orchestrate` | Orchestrator/worker lane handoffs across Claude Code
|
|
357
|
+
| `/eagle-mem-orchestrate` | Orchestrator/worker lane handoffs across Claude Code, Codex, Grok, and Google Antigravity |
|
|
328
358
|
|
|
329
359
|
## Data
|
|
330
360
|
|
package/architecture.html
CHANGED
|
@@ -683,7 +683,7 @@
|
|
|
683
683
|
<div class="brand">
|
|
684
684
|
<span class="brand-label">Eagle Mem</span>
|
|
685
685
|
<h1>Agent Architecture Tutorial</h1>
|
|
686
|
-
<p>Technical architecture plus UX architecture for Claude Code, Codex, Grok, and future coding agents.</p>
|
|
686
|
+
<p>Technical architecture plus UX architecture for Claude Code, Codex, Grok, Google Antigravity, and future coding agents.</p>
|
|
687
687
|
</div>
|
|
688
688
|
|
|
689
689
|
<p class="nav-title">Start Here</p>
|
|
@@ -719,7 +719,7 @@
|
|
|
719
719
|
<main id="main">
|
|
720
720
|
<section class="hero" id="purpose">
|
|
721
721
|
<div class="hero-inner">
|
|
722
|
-
<p class="eyebrow">Architecture guide - technical + UX - updated 2026-05-
|
|
722
|
+
<p class="eyebrow">Architecture guide - technical + UX - updated 2026-05-26</p>
|
|
723
723
|
<h2>Eagle Mem is a local memory and safety layer that makes AI coding agents start warmer, ship safer, and coordinate better.</h2>
|
|
724
724
|
<p class="hero-summary">
|
|
725
725
|
This tutorial explains the project from first principles. You do not need to know the codebase first. Read it like a guided tour: what problem Eagle Mem solves, how hooks move data through the system, how SQLite stores shared memory, and why the user experience is intentionally quiet until the agent needs help.
|
|
@@ -728,11 +728,11 @@
|
|
|
728
728
|
<div class="hero-meta">
|
|
729
729
|
<div class="meta-box">
|
|
730
730
|
<strong>Runtime</strong>
|
|
731
|
-
<span>Bash hooks,
|
|
731
|
+
<span>Bash hooks, Python integrations, SQLite/FTS5, jq, optional RTK.</span>
|
|
732
732
|
</div>
|
|
733
733
|
<div class="meta-box">
|
|
734
734
|
<strong>Agents</strong>
|
|
735
|
-
<span>Claude Code and
|
|
735
|
+
<span>Claude Code, Codex, and Google Antigravity with hooks; Grok via skills.</span>
|
|
736
736
|
</div>
|
|
737
737
|
<div class="meta-box">
|
|
738
738
|
<strong>Core Promise</strong>
|
|
@@ -793,7 +793,7 @@
|
|
|
793
793
|
|
|
794
794
|
<div class="callout">
|
|
795
795
|
<h3>Compaction Survival is shared state</h3>
|
|
796
|
-
<p><code>eagle-mem compaction</code>
|
|
796
|
+
<p><code>eagle-mem compaction</code> reads project-level state from SQLite: enriched summaries, durable tasks, stale tasks, active orchestration lanes, and recent durable updates. Claude Code, Codex, and Google Antigravity have hook-backed automatic recovery around compact and clear events. Grok can use the same state through linked skills and CLI commands.</p>
|
|
797
797
|
</div>
|
|
798
798
|
|
|
799
799
|
<h3>Vocabulary</h3>
|
|
@@ -862,7 +862,7 @@
|
|
|
862
862
|
<text x="40" y="42" class="label">User Layer</text>
|
|
863
863
|
<rect x="40" y="58" width="220" height="72" class="box-cyan"></rect>
|
|
864
864
|
<text x="62" y="90">Developer</text>
|
|
865
|
-
<text x="62" y="112" class="small">Works in Claude
|
|
865
|
+
<text x="62" y="112" class="small">Works in Claude, Codex, Grok, or Antigravity</text>
|
|
866
866
|
|
|
867
867
|
<text x="40" y="178" class="label">Agent Layer</text>
|
|
868
868
|
<rect x="40" y="194" width="220" height="78" class="box"></rect>
|
|
@@ -871,6 +871,12 @@
|
|
|
871
871
|
<rect x="300" y="194" width="220" height="78" class="box"></rect>
|
|
872
872
|
<text x="322" y="225">Codex</text>
|
|
873
873
|
<text x="322" y="247" class="small">config.toml, hooks.json, AGENTS.md</text>
|
|
874
|
+
<rect x="560" y="194" width="220" height="78" class="box"></rect>
|
|
875
|
+
<text x="582" y="225">Grok</text>
|
|
876
|
+
<text x="582" y="247" class="small">skills link, grok-bootstrap</text>
|
|
877
|
+
<rect x="820" y="194" width="220" height="78" class="box"></rect>
|
|
878
|
+
<text x="842" y="225">Antigravity (AGY)</text>
|
|
879
|
+
<text x="842" y="247" class="small">google_antigravity_hook.py</text>
|
|
874
880
|
|
|
875
881
|
<text x="40" y="320" class="label">Hook Runtime</text>
|
|
876
882
|
<rect x="40" y="336" width="160" height="68" class="box-soft"></rect>
|
|
@@ -902,8 +908,12 @@
|
|
|
902
908
|
|
|
903
909
|
<path d="M150,130 L150,190" class="arrow"></path>
|
|
904
910
|
<path d="M260,232 L300,232" class="arrow"></path>
|
|
911
|
+
<path d="M520,232 L560,232" class="arrow"></path>
|
|
912
|
+
<path d="M780,232 L820,232" class="arrow"></path>
|
|
905
913
|
<path d="M150,272 L150,330" class="arrow"></path>
|
|
906
914
|
<path d="M410,272 L410,330" class="arrow"></path>
|
|
915
|
+
<path d="M670,272 L670,330" class="arrow"></path>
|
|
916
|
+
<path d="M870,272 L870,330" class="arrow"></path>
|
|
907
917
|
<path d="M840,404 L735,462" class="arrow"></path>
|
|
908
918
|
<path d="M660,404 L452,462" class="arrow"></path>
|
|
909
919
|
<path d="M300,404 L182,462" class="arrow"></path>
|
|
@@ -964,7 +974,7 @@
|
|
|
964
974
|
<div class="step-num">5</div>
|
|
965
975
|
<div>
|
|
966
976
|
<h3>Agent hooks are registered</h3>
|
|
967
|
-
<p>Claude Code hooks are patched into <code>~/.claude/settings.json</code>. Codex hooks are registered in <code>~/.codex/hooks.json</code
|
|
977
|
+
<p>Claude Code hooks are patched into <code>~/.claude/settings.json</code>. Codex hooks are registered in <code>~/.codex/hooks.json</code>. Google Antigravity SDK hooks are registered programmatically in <code>google_antigravity_hook.py</code> using native Python async hooks. Grok has skills and CLI access.</p>
|
|
968
978
|
</div>
|
|
969
979
|
</article>
|
|
970
980
|
|
|
@@ -972,7 +982,7 @@
|
|
|
972
982
|
<div class="step-num">6</div>
|
|
973
983
|
<div>
|
|
974
984
|
<h3>Skills and instructions are linked</h3>
|
|
975
|
-
<p>Eagle Mem skills are symlinked into each agent's skill directory. Claude receives <code>CLAUDE.md</code>
|
|
985
|
+
<p>Eagle Mem skills are symlinked into each agent's skill directory. Claude receives <code>CLAUDE.md</code> instructions. Codex receives <code>AGENTS.md</code> instructions. Google Antigravity SDK maps and mirrors standard brain planning artifacts (plans, tasks, walkthroughs). Grok receives skills under <code>~/.grok/skills</code>.</p>
|
|
976
986
|
</div>
|
|
977
987
|
</article>
|
|
978
988
|
</div>
|
|
@@ -1015,7 +1025,7 @@
|
|
|
1015
1025
|
<tr>
|
|
1016
1026
|
<td><code>~/.eagle-mem/memory.db</code></td>
|
|
1017
1027
|
<td>Single source of local memory</td>
|
|
1018
|
-
<td>Stores shared state across Claude Code, Codex, Grok skills, compactions, and sessions.</td>
|
|
1028
|
+
<td>Stores shared state across Claude Code, Codex, Grok skills, Google Antigravity hooks, compactions, and sessions.</td>
|
|
1019
1029
|
</tr>
|
|
1020
1030
|
</tbody>
|
|
1021
1031
|
</table>
|
|
@@ -1087,7 +1097,7 @@
|
|
|
1087
1097
|
<p class="section-number">06 / Technical Architecture</p>
|
|
1088
1098
|
<h2>The Agent Adapter Layer</h2>
|
|
1089
1099
|
<p class="lead">
|
|
1090
|
-
The project is agent-generic below the adapter layer. Claude Code
|
|
1100
|
+
The project is agent-generic below the adapter layer. Claude Code, Codex, and Google Antigravity have direct lifecycle hook adapters, while Grok participates through skills and CLI commands. Eagle Mem normalizes each supported surface into the same project memory model.
|
|
1091
1101
|
</p>
|
|
1092
1102
|
|
|
1093
1103
|
<div class="grid-2">
|
|
@@ -1125,6 +1135,17 @@
|
|
|
1125
1135
|
<span class="tag">tasks</span>
|
|
1126
1136
|
</div>
|
|
1127
1137
|
</article>
|
|
1138
|
+
|
|
1139
|
+
<article class="adapter-rule">
|
|
1140
|
+
<h3>Google Antigravity SDK adapter</h3>
|
|
1141
|
+
<p>Integrated programmatically via <code>integrations/google_antigravity_hook.py</code>. Connects Antigravity's async Python hook triggers directly to Eagle Mem's lifecycle events, enabling direct database writes and background curation/sync.</p>
|
|
1142
|
+
<div class="tag-row">
|
|
1143
|
+
<span class="tag">on_session_start</span>
|
|
1144
|
+
<span class="tag">pre_tool_call_decide</span>
|
|
1145
|
+
<span class="tag">post_tool_call</span>
|
|
1146
|
+
<span class="tag">on_compaction</span>
|
|
1147
|
+
</div>
|
|
1148
|
+
</article>
|
|
1128
1149
|
</div>
|
|
1129
1150
|
|
|
1130
1151
|
<h3>Normalization rules</h3>
|
|
@@ -1275,7 +1296,7 @@
|
|
|
1275
1296
|
</tr>
|
|
1276
1297
|
<tr>
|
|
1277
1298
|
<td><code>agent_memories</code>, <code>agent_plans</code>, <code>agent_tasks</code></td>
|
|
1278
|
-
<td>What durable agent artifacts should be shared across Claude Code and
|
|
1299
|
+
<td>What durable agent artifacts should be shared across Claude Code, Codex, Grok, and Antigravity?</td>
|
|
1279
1300
|
<td>PostToolUse, SessionEnd, skills, search CLI</td>
|
|
1280
1301
|
</tr>
|
|
1281
1302
|
<tr>
|
|
@@ -1464,7 +1485,7 @@
|
|
|
1464
1485
|
|
|
1465
1486
|
<h3>Default routing</h3>
|
|
1466
1487
|
<p>
|
|
1467
|
-
Eagle Mem can route broad work to
|
|
1488
|
+
Eagle Mem can route broad work to different agents by default. For example, a Codex or Antigravity coordinator can spawn Claude Code or Grok workers; any agent can coordinate or execute worker lanes. The goal is not to make agents compete, but to let each lane have one owner and one validation path.
|
|
1468
1489
|
</p>
|
|
1469
1490
|
|
|
1470
1491
|
<pre><code># Agent-run protocol, not usually a user task
|
|
@@ -1476,7 +1497,7 @@ eagle-mem orchestrate handoff --write docs/handoff-context.md</code></pre>
|
|
|
1476
1497
|
|
|
1477
1498
|
<h3>Why lanes matter for all agents</h3>
|
|
1478
1499
|
<p>
|
|
1479
|
-
Context windows end. Tools fail. A user may switch from Claude Code to Codex. A worker may finish while the coordinator is compacted. Lanes make this survivable because the state is outside any one transcript.
|
|
1500
|
+
Context windows end. Tools fail. A user may switch from Claude Code to Codex, Grok, or Antigravity. A worker may finish while the coordinator is compacted. Lanes make this survivable because the state is outside any one transcript.
|
|
1480
1501
|
</p>
|
|
1481
1502
|
</div>
|
|
1482
1503
|
</section>
|
|
@@ -1492,7 +1513,7 @@ eagle-mem orchestrate handoff --write docs/handoff-context.md</code></pre>
|
|
|
1492
1513
|
<div class="grid-2">
|
|
1493
1514
|
<article class="principle">
|
|
1494
1515
|
<h3>1. Stay in the agent's flow</h3>
|
|
1495
|
-
<p>Users already live in Claude Code or
|
|
1516
|
+
<p>Users already live in Claude Code, Codex, Grok, or Antigravity. Eagle Mem should not require them to open a dashboard before every task. Hooks/skills inject context where the agent already is.</p>
|
|
1496
1517
|
</article>
|
|
1497
1518
|
<article class="principle">
|
|
1498
1519
|
<h3>2. Show up only when useful</h3>
|
|
@@ -1512,7 +1533,7 @@ eagle-mem orchestrate handoff --write docs/handoff-context.md</code></pre>
|
|
|
1512
1533
|
</article>
|
|
1513
1534
|
<article class="principle">
|
|
1514
1535
|
<h3>6. Respect each agent's display model</h3>
|
|
1515
|
-
<p>Claude can handle richer hook context. Codex should keep user-facing replies clean and let Stop capture summaries from normal prose.</p>
|
|
1536
|
+
<p>Claude and Antigravity can handle richer hook context. Codex and Grok should keep user-facing replies clean and let Turn Stop/Stop capture summaries from normal prose.</p>
|
|
1516
1537
|
</article>
|
|
1517
1538
|
</div>
|
|
1518
1539
|
</div>
|
|
@@ -1583,7 +1604,7 @@ eagle-mem orchestrate handoff --write docs/handoff-context.md</code></pre>
|
|
|
1583
1604
|
<p class="section-number">13 / Tutorial</p>
|
|
1584
1605
|
<h2>Walkthrough: From Empty Memory To Safer Release</h2>
|
|
1585
1606
|
<p class="lead">
|
|
1586
|
-
This walkthrough explains how the system feels to a beginner. The same flow works whether the active agent is Claude Code or
|
|
1607
|
+
This walkthrough explains how the system feels to a beginner. The same flow works whether the active agent is Claude Code, Codex, Grok, or Antigravity.
|
|
1587
1608
|
</p>
|
|
1588
1609
|
|
|
1589
1610
|
<div class="step-list">
|
|
@@ -1641,7 +1662,7 @@ eagle-mem install</code></pre>
|
|
|
1641
1662
|
<div class="step-num">7</div>
|
|
1642
1663
|
<div>
|
|
1643
1664
|
<h3>The turn ends with durable memory</h3>
|
|
1644
|
-
<p>Stop saves what happened so the next session can recall it. Codex can keep the final reply clean; Claude can use richer summary fields when appropriate. Either way, the durable memory lands in SQLite.</p>
|
|
1665
|
+
<p>Stop saves what happened so the next session can recall it. Codex and Grok can keep the final reply clean; Claude and Antigravity can use richer summary fields/artifacts when appropriate. Either way, the durable memory lands in SQLite.</p>
|
|
1645
1666
|
</div>
|
|
1646
1667
|
</article>
|
|
1647
1668
|
</div>
|
|
@@ -1661,7 +1682,7 @@ eagle-mem install</code></pre>
|
|
|
1661
1682
|
<p class="section-number">14 / All-Agent Applicability</p>
|
|
1662
1683
|
<h2>How To Add Another Agent</h2>
|
|
1663
1684
|
<p class="lead">
|
|
1664
|
-
The project supports Claude Code and
|
|
1685
|
+
The project natively supports Claude Code, Codex, and Google Antigravity with lifecycle hooks, and Grok with skills plus CLI workflows. The architecture should scale to any coding agent that can run local hooks, wrapper scripts, or a skill-like command surface. The integration work should live at the edge, not inside the database model.
|
|
1665
1686
|
</p>
|
|
1666
1687
|
|
|
1667
1688
|
<h3>Minimum viable adapter</h3>
|
|
@@ -1705,7 +1726,7 @@ eagle-mem install</code></pre>
|
|
|
1705
1726
|
<h3>Adapter checklist</h3>
|
|
1706
1727
|
<ol>
|
|
1707
1728
|
<li>Register the agent's lifecycle events to call the installed scripts in <code>~/.eagle-mem/hooks/</code>.</li>
|
|
1708
|
-
<li>Set <code>EAGLE_AGENT_SOURCE</code> to a stable value, such as <code>codex</code>, <code>claude-code</code>, or a future agent key.</li>
|
|
1729
|
+
<li>Set <code>EAGLE_AGENT_SOURCE</code> to a stable value, such as <code>codex</code>, <code>claude-code</code>, <code>grok</code>, <code>antigravity</code>, or a future agent key.</li>
|
|
1709
1730
|
<li>Map the agent's shell and file tools into Eagle Mem's known categories.</li>
|
|
1710
1731
|
<li>Pass enough JSON to identify <code>session_id</code>, <code>cwd</code>, tool name, tool input, and optional transcript path.</li>
|
|
1711
1732
|
<li>Teach output formatting for the agent: compact JSON, plain additional context, or deny decisions.</li>
|
package/hooks/pre-tool-use.sh
CHANGED
|
@@ -318,12 +318,8 @@ esac
|
|
|
318
318
|
|
|
319
319
|
[ -z "$context" ] && [ -z "$updated_input" ] && exit 0
|
|
320
320
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
# additionalContext or updatedInput. Deny paths above already returned JSON;
|
|
324
|
-
# non-blocking reminders are delivered through SessionStart/UserPromptSubmit.
|
|
325
|
-
exit 0
|
|
326
|
-
fi
|
|
321
|
+
# Codex PreToolUse now natively receives both blocking decisions and advisory context.
|
|
322
|
+
# Removing the old early-exit to align Codex's pre-tool capabilities with Claude and Antigravity.
|
|
327
323
|
|
|
328
324
|
if [ -n "$updated_input" ]; then
|
|
329
325
|
jq -nc --arg ctx "$context" --argjson ui "$updated_input" \
|
|
Binary file
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Native Google Antigravity (AGY) SDK integration module for Eagle Mem.
|
|
3
|
+
Enables Antigravity agents to automatically record session summaries, observations,
|
|
4
|
+
and tasks, enforce release-boundary guardrails, and survive context compaction.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
import asyncio
|
|
11
|
+
import logging
|
|
12
|
+
import inspect
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
# Setup dedicated logger
|
|
16
|
+
logging.basicConfig(level=logging.INFO)
|
|
17
|
+
logger = logging.getLogger("eagle_mem.antigravity")
|
|
18
|
+
|
|
19
|
+
# --- Import and Mock Fallback for google-antigravity SDK ---
|
|
20
|
+
try:
|
|
21
|
+
from google.antigravity import types
|
|
22
|
+
from google.antigravity.hooks import hooks
|
|
23
|
+
HAS_ANTIGRAVITY = True
|
|
24
|
+
except ImportError:
|
|
25
|
+
HAS_ANTIGRAVITY = False
|
|
26
|
+
|
|
27
|
+
# Mock fallback to prevent import errors and allow standalone execution/testing
|
|
28
|
+
class MockHooksRegistry:
|
|
29
|
+
def on_session_start(self, func):
|
|
30
|
+
return func
|
|
31
|
+
def on_session_end(self, func):
|
|
32
|
+
return func
|
|
33
|
+
def pre_turn(self, func):
|
|
34
|
+
return func
|
|
35
|
+
def post_turn(self, func):
|
|
36
|
+
return func
|
|
37
|
+
def pre_tool_call_decide(self, func):
|
|
38
|
+
return func
|
|
39
|
+
def post_tool_call(self, func):
|
|
40
|
+
return func
|
|
41
|
+
def on_tool_error(self, func):
|
|
42
|
+
return func
|
|
43
|
+
def on_compaction(self, func):
|
|
44
|
+
return func
|
|
45
|
+
def on_interaction(self, func):
|
|
46
|
+
return func
|
|
47
|
+
|
|
48
|
+
async def inject_agent_context(self, context: str):
|
|
49
|
+
logger.info(f"[Mock SDK] Injected context:\n{context[:100]}...")
|
|
50
|
+
|
|
51
|
+
class MockTypes:
|
|
52
|
+
class HookResult:
|
|
53
|
+
def __init__(self, allow: bool = True):
|
|
54
|
+
self.allow = allow
|
|
55
|
+
class ToolCallResult:
|
|
56
|
+
def __init__(self, tool_call: Any, output: str):
|
|
57
|
+
self.tool_call = tool_call
|
|
58
|
+
self.output = output
|
|
59
|
+
class ToolCall:
|
|
60
|
+
def __init__(self, name: str, arguments: Dict[str, Any]):
|
|
61
|
+
self.name = name
|
|
62
|
+
self.arguments = arguments
|
|
63
|
+
|
|
64
|
+
hooks = MockHooksRegistry()
|
|
65
|
+
types = MockTypes()
|
|
66
|
+
|
|
67
|
+
# --- Asynchronous Subprocess Helpers ---
|
|
68
|
+
|
|
69
|
+
async def run_cmd_async(cmd: List[str]) -> str:
|
|
70
|
+
"""Runs a shell command asynchronously and returns stdout."""
|
|
71
|
+
# Prioritize workspace-local bin/eagle-mem if running from workspace or if it exists in cwd or relative to this script
|
|
72
|
+
if cmd and cmd[0] == "eagle-mem":
|
|
73
|
+
local_paths = [
|
|
74
|
+
os.path.join(os.getcwd(), "bin", "eagle-mem"),
|
|
75
|
+
os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "bin", "eagle-mem")
|
|
76
|
+
]
|
|
77
|
+
for path in local_paths:
|
|
78
|
+
if os.path.exists(path):
|
|
79
|
+
cmd = [path] + cmd[1:]
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
proc = await asyncio.create_subprocess_exec(
|
|
84
|
+
*cmd,
|
|
85
|
+
stdout=asyncio.subprocess.PIPE,
|
|
86
|
+
stderr=asyncio.subprocess.PIPE
|
|
87
|
+
)
|
|
88
|
+
stdout, stderr = await proc.communicate()
|
|
89
|
+
if proc.returncode == 0:
|
|
90
|
+
return stdout.decode('utf-8', errors='ignore')
|
|
91
|
+
else:
|
|
92
|
+
err_msg = stderr.decode('utf-8', errors='ignore').strip()
|
|
93
|
+
logger.warning(f"Command {' '.join(cmd)} failed with code {proc.returncode}: {err_msg}")
|
|
94
|
+
return ""
|
|
95
|
+
except FileNotFoundError:
|
|
96
|
+
# Fallback if command still fails (e.g. if local_bin substitution didn't happen or is broken)
|
|
97
|
+
logger.error(f"Executable not found for: {cmd[0]}")
|
|
98
|
+
return ""
|
|
99
|
+
except Exception as e:
|
|
100
|
+
logger.error(f"Error running {' '.join(cmd)}: {e}")
|
|
101
|
+
return ""
|
|
102
|
+
|
|
103
|
+
async def run_hook_async(script_name: str, input_data: Dict[str, Any]) -> str:
|
|
104
|
+
"""Runs an Eagle Mem bash hook script asynchronously, piping JSON via stdin."""
|
|
105
|
+
# Resolve the physical path of the hook script
|
|
106
|
+
# First check ~/.eagle-mem/hooks/, then fall back to workspace hooks/
|
|
107
|
+
home_dir = os.path.expanduser("~")
|
|
108
|
+
hook_path = os.path.join(home_dir, ".eagle-mem", "hooks", script_name)
|
|
109
|
+
if not os.path.exists(hook_path):
|
|
110
|
+
hook_path = os.path.join(os.getcwd(), "hooks", script_name)
|
|
111
|
+
|
|
112
|
+
if not os.path.exists(hook_path):
|
|
113
|
+
logger.error(f"Eagle Mem hook script not found: {script_name}")
|
|
114
|
+
return ""
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
proc = await asyncio.create_subprocess_exec(
|
|
118
|
+
"bash", hook_path,
|
|
119
|
+
stdin=asyncio.subprocess.PIPE,
|
|
120
|
+
stdout=asyncio.subprocess.PIPE,
|
|
121
|
+
stderr=asyncio.subprocess.PIPE
|
|
122
|
+
)
|
|
123
|
+
input_json = json.dumps(input_data).encode('utf-8')
|
|
124
|
+
stdout, stderr = await proc.communicate(input=input_json)
|
|
125
|
+
if proc.returncode == 0:
|
|
126
|
+
return stdout.decode('utf-8', errors='ignore')
|
|
127
|
+
else:
|
|
128
|
+
err_msg = stderr.decode('utf-8', errors='ignore').strip()
|
|
129
|
+
logger.warning(f"Hook script {script_name} failed with code {proc.returncode}: {err_msg}")
|
|
130
|
+
return ""
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.error(f"Error executing hook {script_name}: {e}")
|
|
133
|
+
return ""
|
|
134
|
+
|
|
135
|
+
# --- Utility Helpers ---
|
|
136
|
+
|
|
137
|
+
def get_session_id() -> str:
|
|
138
|
+
"""Retrieves or generates a persistent session ID for the current context."""
|
|
139
|
+
# Use environment variables if present (matches Claude/Codex)
|
|
140
|
+
session_id = os.environ.get("EAGLE_SESSION_ID") or os.environ.get("SESSION_ID")
|
|
141
|
+
if not session_id:
|
|
142
|
+
# Generate a unique hex session ID
|
|
143
|
+
import uuid
|
|
144
|
+
session_id = f"agy-{uuid.uuid4().hex[:16]}"
|
|
145
|
+
return session_id
|
|
146
|
+
|
|
147
|
+
def map_tool_name(agy_name: str) -> str:
|
|
148
|
+
"""Maps Google Antigravity SDK tool names to Eagle Mem canonical tool names."""
|
|
149
|
+
name = agy_name.lower()
|
|
150
|
+
if "command" in name or "bash" in name or "shell" in name:
|
|
151
|
+
return "Bash"
|
|
152
|
+
elif "read" in name or "view" in name:
|
|
153
|
+
return "Read"
|
|
154
|
+
elif "write" in name or "create" in name:
|
|
155
|
+
return "Write"
|
|
156
|
+
elif "edit" in name or "replace" in name or "patch" in name:
|
|
157
|
+
return "Edit"
|
|
158
|
+
return agy_name
|
|
159
|
+
|
|
160
|
+
# --- Eagle Mem Antigravity Hook Implementation ---
|
|
161
|
+
|
|
162
|
+
class EagleMemAntigravityHook:
|
|
163
|
+
"""
|
|
164
|
+
Natively compatible Google Antigravity SDK lifecycle hooks class for Eagle Mem.
|
|
165
|
+
Translates and routes events directly to Eagle Mem's native bash scripts.
|
|
166
|
+
"""
|
|
167
|
+
def __init__(self, agent_name: str = "antigravity"):
|
|
168
|
+
self.agent_name = agent_name
|
|
169
|
+
|
|
170
|
+
async def on_session_start(self):
|
|
171
|
+
"""Fires when the agent session starts. Injects overview, memories, tasks, and history."""
|
|
172
|
+
logger.info("Eagle Mem Hook: Session starting. Syncing context...")
|
|
173
|
+
session_id = get_session_id()
|
|
174
|
+
cwd = os.getcwd()
|
|
175
|
+
|
|
176
|
+
input_data = {
|
|
177
|
+
"session_id": session_id,
|
|
178
|
+
"cwd": cwd,
|
|
179
|
+
"source": "startup",
|
|
180
|
+
"model": "gemini",
|
|
181
|
+
"agent": self.agent_name
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
# Execute session-start.sh via stdin
|
|
185
|
+
stdout = await run_hook_async("session-start.sh", input_data)
|
|
186
|
+
context_injected = False
|
|
187
|
+
|
|
188
|
+
if stdout.strip():
|
|
189
|
+
try:
|
|
190
|
+
output_json = json.loads(stdout)
|
|
191
|
+
context = output_json.get("hookSpecificOutput", {}).get("additionalContext", "")
|
|
192
|
+
if context.strip():
|
|
193
|
+
res = hooks.inject_agent_context(context)
|
|
194
|
+
if inspect.isawaitable(res):
|
|
195
|
+
await res
|
|
196
|
+
context_injected = True
|
|
197
|
+
logger.info("Eagle Mem Hook: Injected session briefing.")
|
|
198
|
+
except Exception as e:
|
|
199
|
+
logger.warning(f"Eagle Mem Hook: Failed to parse session-start JSON: {e}")
|
|
200
|
+
|
|
201
|
+
# Robust fallback if native sessionstart failed or returned empty
|
|
202
|
+
if not context_injected:
|
|
203
|
+
logger.info("Eagle Mem Hook: Running fallback search context...")
|
|
204
|
+
timeline_task = run_cmd_async(["eagle-mem", "search", "--timeline"])
|
|
205
|
+
overview_task = run_cmd_async(["eagle-mem", "search", "--overview"])
|
|
206
|
+
timeline, overview = await asyncio.gather(timeline_task, overview_task)
|
|
207
|
+
|
|
208
|
+
fallback_context = ""
|
|
209
|
+
if overview.strip():
|
|
210
|
+
fallback_context += f"=== Eagle Mem Project Overview ===\n{overview.strip()}\n\n"
|
|
211
|
+
if timeline.strip():
|
|
212
|
+
fallback_context += f"=== Eagle Mem Session Timeline ===\n{timeline.strip()}\n"
|
|
213
|
+
|
|
214
|
+
if fallback_context.strip():
|
|
215
|
+
res = hooks.inject_agent_context(fallback_context)
|
|
216
|
+
if inspect.isawaitable(res):
|
|
217
|
+
await res
|
|
218
|
+
logger.info("Eagle Mem Hook: Injected fallback context.")
|
|
219
|
+
|
|
220
|
+
async def pre_tool_call_decide(self, tool_call: Any) -> Any:
|
|
221
|
+
"""Fires before a tool execution. Blocks dangerous tasks or injects co-edits/recalls."""
|
|
222
|
+
tool_name = getattr(tool_call, "name", "")
|
|
223
|
+
tool_args = getattr(tool_call, "arguments", getattr(tool_call, "args", {}))
|
|
224
|
+
if not isinstance(tool_args, dict):
|
|
225
|
+
tool_args = {}
|
|
226
|
+
|
|
227
|
+
mapped_tool = map_tool_name(tool_name)
|
|
228
|
+
session_id = get_session_id()
|
|
229
|
+
cwd = os.getcwd()
|
|
230
|
+
|
|
231
|
+
# Build stdin payload matching PreToolUse schema
|
|
232
|
+
tool_input = {}
|
|
233
|
+
if mapped_tool == "Bash":
|
|
234
|
+
cmd = tool_args.get("CommandLine", tool_args.get("command", ""))
|
|
235
|
+
tool_input["command"] = cmd
|
|
236
|
+
else:
|
|
237
|
+
fp = tool_args.get("AbsolutePath", tool_args.get("TargetFile", tool_args.get("file_path", tool_args.get("path", ""))))
|
|
238
|
+
tool_input["file_path"] = fp
|
|
239
|
+
|
|
240
|
+
input_data = {
|
|
241
|
+
"session_id": session_id,
|
|
242
|
+
"cwd": cwd,
|
|
243
|
+
"tool_name": mapped_tool,
|
|
244
|
+
"tool_input": tool_input,
|
|
245
|
+
"agent": self.agent_name
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
stdout = await run_hook_async("pre-tool-use.sh", input_data)
|
|
249
|
+
if not stdout.strip():
|
|
250
|
+
return types.HookResult(allow=True)
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
output_json = json.loads(stdout)
|
|
254
|
+
specific_output = output_json.get("hookSpecificOutput", {})
|
|
255
|
+
|
|
256
|
+
# 1. Check for deny decisions (safety guards/release boundaries)
|
|
257
|
+
if specific_output.get("permissionDecision") == "deny":
|
|
258
|
+
reason = specific_output.get("permissionDecisionReason", "Blocked by Eagle Mem guardrail.")
|
|
259
|
+
# Print clearly to the user's console
|
|
260
|
+
print(f"\n\033[1;31m[Eagle Mem Blocked]\033[0m {reason}\n", file=sys.stderr, flush=True)
|
|
261
|
+
return types.HookResult(allow=False)
|
|
262
|
+
|
|
263
|
+
# 2. Check for advisory additionalContext (co-edits, stuck loops, decision recalls)
|
|
264
|
+
additional_context = specific_output.get("additionalContext", "")
|
|
265
|
+
if additional_context.strip():
|
|
266
|
+
res = hooks.inject_agent_context(additional_context)
|
|
267
|
+
if inspect.isawaitable(res):
|
|
268
|
+
await res
|
|
269
|
+
logger.info(f"Eagle Mem Hook: Injected pre-tool context.")
|
|
270
|
+
|
|
271
|
+
# 3. Check for updatedInput (RTK rewriting)
|
|
272
|
+
updated_input = specific_output.get("updatedInput", {})
|
|
273
|
+
if updated_input and mapped_tool == "Bash" and "command" in updated_input:
|
|
274
|
+
rewritten_cmd = updated_input["command"]
|
|
275
|
+
logger.info(f"Eagle Mem Hook: Rewrote command to: {rewritten_cmd}")
|
|
276
|
+
if hasattr(tool_call, "arguments") and isinstance(tool_call.arguments, dict):
|
|
277
|
+
if "CommandLine" in tool_call.arguments:
|
|
278
|
+
tool_call.arguments["CommandLine"] = rewritten_cmd
|
|
279
|
+
elif "command" in tool_call.arguments:
|
|
280
|
+
tool_call.arguments["command"] = rewritten_cmd
|
|
281
|
+
elif hasattr(tool_call, "args") and isinstance(tool_call.args, dict):
|
|
282
|
+
if "CommandLine" in tool_call.args:
|
|
283
|
+
tool_call.args["CommandLine"] = rewritten_cmd
|
|
284
|
+
elif "command" in tool_call.args:
|
|
285
|
+
tool_call.args["command"] = rewritten_cmd
|
|
286
|
+
|
|
287
|
+
except Exception as e:
|
|
288
|
+
logger.warning(f"Eagle Mem Hook: Failed to parse pre-tool JSON: {e}")
|
|
289
|
+
|
|
290
|
+
return types.HookResult(allow=True)
|
|
291
|
+
|
|
292
|
+
async def post_tool_call(self, tool_call_result: Any):
|
|
293
|
+
"""Fires after a tool completes. Records observations and triggers curation in the background."""
|
|
294
|
+
tool_call = getattr(tool_call_result, "tool_call", None)
|
|
295
|
+
tool_name = getattr(tool_call, "name", "") if tool_call else ""
|
|
296
|
+
tool_args = getattr(tool_call, "arguments", getattr(tool_call, "args", {})) if tool_call else {}
|
|
297
|
+
if not isinstance(tool_args, dict):
|
|
298
|
+
tool_args = {}
|
|
299
|
+
|
|
300
|
+
mapped_tool = map_tool_name(tool_name)
|
|
301
|
+
session_id = get_session_id()
|
|
302
|
+
cwd = os.getcwd()
|
|
303
|
+
|
|
304
|
+
tool_input = {}
|
|
305
|
+
if mapped_tool == "Bash":
|
|
306
|
+
cmd = tool_args.get("CommandLine", tool_args.get("command", ""))
|
|
307
|
+
tool_input["command"] = cmd
|
|
308
|
+
else:
|
|
309
|
+
fp = tool_args.get("AbsolutePath", tool_args.get("TargetFile", tool_args.get("file_path", tool_args.get("path", ""))))
|
|
310
|
+
tool_input["file_path"] = fp
|
|
311
|
+
|
|
312
|
+
output_str = getattr(tool_call_result, "output", getattr(tool_call_result, "result", ""))
|
|
313
|
+
if not isinstance(output_str, str):
|
|
314
|
+
output_str = str(output_str)
|
|
315
|
+
|
|
316
|
+
input_data = {
|
|
317
|
+
"session_id": session_id,
|
|
318
|
+
"cwd": cwd,
|
|
319
|
+
"tool_name": mapped_tool,
|
|
320
|
+
"tool_input": tool_input,
|
|
321
|
+
"tool_response": {"stdout": output_str},
|
|
322
|
+
"agent": self.agent_name
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# 1. Trigger post-tool-use.sh in background
|
|
326
|
+
asyncio.create_task(run_hook_async("post-tool-use.sh", input_data))
|
|
327
|
+
|
|
328
|
+
# 2. Concurrently run eagle-mem curate in background to index changes
|
|
329
|
+
asyncio.create_task(run_cmd_async(["eagle-mem", "curate"]))
|
|
330
|
+
|
|
331
|
+
async def post_turn(self, final_response: str):
|
|
332
|
+
"""Fires after each response. Records observaciones and saves sessions in the background."""
|
|
333
|
+
session_id = get_session_id()
|
|
334
|
+
cwd = os.getcwd()
|
|
335
|
+
|
|
336
|
+
input_data = {
|
|
337
|
+
"session_id": session_id,
|
|
338
|
+
"cwd": cwd,
|
|
339
|
+
"last_assistant_message": final_response,
|
|
340
|
+
"agent": self.agent_name
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
# 1. Trigger Stop hook (stop.sh) in background to capture transcript/summary
|
|
344
|
+
asyncio.create_task(run_hook_async("stop.sh", input_data))
|
|
345
|
+
|
|
346
|
+
# 2. Concurrently trigger session save CLI in background
|
|
347
|
+
summary = final_response[:200] if final_response else ""
|
|
348
|
+
asyncio.create_task(run_cmd_async([
|
|
349
|
+
"eagle-mem", "session", "save",
|
|
350
|
+
"--summary", summary,
|
|
351
|
+
"--agent", self.agent_name
|
|
352
|
+
]))
|
|
353
|
+
|
|
354
|
+
async def on_compaction(self, data: Any):
|
|
355
|
+
"""Fires when context is compacted. Queries active tasks and injects them to survive compaction."""
|
|
356
|
+
logger.info("Eagle Mem Hook: Compaction event. Preserving active tasks...")
|
|
357
|
+
|
|
358
|
+
# Query active tasks from SQLite FTS5 index
|
|
359
|
+
tasks_text = await run_cmd_async(["eagle-mem", "tasks"])
|
|
360
|
+
if tasks_text.strip():
|
|
361
|
+
compaction_banner = (
|
|
362
|
+
f"\n=== Eagle Mem: Context Compaction Task Survival ===\n"
|
|
363
|
+
f"The following active tasks have survived compaction:\n"
|
|
364
|
+
f"{tasks_text.strip()}\n"
|
|
365
|
+
f"==================================================\n"
|
|
366
|
+
)
|
|
367
|
+
res = hooks.inject_agent_context(compaction_banner)
|
|
368
|
+
if inspect.isawaitable(res):
|
|
369
|
+
await res
|
|
370
|
+
logger.info("Eagle Mem Hook: Tasks injected for compaction survival.")
|
|
371
|
+
|
|
372
|
+
async def on_session_end(self):
|
|
373
|
+
"""Fires when the agent session closes. Cleans up runtime lock files."""
|
|
374
|
+
logger.info("Eagle Mem Hook: Session ending. Pruning locks...")
|
|
375
|
+
session_id = get_session_id()
|
|
376
|
+
cwd = os.getcwd()
|
|
377
|
+
|
|
378
|
+
input_data = {
|
|
379
|
+
"session_id": session_id,
|
|
380
|
+
"cwd": cwd,
|
|
381
|
+
"agent": self.agent_name
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
# 1. Trigger session-end.sh in background
|
|
385
|
+
asyncio.create_task(run_hook_async("session-end.sh", input_data))
|
|
386
|
+
|
|
387
|
+
# 2. Concurrently trigger prune CLI in background
|
|
388
|
+
asyncio.create_task(run_cmd_async(["eagle-mem", "prune"]))
|
|
389
|
+
|
|
390
|
+
# --- Expose Bound Methods as Global Functions ---
|
|
391
|
+
|
|
392
|
+
_global_hook = EagleMemAntigravityHook()
|
|
393
|
+
|
|
394
|
+
@hooks.on_session_start
|
|
395
|
+
async def on_session_start():
|
|
396
|
+
await _global_hook.on_session_start()
|
|
397
|
+
|
|
398
|
+
@hooks.pre_tool_call_decide
|
|
399
|
+
async def pre_tool_call_decide(tool_call: Any) -> Any:
|
|
400
|
+
return await _global_hook.pre_tool_call_decide(tool_call)
|
|
401
|
+
|
|
402
|
+
@hooks.post_tool_call
|
|
403
|
+
async def post_tool_call(tool_call_result: Any):
|
|
404
|
+
await _global_hook.post_tool_call(tool_call_result)
|
|
405
|
+
|
|
406
|
+
@hooks.post_turn
|
|
407
|
+
async def post_turn(final_response: str):
|
|
408
|
+
await _global_hook.post_turn(final_response)
|
|
409
|
+
|
|
410
|
+
@hooks.on_compaction
|
|
411
|
+
async def on_compaction(data: Any):
|
|
412
|
+
await _global_hook.on_compaction(data)
|
|
413
|
+
|
|
414
|
+
@hooks.on_session_end
|
|
415
|
+
async def on_session_end():
|
|
416
|
+
await _global_hook.on_session_end()
|
package/lib/codex-hooks.sh
CHANGED
|
@@ -3,13 +3,27 @@
|
|
|
3
3
|
# Eagle Mem — Codex hook registration helpers
|
|
4
4
|
# Shared by install.sh, update.sh, and uninstall.sh
|
|
5
5
|
# ═══════════════════════════════════════════════════════════
|
|
6
|
+
# ═══════════════════════════════════════════════════════════
|
|
6
7
|
[ -n "${_EAGLE_CODEX_HOOKS_LOADED:-}" ] && return 0
|
|
7
8
|
_EAGLE_CODEX_HOOKS_LOADED=1
|
|
8
9
|
|
|
10
|
+
eagle_codex_backup_file() {
|
|
11
|
+
local file="$1"
|
|
12
|
+
[ -f "$file" ] || return 0
|
|
13
|
+
if [ -z "${_EAGLE_CODEX_BACKUP_DONE:-}" ]; then
|
|
14
|
+
local timestamp
|
|
15
|
+
timestamp=$(date +%Y%m%d%H%M%S)
|
|
16
|
+
cp "$file" "${file}.bak.${timestamp}" 2>/dev/null || true
|
|
17
|
+
_EAGLE_CODEX_BACKUP_DONE=1
|
|
18
|
+
fi
|
|
19
|
+
}
|
|
20
|
+
|
|
9
21
|
eagle_enable_codex_hooks() {
|
|
10
22
|
local config="$EAGLE_CODEX_CONFIG"
|
|
11
23
|
mkdir -p "$(dirname "$config")"
|
|
12
24
|
|
|
25
|
+
eagle_codex_backup_file "$config"
|
|
26
|
+
|
|
13
27
|
if [ ! -f "$config" ]; then
|
|
14
28
|
cat > "$config" << 'TOML'
|
|
15
29
|
[features]
|
|
@@ -71,6 +85,7 @@ eagle_patch_codex_hook() {
|
|
|
71
85
|
script_path="${script_path%\"}"
|
|
72
86
|
|
|
73
87
|
mkdir -p "$(dirname "$hooks_file")"
|
|
88
|
+
eagle_codex_backup_file "$hooks_file"
|
|
74
89
|
if [ ! -f "$hooks_file" ]; then
|
|
75
90
|
printf '{"hooks":{}}\n' > "$hooks_file"
|
|
76
91
|
chmod 600 "$hooks_file" 2>/dev/null || true
|
|
@@ -171,6 +186,8 @@ eagle_remove_codex_hooks() {
|
|
|
171
186
|
[ -f "$hooks_file" ] || return 1
|
|
172
187
|
command -v jq &>/dev/null || return 1
|
|
173
188
|
|
|
189
|
+
eagle_codex_backup_file "$hooks_file"
|
|
190
|
+
|
|
174
191
|
local tmp
|
|
175
192
|
tmp=$(mktemp)
|
|
176
193
|
jq '
|
package/lib/common.sh
CHANGED
|
@@ -771,6 +771,7 @@ eagle_agent_source() {
|
|
|
771
771
|
case "$agent" in
|
|
772
772
|
codex|openai-codex) echo "codex" ;;
|
|
773
773
|
claude|claude-code|cloud-code) echo "claude-code" ;;
|
|
774
|
+
antigravity*|google-antigravity*|google_antigravity*) echo "antigravity" ;;
|
|
774
775
|
*)
|
|
775
776
|
if [ -n "${CODEX_THREAD_ID:-}" ] || [ -n "${CODEX_CI:-}" ] || [ -n "${CODEX_MANAGED_BY_NPM:-}" ]; then
|
|
776
777
|
echo "codex"
|
|
@@ -789,10 +790,15 @@ eagle_agent_source_from_json() {
|
|
|
789
790
|
return
|
|
790
791
|
fi
|
|
791
792
|
|
|
792
|
-
local transcript_path turn_id tool_name
|
|
793
|
+
local transcript_path turn_id tool_name agent_field
|
|
793
794
|
transcript_path=$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
794
795
|
turn_id=$(printf '%s' "$input" | jq -r '.turn_id // empty' 2>/dev/null)
|
|
795
796
|
tool_name=$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
797
|
+
agent_field=$(printf '%s' "$input" | jq -r '.agent // empty' 2>/dev/null)
|
|
798
|
+
|
|
799
|
+
case "$agent_field" in
|
|
800
|
+
antigravity*) echo "antigravity"; return ;;
|
|
801
|
+
esac
|
|
796
802
|
|
|
797
803
|
case "$transcript_path" in
|
|
798
804
|
"$HOME/.codex/"*|*/.codex/*) echo "codex"; return ;;
|
|
@@ -807,6 +813,7 @@ eagle_agent_source_from_json() {
|
|
|
807
813
|
eagle_agent_label() {
|
|
808
814
|
case "${1:-$(eagle_agent_source)}" in
|
|
809
815
|
codex) echo "Codex" ;;
|
|
816
|
+
antigravity*) echo "Antigravity" ;;
|
|
810
817
|
*) echo "Claude Code" ;;
|
|
811
818
|
esac
|
|
812
819
|
}
|
package/lib/hooks-posttool.sh
CHANGED
|
@@ -27,6 +27,16 @@ eagle_posttool_mirror_writes() {
|
|
|
27
27
|
eagle_capture_agent_plan "$fp" "$session_id" "$project" "$agent"
|
|
28
28
|
fi
|
|
29
29
|
;;
|
|
30
|
+
*/brain/*/implementation_plan.md)
|
|
31
|
+
if [ -f "$fp" ]; then
|
|
32
|
+
eagle_capture_agent_plan "$fp" "$session_id" "$project" "$agent"
|
|
33
|
+
fi
|
|
34
|
+
;;
|
|
35
|
+
*/brain/*/task.md|*/brain/*/walkthrough.md)
|
|
36
|
+
if [ -f "$fp" ]; then
|
|
37
|
+
eagle_capture_agent_memory "$fp" "$session_id" "$project" "$agent"
|
|
38
|
+
fi
|
|
39
|
+
;;
|
|
30
40
|
esac
|
|
31
41
|
fi
|
|
32
42
|
;;
|
package/lib/hooks.sh
CHANGED
|
@@ -4,11 +4,26 @@
|
|
|
4
4
|
# Shared by install.sh and update.sh
|
|
5
5
|
# ═══════════════════════════════════════════════════════════
|
|
6
6
|
|
|
7
|
+
# ═══════════════════════════════════════════════════════════
|
|
8
|
+
|
|
9
|
+
eagle_backup_file() {
|
|
10
|
+
local file="$1"
|
|
11
|
+
[ -f "$file" ] || return 0
|
|
12
|
+
if [ -z "${_EAGLE_BACKUP_DONE:-}" ]; then
|
|
13
|
+
local timestamp
|
|
14
|
+
timestamp=$(date +%Y%m%d%H%M%S)
|
|
15
|
+
cp "$file" "${file}.bak.${timestamp}" 2>/dev/null || true
|
|
16
|
+
_EAGLE_BACKUP_DONE=1
|
|
17
|
+
fi
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
eagle_clean_hook_entries() {
|
|
8
21
|
local settings="$1"
|
|
9
22
|
local event="$2"
|
|
10
23
|
local command="$3"
|
|
11
24
|
|
|
25
|
+
eagle_backup_file "$settings"
|
|
26
|
+
|
|
12
27
|
local tmp
|
|
13
28
|
tmp=$(mktemp)
|
|
14
29
|
jq --arg cmd "$command" \
|
|
@@ -23,6 +38,8 @@ eagle_patch_hook() {
|
|
|
23
38
|
local command="$4"
|
|
24
39
|
local description="${5:-}"
|
|
25
40
|
|
|
41
|
+
eagle_backup_file "$settings"
|
|
42
|
+
|
|
26
43
|
# Check both command AND matcher to avoid skipping entries with different matchers
|
|
27
44
|
# (e.g. PreToolUse with "Bash" vs "Read" matcher using the same script)
|
|
28
45
|
local match_query
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eagle-mem",
|
|
3
|
-
"version": "4.10.
|
|
4
|
-
"description": "Shared memory, release guardrails, RTK token protection, and worker lanes for Claude Code, Codex, and
|
|
3
|
+
"version": "4.10.2",
|
|
4
|
+
"description": "Shared memory, release guardrails, RTK token protection, and worker lanes for Claude Code, Codex, Grok, and Google Antigravity",
|
|
5
5
|
"bin": {
|
|
6
6
|
"eagle-mem": "bin/eagle-mem"
|
|
7
7
|
},
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"db/",
|
|
14
14
|
"skills/",
|
|
15
15
|
"docs/",
|
|
16
|
-
"architecture.html"
|
|
16
|
+
"architecture.html",
|
|
17
|
+
"integrations/"
|
|
17
18
|
],
|
|
18
19
|
"keywords": [
|
|
19
20
|
"claude-code",
|
|
@@ -26,22 +26,44 @@ eagle_ok "Grok environment detected ($EAGLE_GROK_DIR)"
|
|
|
26
26
|
|
|
27
27
|
# Check skills
|
|
28
28
|
skill_count=0
|
|
29
|
+
broken_links=0
|
|
29
30
|
if [ -d "$EAGLE_GROK_SKILLS_DIR" ]; then
|
|
30
|
-
|
|
31
|
+
for link in "$EAGLE_GROK_SKILLS_DIR"/eagle-mem-*; do
|
|
32
|
+
if [ -L "$link" ] && [ ! -e "$link" ]; then
|
|
33
|
+
broken_links=$((broken_links + 1))
|
|
34
|
+
elif [ -d "$link" ]; then
|
|
35
|
+
skill_count=$((skill_count + 1))
|
|
36
|
+
fi
|
|
37
|
+
done
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
if [ "$broken_links" -gt 0 ]; then
|
|
41
|
+
eagle_warn "Detected $broken_links broken or invalid Grok skill symlinks!"
|
|
31
42
|
fi
|
|
32
43
|
|
|
33
|
-
if [ "$skill_count" -gt 0 ]; then
|
|
44
|
+
if [ "$skill_count" -gt 0 ] && [ "$broken_links" -eq 0 ]; then
|
|
34
45
|
eagle_ok "Grok skills installed ($skill_count eagle-mem-* skills)"
|
|
35
46
|
else
|
|
36
|
-
|
|
47
|
+
if [ "$broken_links" -gt 0 ]; then
|
|
48
|
+
echo ""
|
|
49
|
+
eagle_info "Repairing broken skill symlinks now..."
|
|
50
|
+
fi
|
|
37
51
|
if [ -d "$PACKAGE_DIR/skills" ]; then
|
|
38
52
|
echo ""
|
|
39
|
-
if eagle_confirm "Link Eagle Mem skills into ~/.grok/skills/ now?"; then
|
|
53
|
+
if eagle_confirm "Link/repair Eagle Mem skills into ~/.grok/skills/ now?"; then
|
|
40
54
|
mkdir -p "$EAGLE_GROK_SKILLS_DIR"
|
|
41
55
|
for skill_dir in "$PACKAGE_DIR"/skills/*/; do
|
|
42
56
|
[ ! -d "$skill_dir" ] && continue
|
|
43
57
|
skill_name=$(basename "$skill_dir")
|
|
44
58
|
dst="$EAGLE_GROK_SKILLS_DIR/$skill_name"
|
|
59
|
+
|
|
60
|
+
# Protect custom user folders with a timestamped backup
|
|
61
|
+
if [ -d "$dst" ] && [ ! -L "$dst" ]; then
|
|
62
|
+
backup_dst="${dst}.bak.$(date +%Y%m%d%H%M%S)"
|
|
63
|
+
eagle_warn "Custom directory found at $dst. Backing up to $(basename "$backup_dst")..."
|
|
64
|
+
mv "$dst" "$backup_dst"
|
|
65
|
+
fi
|
|
66
|
+
|
|
45
67
|
[ -L "$dst" ] && rm "$dst"
|
|
46
68
|
ln -sf "$skill_dir" "$dst"
|
|
47
69
|
eagle_ok "Linked: $skill_name"
|
package/scripts/install.sh
CHANGED
|
@@ -195,13 +195,14 @@ echo ""
|
|
|
195
195
|
echo -e " ${BOLD}Installing Eagle Mem...${RESET}"
|
|
196
196
|
echo ""
|
|
197
197
|
|
|
198
|
-
mkdir -p "$EAGLE_MEM_DIR"/{hooks,lib,db,scripts}
|
|
198
|
+
mkdir -p "$EAGLE_MEM_DIR"/{hooks,lib,db,scripts,integrations}
|
|
199
199
|
|
|
200
200
|
cp "$PACKAGE_DIR"/hooks/*.sh "$EAGLE_MEM_DIR/hooks/"
|
|
201
201
|
cp "$PACKAGE_DIR"/lib/*.sh "$EAGLE_MEM_DIR/lib/"
|
|
202
202
|
cp "$PACKAGE_DIR"/db/*.sh "$EAGLE_MEM_DIR/db/"
|
|
203
203
|
cp "$PACKAGE_DIR"/db/*.sql "$EAGLE_MEM_DIR/db/"
|
|
204
204
|
cp "$PACKAGE_DIR"/scripts/*.sh "$EAGLE_MEM_DIR/scripts/" 2>/dev/null
|
|
205
|
+
cp -r "$PACKAGE_DIR"/integrations/* "$EAGLE_MEM_DIR/integrations/" 2>/dev/null || true
|
|
205
206
|
|
|
206
207
|
chmod +x "$EAGLE_MEM_DIR"/hooks/*.sh
|
|
207
208
|
chmod +x "$EAGLE_MEM_DIR"/db/migrate.sh
|
|
@@ -410,6 +411,7 @@ eagle_kv "Hooks:" "$EAGLE_MEM_DIR/hooks/"
|
|
|
410
411
|
[ "$claude_found" = true ] && eagle_kv "Claude settings:" "$SETTINGS"
|
|
411
412
|
[ "$codex_found" = true ] && eagle_kv "Codex hooks:" "$EAGLE_CODEX_HOOKS"
|
|
412
413
|
[ "$grok_found" = true ] && eagle_kv "Grok skills:" "$EAGLE_GROK_SKILLS_DIR"
|
|
414
|
+
eagle_kv "Antigravity Hook:" "$EAGLE_MEM_DIR/integrations/google_antigravity_hook.py"
|
|
413
415
|
|
|
414
416
|
echo ""
|
|
415
417
|
if [ "$grok_found" = true ]; then
|
|
@@ -419,3 +421,9 @@ if [ "$claude_found" = true ] || [ "$codex_found" = true ]; then
|
|
|
419
421
|
eagle_dim "Start a new Claude Code or Codex session — Eagle Mem will activate automatically."
|
|
420
422
|
fi
|
|
421
423
|
echo ""
|
|
424
|
+
eagle_info "Google Antigravity SDK Integration:"
|
|
425
|
+
eagle_dim " To use Eagle Mem inside your Python Antigravity agents, simply import and register the hook:"
|
|
426
|
+
echo ""
|
|
427
|
+
eagle_dim " from integrations.google_antigravity_hook import EagleMemAntigravityHook"
|
|
428
|
+
eagle_dim " config = LocalAgentConfig(hooks=EagleMemAntigravityHook().get_hooks())"
|
|
429
|
+
echo ""
|
package/scripts/memories.sh
CHANGED
|
@@ -636,7 +636,7 @@ memories_sync() {
|
|
|
636
636
|
|
|
637
637
|
if [ "$existing_hash" = "$new_hash" ] && { [ -z "$mem_project" ] || [ "$existing_project" = "$mem_project" ]; }; then
|
|
638
638
|
mem_skipped=$((mem_skipped + 1))
|
|
639
|
-
|
|
639
|
+
return 0
|
|
640
640
|
fi
|
|
641
641
|
|
|
642
642
|
eagle_capture_agent_memory "$memfile" "" "$mem_project" "claude-code"
|
|
@@ -762,6 +762,84 @@ EOF
|
|
|
762
762
|
eagle_kv "Tasks:" "$task_synced synced, $task_skipped unchanged"
|
|
763
763
|
echo ""
|
|
764
764
|
|
|
765
|
+
# ─── Sync brain planning artifacts ───────────────────
|
|
766
|
+
eagle_info "Scanning for agent brain planning artifacts..."
|
|
767
|
+
echo ""
|
|
768
|
+
|
|
769
|
+
local artifact_synced=0
|
|
770
|
+
local artifact_skipped=0
|
|
771
|
+
|
|
772
|
+
# Determine brain paths to scan
|
|
773
|
+
local brain_paths=(
|
|
774
|
+
"$HOME/.claude/brain"
|
|
775
|
+
"$HOME/.codex/brain"
|
|
776
|
+
"$HOME/.gemini/antigravity/brain"
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
for bdir in "${brain_paths[@]}"; do
|
|
780
|
+
[ ! -d "$bdir" ] && continue
|
|
781
|
+
|
|
782
|
+
# 1. Sync plans (implementation_plan.md)
|
|
783
|
+
while IFS= read -r -d '' planfile; do
|
|
784
|
+
[ ! -f "$planfile" ] && continue
|
|
785
|
+
|
|
786
|
+
local existing_hash
|
|
787
|
+
existing_hash=$(eagle_db "SELECT content_hash FROM agent_plans WHERE file_path = '$(eagle_sql_escape "$planfile")';")
|
|
788
|
+
local new_hash
|
|
789
|
+
new_hash=$(shasum -a 256 "$planfile" | awk '{print $1}')
|
|
790
|
+
|
|
791
|
+
if [ "$existing_hash" = "$new_hash" ]; then
|
|
792
|
+
artifact_skipped=$((artifact_skipped + 1))
|
|
793
|
+
continue
|
|
794
|
+
fi
|
|
795
|
+
|
|
796
|
+
# Determine agent name based on brain folder path
|
|
797
|
+
local origin_agent="claude-code"
|
|
798
|
+
[[ "$planfile" == *".codex"* ]] && origin_agent="codex"
|
|
799
|
+
[[ "$planfile" == *"antigravity"* ]] && origin_agent="antigravity"
|
|
800
|
+
|
|
801
|
+
local artifact_project="$project"
|
|
802
|
+
[ -z "$artifact_project" ] && artifact_project=$(eagle_project_from_cwd "$(pwd)")
|
|
803
|
+
|
|
804
|
+
eagle_capture_agent_plan "$planfile" "" "$artifact_project" "$origin_agent"
|
|
805
|
+
artifact_synced=$((artifact_synced + 1))
|
|
806
|
+
local ptitle
|
|
807
|
+
ptitle=$(awk '/^# /{print; exit}' "$planfile" | sed 's/^# //')
|
|
808
|
+
[ -z "$ptitle" ] && ptitle="Implementation Plan"
|
|
809
|
+
eagle_ok "Mirrored plan [$(eagle_agent_label "$origin_agent")]: $ptitle"
|
|
810
|
+
done < <(find "$bdir" -name "implementation_plan.md" -print0 2>/dev/null)
|
|
811
|
+
|
|
812
|
+
# 2. Sync tasks and walkthroughs (task.md, walkthrough.md)
|
|
813
|
+
while IFS= read -r -d '' memfile; do
|
|
814
|
+
[ ! -f "$memfile" ] && continue
|
|
815
|
+
|
|
816
|
+
local existing_hash
|
|
817
|
+
existing_hash=$(eagle_db "SELECT content_hash FROM agent_memories WHERE file_path = '$(eagle_sql_escape "$memfile")';")
|
|
818
|
+
local new_hash
|
|
819
|
+
new_hash=$(shasum -a 256 "$memfile" | awk '{print $1}')
|
|
820
|
+
|
|
821
|
+
if [ "$existing_hash" = "$new_hash" ]; then
|
|
822
|
+
artifact_skipped=$((artifact_skipped + 1))
|
|
823
|
+
continue
|
|
824
|
+
fi
|
|
825
|
+
|
|
826
|
+
# Determine agent name based on brain folder path
|
|
827
|
+
local origin_agent="claude-code"
|
|
828
|
+
[[ "$memfile" == *".codex"* ]] && origin_agent="codex"
|
|
829
|
+
[[ "$memfile" == *"antigravity"* ]] && origin_agent="antigravity"
|
|
830
|
+
|
|
831
|
+
local artifact_project="$project"
|
|
832
|
+
[ -z "$artifact_project" ] && artifact_project=$(eagle_project_from_cwd "$(pwd)")
|
|
833
|
+
|
|
834
|
+
eagle_capture_agent_memory "$memfile" "" "$artifact_project" "$origin_agent"
|
|
835
|
+
artifact_synced=$((artifact_synced + 1))
|
|
836
|
+
eagle_ok "Mirrored $(basename "$memfile") [$(eagle_agent_label "$origin_agent")]"
|
|
837
|
+
done < <(find "$bdir" \( -name "task.md" -o -name "walkthrough.md" \) -print0 2>/dev/null)
|
|
838
|
+
done
|
|
839
|
+
|
|
840
|
+
eagle_kv "Brain Artifacts:" "$artifact_synced synced, $artifact_skipped unchanged"
|
|
841
|
+
echo ""
|
|
842
|
+
|
|
765
843
|
# ─── Backfill project names ──────────────────────────
|
|
766
844
|
eagle_info "Resolving project names from agent transcripts..."
|
|
767
845
|
|
package/scripts/session.sh
CHANGED
|
@@ -164,8 +164,9 @@ save_session() {
|
|
|
164
164
|
case "$agent" in
|
|
165
165
|
codex|openai-codex) agent="codex" ;;
|
|
166
166
|
claude|claude-code|cloud-code) agent="claude-code" ;;
|
|
167
|
+
antigravity*|google-antigravity*|google_antigravity*) agent="antigravity" ;;
|
|
167
168
|
*)
|
|
168
|
-
eagle_err "--agent must be codex
|
|
169
|
+
eagle_err "--agent must be codex, claude-code, or antigravity"
|
|
169
170
|
exit 1
|
|
170
171
|
;;
|
|
171
172
|
esac
|
package/scripts/uninstall.sh
CHANGED
|
@@ -99,6 +99,13 @@ else
|
|
|
99
99
|
eagle_info "Statusline integration not present or not auto-removable"
|
|
100
100
|
fi
|
|
101
101
|
|
|
102
|
+
# ─── Remove integration files ─────────────────────────────
|
|
103
|
+
|
|
104
|
+
if [ -d "$EAGLE_MEM_DIR/integrations" ]; then
|
|
105
|
+
rm -rf "$EAGLE_MEM_DIR/integrations"
|
|
106
|
+
eagle_ok "Antigravity integration files removed"
|
|
107
|
+
fi
|
|
108
|
+
|
|
102
109
|
# ─── Remove skill symlinks ────────────────────────────────
|
|
103
110
|
|
|
104
111
|
if [ -d "$EAGLE_SKILLS_DIR" ]; then
|