eagle-mem 1.6.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  # Eagle Mem
7
7
 
8
- Lightweight persistent memory for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Inspired by [claude-mem](https://github.com/thedotmack/claude-mem) but without the resource-heavy Chroma DB + Bun daemon architecture that consumed 300-600MB per instance.
8
+ Persistent memory for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Every session starts with context from previous sessions summaries, memories, tasks, and project overviews injected automatically via hooks.
9
9
 
10
10
  **Zero per-instance overhead.** No daemon, no vector DB, no MCP server. Just bash scripts, sqlite3, and jq.
11
11
 
@@ -16,33 +16,7 @@ npm install -g eagle-mem
16
16
  eagle-mem install
17
17
  ```
18
18
 
19
- The installer checks prerequisites and offers to install missing ones:
20
-
21
- ```
22
- █▀▀ ▄▀█ █▀▀ █ █▀▀ █▀▄▀█ █▀▀ █▀▄▀█
23
- ██▄ █▀█ █▄█ █▄▄ ██▄ █ ▀ █ ██▄ █ ▀ █
24
-
25
- Eagle Mem Install
26
- ─────────────────────────────────────
27
-
28
- Checking prerequisites...
29
-
30
- ✓ sqlite3 (3.39.5)
31
- ✓ FTS5 support
32
- ✓ jq (1.7.1)
33
- ✓ Claude Code (~/.claude/)
34
-
35
- Installing Eagle Mem...
36
-
37
- ✓ Files copied to ~/.eagle-mem
38
- ✓ Database ready
39
- ✓ Hooks registered
40
- ✓ Skills installed
41
-
42
- Eagle Mem installed successfully.
43
- ```
44
-
45
- Start a new Claude Code session — Eagle Mem activates automatically and shows:
19
+ Start a new Claude Code session Eagle Mem activates automatically:
46
20
 
47
21
  ```
48
22
  █▀▀ ▄▀█ █▀▀ █ █▀▀ █▀▄▀█ █▀▀ █▀▄▀█
@@ -53,154 +27,71 @@ Sessions: 5 recent | Memories: 3 | Tasks: 2 pending
53
27
  Last: Added auth middleware with JWT validation
54
28
  ```
55
29
 
56
- ## Commands
57
-
58
- | Command | What it does |
59
- |---------|-------------|
60
- | `eagle-mem install` | First-time setup: checks prerequisites, deploys hooks, creates database, installs skills |
61
- | `eagle-mem update` | Re-deploys hooks/lib files, runs pending migrations, backfills project names |
62
- | `eagle-mem scan .` | Analyze a project and generate an overview (auto-injected at session start) |
63
- | `eagle-mem index .` | Index source files into FTS5-searchable chunks (incremental via mtime) |
64
- | `eagle-mem search <query>` | Full-text search across summaries, observations, and code chunks |
65
- | `eagle-mem tasks` | View mirrored Claude Code tasks (read-only — Claude Code manages task state) |
66
- | `eagle-mem overview` | View or regenerate project overviews |
67
- | `eagle-mem memories` | List, search, and sync Claude Code auto-memories, plans, and tasks |
68
- | `eagle-mem memories sync` | Backfill all Claude Code memories, plans, and tasks into Eagle Mem |
69
- | `eagle-mem prune` | Clean up orphan code chunks and stale data |
70
- | `eagle-mem uninstall` | Removes hooks from settings.json and optionally deletes data |
71
- | `eagle-mem help` | Shows usage, commands, and available skills |
72
- | `eagle-mem version` | Shows current version |
73
-
74
- ## Why
30
+ ## What It Does
75
31
 
76
- Claude Code sessions lose context on `/compact` and between sessions. Eagle Mem solves this with:
32
+ Eagle Mem hooks into Claude Code's lifecycle to solve the context loss problem:
77
33
 
78
- - **Automatic session summaries** saved to a shared SQLite database
79
- - **Claude Code memory mirror** — mirrors Claude's auto-memories, plans, and tasks into Eagle Mem's SQLite + FTS5
80
- - **Session-start injection** — project overview, recent summaries, memories, plans, and in-progress tasks surfaced automatically
81
- - **Compact-safe reload** — full context re-injects after compaction with trigger awareness
82
- - **TaskAware Compact Loop** using Claude Code's native `TaskCreate`/`TaskUpdate` with dependency support
83
- - **FTS5 full-text search** across all sessions and projects
84
- - **Contextual memory injection** — relevant past sessions surfaced when you ask related questions
85
- - **Privacy controls** — `<private>` tags strip sensitive content before storage
86
- - **Observation deduplication** — prevents DB bloat from repeated tool calls
87
- - **Project overviews** — persistent one-paragraph project summaries injected at session start
88
- - **Concurrent-safe** WAL mode with busy timeout — runs fine across 4-5 simultaneous sessions
89
- - **Codebase scanning** — auto-generates project overviews from structure analysis
90
- - **Code indexing** — FTS5-searchable source chunks with incremental re-indexing
91
- - **Stale data filtering** — noisy auto-captured summaries and 7-day-old tasks are excluded from injection
34
+ - **Session summaries** automatically captured when Claude's turn ends, stored in SQLite with FTS5 search
35
+ - **Memory mirror** — mirrors Claude Code's auto-memories, plans, and tasks into a searchable database
36
+ - **Context injection** — at session start, injects project overview + recent summaries + relevant memories + in-progress tasks
37
+ - **Compact survival** — after `/compact`, Eagle Mem re-injects full context so Claude picks up where it left off
38
+ - **Privacy** wrap sensitive content in `<private>` tags and it's stripped before storage
92
39
 
93
- ## How It Works
94
-
95
- ### Hook Lifecycle
40
+ ## Hook Lifecycle
96
41
 
97
42
  | Hook | Fires When | What It Does |
98
43
  |------|-----------|--------------|
99
- | **SessionStart** | startup, resume, clear, compact | Queries DB for project overview, recent summaries, memories, plans, and in-progress tasks. Injects context via stdout. Shows trigger type (startup/compact/clear/resume). |
100
- | **UserPromptSubmit** | user sends a message | Searches FTS5 for memories relevant to the user's prompt. Injects matching context with ASCII eagle branding. |
101
- | **Stop** | Claude's turn ends | Parses `<eagle-summary>` from transcript (strips `<private>` tags first). Heuristic fallback extracts user prompt + file paths. Saves summary to DB. |
102
- | **PostToolUse** | after Read/Write/Edit/Bash/TaskCreate/TaskUpdate | Captures lightweight observations with deduplication (5-second window). Mirrors Claude Code auto-memory, plan, and task writes. |
103
- | **SessionEnd** | session closes | Re-syncs all task files from `~/.claude/tasks/` to catch status changes, then marks session as completed. |
104
-
105
- ### Claude Code Memory Mirror
106
-
107
- Eagle Mem intercepts Claude Code's built-in memory, plan, and task writes via the PostToolUse hook:
108
-
109
- - **Memories** — when Claude writes to `~/.claude/projects/*/memory/*.md`, Eagle Mem mirrors the content with FTS5 indexing
110
- - **Plans** — when Claude writes to `~/.claude/plans/*.md`, Eagle Mem captures the plan
111
- - **Tasks** — when Claude calls `TaskCreate` or `TaskUpdate`, Eagle Mem captures the task JSON
112
-
113
- These are injected at session start (top 5 memories, top 3 plans, in-progress tasks) and can be searched via CLI:
114
-
115
- ```bash
116
- eagle-mem memories # list all mirrored memories
117
- eagle-mem memories search "auth" # full-text search
118
- eagle-mem memories plans # list captured plans
119
- eagle-mem memories tasks # list captured tasks
120
- eagle-mem memories sync # backfill everything from Claude Code
121
- ```
122
-
123
- **Task resync:** At session end, Eagle Mem re-reads all task JSON files to catch status changes that bypassed the PostToolUse hook (Claude Code can update tasks internally without tool calls).
124
-
125
- ### Summary Extraction
126
-
127
- Eagle Mem injects instructions for Claude to emit an `<eagle-summary>` block before its final response:
128
-
129
- ```
130
- <eagle-summary>
131
- request: What the user asked for
132
- investigated: Key files/areas explored
133
- learned: Non-obvious discoveries
134
- completed: What was accomplished
135
- next_steps: What should happen next
136
- files_read: [list of files read]
137
- files_modified: [list of files modified]
138
- </eagle-summary>
139
- ```
44
+ | **SessionStart** | startup, resume, clear, compact | Injects project overview, recent summaries, memories, and in-progress tasks |
45
+ | **UserPromptSubmit** | user sends a message | Searches FTS5 for memories relevant to the prompt |
46
+ | **Stop** | Claude's turn ends | Parses `<eagle-summary>` from transcript, saves to DB |
47
+ | **PostToolUse** | after tool calls | Captures observations, mirrors memory/plan/task writes |
48
+ | **SessionEnd** | session closes | Re-syncs tasks, marks session completed |
140
49
 
141
- The Stop hook parses this from the transcript. If Claude doesn't emit one, a heuristic fallback captures the first user prompt and files touched via tool calls.
142
-
143
- ### Privacy
144
-
145
- Wrap sensitive content in `<private>` tags and it will be stripped before storage:
146
-
147
- ```
148
- <private>
149
- API_KEY=sk-secret-123
150
- DB_PASSWORD=hunter2
151
- </private>
152
- ```
153
-
154
- The Stop hook removes `<private>` blocks at the edge — before any data reaches the database.
50
+ ## Commands
155
51
 
156
- ### TaskAware Compact Loop
52
+ | Command | What it does |
53
+ |---------|-------------|
54
+ | `eagle-mem refresh` | Full project sync: overview + scan + index + memories + tasks |
55
+ | `eagle-mem search <query>` | FTS5 search across summaries, memories, and code chunks |
56
+ | `eagle-mem search --timeline` | Recent sessions in chronological order |
57
+ | `eagle-mem overview` | View or set the project overview |
58
+ | `eagle-mem scan` | Analyze codebase structure (languages, frameworks, entry points) |
59
+ | `eagle-mem index` | Index source files into FTS5-searchable chunks |
60
+ | `eagle-mem memories` | View/sync mirrored Claude Code memories, plans, and tasks |
61
+ | `eagle-mem tasks` | View mirrored Claude Code tasks |
62
+ | `eagle-mem prune` | Remove old observations and orphaned chunks |
63
+ | `eagle-mem install` | First-time setup: hooks, database, skills |
64
+ | `eagle-mem update` | Re-deploy hooks and run pending migrations |
65
+ | `eagle-mem uninstall` | Remove hooks and optionally delete data |
157
66
 
158
- For complex multi-step work, using Claude Code's native task system:
67
+ ## Skills
159
68
 
160
- 1. **Plan** Break the work into tasks via `TaskCreate` with `addBlockedBy` for dependencies
161
- 2. **Execute** — Work on one task at a time (`TaskUpdate` to `in_progress`)
162
- 3. **Complete** — Mark done via `TaskUpdate` to `completed`
163
- 4. **Compact** — Run `/compact` when context fills up
164
- 5. **Resume** — SessionStart re-injects memory + mirrored task state from Eagle Mem
69
+ Seven skills available inside Claude Code sessions:
165
70
 
166
- Claude Code drives all task state. Eagle Mem mirrors it for cross-session recall.
71
+ | Skill | What it does |
72
+ |-------|-------------|
73
+ | `/eagle-mem-search` | Progressive memory recall — search, expand, drill into sessions |
74
+ | `/eagle-mem-overview` | Build a structured project briefing from code, README, and git history |
75
+ | `/eagle-mem-scan` | Analyze codebase structure — languages, frameworks, entry points |
76
+ | `/eagle-mem-index` | Index source files for FTS5 code search across sessions |
77
+ | `/eagle-mem-memories` | View, search, and sync Claude Code's mirrored memories and plans |
78
+ | `/eagle-mem-tasks` | TaskAware Compact Loop — break work into tasks that survive compaction |
79
+ | `/eagle-mem-prune` | Database hygiene — graduated cleanup of stale data |
167
80
 
168
81
  ## Database
169
82
 
170
- Single shared SQLite database at `~/.eagle-mem/memory.db` with a `project` column on every table for filtering.
171
-
172
- ### Tables
173
-
174
- - **sessions** — Track active/completed sessions per project
175
- - **observations** — Per-tool-use records with deduplication (files read/modified)
176
- - **summaries** — Per-session summaries with FTS5 search (UNIQUE on session_id, merge UPSERT)
177
- - **overviews** — One rolling overview per project (injected at session start)
178
- - **code_chunks** — FTS5-indexed source file chunks for code-level search
179
- - **claude_memories** — Mirror of Claude Code auto-memories with FTS5 search
180
- - **claude_plans** — Mirror of Claude Code plan files with FTS5 search
181
- - **claude_tasks** — Mirror of Claude Code tasks with FTS5 search (statuses: pending/in_progress/completed/deleted)
182
-
183
- ### Key Design Choices
184
-
185
- - **WAL mode** for concurrent readers across sessions
186
- - **busy_timeout=5000** to retry on write contention instead of failing
187
- - **FTS5 content-sync** with auto-triggers to keep search indexes in sync
188
- - **trusted_schema=ON** required for FTS5 virtual tables
189
- - **Project identification** via `git rev-parse --show-toplevel` (handles monorepo subdirectories correctly)
190
- - **Backfill system** resolves project names from Claude Code transcript files at `~/.claude/projects/`
191
- - PRAGMAs set on every connection (they're connection-scoped, not persistent)
83
+ Single shared SQLite database at `~/.eagle-mem/memory.db`. WAL mode for concurrent sessions, FTS5 for full-text search.
192
84
 
193
- ## Skills
194
-
195
- Eagle Mem ships with seven skills for use inside Claude Code sessions:
196
-
197
- - **eagle-mem-search** 3-layer search: compact FTS5 search, timeline view, full observations
198
- - **eagle-mem-tasks** TaskAware Compact Loop using Claude Code's native TaskCreate/TaskUpdate with dependencies
199
- - **eagle-mem-overview** Generate and update a persistent project overview
200
- - **eagle-mem-index** Index source files for FTS5 code-level search
201
- - **eagle-mem-scan** Scan and analyze a project to generate an overview
202
- - **eagle-mem-memories** Browse and search Claude Code memories, plans, and tasks
203
- - **eagle-mem-prune** — Clean up old observations and orphaned chunks
85
+ | Table | Purpose |
86
+ |-------|---------|
87
+ | sessions | Active/completed sessions per project |
88
+ | summaries | Per-session summaries with FTS5 (UPSERT on session_id) |
89
+ | observations | Per-tool-use records with deduplication |
90
+ | overviews | One rolling overview per project (scan vs manual source tracking) |
91
+ | code_chunks | FTS5-indexed source file chunks |
92
+ | claude_memories | Mirror of Claude Code auto-memories |
93
+ | claude_plans | Mirror of Claude Code plans |
94
+ | claude_tasks | Mirror of Claude Code tasks |
204
95
 
205
96
  ## Architecture
206
97
 
@@ -208,36 +99,27 @@ Eagle Mem ships with seven skills for use inside Claude Code sessions:
208
99
  Package (npm) Runtime (~/.eagle-mem/)
209
100
  ├── bin/eagle-mem CLI ├── memory.db SQLite + FTS5
210
101
  ├── scripts/ ├── eagle-mem.log Debug log
211
- │ ├── style.sh ├── hooks/
212
- │ ├── install.sh │ ├── session-start.sh
102
+ │ ├── install.sh ├── hooks/
103
+ │ ├── update.sh │ ├── session-start.sh
213
104
  │ ├── uninstall.sh │ ├── user-prompt-submit.sh
214
- │ ├── update.sh │ ├── stop.sh
215
- │ ├── scan.sh │ ├── post-tool-use.sh
216
- │ ├── index.sh │ └── session-end.sh
217
- │ ├── search.sh ├── lib/
218
- │ ├── tasks.sh │ ├── common.sh
219
- │ ├── overview.sh │ ├── db.sh
220
- │ ├── memories.sh │ └── hooks.sh
221
- └── db/
222
- ├── prune.sh ├── migrate.sh
223
- ├── statusline-em.sh ├── schema.sql
224
- │ └── help.sh ├── 002_overviews.sql
225
- ├── hooks/ Source ├── 003_code_chunks.sql
226
- ├── lib/ Source ├── 004_observation_indexes.sql
227
- │ ├── common.sh ├── 005_claude_memories.sql
228
- │ ├── db.sh ├── 006_claude_plans.sql
229
- │ └── hooks.sh ├── 007_claude_tasks.sql
230
- ├── db/ Source ├── 008_summary_upsert.sql
231
- │ ├── migrate.sh └── 009_drop_dead_tasks.sql
232
- │ ├── schema.sql
233
- │ └── [0-9]*.sql Migrations
234
- └── skills/ Symlinked → ~/.claude/skills/
105
+ │ ├── search.sh │ ├── stop.sh
106
+ │ ├── overview.sh │ ├── post-tool-use.sh
107
+ │ ├── tasks.sh │ └── session-end.sh
108
+ │ ├── prune.sh ├── lib/
109
+ │ ├── scan.sh │ ├── common.sh
110
+ │ ├── index.sh │ ├── db.sh
111
+ │ ├── refresh.sh │ └── hooks.sh
112
+ │ └── help.sh └── db/
113
+ ├── hooks/ Source ├── schema.sql
114
+ ├── lib/ Source └── [0-9]*.sql Migrations
115
+ ├── db/ Source
116
+ └── skills/ ~/.claude/skills/
235
117
  ├── eagle-mem-search/
236
- ├── eagle-mem-tasks/
237
118
  ├── eagle-mem-overview/
238
- ├── eagle-mem-index/
239
119
  ├── eagle-mem-scan/
120
+ ├── eagle-mem-index/
240
121
  ├── eagle-mem-memories/
122
+ ├── eagle-mem-tasks/
241
123
  └── eagle-mem-prune/
242
124
  ```
243
125
 
@@ -245,13 +127,6 @@ Package (npm) Runtime (~/.eagle-mem/)
245
127
 
246
128
  ```bash
247
129
  eagle-mem uninstall
248
- ```
249
-
250
- Removes hooks from `~/.claude/settings.json` and skill symlinks. Optionally deletes `~/.eagle-mem/` (prompts for confirmation).
251
-
252
- To also remove the npm package:
253
-
254
- ```bash
255
130
  npm uninstall -g eagle-mem
256
131
  ```
257
132
 
@@ -261,14 +136,6 @@ npm uninstall -g eagle-mem
261
136
  - `jq` (the installer offers to install if missing)
262
137
  - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed (`~/.claude/` must exist)
263
138
 
264
- ## Roadmap
265
-
266
- - [ ] **v2**: sqlite-vec embeddings for semantic code search
267
- - [ ] Timeline report skill (narrative project history from pure SQL)
268
- - [x] ~~Claude Code memory/plan/task mirror~~
269
- - [x] ~~ASCII eagle branding across hooks and CLI~~
270
- - [x] ~~Compact-safe context reload~~
271
-
272
139
  ## License
273
140
 
274
141
  MIT
package/bin/eagle-mem CHANGED
@@ -24,6 +24,7 @@ case "$command" in
24
24
  search) bash "$SCRIPTS_DIR/search.sh" "$@" ;;
25
25
  tasks) bash "$SCRIPTS_DIR/tasks.sh" "$@" ;;
26
26
  overview) bash "$SCRIPTS_DIR/overview.sh" "$@" ;;
27
+ refresh) bash "$SCRIPTS_DIR/refresh.sh" "$@" ;;
27
28
  prune) bash "$SCRIPTS_DIR/prune.sh" "$@" ;;
28
29
  memories) bash "$SCRIPTS_DIR/memories.sh" "$@" ;;
29
30
  help|--help|-h)
@@ -0,0 +1,15 @@
1
+ -- ═══════════════════════════════════════════════════════════
2
+ -- Eagle Mem — Migration 010: Session activity tracking
3
+ -- Adds last_activity_at for accurate stuck-session sweeping
4
+ -- ═══════════════════════════════════════════════════════════
5
+
6
+ PRAGMA foreign_keys = ON;
7
+
8
+ ALTER TABLE sessions ADD COLUMN last_activity_at TEXT;
9
+
10
+ UPDATE sessions SET last_activity_at = COALESCE(ended_at, started_at);
11
+
12
+ CREATE TRIGGER IF NOT EXISTS observations_touch_session AFTER INSERT ON observations
13
+ BEGIN
14
+ UPDATE sessions SET last_activity_at = NEW.created_at WHERE id = NEW.session_id;
15
+ END;
@@ -0,0 +1,10 @@
1
+ -- ═══════════════════════════════════════════════════════════
2
+ -- Eagle Mem — Migration 011: Overview source tracking
3
+ -- Tracks whether overview was written by scan or manually
4
+ -- ═══════════════════════════════════════════════════════════
5
+
6
+ PRAGMA foreign_keys = ON;
7
+
8
+ ALTER TABLE overviews ADD COLUMN source TEXT NOT NULL DEFAULT 'manual';
9
+
10
+ UPDATE overviews SET source = 'scan' WHERE length(content) <= 300;
@@ -124,12 +124,6 @@ case "$tool_name" in
124
124
  ;;
125
125
  esac
126
126
 
127
- # Deduplicate: skip if exact same observation within last 5 seconds
128
- dup_count=$(eagle_observation_exists "$session_id" "$tool_name" "$tool_summary")
129
- if [ "$dup_count" != "0" ]; then
130
- exit 0
131
- fi
132
-
133
127
  eagle_insert_observation "$session_id" "$project" "$tool_name" "$tool_summary" "$files_read" "$files_modified"
134
128
 
135
129
  exit 0
@@ -30,12 +30,13 @@ eagle_log "INFO" "SessionStart: session=$session_id project=$project source=$sou
30
30
 
31
31
  eagle_upsert_session "$session_id" "$project" "$cwd" "$model" "$source_type"
32
32
 
33
- # ─── Sweep stuck sessions (older than 4 hours) ─────────────
34
- # Exclude the current session it may be a resumed session older than 4h
33
+ # ─── Sweep stuck sessions (no activity for 7 days) ─────────
34
+ # Uses last_activity_at (updated by trigger on every observation insert)
35
+ # so long-lived sessions with regular compactions aren't falsely abandoned
35
36
  eagle_db "UPDATE sessions SET status = 'abandoned'
36
37
  WHERE status = 'active'
37
38
  AND id != '$(eagle_sql_escape "$session_id")'
38
- AND started_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-4 hours');"
39
+ AND COALESCE(last_activity_at, started_at) < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-7 days');"
39
40
 
40
41
  # ─── Build context injection ────────────────────────────────
41
42
 
@@ -49,12 +50,17 @@ Eagle Mem (https://github.com/eagleisbatman/eagle-mem) is providing persistent m
49
50
 
50
51
  "
51
52
 
52
- # Project overview (if one exists)
53
+ # Project overview
53
54
  overview=$(eagle_get_overview "$project")
54
55
  if [ -n "$overview" ]; then
55
56
  context+="=== EAGLE MEM — Project Overview ===
56
57
  $overview
57
58
 
59
+ "
60
+ else
61
+ context+="=== EAGLE MEM — Action Required ===
62
+ No overview exists for '$project'. On the user's first prompt, run /eagle-mem-overview to build a structured project briefing. The skill has full instructions for what to read and how to write a rich overview.
63
+
58
64
  "
59
65
  fi
60
66
 
@@ -129,11 +135,11 @@ Tasks for '$project':
129
135
  "
130
136
  while IFS='|' read -r tsubject tstatus tblocked; do
131
137
  [ -z "$tsubject" ] && continue
132
- local_marker=""
138
+ block_marker=""
133
139
  if [ "$tblocked" != "[]" ] && [ -n "$tblocked" ]; then
134
- local_marker=" (blocked)"
140
+ block_marker=" (blocked)"
135
141
  fi
136
- context+=" - [$tstatus] $tsubject$local_marker
142
+ context+=" - [$tstatus] $tsubject$block_marker
137
143
  "
138
144
  done <<< "$synced_tasks"
139
145
  fi
package/lib/db.sh CHANGED
@@ -40,13 +40,14 @@ eagle_upsert_session() {
40
40
  local model; model=$(eagle_sql_escape "${4:-}")
41
41
  local source; source=$(eagle_sql_escape "${5:-}")
42
42
 
43
- eagle_db "INSERT INTO sessions (id, project, cwd, model, source)
44
- VALUES ('$session_id', '$project', '$cwd', '$model', '$source')
43
+ eagle_db "INSERT INTO sessions (id, project, cwd, model, source, last_activity_at)
44
+ VALUES ('$session_id', '$project', '$cwd', '$model', '$source', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
45
45
  ON CONFLICT(id) DO UPDATE SET
46
46
  cwd = COALESCE(excluded.cwd, sessions.cwd),
47
47
  model = COALESCE(excluded.model, sessions.model),
48
48
  source = COALESCE(excluded.source, sessions.source),
49
- status = 'active';"
49
+ status = 'active',
50
+ last_activity_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');"
50
51
  }
51
52
 
52
53
  eagle_end_session() {
@@ -63,7 +64,14 @@ eagle_insert_observation() {
63
64
  local files_modified; files_modified=$(eagle_sql_escape "$6")
64
65
 
65
66
  eagle_db "INSERT INTO observations (session_id, project, tool_name, tool_input_summary, files_read, files_modified)
66
- VALUES ('$session_id', '$project', '$tool_name', '$tool_input_summary', '$files_read', '$files_modified');"
67
+ SELECT '$session_id', '$project', '$tool_name', '$tool_input_summary', '$files_read', '$files_modified'
68
+ WHERE NOT EXISTS (
69
+ SELECT 1 FROM observations
70
+ WHERE session_id = '$session_id'
71
+ AND tool_name = '$tool_name'
72
+ AND tool_input_summary = '$tool_input_summary'
73
+ AND created_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 seconds')
74
+ );"
67
75
  }
68
76
 
69
77
  eagle_insert_summary() {
@@ -138,29 +146,31 @@ eagle_search_summaries() {
138
146
  LIMIT $limit;"
139
147
  }
140
148
 
141
- eagle_observation_exists() {
142
- local session_id; session_id=$(eagle_sql_escape "$1")
143
- local tool_name; tool_name=$(eagle_sql_escape "$2")
144
- local tool_summary; tool_summary=$(eagle_sql_escape "$3")
145
-
146
- eagle_db "SELECT COUNT(*) FROM observations
147
- WHERE session_id = '$session_id'
148
- AND tool_name = '$tool_name'
149
- AND tool_input_summary = '$tool_summary'
150
- AND created_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-5 seconds');"
151
- }
152
-
153
149
  eagle_upsert_overview() {
154
150
  local project; project=$(eagle_sql_escape "$1")
155
- local content; content=$(eagle_sql_escape "$2")
151
+ local raw_content="$2"
152
+ local ov_source; ov_source=$(eagle_sql_escape "${3:-manual}")
153
+
154
+ if [ ${#raw_content} -gt 16384 ]; then
155
+ raw_content="${raw_content:0:16384}"
156
+ eagle_log "WARN" "Overview for '$1' truncated to 16 KB (was ${#2} bytes)"
157
+ fi
156
158
 
157
- eagle_db "INSERT INTO overviews (project, content, updated_at)
158
- VALUES ('$project', '$content', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
159
+ local content; content=$(eagle_sql_escape "$raw_content")
160
+
161
+ eagle_db "INSERT INTO overviews (project, content, source, updated_at)
162
+ VALUES ('$project', '$content', '$ov_source', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
159
163
  ON CONFLICT(project) DO UPDATE SET
160
164
  content = excluded.content,
165
+ source = excluded.source,
161
166
  updated_at = excluded.updated_at;"
162
167
  }
163
168
 
169
+ eagle_get_overview_source() {
170
+ local project; project=$(eagle_sql_escape "$1")
171
+ eagle_db "SELECT source FROM overviews WHERE project = '$project';"
172
+ }
173
+
164
174
  eagle_get_overview() {
165
175
  local project; project=$(eagle_sql_escape "$1")
166
176
 
@@ -495,24 +505,24 @@ eagle_backfill_projects() {
495
505
  sid_sql=$(eagle_sql_escape "$sid")
496
506
  proj_sql=$(eagle_sql_escape "$project")
497
507
 
508
+ # All six tables updated atomically per session to prevent
509
+ # partial backfill if the process is interrupted.
510
+ # Note: total_changes() includes FTS trigger changes, so the
511
+ # reported count may be higher than actual rows updated.
512
+ # This is cosmetic — the count is only used for status messages.
498
513
  local ch
499
- ch=$(eagle_db "UPDATE sessions SET project = '$proj_sql' WHERE id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
500
- SELECT changes();")
501
- [ "${ch:-0}" -gt 0 ] && updated=$((updated + ch))
502
- ch=$(eagle_db "UPDATE claude_tasks SET project = '$proj_sql' WHERE source_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
503
- SELECT changes();")
504
- [ "${ch:-0}" -gt 0 ] && updated=$((updated + ch))
505
- ch=$(eagle_db "UPDATE claude_memories SET project = '$proj_sql' WHERE origin_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
506
- SELECT changes();")
507
- [ "${ch:-0}" -gt 0 ] && updated=$((updated + ch))
508
- ch=$(eagle_db "UPDATE claude_plans SET project = '$proj_sql' WHERE origin_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
509
- SELECT changes();")
510
- [ "${ch:-0}" -gt 0 ] && updated=$((updated + ch))
511
- ch=$(eagle_db "UPDATE summaries SET project = '$proj_sql' WHERE session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
512
- SELECT changes();")
513
- [ "${ch:-0}" -gt 0 ] && updated=$((updated + ch))
514
- ch=$(eagle_db "UPDATE observations SET project = '$proj_sql' WHERE session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
515
- SELECT changes();")
514
+ ch=$(eagle_db_pipe <<SQL
515
+ BEGIN;
516
+ UPDATE sessions SET project = '$proj_sql' WHERE id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
517
+ UPDATE claude_tasks SET project = '$proj_sql' WHERE source_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
518
+ UPDATE claude_memories SET project = '$proj_sql' WHERE origin_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
519
+ UPDATE claude_plans SET project = '$proj_sql' WHERE origin_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
520
+ UPDATE summaries SET project = '$proj_sql' WHERE session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
521
+ UPDATE observations SET project = '$proj_sql' WHERE session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
522
+ SELECT total_changes();
523
+ COMMIT;
524
+ SQL
525
+ )
516
526
  [ "${ch:-0}" -gt 0 ] && updated=$((updated + ch))
517
527
  done <<< "$map"
518
528
 
@@ -528,13 +538,21 @@ eagle_prune_orphan_chunks() {
528
538
  paths=$(eagle_db "SELECT DISTINCT file_path FROM code_chunks WHERE project = '$project';")
529
539
 
530
540
  local removed=0
541
+ local txn_sql="BEGIN;"
531
542
  while IFS= read -r fpath; do
532
543
  [ -z "$fpath" ] && continue
533
544
  if [ ! -f "$target_dir/$fpath" ]; then
534
545
  local fpath_sql; fpath_sql=$(eagle_sql_escape "$fpath")
535
- eagle_db "DELETE FROM code_chunks WHERE project = '$project' AND file_path = '$fpath_sql';"
546
+ txn_sql+="
547
+ DELETE FROM code_chunks WHERE project = '$project' AND file_path = '$fpath_sql';"
536
548
  removed=$((removed + 1))
537
549
  fi
538
550
  done <<< "$paths"
551
+ txn_sql+="
552
+ COMMIT;"
553
+
554
+ if [ "$removed" -gt 0 ]; then
555
+ eagle_db_pipe <<< "$txn_sql"
556
+ fi
539
557
  echo "$removed"
540
558
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eagle-mem",
3
- "version": "1.6.3",
4
- "description": "Lightweight persistent memory for Claude Code — SQLite + FTS5, no daemon, no bloat",
3
+ "version": "2.0.0",
4
+ "description": "Persistent memory for Claude Code — SQLite + FTS5, no daemon, no bloat",
5
5
  "bin": {
6
6
  "eagle-mem": "bin/eagle-mem"
7
7
  },