eagle-mem 1.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 ADDED
@@ -0,0 +1,227 @@
1
+ ```
2
+ ███████╗ █████╗ ██████╗ ██╗ ███████╗
3
+ ██╔════╝██╔══██╗██╔════╝ ██║ ██╔════╝
4
+ █████╗ ███████║██║ ██╗ ██║ █████╗
5
+ ██╔══╝ ██╔══██║██║ ╚██╗██║ ██╔══╝
6
+ ███████╗██║ ██║╚██████╔╝███████╗███████╗
7
+ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝
8
+ ███╗ ███╗███████╗███╗ ███╗
9
+ ████╗ ████║██╔════╝████╗ ████║
10
+ ██╔████╔██║█████╗ ██╔████╔██║
11
+ ██║╚██╔╝██║██╔══╝ ██║╚██╔╝██║
12
+ ██║ ╚═╝ ██║███████╗██║ ╚═╝ ██║
13
+ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
14
+ ```
15
+
16
+ # Eagle Mem
17
+
18
+ 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.
19
+
20
+ **Zero per-instance overhead.** No daemon, no vector DB, no MCP server. Just bash scripts, sqlite3, and jq.
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ npm install -g eagle-mem
26
+ eagle-mem install
27
+ ```
28
+
29
+ The installer checks prerequisites and offers to install missing ones:
30
+
31
+ ```
32
+ Eagle Mem Install
33
+ ─────��───────────────────────────────
34
+
35
+ Checking prerequisites...
36
+
37
+ ✓ sqlite3 (3.39.5)
38
+ ✓ FTS5 support
39
+ ✗ jq not found
40
+ ? Install jq? [y/N] y
41
+ → Running: brew install jq
42
+ ✓ jq installed (1.7.1)
43
+ ✓ Claude Code (~/.claude/)
44
+
45
+ Installing Eagle Mem...
46
+
47
+ ✓ Files copied to ~/.eagle-mem
48
+ ✓ Database ready
49
+ ✓ SessionStart hook
50
+ ✓ Stop hook
51
+ ✓ PostToolUse hook
52
+ ✓ SessionEnd hook
53
+ ✓ UserPromptSubmit hook
54
+ ✓ Skill: eagle-mem-overview
55
+ ✓ Skill: eagle-mem-search
56
+ ✓ Skill: eagle-mem-tasks
57
+
58
+ Eagle Mem installed successfully.
59
+ ```
60
+
61
+ Start a new Claude Code session — Eagle Mem activates automatically.
62
+
63
+ ## Commands
64
+
65
+ | Command | What it does |
66
+ |---------|-------------|
67
+ | `eagle-mem install` | First-time setup: checks prerequisites, deploys hooks, creates database, installs skills |
68
+ | `eagle-mem update` | Re-deploys hooks/lib files and runs pending database migrations |
69
+ | `eagle-mem scan .` | Analyze a project and generate an overview (auto-injected at session start) |
70
+ | `eagle-mem index .` | Index source files into FTS5-searchable chunks (incremental via mtime) |
71
+ | `eagle-mem uninstall` | Removes hooks from settings.json and optionally deletes data |
72
+ | `eagle-mem help` | Shows usage, commands, and available skills |
73
+ | `eagle-mem version` | Shows current version |
74
+
75
+ ## Why
76
+
77
+ Claude Code sessions lose context on `/compact` and between sessions. Eagle Mem solves this with:
78
+
79
+ - **Automatic session summaries** saved to a shared SQLite database
80
+ - **TaskAware Compact Loop** for breaking complex work into subtasks that survive compaction
81
+ - **FTS5 full-text search** across all sessions and projects
82
+ - **Contextual memory injection** — relevant past sessions surfaced when you ask related questions
83
+ - **Privacy controls** — `<private>` tags strip sensitive content before storage
84
+ - **Observation deduplication** — prevents DB bloat from repeated tool calls
85
+ - **Project overviews** — persistent one-paragraph project summaries injected at session start
86
+ - **Concurrent-safe** WAL mode with busy timeout — runs fine across 4-5 simultaneous sessions
87
+ - **Codebase scanning** — auto-generates project overviews from structure analysis
88
+ - **Code indexing** — FTS5-searchable source chunks with incremental re-indexing
89
+
90
+ ## How It Works
91
+
92
+ ### Hook Lifecycle
93
+
94
+ | Hook | Fires When | What It Does |
95
+ |------|-----------|--------------|
96
+ | **SessionStart** | startup, resume, clear, compact | Queries DB for project overview, recent summaries, and pending tasks. Injects context via stdout. |
97
+ | **UserPromptSubmit** | user sends a message | Searches FTS5 for memories relevant to the user's prompt. Injects matching context. |
98
+ | **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. |
99
+ | **PostToolUse** | after Read/Write/Edit/Bash | Captures lightweight observations with deduplication (5-second window). |
100
+ | **SessionEnd** | session closes | Marks session as completed with timestamp. |
101
+
102
+ ### Summary Extraction
103
+
104
+ Eagle Mem injects instructions for Claude to emit an `<eagle-summary>` block before its final response:
105
+
106
+ ```
107
+ <eagle-summary>
108
+ request: What the user asked for
109
+ investigated: Key files/areas explored
110
+ learned: Non-obvious discoveries
111
+ completed: What was accomplished
112
+ next_steps: What should happen next
113
+ files_read: [list of files read]
114
+ files_modified: [list of files modified]
115
+ </eagle-summary>
116
+ ```
117
+
118
+ 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.
119
+
120
+ ### Privacy
121
+
122
+ Wrap sensitive content in `<private>` tags and it will be stripped before storage:
123
+
124
+ ```
125
+ <private>
126
+ API_KEY=sk-secret-123
127
+ DB_PASSWORD=hunter2
128
+ </private>
129
+ ```
130
+
131
+ The Stop hook removes `<private>` blocks at the edge — before any data reaches the database.
132
+
133
+ ### TaskAware Compact Loop
134
+
135
+ For complex multi-step work:
136
+
137
+ 1. **Plan** — Break the work into subtasks stored in the DB
138
+ 2. **Execute** — Work on one task at a time
139
+ 3. **Compact** — Run `/compact` when context fills up
140
+ 4. **Resume** — SessionStart re-injects memory + the next pending task
141
+
142
+ This prevents context bloat and hallucination on long tasks.
143
+
144
+ ## Database
145
+
146
+ Single shared SQLite database at `~/.eagle-mem/memory.db` with a `project` column on every table for filtering.
147
+
148
+ ### Tables
149
+
150
+ - **sessions** — Track active/completed sessions per project
151
+ - **observations** — Per-tool-use records with deduplication (files read/modified)
152
+ - **summaries** — Per-session summaries with FTS5 search
153
+ - **tasks** — Subtasks for the TaskAware Compact Loop with FTS5 search
154
+ - **overviews** — One rolling overview per project (injected at session start)
155
+ - **code_chunks** — FTS5-indexed source file chunks for code-level search
156
+
157
+ ### Key Design Choices
158
+
159
+ - **WAL mode** for concurrent readers across sessions
160
+ - **busy_timeout=5000** to retry on write contention instead of failing
161
+ - **FTS5 content-sync** with auto-triggers to keep search indexes in sync
162
+ - **trusted_schema=ON** required for FTS5 virtual tables
163
+ - PRAGMAs set on every connection (they're connection-scoped, not persistent)
164
+
165
+ ## Skills
166
+
167
+ Eagle Mem ships with three skills for use inside Claude Code sessions:
168
+
169
+ - **eagle-mem-search** — 3-layer search: compact FTS5 search, timeline view, full observations
170
+ - **eagle-mem-tasks** — TaskAware Compact Loop: create, view, complete, and manage subtasks
171
+ - **eagle-mem-overview** — Generate and update a persistent project overview
172
+
173
+ ## Architecture
174
+
175
+ ```
176
+ Package (npm) Runtime (~/.eagle-mem/)
177
+ ├── bin/eagle-mem CLI ├── memory.db SQLite + FTS5
178
+ ├── scripts/ ├── eagle-mem.log Debug log
179
+ │ ├── style.sh ├── hooks/
180
+ │ ├── install.sh │ ├── session-start.sh
181
+ │ ├── uninstall.sh │ ├── user-prompt-submit.sh
182
+ │ ├── update.sh │ ├── stop.sh
183
+ │ ├── scan.sh │ ├── post-tool-use.sh
184
+ │ ├── index.sh │ └── session-end.sh
185
+ │ └── help.sh ├── lib/
186
+ ├── hooks/ Source │ ├── common.sh
187
+ ├── lib/ Source │ └── db.sh
188
+ │ ├── common.sh └── db/
189
+ │ └── db.sh ├── migrate.sh
190
+ ├── db/ Source ├── schema.sql
191
+ │ ├── migrate.sh ├── 002_overviews.sql
192
+ │ ├── schema.sql └── 003_code_chunks.sql
193
+ │ ├── 002_overviews.sql
194
+ │ └── 003_code_chunks.sql
195
+ └── skills/ Symlinked → ~/.claude/skills/
196
+ ```
197
+
198
+ ## Uninstall
199
+
200
+ ```bash
201
+ eagle-mem uninstall
202
+ ```
203
+
204
+ Removes hooks from `~/.claude/settings.json` and skill symlinks. Optionally deletes `~/.eagle-mem/` (prompts for confirmation).
205
+
206
+ To also remove the npm package:
207
+
208
+ ```bash
209
+ npm uninstall -g eagle-mem
210
+ ```
211
+
212
+ ## Prerequisites
213
+
214
+ - `sqlite3` with FTS5 support (ships with macOS; the installer offers to install if missing)
215
+ - `jq` (the installer offers to install if missing)
216
+ - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed (`~/.claude/` must exist)
217
+
218
+ ## Roadmap
219
+
220
+ - [ ] **v2**: sqlite-vec embeddings for semantic code search
221
+ - [ ] Integration into [Eagle Skills](https://github.com/eagleisbatman/eagle-skills)
222
+ - [ ] Timeline report skill (narrative project history from pure SQL)
223
+ - [ ] GitHub Pages site (matching Eagle Skills)
224
+
225
+ ## License
226
+
227
+ MIT
package/bin/eagle-mem ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SOURCE="${BASH_SOURCE[0]}"
5
+ while [ -L "$SOURCE" ]; do
6
+ DIR="$(cd "$(dirname "$SOURCE")" && pwd)"
7
+ SOURCE="$(readlink "$SOURCE")"
8
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
9
+ done
10
+ PACKAGE_DIR="$(cd "$(dirname "$SOURCE")/.." && pwd)"
11
+ SCRIPTS_DIR="$PACKAGE_DIR/scripts"
12
+
13
+ . "$SCRIPTS_DIR/style.sh"
14
+
15
+ command="${1:-help}"
16
+ shift 2>/dev/null || true
17
+
18
+ case "$command" in
19
+ install) bash "$SCRIPTS_DIR/install.sh" "$PACKAGE_DIR" "$@" ;;
20
+ uninstall) bash "$SCRIPTS_DIR/uninstall.sh" "$@" ;;
21
+ update) bash "$SCRIPTS_DIR/update.sh" "$PACKAGE_DIR" "$@" ;;
22
+ scan) bash "$SCRIPTS_DIR/scan.sh" "$@" ;;
23
+ index) bash "$SCRIPTS_DIR/index.sh" "$@" ;;
24
+ help|--help|-h)
25
+ bash "$SCRIPTS_DIR/help.sh" ;;
26
+ version|--version|-v|-V)
27
+ version=$(node -e "console.log(require('$PACKAGE_DIR/package.json').version)" 2>/dev/null || echo "unknown")
28
+ echo -e " ${BOLD}Eagle Mem${RESET} v${version}"
29
+ ;;
30
+ *)
31
+ eagle_err "Unknown command: $command"
32
+ echo ""
33
+ bash "$SCRIPTS_DIR/help.sh"
34
+ exit 1
35
+ ;;
36
+ esac
@@ -0,0 +1,16 @@
1
+ -- ═══════════════════════════════════════════════════════════
2
+ -- Eagle Mem — Migration 002: Project overviews
3
+ -- One rolling overview per project for quick context injection
4
+ -- ═══════════════════════════════════════════════════════════
5
+
6
+ -- NOTE: PRAGMAs are connection-scoped, set in lib/db.sh
7
+ PRAGMA foreign_keys = ON;
8
+
9
+ CREATE TABLE IF NOT EXISTS overviews (
10
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
11
+ project TEXT NOT NULL UNIQUE,
12
+ content TEXT NOT NULL,
13
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
14
+ );
15
+
16
+ CREATE INDEX IF NOT EXISTS idx_overviews_project ON overviews(project);
@@ -0,0 +1,46 @@
1
+ -- ═══════════════════════════════════════════════════════════
2
+ -- Eagle Mem — Migration 003: Code chunks for source indexing
3
+ -- FTS5 searchable chunks of source files
4
+ -- ═══════════════════════════════════════════════════════════
5
+
6
+ PRAGMA foreign_keys = ON;
7
+
8
+ CREATE TABLE IF NOT EXISTS code_chunks (
9
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
10
+ project TEXT NOT NULL,
11
+ file_path TEXT NOT NULL,
12
+ language TEXT,
13
+ start_line INTEGER NOT NULL,
14
+ end_line INTEGER NOT NULL,
15
+ content TEXT NOT NULL,
16
+ mtime INTEGER NOT NULL,
17
+ indexed_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
18
+ );
19
+
20
+ CREATE INDEX IF NOT EXISTS idx_chunks_project ON code_chunks(project);
21
+ CREATE INDEX IF NOT EXISTS idx_chunks_file ON code_chunks(project, file_path);
22
+
23
+ -- FTS5 for searching code content
24
+ CREATE VIRTUAL TABLE IF NOT EXISTS code_chunks_fts USING fts5(
25
+ file_path,
26
+ content,
27
+ content='code_chunks',
28
+ content_rowid='id'
29
+ );
30
+
31
+ CREATE TRIGGER IF NOT EXISTS chunks_ai AFTER INSERT ON code_chunks BEGIN
32
+ INSERT INTO code_chunks_fts(rowid, file_path, content)
33
+ VALUES (new.id, new.file_path, new.content);
34
+ END;
35
+
36
+ CREATE TRIGGER IF NOT EXISTS chunks_ad AFTER DELETE ON code_chunks BEGIN
37
+ INSERT INTO code_chunks_fts(code_chunks_fts, rowid, file_path, content)
38
+ VALUES ('delete', old.id, old.file_path, old.content);
39
+ END;
40
+
41
+ CREATE TRIGGER IF NOT EXISTS chunks_au AFTER UPDATE ON code_chunks BEGIN
42
+ INSERT INTO code_chunks_fts(code_chunks_fts, rowid, file_path, content)
43
+ VALUES ('delete', old.id, old.file_path, old.content);
44
+ INSERT INTO code_chunks_fts(rowid, file_path, content)
45
+ VALUES (new.id, new.file_path, new.content);
46
+ END;
package/db/migrate.sh ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Eagle Mem — Database migration runner
4
+ # Idempotent: safe to run multiple times
5
+ # ═══════════════════════════════════════════════════════════
6
+ set -euo pipefail
7
+
8
+ EAGLE_MEM_DIR="${EAGLE_MEM_DIR:-$HOME/.eagle-mem}"
9
+ DB="$EAGLE_MEM_DIR/memory.db"
10
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
11
+
12
+ mkdir -p "$EAGLE_MEM_DIR"
13
+
14
+ run_migration() {
15
+ local name="$1"
16
+ local file="$2"
17
+
18
+ local already_applied
19
+ already_applied=$(sqlite3 "$DB" "SELECT COUNT(*) FROM _migrations WHERE name = '$name';" 2>/dev/null || echo "0")
20
+
21
+ if [ "$already_applied" = "0" ]; then
22
+ sqlite3 "$DB" < "$file"
23
+ sqlite3 "$DB" "INSERT INTO _migrations (name) VALUES ('$name');"
24
+ echo " applied: $name"
25
+ fi
26
+ }
27
+
28
+ # Ensure _migrations table exists (bootstrap)
29
+ sqlite3 "$DB" "CREATE TABLE IF NOT EXISTS _migrations (
30
+ id INTEGER PRIMARY KEY,
31
+ name TEXT NOT NULL UNIQUE,
32
+ applied_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
33
+ );"
34
+
35
+ # Set PRAGMAs (these must be set on every connection)
36
+ sqlite3 "$DB" "PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA busy_timeout = 5000; PRAGMA foreign_keys = ON;"
37
+
38
+ # ─── Migration 001: Initial schema ─────────────────────────
39
+ run_migration "001_initial_schema" "$SCRIPT_DIR/schema.sql"
40
+
41
+ # ─── Migration 002: Project overviews ──────────────────────
42
+ run_migration "002_overviews" "$SCRIPT_DIR/002_overviews.sql"
43
+
44
+ # ─── Migration 003: Code chunks ───────────────────────────
45
+ run_migration "003_code_chunks" "$SCRIPT_DIR/003_code_chunks.sql"
46
+
47
+ echo " Eagle Mem database ready: $DB"
package/db/schema.sql ADDED
@@ -0,0 +1,154 @@
1
+ -- ═══════════════════════════════════════════════════════════
2
+ -- Eagle Mem — Schema v1
3
+ -- Single shared SQLite database at ~/.eagle-mem/memory.db
4
+ -- ═══════════════════════════════════════════════════════════
5
+
6
+ -- NOTE: PRAGMAs are connection-scoped and do NOT persist.
7
+ -- These only apply during migration. Runtime PRAGMAs are set
8
+ -- in lib/db.sh EAGLE_DB_SETUP on every connection.
9
+ PRAGMA journal_mode = WAL;
10
+ PRAGMA synchronous = NORMAL;
11
+ PRAGMA foreign_keys = ON;
12
+ PRAGMA temp_store = memory;
13
+
14
+ -- ─── Migration tracking ────────────────────────────────────
15
+
16
+ CREATE TABLE IF NOT EXISTS _migrations (
17
+ id INTEGER PRIMARY KEY,
18
+ name TEXT NOT NULL UNIQUE,
19
+ applied_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
20
+ );
21
+
22
+ -- ─── Sessions ──────────────────────────────────────────────
23
+
24
+ CREATE TABLE IF NOT EXISTS sessions (
25
+ id TEXT PRIMARY KEY,
26
+ project TEXT NOT NULL,
27
+ cwd TEXT,
28
+ model TEXT,
29
+ source TEXT,
30
+ started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
31
+ ended_at TEXT,
32
+ status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'completed', 'abandoned'))
33
+ );
34
+
35
+ CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project);
36
+ CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
37
+
38
+ -- ─── Observations ──────────────────────────────────────────
39
+ -- Lightweight per-tool-use records: what files were touched
40
+
41
+ CREATE TABLE IF NOT EXISTS observations (
42
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
43
+ session_id TEXT NOT NULL REFERENCES sessions(id),
44
+ project TEXT NOT NULL,
45
+ tool_name TEXT NOT NULL,
46
+ tool_input_summary TEXT,
47
+ files_read TEXT DEFAULT '[]',
48
+ files_modified TEXT DEFAULT '[]',
49
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
50
+ );
51
+
52
+ CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
53
+ CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project);
54
+
55
+ -- ─── Summaries ─────────────────────────────────────────────
56
+ -- Per-session summaries: what was accomplished
57
+
58
+ CREATE TABLE IF NOT EXISTS summaries (
59
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
60
+ session_id TEXT NOT NULL REFERENCES sessions(id),
61
+ project TEXT NOT NULL,
62
+ request TEXT,
63
+ investigated TEXT,
64
+ learned TEXT,
65
+ completed TEXT,
66
+ next_steps TEXT,
67
+ files_read TEXT DEFAULT '[]',
68
+ files_modified TEXT DEFAULT '[]',
69
+ notes TEXT,
70
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
71
+ );
72
+
73
+ CREATE INDEX IF NOT EXISTS idx_summaries_session ON summaries(session_id);
74
+ CREATE INDEX IF NOT EXISTS idx_summaries_project ON summaries(project);
75
+
76
+ -- ─── Tasks (TaskAware Compact Loop) ───────────────────────
77
+ -- Subtasks for multi-step work with compaction between each
78
+
79
+ CREATE TABLE IF NOT EXISTS tasks (
80
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
81
+ project TEXT NOT NULL,
82
+ session_id TEXT REFERENCES sessions(id),
83
+ parent_id INTEGER REFERENCES tasks(id),
84
+ title TEXT NOT NULL,
85
+ instructions TEXT,
86
+ context_snapshot TEXT,
87
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'active', 'done', 'blocked', 'cancelled')),
88
+ ordinal INTEGER NOT NULL DEFAULT 0,
89
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
90
+ started_at TEXT,
91
+ completed_at TEXT
92
+ );
93
+
94
+ CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project);
95
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
96
+ CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_id);
97
+
98
+ -- ─── FTS5: Full-text search on summaries ───────────────────
99
+
100
+ CREATE VIRTUAL TABLE IF NOT EXISTS summaries_fts USING fts5(
101
+ request,
102
+ investigated,
103
+ learned,
104
+ completed,
105
+ next_steps,
106
+ notes,
107
+ content='summaries',
108
+ content_rowid='id'
109
+ );
110
+
111
+ -- Triggers to keep FTS in sync
112
+ CREATE TRIGGER IF NOT EXISTS summaries_ai AFTER INSERT ON summaries BEGIN
113
+ INSERT INTO summaries_fts(rowid, request, investigated, learned, completed, next_steps, notes)
114
+ VALUES (new.id, new.request, new.investigated, new.learned, new.completed, new.next_steps, new.notes);
115
+ END;
116
+
117
+ CREATE TRIGGER IF NOT EXISTS summaries_ad AFTER DELETE ON summaries BEGIN
118
+ INSERT INTO summaries_fts(summaries_fts, rowid, request, investigated, learned, completed, next_steps, notes)
119
+ VALUES ('delete', old.id, old.request, old.investigated, old.learned, old.completed, old.next_steps, old.notes);
120
+ END;
121
+
122
+ CREATE TRIGGER IF NOT EXISTS summaries_au AFTER UPDATE ON summaries BEGIN
123
+ INSERT INTO summaries_fts(summaries_fts, rowid, request, investigated, learned, completed, next_steps, notes)
124
+ VALUES ('delete', old.id, old.request, old.investigated, old.learned, old.completed, old.next_steps, old.notes);
125
+ INSERT INTO summaries_fts(rowid, request, investigated, learned, completed, next_steps, notes)
126
+ VALUES (new.id, new.request, new.investigated, new.learned, new.completed, new.next_steps, new.notes);
127
+ END;
128
+
129
+ -- ─── FTS5: Full-text search on tasks ──────────────────────
130
+
131
+ CREATE VIRTUAL TABLE IF NOT EXISTS tasks_fts USING fts5(
132
+ title,
133
+ instructions,
134
+ context_snapshot,
135
+ content='tasks',
136
+ content_rowid='id'
137
+ );
138
+
139
+ CREATE TRIGGER IF NOT EXISTS tasks_ai AFTER INSERT ON tasks BEGIN
140
+ INSERT INTO tasks_fts(rowid, title, instructions, context_snapshot)
141
+ VALUES (new.id, new.title, new.instructions, new.context_snapshot);
142
+ END;
143
+
144
+ CREATE TRIGGER IF NOT EXISTS tasks_ad AFTER DELETE ON tasks BEGIN
145
+ INSERT INTO tasks_fts(tasks_fts, rowid, title, instructions, context_snapshot)
146
+ VALUES ('delete', old.id, old.title, old.instructions, old.context_snapshot);
147
+ END;
148
+
149
+ CREATE TRIGGER IF NOT EXISTS tasks_au AFTER UPDATE ON tasks BEGIN
150
+ INSERT INTO tasks_fts(tasks_fts, rowid, title, instructions, context_snapshot)
151
+ VALUES ('delete', old.id, old.title, old.instructions, old.context_snapshot);
152
+ INSERT INTO tasks_fts(rowid, title, instructions, context_snapshot)
153
+ VALUES (new.id, new.title, new.instructions, new.context_snapshot);
154
+ END;
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Eagle Mem — PostToolUse hook
4
+ # Fires after every tool use
5
+ # Captures file read/write operations as lightweight observations
6
+ # ═══════════════════════════════════════════════════════════
7
+ set +e
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
10
+ LIB_DIR="$SCRIPT_DIR/../lib"
11
+
12
+ . "$LIB_DIR/common.sh"
13
+ . "$LIB_DIR/db.sh"
14
+
15
+ input=$(eagle_read_stdin)
16
+ [ -z "$input" ] && exit 0
17
+
18
+ session_id=$(echo "$input" | jq -r '.session_id // empty')
19
+ cwd=$(echo "$input" | jq -r '.cwd // empty')
20
+ tool_name=$(echo "$input" | jq -r '.tool_name // empty')
21
+
22
+ [ -z "$session_id" ] || [ -z "$tool_name" ] && exit 0
23
+
24
+ # Only track file-related tools
25
+ case "$tool_name" in
26
+ Read|Write|Edit|Bash) ;;
27
+ *) exit 0 ;;
28
+ esac
29
+
30
+ [ ! -f "$EAGLE_MEM_DB" ] && exit 0
31
+
32
+ project=$(eagle_project_from_cwd "$cwd")
33
+
34
+ files_read="[]"
35
+ files_modified="[]"
36
+ tool_summary=""
37
+
38
+ case "$tool_name" in
39
+ Read)
40
+ fp=$(echo "$input" | jq -r '.tool_input.file_path // empty')
41
+ [ -n "$fp" ] && files_read="[\"$fp\"]"
42
+ tool_summary="Read $fp"
43
+ ;;
44
+ Write)
45
+ fp=$(echo "$input" | jq -r '.tool_input.file_path // empty')
46
+ [ -n "$fp" ] && files_modified="[\"$fp\"]"
47
+ tool_summary="Write $fp"
48
+ ;;
49
+ Edit)
50
+ fp=$(echo "$input" | jq -r '.tool_input.file_path // empty')
51
+ [ -n "$fp" ] && files_modified="[\"$fp\"]"
52
+ tool_summary="Edit $fp"
53
+ ;;
54
+ Bash)
55
+ cmd=$(echo "$input" | jq -r '.tool_input.command // empty' | cut -c1-200)
56
+ # Redact common secret patterns before storing
57
+ cmd=$(echo "$cmd" | sed -E \
58
+ -e 's/(Bearer )[^ ]*/\1[REDACTED]/gi' \
59
+ -e 's/(api[_-]?key[= :])[^ ]*/\1[REDACTED]/gi' \
60
+ -e 's/(password[= :])[^ ]*/\1[REDACTED]/gi' \
61
+ -e 's/(secret[= :])[^ ]*/\1[REDACTED]/gi' \
62
+ -e 's/(token[= :])[^ ]*/\1[REDACTED]/gi' \
63
+ -e 's/(Authorization: )[^ ]*/\1[REDACTED]/gi')
64
+ tool_summary="Bash: $cmd"
65
+ ;;
66
+ esac
67
+
68
+ # Deduplicate: skip if exact same observation within last 5 seconds
69
+ dup_count=$(eagle_observation_exists "$session_id" "$tool_name" "$tool_summary")
70
+ if [ "$dup_count" != "0" ]; then
71
+ exit 0
72
+ fi
73
+
74
+ eagle_insert_observation "$session_id" "$project" "$tool_name" "$tool_summary" "$files_read" "$files_modified"
75
+
76
+ exit 0
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Eagle Mem — SessionEnd hook
4
+ # Fires when the Claude Code session ends
5
+ # Marks the session as completed
6
+ # ═══════════════════════════════════════════════════════════
7
+ set +e
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
10
+ LIB_DIR="$SCRIPT_DIR/../lib"
11
+
12
+ . "$LIB_DIR/common.sh"
13
+ . "$LIB_DIR/db.sh"
14
+
15
+ input=$(eagle_read_stdin)
16
+ [ -z "$input" ] && exit 0
17
+
18
+ session_id=$(echo "$input" | jq -r '.session_id // empty')
19
+ [ -z "$session_id" ] && exit 0
20
+ [ ! -f "$EAGLE_MEM_DB" ] && exit 0
21
+
22
+ eagle_end_session "$session_id"
23
+ eagle_log "INFO" "SessionEnd: session=$session_id marked completed"
24
+
25
+ exit 0