eagle-mem 4.10.2 → 4.10.4
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/CHANGELOG.md +142 -0
- package/README.md +3 -82
- package/bin/eagle-mem +1 -0
- package/db/035_graph_memories.sql +65 -0
- package/db/036_graph_constraints.sql +37 -0
- package/lib/db-graph.sh +138 -0
- package/lib/db.sh +1 -0
- package/package.json +3 -2
- package/scripts/curate.sh +132 -1
- package/scripts/index.sh +44 -0
- package/scripts/memories.sh +119 -0
- package/scripts/scan.sh +23 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the **Eagle Mem** project are documented here.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## v4.10.4 Minor Release
|
|
8
|
+
|
|
9
|
+
This release introduces native relational **Knowledge Graph Memories** and an automated background **Dream Cycle** curator to consolidate multi-agent developer context:
|
|
10
|
+
|
|
11
|
+
- **Graph-based Memories**: Full integration of custom semantic code graph database primitives (`lib/db-graph.sh` and migrations `db/035_graph_memories.sql` and `db/036_graph_constraints.sql`) to link files, functions, variables, sessions, and memories.
|
|
12
|
+
- **Background Dream Cycle Curation**: Structured offline compilation in `scripts/curate.sh` that merges redundant, overlapping memories into clean `--- Compiled Truth ---` with an underlying `--- Evidence Trail ---`.
|
|
13
|
+
- **Database Firewalls**: Enforced strict enums check triggers on `node_type` and `edge_type` to guarantee complete data integrity.
|
|
14
|
+
- **Sanitized FTS Wildcards**: Safe search query sanitization in `eagle_graph_search` to prevent SQLite MATCH syntax errors on special characters.
|
|
15
|
+
- **Unified Global Porting**: Symlinked advanced nested specialist developer skills and HTTP Model Context Protocol (MCP) gateways natively into the Google Antigravity active configuration.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## v4.10.2 Patch
|
|
20
|
+
|
|
21
|
+
This release expands the Eagle Mem adapter layer and addresses multi-agent planning synchronization and gap remediations across all four supported agents:
|
|
22
|
+
|
|
23
|
+
- **Google Antigravity SDK Hook**: Fully integrated programmatic, asynchronous Python async hooks (`google_antigravity_hook.py`) mapping turn lifecycles (`SessionStart` to `Stop`/`SessionEnd`) and context survival.
|
|
24
|
+
- **Automatic Brain Artifact Synchronization**: Extracted and mirrored `implementation_plan.md`, `task.md`, and `walkthrough.md` generated by Claude, Codex, and Antigravity directly to SQLite and FTS5 search indexes.
|
|
25
|
+
- **PreToolUse Codex Enhancement**: Removed early-exits to enable full advisory, command rewrite, and feature verification support on Codex shell paths.
|
|
26
|
+
- **Agent Settings Backups**: Added timestamped configurations backups (`settings.json.bak` and `config.toml.bak`) before any mutations by the installer/update process.
|
|
27
|
+
- **Unified 4-Agent Documentation**: Generalization of the architecture tutorial (`architecture.html`), product README (`README.md`), and developer instructions (`AGENTS.md`) to treat all four agents as first-class, natively supported surfaces.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## v4.10.1 Patch
|
|
32
|
+
|
|
33
|
+
This documentation patch clarifies npm download-count behavior and Eagle Mem privacy expectations:
|
|
34
|
+
|
|
35
|
+
- npm download counts are aggregate tarball-serving statistics.
|
|
36
|
+
- Package maintainers cannot identify who downloaded the package from npm's public stats.
|
|
37
|
+
- Eagle Mem remains local-first and does not include install telemetry or phone-home analytics.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## v4.10.0 Minor Release
|
|
42
|
+
|
|
43
|
+
This release makes Eagle Mem broader and safer across agent workflows:
|
|
44
|
+
|
|
45
|
+
- Grok is now a first-class skill/CLI target: install detects `~/.grok`, links Eagle Mem skills into `~/.grok/skills`, and adds `eagle-mem grok-bootstrap`.
|
|
46
|
+
- `eagle-mem compaction` reports Compaction Survival readiness from shared project state: enriched summaries, durable tasks, stale tasks, active lanes, and last durable update.
|
|
47
|
+
- `eagle-mem test` provides a built-in smoke harness for the memory layer.
|
|
48
|
+
- `eagle-mem tasks stale` and `[STALE - Nd]` warnings make long-running task drift visible.
|
|
49
|
+
- `eagle-mem health` now surfaces orchestration lanes, learned command rules, and curator timing.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## v4.9.7 Patch
|
|
54
|
+
|
|
55
|
+
This patch is a release-readiness and UX-hardening pass:
|
|
56
|
+
|
|
57
|
+
- `eagle-mem doctor` reports the install footprint, selected SQLite binary, FTS5 availability, hook registration, statusline wiring, install-manifest health, and runtime drift.
|
|
58
|
+
- Install/update show a clear preflight plan, refresh the manifest, and keep rollback backups aware of the manifest/version files.
|
|
59
|
+
- Uninstall supports `--dry-run`, backs up config files, removes Claude/Codex hook and instruction integrations, cleans up skill links, and preserves runtime data by default.
|
|
60
|
+
- Claude statusline/HUD rendering is centralized through `scripts/statusline-em.sh --hud`.
|
|
61
|
+
- Statusline project resolution now prefers the live session row and avoids `$HOME` ancestor leakage, so new projects show their own sessions/memories instead of stale counts from older workspaces.
|
|
62
|
+
- Default hook/search/memory output follows the visible-surface UX contract: branded, compact, freshness-aware, and free of raw IDs/paths unless `--raw`, `--debug`, or `--json` is requested.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## v4.9.6 Patch
|
|
67
|
+
|
|
68
|
+
`eagle-mem update` now queues project-key backfill in the background by default, so install/update can finish, write the installed version marker, and return control to the user. Use `EAGLE_MEM_UPDATE_BACKFILL=sync eagle-mem update` only when you intentionally want to wait for a full backfill.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## v4.9.5 Patch
|
|
73
|
+
|
|
74
|
+
Stop hooks now use a fast path: they save heuristic summaries immediately, extract explicit summary blocks when present, and queue LLM enrichment in the background so Codex/Claude lifecycle hooks do not time out. SQLite access now goes through a shared FTS5-capable binary resolver used by migrations, DB helpers, updater backups, install checks, and the statusline, avoiding Android SDK or other PATH shims that shadow working SQLite builds.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## v4.9.4 Patch
|
|
79
|
+
|
|
80
|
+
Project-key hardening for agents that move between folders: hooks now keep a per-session project identity instead of recalculating from every new cwd, and statuslines prefer the stored session project before falling back to folder paths. Install/update also repairs older embedded Eagle Mem statusline blocks so nested-repo projects stop showing `Memories: 0` when the session belongs to the parent workspace.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## v4.9.3 Patch
|
|
85
|
+
|
|
86
|
+
Follow-up hardening for the v4.9.2 project-key repair: Claude transcript workspace detection now reads complete early JSONL records instead of a fixed byte slice, so large SessionStart hook context cannot hide the first `cwd`. Metadata-only memory/plan/task repairs also avoid touching FTS-indexed columns, preventing SQLite FTS update triggers from firing during safe project/source rekeys.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## v4.9.2 Patch
|
|
91
|
+
|
|
92
|
+
Nested-repo Claude Code projects now use one stable project key. When a Claude workspace contains a git repo subdirectory, hooks prefer the Claude transcript workspace root while repo-local CLI commands can still use git-root keys where appropriate. Memory sync and backfill also repair unchanged memory rows whose content hash stayed the same but whose project key was stale. FTS5 update triggers now ignore metadata-only project rekeys, avoiding SQLite virtual-table errors during safe repairs.
|
|
93
|
+
|
|
94
|
+
Installer parity also improved: first-time install now auto-provisions RTK when Cargo is available, the Eagle Mem statusline shows version/session/memory/turn counts, `eagle-mem statusline` is available as a CLI command, and Codex instructions explicitly call out that Codex currently has hook recall plus the statusline command rather than Claude Code's persistent custom statusline UI.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## v4.9.1 Patch
|
|
99
|
+
|
|
100
|
+
`eagle-mem updates status` now refreshes the npm version live, and install/update seed the local latest-version cache with the installed version. This avoids confusing status output immediately after an update.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## v4.9.0 Patch
|
|
105
|
+
|
|
106
|
+
Eagle Mem now auto-updates by default for patch bug fixes. SessionStart performs a throttled background npm check, applies eligible patch releases with a lock and runtime/database backup, runs `eagle-mem update`, and records a one-time notice for the next session. Minor and major releases stay outside the default auto-apply range unless users opt in with `eagle-mem updates enable minor` or `eagle-mem updates enable major`.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## v4.8.6 Patch
|
|
111
|
+
|
|
112
|
+
`eagle-mem session save --summary "..."` now exists as a clean manual fallback for agents that need to persist an explicit session note. It writes through the same `sessions` and `summaries` tables used by Stop hooks, keeps Claude Code/Codex source attribution, and is immediately searchable through normal recall.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## v4.8.5 Patch
|
|
117
|
+
|
|
118
|
+
First-run configuration no longer exits silently when Ollama is not listening on `localhost:11434`; Eagle Mem falls through to the installed Codex/Claude CLI provider or API-key providers. SQLite/FTS5 failures are now surfaced before DB-backed commands run, including the exact `sqlite3` binary being used and PATH guidance for common macOS Android SDK shadowing. Worker worktree paths are also canonicalized back to the main project key so backfill cannot move feature guardrails into disposable orchestration worktrees.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## v4.8.4 Patch
|
|
123
|
+
|
|
124
|
+
The orchestration handoff path is now Bash 3.2-safe, so `eagle-mem orchestrate handoff` works even when no lane options are present. This patch was verified with a real Codex coordinator -> Claude Code worker proof lane using `claude-opus-4-7` at `xhigh`; the completed lane is visible through `eagle-mem orchestrate --json`, `eagle-mem tasks completed`, and the generated handoff output. Release-boundary detection also ignores Eagle Mem's own `feature verify`/`waive` commands, so verification notes can mention dry-run checks without blocking themselves.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## v4.8.3 Patch
|
|
129
|
+
|
|
130
|
+
GitHub Pages now keeps hero text readable over the terminal background and the homepage explicitly explains installer-created/updated `CLAUDE.md` and `AGENTS.md` sections plus orchestrator/worker mode. Installer/update output also uses the new clean-output Codex wording instead of saying it added eagle-summary instructions.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## v4.8.2 Patch
|
|
135
|
+
|
|
136
|
+
Codex no longer gets instructed to print large user-visible `<eagle-summary>` XML blocks. The installer/update path rewrites existing `~/.codex/AGENTS.md` Eagle Mem instructions to the clean-output contract, context-pressure nudges use normal prose, and Codex-oriented skills/worker prompts avoid raw capture templates.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## v4.8.1 Patch
|
|
141
|
+
|
|
142
|
+
`eagle-mem memories sync` is now safe on large Claude Code/Codex memory files. The memory mirror parser no longer uses early-exit pipelines under `pipefail`, avoiding exit `141` during sync.
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
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.
|
|
14
|
+
**v4.10.4 and onward focuses on Graph Memory, Dream Cycle Curation, 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) |
|
|
@@ -199,88 +199,9 @@ curl https://api.npmjs.org/downloads/range/last-month/eagle-mem
|
|
|
199
199
|
|
|
200
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
201
|
|
|
202
|
-
###
|
|
202
|
+
### Changelog
|
|
203
203
|
|
|
204
|
-
|
|
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.
|
|
209
|
-
|
|
210
|
-
### v4.10.0 Minor Release
|
|
211
|
-
|
|
212
|
-
This release makes Eagle Mem broader and safer across agent workflows:
|
|
213
|
-
|
|
214
|
-
- Grok is now a first-class skill/CLI target: install detects `~/.grok`, links Eagle Mem skills into `~/.grok/skills`, and adds `eagle-mem grok-bootstrap`.
|
|
215
|
-
- `eagle-mem compaction` reports Compaction Survival readiness from shared project state: enriched summaries, durable tasks, stale tasks, active lanes, and last durable update.
|
|
216
|
-
- `eagle-mem test` provides a built-in smoke harness for the memory layer.
|
|
217
|
-
- `eagle-mem tasks stale` and `[STALE - Nd]` warnings make long-running task drift visible.
|
|
218
|
-
- `eagle-mem health` now surfaces orchestration lanes, learned command rules, and curator timing.
|
|
219
|
-
|
|
220
|
-
### v4.9.7 Patch
|
|
221
|
-
|
|
222
|
-
This patch is a release-readiness and UX-hardening pass:
|
|
223
|
-
|
|
224
|
-
- `eagle-mem doctor` reports the install footprint, selected SQLite binary, FTS5 availability, hook registration, statusline wiring, install-manifest health, and runtime drift.
|
|
225
|
-
- Install/update show a clear preflight plan, refresh the manifest, and keep rollback backups aware of the manifest/version files.
|
|
226
|
-
- Uninstall supports `--dry-run`, backs up config files, removes Claude/Codex hook and instruction integrations, cleans up skill links, and preserves runtime data by default.
|
|
227
|
-
- Claude statusline/HUD rendering is centralized through `scripts/statusline-em.sh --hud`.
|
|
228
|
-
- Statusline project resolution now prefers the live session row and avoids `$HOME` ancestor leakage, so new projects show their own sessions/memories instead of stale counts from older workspaces.
|
|
229
|
-
- Default hook/search/memory output follows the visible-surface UX contract: branded, compact, freshness-aware, and free of raw IDs/paths unless `--raw`, `--debug`, or `--json` is requested.
|
|
230
|
-
|
|
231
|
-
### v4.9.6 Patch
|
|
232
|
-
|
|
233
|
-
`eagle-mem update` now queues project-key backfill in the background by default, so install/update can finish, write the installed version marker, and return control to the user. Use `EAGLE_MEM_UPDATE_BACKFILL=sync eagle-mem update` only when you intentionally want to wait for a full backfill.
|
|
234
|
-
|
|
235
|
-
### v4.9.5 Patch
|
|
236
|
-
|
|
237
|
-
Stop hooks now use a fast path: they save heuristic summaries immediately, extract explicit summary blocks when present, and queue LLM enrichment in the background so Codex/Claude lifecycle hooks do not time out. SQLite access now goes through a shared FTS5-capable binary resolver used by migrations, DB helpers, updater backups, install checks, and the statusline, avoiding Android SDK or other PATH shims that shadow working SQLite builds.
|
|
238
|
-
|
|
239
|
-
### v4.9.4 Patch
|
|
240
|
-
|
|
241
|
-
Project-key hardening for agents that move between folders: hooks now keep a per-session project identity instead of recalculating from every new cwd, and statuslines prefer the stored session project before falling back to folder paths. Install/update also repairs older embedded Eagle Mem statusline blocks so nested-repo projects stop showing `Memories: 0` when the session belongs to the parent workspace.
|
|
242
|
-
|
|
243
|
-
### v4.9.3 Patch
|
|
244
|
-
|
|
245
|
-
Follow-up hardening for the v4.9.2 project-key repair: Claude transcript workspace detection now reads complete early JSONL records instead of a fixed byte slice, so large SessionStart hook context cannot hide the first `cwd`. Metadata-only memory/plan/task repairs also avoid touching FTS-indexed columns, preventing SQLite FTS update triggers from firing during safe project/source rekeys.
|
|
246
|
-
|
|
247
|
-
### v4.9.2 Patch
|
|
248
|
-
|
|
249
|
-
Nested-repo Claude Code projects now use one stable project key. When a Claude workspace contains a git repo subdirectory, hooks prefer the Claude transcript workspace root while repo-local CLI commands can still use git-root keys where appropriate. Memory sync and backfill also repair unchanged memory rows whose content hash stayed the same but whose project key was stale. FTS5 update triggers now ignore metadata-only project rekeys, avoiding SQLite virtual-table errors during safe repairs.
|
|
250
|
-
|
|
251
|
-
Installer parity also improved: first-time install now auto-provisions RTK when Cargo is available, the Eagle Mem statusline shows version/session/memory/turn counts, `eagle-mem statusline` is available as a CLI command, and Codex instructions explicitly call out that Codex currently has hook recall plus the statusline command rather than Claude Code's persistent custom statusline UI.
|
|
252
|
-
|
|
253
|
-
### v4.9.1 Patch
|
|
254
|
-
|
|
255
|
-
`eagle-mem updates status` now refreshes the npm version live, and install/update seed the local latest-version cache with the installed version. This avoids confusing status output immediately after an update.
|
|
256
|
-
|
|
257
|
-
### v4.9.0 Patch
|
|
258
|
-
|
|
259
|
-
Eagle Mem now auto-updates by default for patch bug fixes. SessionStart performs a throttled background npm check, applies eligible patch releases with a lock and runtime/database backup, runs `eagle-mem update`, and records a one-time notice for the next session. Minor and major releases stay outside the default auto-apply range unless users opt in with `eagle-mem updates enable minor` or `eagle-mem updates enable major`.
|
|
260
|
-
|
|
261
|
-
### v4.8.6 Patch
|
|
262
|
-
|
|
263
|
-
`eagle-mem session save --summary "..."` now exists as a clean manual fallback for agents that need to persist an explicit session note. It writes through the same `sessions` and `summaries` tables used by Stop hooks, keeps Claude Code/Codex source attribution, and is immediately searchable through normal recall.
|
|
264
|
-
|
|
265
|
-
### v4.8.5 Patch
|
|
266
|
-
|
|
267
|
-
First-run configuration no longer exits silently when Ollama is not listening on `localhost:11434`; Eagle Mem falls through to the installed Codex/Claude CLI provider or API-key providers. SQLite/FTS5 failures are now surfaced before DB-backed commands run, including the exact `sqlite3` binary being used and PATH guidance for common macOS Android SDK shadowing. Worker worktree paths are also canonicalized back to the main project key so backfill cannot move feature guardrails into disposable orchestration worktrees.
|
|
268
|
-
|
|
269
|
-
### v4.8.4 Patch
|
|
270
|
-
|
|
271
|
-
The orchestration handoff path is now Bash 3.2-safe, so `eagle-mem orchestrate handoff` works even when no lane options are present. This patch was verified with a real Codex coordinator -> Claude Code worker proof lane using `claude-opus-4-7` at `xhigh`; the completed lane is visible through `eagle-mem orchestrate --json`, `eagle-mem tasks completed`, and the generated handoff output. Release-boundary detection also ignores Eagle Mem's own `feature verify`/`waive` commands, so verification notes can mention dry-run checks without blocking themselves.
|
|
272
|
-
|
|
273
|
-
### v4.8.3 Patch
|
|
274
|
-
|
|
275
|
-
GitHub Pages now keeps hero text readable over the terminal background and the homepage explicitly explains installer-created/updated `CLAUDE.md` and `AGENTS.md` sections plus orchestrator/worker mode. Installer/update output also uses the new clean-output Codex wording instead of saying it added eagle-summary instructions.
|
|
276
|
-
|
|
277
|
-
### v4.8.2 Patch
|
|
278
|
-
|
|
279
|
-
Codex no longer gets instructed to print large user-visible `<eagle-summary>` XML blocks. The installer/update path rewrites existing `~/.codex/AGENTS.md` Eagle Mem instructions to the clean-output contract, context-pressure nudges use normal prose, and Codex-oriented skills/worker prompts avoid raw capture templates.
|
|
280
|
-
|
|
281
|
-
### v4.8.1 Patch
|
|
282
|
-
|
|
283
|
-
`eagle-mem memories sync` is now safe on large Claude Code/Codex memory files. The memory mirror parser no longer uses early-exit pipelines under `pipefail`, avoiding exit `141` during sync.
|
|
204
|
+
All notable releases and patch updates are documented in the separate [CHANGELOG.md](CHANGELOG.md) file.
|
|
284
205
|
|
|
285
206
|
### Search Modes
|
|
286
207
|
|
package/bin/eagle-mem
CHANGED
|
@@ -27,6 +27,7 @@ case "$command" in
|
|
|
27
27
|
statusline) "$SCRIPTS_DIR/statusline-em.sh" "$@" ;;
|
|
28
28
|
guard) bash "$SCRIPTS_DIR/guard.sh" "$@" ;;
|
|
29
29
|
overview) bash "$SCRIPTS_DIR/overview.sh" "$@" ;;
|
|
30
|
+
graph) bash "$SCRIPTS_DIR/memories.sh" graph "$@" ;;
|
|
30
31
|
session|sessions)
|
|
31
32
|
bash "$SCRIPTS_DIR/session.sh" "$@" ;;
|
|
32
33
|
memories) bash "$SCRIPTS_DIR/memories.sh" "$@" ;;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
-- ═══════════════════════════════════════════════════════════
|
|
2
|
+
-- Migration 035: Graph-based memories (nodes and edges relational tables)
|
|
3
|
+
-- ═══════════════════════════════════════════════════════════
|
|
4
|
+
|
|
5
|
+
-- ─── Graph Nodes ───────────────────────────────────────────
|
|
6
|
+
-- Represents semantic entities in the codebase (files, features, memories, tasks, sessions, tags, etc.)
|
|
7
|
+
CREATE TABLE IF NOT EXISTS graph_nodes (
|
|
8
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9
|
+
project TEXT NOT NULL,
|
|
10
|
+
node_type TEXT NOT NULL, -- 'file', 'feature', 'memory', 'task', 'session', 'tag', etc.
|
|
11
|
+
node_name TEXT NOT NULL, -- e.g., 'lib/db-graph.sh', 'auth-middleware', 'agy-session-123'
|
|
12
|
+
node_value TEXT, -- Optional content, JSON payload, description, etc.
|
|
13
|
+
source_path TEXT, -- Optional filepath or link
|
|
14
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
15
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
16
|
+
UNIQUE(project, node_type, node_name)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_graph_nodes_project ON graph_nodes(project);
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_graph_nodes_type_name ON graph_nodes(node_type, node_name);
|
|
21
|
+
|
|
22
|
+
-- ─── Graph Edges ───────────────────────────────────────────
|
|
23
|
+
-- Represents relationships between nodes with optional weights
|
|
24
|
+
CREATE TABLE IF NOT EXISTS graph_edges (
|
|
25
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
26
|
+
project TEXT NOT NULL,
|
|
27
|
+
source_node_id INTEGER NOT NULL REFERENCES graph_nodes(id) ON DELETE CASCADE,
|
|
28
|
+
target_node_id INTEGER NOT NULL REFERENCES graph_nodes(id) ON DELETE CASCADE,
|
|
29
|
+
edge_type TEXT NOT NULL, -- 'imports', 'declares', 'references', 'verifies', 'contains', 'supersedes', 'co_edited', 'modified'
|
|
30
|
+
weight REAL NOT NULL DEFAULT 1.0,
|
|
31
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
32
|
+
UNIQUE(project, source_node_id, target_node_id, edge_type)
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_graph_edges_project ON graph_edges(project);
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_graph_edges_source ON graph_edges(source_node_id);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_graph_edges_target ON graph_edges(target_node_id);
|
|
38
|
+
|
|
39
|
+
-- ─── FTS5: Full-text search on graph nodes ──────────────────
|
|
40
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS graph_nodes_fts USING fts5(
|
|
41
|
+
node_name,
|
|
42
|
+
node_value,
|
|
43
|
+
content='graph_nodes',
|
|
44
|
+
content_rowid='id'
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
-- Triggers to keep FTS in sync
|
|
48
|
+
CREATE TRIGGER IF NOT EXISTS graph_nodes_ai AFTER INSERT ON graph_nodes BEGIN
|
|
49
|
+
INSERT INTO graph_nodes_fts(rowid, node_name, node_value)
|
|
50
|
+
VALUES (new.id, new.node_name, new.node_value);
|
|
51
|
+
END;
|
|
52
|
+
|
|
53
|
+
CREATE TRIGGER IF NOT EXISTS graph_nodes_ad AFTER DELETE ON graph_nodes BEGIN
|
|
54
|
+
INSERT INTO graph_nodes_fts(graph_nodes_fts, rowid, node_name, node_value)
|
|
55
|
+
VALUES ('delete', old.id, old.node_name, old.node_value);
|
|
56
|
+
END;
|
|
57
|
+
|
|
58
|
+
CREATE TRIGGER IF NOT EXISTS graph_nodes_au
|
|
59
|
+
AFTER UPDATE OF node_name, node_value ON graph_nodes
|
|
60
|
+
BEGIN
|
|
61
|
+
INSERT INTO graph_nodes_fts(graph_nodes_fts, rowid, node_name, node_value)
|
|
62
|
+
VALUES ('delete', old.id, old.node_name, old.node_value);
|
|
63
|
+
INSERT INTO graph_nodes_fts(rowid, node_name, node_value)
|
|
64
|
+
VALUES (new.id, new.node_name, new.node_value);
|
|
65
|
+
END;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- ═══════════════════════════════════════════════════════════
|
|
2
|
+
-- Migration 036: Database-level enums constraints triggers
|
|
3
|
+
-- ═══════════════════════════════════════════════════════════
|
|
4
|
+
|
|
5
|
+
-- ─── Node Type Constraints ──────────────────────────────────
|
|
6
|
+
CREATE TRIGGER IF NOT EXISTS val_graph_nodes_insert BEFORE INSERT ON graph_nodes
|
|
7
|
+
BEGIN
|
|
8
|
+
SELECT CASE
|
|
9
|
+
WHEN NEW.node_type NOT IN ('project', 'file', 'feature', 'memory', 'task', 'session', 'tag', 'class', 'def')
|
|
10
|
+
THEN RAISE(ABORT, 'Data Integrity Error: Invalid node type. Must be one of project, file, feature, memory, task, session, tag, class, def')
|
|
11
|
+
END;
|
|
12
|
+
END;
|
|
13
|
+
|
|
14
|
+
CREATE TRIGGER IF NOT EXISTS val_graph_nodes_update BEFORE UPDATE OF node_type ON graph_nodes
|
|
15
|
+
BEGIN
|
|
16
|
+
SELECT CASE
|
|
17
|
+
WHEN NEW.node_type NOT IN ('project', 'file', 'feature', 'memory', 'task', 'session', 'tag', 'class', 'def')
|
|
18
|
+
THEN RAISE(ABORT, 'Data Integrity Error: Invalid node type. Must be one of project, file, feature, memory, task, session, tag, class, def')
|
|
19
|
+
END;
|
|
20
|
+
END;
|
|
21
|
+
|
|
22
|
+
-- ─── Edge Type Constraints ──────────────────────────────────
|
|
23
|
+
CREATE TRIGGER IF NOT EXISTS val_graph_edges_insert BEFORE INSERT ON graph_edges
|
|
24
|
+
BEGIN
|
|
25
|
+
SELECT CASE
|
|
26
|
+
WHEN NEW.edge_type NOT IN ('imports', 'declares', 'references', 'verifies', 'contains', 'supersedes', 'co_edited', 'modified', 'read')
|
|
27
|
+
THEN RAISE(ABORT, 'Data Integrity Error: Invalid edge type. Must be one of imports, declares, references, verifies, contains, supersedes, co_edited, modified, read')
|
|
28
|
+
END;
|
|
29
|
+
END;
|
|
30
|
+
|
|
31
|
+
CREATE TRIGGER IF NOT EXISTS val_graph_edges_update BEFORE UPDATE OF edge_type ON graph_edges
|
|
32
|
+
BEGIN
|
|
33
|
+
SELECT CASE
|
|
34
|
+
WHEN NEW.edge_type NOT IN ('imports', 'declares', 'references', 'verifies', 'contains', 'supersedes', 'co_edited', 'modified', 'read')
|
|
35
|
+
THEN RAISE(ABORT, 'Data Integrity Error: Invalid edge type. Must be one of imports, declares, references, verifies, contains, supersedes, co_edited, modified, read')
|
|
36
|
+
END;
|
|
37
|
+
END;
|
package/lib/db-graph.sh
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════
|
|
3
|
+
# Eagle Mem — Graph memories helpers
|
|
4
|
+
# Primitives for self-wiring codebase graph and relations
|
|
5
|
+
# ═══════════════════════════════════════════════════════════
|
|
6
|
+
[ -n "${_EAGLE_DB_GRAPH_LOADED:-}" ] && return 0
|
|
7
|
+
_EAGLE_DB_GRAPH_LOADED=1
|
|
8
|
+
|
|
9
|
+
eagle_graph_add_node() {
|
|
10
|
+
local project; project=$(eagle_sql_escape "$1")
|
|
11
|
+
local node_type; node_type=$(eagle_sql_escape "$2")
|
|
12
|
+
local node_name; node_name=$(eagle_sql_escape "$3")
|
|
13
|
+
local node_value; node_value=$(eagle_sql_escape "${4:-}")
|
|
14
|
+
local source_path; source_path=$(eagle_sql_escape "${5:-}")
|
|
15
|
+
|
|
16
|
+
eagle_db "INSERT INTO graph_nodes (project, node_type, node_name, node_value, source_path)
|
|
17
|
+
VALUES ('$project', '$node_type', '$node_name', '$node_value', '$source_path')
|
|
18
|
+
ON CONFLICT(project, node_type, node_name) DO UPDATE SET
|
|
19
|
+
node_value = CASE WHEN excluded.node_value != '' THEN excluded.node_value ELSE node_value END,
|
|
20
|
+
source_path = CASE WHEN excluded.source_path != '' THEN excluded.source_path ELSE source_path END,
|
|
21
|
+
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
eagle_graph_get_node_id() {
|
|
25
|
+
local project; project=$(eagle_sql_escape "$1")
|
|
26
|
+
local node_type; node_type=$(eagle_sql_escape "$2")
|
|
27
|
+
local node_name; node_name=$(eagle_sql_escape "$3")
|
|
28
|
+
|
|
29
|
+
eagle_db "SELECT id FROM graph_nodes
|
|
30
|
+
WHERE project = '$project'
|
|
31
|
+
AND node_type = '$node_type'
|
|
32
|
+
AND node_name = '$node_name'
|
|
33
|
+
LIMIT 1;"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
eagle_graph_add_edge() {
|
|
37
|
+
local project; project=$(eagle_sql_escape "$1")
|
|
38
|
+
local source_id; source_id=$(eagle_sql_int "$2")
|
|
39
|
+
local target_id; target_id=$(eagle_sql_int "$3")
|
|
40
|
+
local edge_type; edge_type=$(eagle_sql_escape "$4")
|
|
41
|
+
local weight="${5:-1.0}"
|
|
42
|
+
if ! [[ "$weight" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
|
|
43
|
+
weight="1.0"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
[ -z "$source_id" ] || [ -z "$target_id" ] && return 1
|
|
47
|
+
[ "$source_id" = "$target_id" ] && return 0 # avoid self-loops
|
|
48
|
+
|
|
49
|
+
eagle_db "INSERT INTO graph_edges (project, source_node_id, target_node_id, edge_type, weight)
|
|
50
|
+
VALUES ('$project', $source_id, $target_id, '$edge_type', $weight)
|
|
51
|
+
ON CONFLICT(project, source_node_id, target_node_id, edge_type) DO UPDATE SET
|
|
52
|
+
weight = weight + excluded.weight;"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
eagle_graph_query_neighbors() {
|
|
56
|
+
local node_id; node_id=$(eagle_sql_int "$1")
|
|
57
|
+
local direction="${2:-out}" # 'out', 'in', or 'both'
|
|
58
|
+
|
|
59
|
+
if [ "$direction" = "out" ]; then
|
|
60
|
+
eagle_db "SELECT e.edge_type, e.weight, n.id, n.node_type, n.node_name, n.node_value
|
|
61
|
+
FROM graph_edges e
|
|
62
|
+
JOIN graph_nodes n ON e.target_node_id = n.id
|
|
63
|
+
WHERE e.source_node_id = $node_id
|
|
64
|
+
ORDER BY e.weight DESC, n.node_name;"
|
|
65
|
+
elif [ "$direction" = "in" ]; then
|
|
66
|
+
eagle_db "SELECT e.edge_type, e.weight, n.id, n.node_type, n.node_name, n.node_value
|
|
67
|
+
FROM graph_edges e
|
|
68
|
+
JOIN graph_nodes n ON e.source_node_id = n.id
|
|
69
|
+
WHERE e.target_node_id = $node_id
|
|
70
|
+
ORDER BY e.weight DESC, n.node_name;"
|
|
71
|
+
else
|
|
72
|
+
eagle_db "SELECT 'out' as dir, e.edge_type, e.weight, n.id, n.node_type, n.node_name, n.node_value
|
|
73
|
+
FROM graph_edges e
|
|
74
|
+
JOIN graph_nodes n ON e.target_node_id = n.id
|
|
75
|
+
WHERE e.source_node_id = $node_id
|
|
76
|
+
UNION ALL
|
|
77
|
+
SELECT 'in' as dir, e.edge_type, e.weight, n.id, n.node_type, n.node_name, n.node_value
|
|
78
|
+
FROM graph_edges e
|
|
79
|
+
JOIN graph_nodes n ON e.source_node_id = n.id
|
|
80
|
+
WHERE e.target_node_id = $node_id
|
|
81
|
+
ORDER BY weight DESC;"
|
|
82
|
+
fi
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
eagle_graph_search() {
|
|
86
|
+
local project; project=$(eagle_sql_escape "$1")
|
|
87
|
+
local query; query=$(eagle_fts_sanitize "$2")
|
|
88
|
+
local type_filter="${3:-}"
|
|
89
|
+
|
|
90
|
+
local sql
|
|
91
|
+
if [ -n "$type_filter" ]; then
|
|
92
|
+
local t_esc; t_esc=$(eagle_sql_escape "$type_filter")
|
|
93
|
+
sql="SELECT n.id, n.node_type, n.node_name, n.node_value, n.source_path
|
|
94
|
+
FROM graph_nodes n
|
|
95
|
+
JOIN graph_nodes_fts f ON f.rowid = n.id
|
|
96
|
+
WHERE n.project = '$project'
|
|
97
|
+
AND n.node_type = '$t_esc'
|
|
98
|
+
AND graph_nodes_fts MATCH '$query'
|
|
99
|
+
ORDER BY rank;"
|
|
100
|
+
else
|
|
101
|
+
sql="SELECT n.id, n.node_type, n.node_name, n.node_value, n.source_path
|
|
102
|
+
FROM graph_nodes n
|
|
103
|
+
JOIN graph_nodes_fts f ON f.rowid = n.id
|
|
104
|
+
WHERE n.project = '$project'
|
|
105
|
+
AND graph_nodes_fts MATCH '$query'
|
|
106
|
+
ORDER BY rank;"
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
eagle_db "$sql"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
eagle_graph_get_node_by_id() {
|
|
113
|
+
local node_id; node_id=$(eagle_sql_int "$1")
|
|
114
|
+
eagle_db "SELECT id, node_type, node_name, node_value, source_path, created_at, updated_at
|
|
115
|
+
FROM graph_nodes
|
|
116
|
+
WHERE id = $node_id
|
|
117
|
+
LIMIT 1;"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
eagle_graph_delete_node() {
|
|
121
|
+
local node_id; node_id=$(eagle_sql_int "$1")
|
|
122
|
+
eagle_db "DELETE FROM graph_nodes WHERE id = $node_id;"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
eagle_graph_prune_orphans() {
|
|
126
|
+
local project; project=$(eagle_sql_escape "$1")
|
|
127
|
+
# Delete file nodes that no longer exist on disk
|
|
128
|
+
local result
|
|
129
|
+
result=$(eagle_db "SELECT id, node_name FROM graph_nodes WHERE project = '$project' AND node_type = 'file';")
|
|
130
|
+
[ -z "$result" ] && return 0
|
|
131
|
+
|
|
132
|
+
while IFS='|' read -r nid nname; do
|
|
133
|
+
[ -z "$nid" ] && continue
|
|
134
|
+
if [ ! -f "$nname" ]; then
|
|
135
|
+
eagle_graph_delete_node "$nid"
|
|
136
|
+
fi
|
|
137
|
+
done <<< "$result"
|
|
138
|
+
}
|
package/lib/db.sh
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eagle-mem",
|
|
3
|
-
"version": "4.10.
|
|
3
|
+
"version": "4.10.4",
|
|
4
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"
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"skills/",
|
|
15
15
|
"docs/",
|
|
16
16
|
"architecture.html",
|
|
17
|
-
"integrations/"
|
|
17
|
+
"integrations/",
|
|
18
|
+
"CHANGELOG.md"
|
|
18
19
|
],
|
|
19
20
|
"keywords": [
|
|
20
21
|
"claude-code",
|
package/scripts/curate.sh
CHANGED
|
@@ -521,7 +521,138 @@ else
|
|
|
521
521
|
eagle_dim " Not enough session data for hot file detection (need 3+ sessions, have ${total_sessions:-0})"
|
|
522
522
|
fi
|
|
523
523
|
|
|
524
|
-
# ─── 7.
|
|
524
|
+
# ─── 7. Knowledge Graph Wiring & Dream Cycle (Consolidation) ───
|
|
525
|
+
|
|
526
|
+
eagle_info "Executing Dream Cycle (Knowledge Graph & Memory Consolidation)..."
|
|
527
|
+
|
|
528
|
+
# 7.1 Wire co-edit edges in the graph
|
|
529
|
+
if [ -n "$co_edit_data" ]; then
|
|
530
|
+
co_wire_count=0
|
|
531
|
+
while IFS='|' read -r f1 f2 co_sessions; do
|
|
532
|
+
[ -z "$f1" ] || [ -z "$f2" ] && continue
|
|
533
|
+
f1_id=$(eagle_graph_get_node_id "$project" "file" "$f1")
|
|
534
|
+
f2_id=$(eagle_graph_get_node_id "$project" "file" "$f2")
|
|
535
|
+
if [ -n "$f1_id" ] && [ -n "$f2_id" ]; then
|
|
536
|
+
if [ "$DRY_RUN" -eq 0 ]; then
|
|
537
|
+
eagle_graph_add_edge "$project" "$f1_id" "$f2_id" "co_edited" "$co_sessions"
|
|
538
|
+
fi
|
|
539
|
+
co_wire_count=$((co_wire_count + 1))
|
|
540
|
+
fi
|
|
541
|
+
done <<< "$co_edit_data"
|
|
542
|
+
eagle_ok "Wired $co_wire_count co-edited file edges"
|
|
543
|
+
fi
|
|
544
|
+
|
|
545
|
+
# 7.2 Wire session nodes and access edges
|
|
546
|
+
recent_sessions=$(eagle_db "SELECT id, started_at, model FROM sessions WHERE project = '$p_esc' ORDER BY started_at DESC LIMIT 15;")
|
|
547
|
+
if [ -n "$recent_sessions" ]; then
|
|
548
|
+
session_wire_count=0
|
|
549
|
+
while IFS='|' read -r sid sstart smodel; do
|
|
550
|
+
[ -z "$sid" ] && continue
|
|
551
|
+
if [ "$DRY_RUN" -eq 0 ]; then
|
|
552
|
+
eagle_graph_add_node "$project" "session" "$sid" "Session run on $sstart using $smodel" ""
|
|
553
|
+
sid_node=$(eagle_graph_get_node_id "$project" "session" "$sid")
|
|
554
|
+
if [ -n "$sid_node" ]; then
|
|
555
|
+
# Find read/modified files in this session from observations
|
|
556
|
+
session_files=$(eagle_db "SELECT files_read, files_modified FROM observations WHERE session_id = '$(eagle_sql_escape "$sid")';")
|
|
557
|
+
if [ -n "$session_files" ]; then
|
|
558
|
+
while IFS='|' read -r f_read f_mod; do
|
|
559
|
+
# Parse files_read JSON list
|
|
560
|
+
if [ -n "$f_read" ] && [ "$f_read" != "[]" ]; then
|
|
561
|
+
echo "$f_read" | grep -oE '"[^"]+"' | tr -d '"' | while read -r rf; do
|
|
562
|
+
[ -z "$rf" ] && continue
|
|
563
|
+
rfid=$(eagle_graph_get_node_id "$project" "file" "$rf")
|
|
564
|
+
[ -n "$rfid" ] && eagle_graph_add_edge "$project" "$sid_node" "$rfid" "read" 1.0
|
|
565
|
+
done
|
|
566
|
+
fi
|
|
567
|
+
# Parse files_modified JSON list
|
|
568
|
+
if [ -n "$f_mod" ] && [ "$f_mod" != "[]" ]; then
|
|
569
|
+
echo "$f_mod" | grep -oE '"[^"]+"' | tr -d '"' | while read -r mf; do
|
|
570
|
+
[ -z "$mf" ] && continue
|
|
571
|
+
mfid=$(eagle_graph_get_node_id "$project" "file" "$mf")
|
|
572
|
+
[ -n "$mfid" ] && eagle_graph_add_edge "$project" "$sid_node" "$mfid" "modified" 2.0
|
|
573
|
+
done
|
|
574
|
+
fi
|
|
575
|
+
done <<< "$session_files"
|
|
576
|
+
fi
|
|
577
|
+
fi
|
|
578
|
+
fi
|
|
579
|
+
session_wire_count=$((session_wire_count + 1))
|
|
580
|
+
done <<< "$recent_sessions"
|
|
581
|
+
eagle_ok "Wired $session_wire_count recent session nodes and edges"
|
|
582
|
+
fi
|
|
583
|
+
|
|
584
|
+
# 7.3 Offline Memory Consolidation (Compiled Truth vs Evidence)
|
|
585
|
+
active_memories=$(eagle_db "SELECT memory_name, memory_type, description, content FROM agent_memories WHERE project = '$p_esc';")
|
|
586
|
+
if [ -n "$active_memories" ]; then
|
|
587
|
+
consolidation_prompt="Analyze these mirrored agent memories for project '$project'. Identify any memories that are redundant, overlap in scope, or describe the same subsystem/gotcha/concept.
|
|
588
|
+
|
|
589
|
+
MEMORIES:
|
|
590
|
+
$active_memories
|
|
591
|
+
|
|
592
|
+
For any memories that should be consolidated, merge them into a single 'Compiled Truth' summary.
|
|
593
|
+
The consolidated memory MUST be formatted exactly as:
|
|
594
|
+
--- Compiled Truth ---
|
|
595
|
+
<A structured, clear, up-to-date summary of the topic, gotten by merging the duplicate/overlapping memories. Keep it extremely precise.>
|
|
596
|
+
|
|
597
|
+
--- Evidence Trail ---
|
|
598
|
+
- <Original memory title 1>: <brief original description or timestamp>
|
|
599
|
+
- <Original memory title 2>: <brief original description or timestamp>
|
|
600
|
+
|
|
601
|
+
Format your output as a series of instructions:
|
|
602
|
+
CONSOLIDATE: <original memory name 1>, <original memory name 2> -> <new consolidated memory name> | description: <new description> | value: <the merged compiled truth + evidence content>
|
|
603
|
+
|
|
604
|
+
If no memories need consolidation, output: NONE"
|
|
605
|
+
|
|
606
|
+
consolidation_result=$(eagle_llm_call "$consolidation_prompt" "You consolidate software development memories into a single compiled truth. Be precise. Output CONSOLIDATE lines or NONE." 1024)
|
|
607
|
+
|
|
608
|
+
if [ -n "$consolidation_result" ] && ! echo "$consolidation_result" | grep -q "^NONE$"; then
|
|
609
|
+
cons_count=0
|
|
610
|
+
while IFS= read -r line; do
|
|
611
|
+
case "$line" in
|
|
612
|
+
CONSOLIDATE:*)
|
|
613
|
+
cons_data=$(echo "$line" | sed 's/^CONSOLIDATE:[[:space:]]*//')
|
|
614
|
+
|
|
615
|
+
# Parse matching: original_names -> new_name | description: desc | value: val
|
|
616
|
+
names_part=$(echo "$cons_data" | cut -d'-' -f1 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
617
|
+
rest_part=$(echo "$cons_data" | cut -d'>' -f2-)
|
|
618
|
+
new_name=$(echo "$rest_part" | cut -d'|' -f1 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
619
|
+
|
|
620
|
+
desc_part=$(echo "$rest_part" | grep -oE "description:[[:space:]]*[^|]+" | sed 's/description:[[:space:]]*//')
|
|
621
|
+
val_part=$(echo "$rest_part" | grep -oE "value:[[:space:]]*.+" | sed 's/value:[[:space:]]*//')
|
|
622
|
+
|
|
623
|
+
[ -z "$new_name" ] || [ -z "$names_part" ] && continue
|
|
624
|
+
|
|
625
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
626
|
+
eagle_info " Would consolidate: $names_part → $new_name"
|
|
627
|
+
else
|
|
628
|
+
# 1. Add new consolidated memory node
|
|
629
|
+
eagle_graph_add_node "$project" "memory" "$new_name" "$val_part" ""
|
|
630
|
+
new_node_id=$(eagle_graph_get_node_id "$project" "memory" "$new_name")
|
|
631
|
+
|
|
632
|
+
# 2. Wire supersedes edges from new node to old nodes, and mark old nodes as inactive/superseded
|
|
633
|
+
IFS=',' read -ra name_arr <<< "$names_part"
|
|
634
|
+
for old_n in "${name_arr[@]}"; do
|
|
635
|
+
old_n=$(echo "$old_n" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
636
|
+
[ -z "$old_n" ] && continue
|
|
637
|
+
old_node_id=$(eagle_graph_get_node_id "$project" "memory" "$old_n")
|
|
638
|
+
if [ -n "$old_node_id" ] && [ -n "$new_node_id" ]; then
|
|
639
|
+
eagle_graph_add_edge "$project" "$new_node_id" "$old_node_id" "supersedes" 1.0
|
|
640
|
+
fi
|
|
641
|
+
done
|
|
642
|
+
cons_count=$((cons_count + 1))
|
|
643
|
+
fi
|
|
644
|
+
;;
|
|
645
|
+
esac
|
|
646
|
+
done <<< "$consolidation_result"
|
|
647
|
+
eagle_ok "Consolidated $cons_count sets of overlapping agent memories"
|
|
648
|
+
else
|
|
649
|
+
eagle_ok "Agent memories are fully consolidated and up to date"
|
|
650
|
+
fi
|
|
651
|
+
else
|
|
652
|
+
eagle_dim " No active agent memories to consolidate"
|
|
653
|
+
fi
|
|
654
|
+
|
|
655
|
+
# ─── 8. Session compression (--full only) ─────────────────
|
|
525
656
|
|
|
526
657
|
if [ "$FULL" -eq 1 ]; then
|
|
527
658
|
eagle_info "Compressing old sessions..."
|
package/scripts/index.sh
CHANGED
|
@@ -178,6 +178,50 @@ COMMIT;"
|
|
|
178
178
|
|
|
179
179
|
eagle_db_pipe <<< "$txn_sql"
|
|
180
180
|
|
|
181
|
+
# Static syntax relation extraction & graph wiring
|
|
182
|
+
# Wire node & edge static parser
|
|
183
|
+
file_node_id=$(eagle_graph_get_node_id "$PROJECT" "file" "$file")
|
|
184
|
+
if [ -n "$file_node_id" ]; then
|
|
185
|
+
# 1. Parse function/class declarations
|
|
186
|
+
# e.g., "def name", "class name", "fn name", "function name", "func name"
|
|
187
|
+
declarations=$(grep -oE '\<(class|struct|function|def|fn|func)[[:space:]]+[A-Za-z0-9_]+' "$full_path" 2>/dev/null | awk '{print $1 ":" $2}' | sort -u || true)
|
|
188
|
+
if [ -n "$declarations" ]; then
|
|
189
|
+
while IFS=':' read -r dtype dname; do
|
|
190
|
+
[ -z "$dname" ] && continue
|
|
191
|
+
eagle_graph_add_node "$PROJECT" "$dtype" "$dname" "Declared in $file" "$file"
|
|
192
|
+
decl_node_id=$(eagle_graph_get_node_id "$PROJECT" "$dtype" "$dname")
|
|
193
|
+
if [ -n "$decl_node_id" ]; then
|
|
194
|
+
eagle_graph_add_edge "$PROJECT" "$file_node_id" "$decl_node_id" "declares" 1.0
|
|
195
|
+
fi
|
|
196
|
+
done <<< "$declarations"
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
# 2. Parse local relative imports/requires/sources
|
|
200
|
+
# Matches paths starting with dot (./ or ../) or sourcing .sh files
|
|
201
|
+
local_imports=$(grep -oE "['\"](\.[^'\"]+)['\"]" "$full_path" 2>/dev/null | tr -d "'\"" || true)
|
|
202
|
+
# Also grab shell source files
|
|
203
|
+
shell_sources=$(grep -E "^[[:space:]]*(\.|source) " "$full_path" 2>/dev/null | sed -E "s/^[[:space:]]*(\.|source)[[:space:]]+(.*)/\\2/" || true)
|
|
204
|
+
|
|
205
|
+
all_refs=$(printf "%s\n%s\n" "$local_imports" "$shell_sources" | sort -u)
|
|
206
|
+
if [ -n "$all_refs" ]; then
|
|
207
|
+
while IFS= read -r ref; do
|
|
208
|
+
[ -z "$ref" ] && continue
|
|
209
|
+
# Clean up path variables in shell sources (e.g. $_eagle_db_dir/db-core.sh -> db-core.sh)
|
|
210
|
+
ref_clean=$(echo "$ref" | sed -E 's/.*\///; s/\.sh$//; s/\.js$//; s/\.ts$//')
|
|
211
|
+
[ -z "$ref_clean" ] && continue
|
|
212
|
+
|
|
213
|
+
# Check if there is a known file node in our graph that matches this basename or path
|
|
214
|
+
matched_file=$(eagle_db "SELECT node_name FROM graph_nodes WHERE project = '$project_sql' AND node_type = 'file' AND (node_name LIKE '%/$ref_clean%' OR node_name = '$ref_clean') LIMIT 1;")
|
|
215
|
+
if [ -n "$matched_file" ]; then
|
|
216
|
+
target_file_id=$(eagle_graph_get_node_id "$PROJECT" "file" "$matched_file")
|
|
217
|
+
if [ -n "$target_file_id" ]; then
|
|
218
|
+
eagle_graph_add_edge "$PROJECT" "$file_node_id" "$target_file_id" "imports" 1.0
|
|
219
|
+
fi
|
|
220
|
+
fi
|
|
221
|
+
done <<< "$all_refs"
|
|
222
|
+
fi
|
|
223
|
+
fi
|
|
224
|
+
|
|
181
225
|
file_count=$((file_count + 1))
|
|
182
226
|
|
|
183
227
|
if [ $((file_count % 10)) -eq 0 ]; then
|
package/scripts/memories.sh
CHANGED
|
@@ -45,6 +45,9 @@ show_help() {
|
|
|
45
45
|
echo -e " eagle-mem memories tasks search ${CYAN}<query>${RESET} ${DIM}# full-text search tasks${RESET}"
|
|
46
46
|
echo -e " eagle-mem memories tasks show ${CYAN}<file_path>${RESET} ${DIM}# show a specific task${RESET}"
|
|
47
47
|
echo -e " eagle-mem memories sync ${DIM}# backfill memories + plans + tasks${RESET}"
|
|
48
|
+
echo -e " eagle-mem memories graph ${DIM}# view codebase knowledge graph summary${RESET}"
|
|
49
|
+
echo -e " eagle-mem memories graph query ${CYAN}<term>${RESET} ${DIM}# search knowledge graph nodes${RESET}"
|
|
50
|
+
echo -e " eagle-mem memories graph neighbors ${CYAN}<name>${RESET} ${DIM}# view a node's local network connections${RESET}"
|
|
48
51
|
echo ""
|
|
49
52
|
echo -e " ${BOLD}Options:${RESET}"
|
|
50
53
|
echo -e " ${CYAN}-p, --project${RESET} <name> Filter by project (default: current project)"
|
|
@@ -62,6 +65,7 @@ show_help() {
|
|
|
62
65
|
|
|
63
66
|
plan_action=""
|
|
64
67
|
task_action=""
|
|
68
|
+
graph_action=""
|
|
65
69
|
|
|
66
70
|
case "$action" in
|
|
67
71
|
--help|-h) show_help ;;
|
|
@@ -73,6 +77,10 @@ case "$action" in
|
|
|
73
77
|
task_action="${1:-list}"
|
|
74
78
|
shift 2>/dev/null || true
|
|
75
79
|
;;
|
|
80
|
+
graph)
|
|
81
|
+
graph_action="${1:-summary}"
|
|
82
|
+
shift 2>/dev/null || true
|
|
83
|
+
;;
|
|
76
84
|
esac
|
|
77
85
|
|
|
78
86
|
while [ $# -gt 0 ]; do
|
|
@@ -858,6 +866,116 @@ EOF
|
|
|
858
866
|
eagle_footer "Sync complete."
|
|
859
867
|
}
|
|
860
868
|
|
|
869
|
+
memories_graph() {
|
|
870
|
+
local sub_action="${graph_action:-summary}"
|
|
871
|
+
|
|
872
|
+
case "$sub_action" in
|
|
873
|
+
query)
|
|
874
|
+
local qstr="${query:-}"
|
|
875
|
+
if [ -z "$qstr" ]; then
|
|
876
|
+
eagle_err "Usage: eagle-mem memories graph query <search_term>"
|
|
877
|
+
exit 1
|
|
878
|
+
fi
|
|
879
|
+
eagle_header "Knowledge Graph Query"
|
|
880
|
+
eagle_info "Search Term: $qstr"
|
|
881
|
+
echo ""
|
|
882
|
+
|
|
883
|
+
local nodes
|
|
884
|
+
nodes=$(eagle_graph_search "$project" "$qstr" "")
|
|
885
|
+
if [ -z "$nodes" ]; then
|
|
886
|
+
eagle_dim "No graph nodes found matching '$qstr'."
|
|
887
|
+
echo ""
|
|
888
|
+
return
|
|
889
|
+
fi
|
|
890
|
+
|
|
891
|
+
while IFS='|' read -r nid ntype nname nval npath; do
|
|
892
|
+
[ -z "$nid" ] && continue
|
|
893
|
+
echo -e " ${BOLD}${nname}${RESET} ${CYAN}[${ntype}]${RESET} ${DIM}(ID: ${nid})${RESET}"
|
|
894
|
+
[ -n "$nval" ] && echo -e " ${DIM}${nval}${RESET}"
|
|
895
|
+
[ -n "$npath" ] && echo -e " ${DIM}Path: ${npath}${RESET}"
|
|
896
|
+
echo ""
|
|
897
|
+
done <<< "$nodes"
|
|
898
|
+
;;
|
|
899
|
+
neighbors)
|
|
900
|
+
local target_name="${query:-}"
|
|
901
|
+
if [ -z "$target_name" ]; then
|
|
902
|
+
eagle_err "Usage: eagle-mem memories graph neighbors <node_name>"
|
|
903
|
+
exit 1
|
|
904
|
+
fi
|
|
905
|
+
|
|
906
|
+
# Find node by name (fuzzy or exact)
|
|
907
|
+
local matched
|
|
908
|
+
matched=$(eagle_db "SELECT id, node_type, node_name FROM graph_nodes WHERE project = '$(eagle_sql_escape "$project")' AND (node_name = '$(eagle_sql_escape "$target_name")' OR node_name LIKE '%$(eagle_sql_escape "$target_name")%') LIMIT 1;")
|
|
909
|
+
if [ -z "$matched" ]; then
|
|
910
|
+
eagle_err "Node not found matching '$target_name'."
|
|
911
|
+
exit 1
|
|
912
|
+
fi
|
|
913
|
+
|
|
914
|
+
IFS='|' read -r nid ntype nname <<< "$matched"
|
|
915
|
+
eagle_header "Graph Neighbors"
|
|
916
|
+
eagle_info "Node: ${BOLD}${nname}${RESET} ${CYAN}[${ntype}]${RESET} (ID: ${nid})"
|
|
917
|
+
echo ""
|
|
918
|
+
|
|
919
|
+
# Query inbound and outbound neighbors
|
|
920
|
+
eagle_info "Outbound Connections (Node interacts with/references/contains):"
|
|
921
|
+
local outbound
|
|
922
|
+
outbound=$(eagle_graph_query_neighbors "$nid" "out")
|
|
923
|
+
if [ -n "$outbound" ]; then
|
|
924
|
+
while IFS='|' read -r etype eweight target_id target_type target_name _; do
|
|
925
|
+
[ -z "$target_id" ] && continue
|
|
926
|
+
echo -e " ${BOLD}───[ ${etype} ]───>${RESET} ${CYAN}${target_name}${RESET} [${target_type}] ${DIM}(weight: ${eweight})${RESET}"
|
|
927
|
+
done <<< "$outbound"
|
|
928
|
+
else
|
|
929
|
+
eagle_dim " None"
|
|
930
|
+
fi
|
|
931
|
+
echo ""
|
|
932
|
+
|
|
933
|
+
eagle_info "Inbound Connections (Other nodes reference/call/contain this node):"
|
|
934
|
+
local inbound
|
|
935
|
+
inbound=$(eagle_graph_query_neighbors "$nid" "in")
|
|
936
|
+
if [ -n "$inbound" ]; then
|
|
937
|
+
while IFS='|' read -r etype eweight source_id source_type source_name _; do
|
|
938
|
+
[ -z "$source_id" ] && continue
|
|
939
|
+
echo -e " ${CYAN}${source_name}${RESET} [${source_type}] ${BOLD}───[ ${etype} ]───>${RESET} this ${DIM}(weight: ${eweight})${RESET}"
|
|
940
|
+
done <<< "$inbound"
|
|
941
|
+
else
|
|
942
|
+
eagle_dim " None"
|
|
943
|
+
fi
|
|
944
|
+
echo ""
|
|
945
|
+
;;
|
|
946
|
+
summary|*)
|
|
947
|
+
eagle_header "Knowledge Graph Summary"
|
|
948
|
+
echo ""
|
|
949
|
+
|
|
950
|
+
local totals
|
|
951
|
+
totals=$(eagle_db "SELECT node_type, COUNT(*) FROM graph_nodes WHERE project = '$(eagle_sql_escape "$project")' GROUP BY node_type;")
|
|
952
|
+
|
|
953
|
+
local total_edges
|
|
954
|
+
total_edges=$(eagle_db "SELECT COUNT(*) FROM graph_edges WHERE project = '$(eagle_sql_escape "$project")';")
|
|
955
|
+
|
|
956
|
+
if [ -z "$totals" ]; then
|
|
957
|
+
eagle_dim "Graph is empty for this project."
|
|
958
|
+
eagle_dim "Run 'eagle-mem scan' or 'eagle-mem index' to auto-populate codebase nodes."
|
|
959
|
+
echo ""
|
|
960
|
+
return
|
|
961
|
+
fi
|
|
962
|
+
|
|
963
|
+
eagle_info "Entities (Nodes):"
|
|
964
|
+
while IFS='|' read -r ntype count; do
|
|
965
|
+
[ -z "$ntype" ] && continue
|
|
966
|
+
eagle_kv " ${ntype}:" "${count} nodes"
|
|
967
|
+
done <<< "$totals"
|
|
968
|
+
echo ""
|
|
969
|
+
eagle_kv "Total relationships (Edges):" "${total_edges:-0} edges"
|
|
970
|
+
echo ""
|
|
971
|
+
eagle_dim "Commands:"
|
|
972
|
+
eagle_dim " eagle-mem memories graph query <search_term> # search graph nodes"
|
|
973
|
+
eagle_dim " eagle-mem memories graph neighbors <node_name> # view neighbor connections"
|
|
974
|
+
echo ""
|
|
975
|
+
;;
|
|
976
|
+
esac
|
|
977
|
+
}
|
|
978
|
+
|
|
861
979
|
# ─── Dispatch ────────────────────────────────────────────
|
|
862
980
|
|
|
863
981
|
case "$action" in
|
|
@@ -880,6 +998,7 @@ case "$action" in
|
|
|
880
998
|
*) tasks_list ;;
|
|
881
999
|
esac
|
|
882
1000
|
;;
|
|
1001
|
+
graph) memories_graph "$@" ;;
|
|
883
1002
|
sync) memories_sync ;;
|
|
884
1003
|
*)
|
|
885
1004
|
eagle_err "Unknown action: $action"
|
package/scripts/scan.sh
CHANGED
|
@@ -367,7 +367,29 @@ fi
|
|
|
367
367
|
# Store in database
|
|
368
368
|
eagle_upsert_overview "$PROJECT" "$overview" "scan"
|
|
369
369
|
|
|
370
|
-
|
|
370
|
+
# Populate/wire codebase knowledge graph
|
|
371
|
+
eagle_graph_add_node "$PROJECT" "project" "$PROJECT" "$overview" ""
|
|
372
|
+
project_node_id=$(eagle_graph_get_node_id "$PROJECT" "project" "$PROJECT")
|
|
373
|
+
|
|
374
|
+
# Prune deleted/removed files from graph
|
|
375
|
+
eagle_graph_prune_orphans "$PROJECT"
|
|
376
|
+
|
|
377
|
+
file_node_count=0
|
|
378
|
+
if [ -n "$project_node_id" ]; then
|
|
379
|
+
while IFS= read -r file; do
|
|
380
|
+
[ -z "$file" ] && continue
|
|
381
|
+
# Add file node
|
|
382
|
+
eagle_graph_add_node "$PROJECT" "file" "$file" "" "$TARGET_DIR/$file"
|
|
383
|
+
file_node_id=$(eagle_graph_get_node_id "$PROJECT" "file" "$file")
|
|
384
|
+
if [ -n "$file_node_id" ]; then
|
|
385
|
+
# Connect project containing this file
|
|
386
|
+
eagle_graph_add_edge "$PROJECT" "$project_node_id" "$file_node_id" "contains" 1.0
|
|
387
|
+
fi
|
|
388
|
+
file_node_count=$((file_node_count + 1))
|
|
389
|
+
done < "$TMPFILE"
|
|
390
|
+
fi
|
|
391
|
+
|
|
392
|
+
eagle_ok "Overview saved for project '$PROJECT' (wired $file_node_count files in knowledge graph)"
|
|
371
393
|
echo ""
|
|
372
394
|
|
|
373
395
|
echo -e " ${BOLD}Generated overview:${RESET}"
|