eagle-mem 1.0.3 → 1.2.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/bin/eagle-mem +4 -0
- package/db/004_observation_indexes.sql +9 -0
- package/db/migrate.sh +3 -0
- package/db/schema.sql +2 -0
- package/hooks/post-tool-use.sh +4 -4
- package/hooks/session-start.sh +6 -29
- package/hooks/stop.sh +3 -5
- package/hooks/user-prompt-submit.sh +4 -19
- package/lib/common.sh +13 -0
- package/lib/db.sh +84 -9
- package/package.json +1 -1
- package/scripts/help.sh +17 -7
- package/scripts/install.sh +6 -15
- package/scripts/overview.sh +170 -0
- package/scripts/prune.sh +154 -0
- package/scripts/search.sh +294 -0
- package/scripts/tasks.sh +291 -0
- package/scripts/uninstall.sh +5 -5
- package/scripts/update.sh +5 -5
- package/skills/eagle-mem-overview/SKILL.md +35 -55
- package/skills/eagle-mem-search/SKILL.md +36 -84
- package/skills/eagle-mem-tasks/SKILL.md +40 -63
package/bin/eagle-mem
CHANGED
|
@@ -21,6 +21,10 @@ case "$command" in
|
|
|
21
21
|
update) bash "$SCRIPTS_DIR/update.sh" "$PACKAGE_DIR" "$@" ;;
|
|
22
22
|
scan) bash "$SCRIPTS_DIR/scan.sh" "$@" ;;
|
|
23
23
|
index) bash "$SCRIPTS_DIR/index.sh" "$@" ;;
|
|
24
|
+
search) bash "$SCRIPTS_DIR/search.sh" "$@" ;;
|
|
25
|
+
tasks) bash "$SCRIPTS_DIR/tasks.sh" "$@" ;;
|
|
26
|
+
overview) bash "$SCRIPTS_DIR/overview.sh" "$@" ;;
|
|
27
|
+
prune) bash "$SCRIPTS_DIR/prune.sh" "$@" ;;
|
|
24
28
|
help|--help|-h)
|
|
25
29
|
bash "$SCRIPTS_DIR/help.sh" ;;
|
|
26
30
|
version|--version|-v|-V)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- ═══════════════════════════════════════════════════════════
|
|
2
|
+
-- Eagle Mem — Migration 004: Observation indexes
|
|
3
|
+
-- Adds created_at index for efficient pruning and time-bound queries
|
|
4
|
+
-- ═══════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
PRAGMA foreign_keys = ON;
|
|
7
|
+
|
|
8
|
+
CREATE INDEX IF NOT EXISTS idx_observations_created_at ON observations(created_at);
|
|
9
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_ended_at ON sessions(status, ended_at);
|
package/db/migrate.sh
CHANGED
|
@@ -44,4 +44,7 @@ run_migration "002_overviews" "$SCRIPT_DIR/002_overviews.sql"
|
|
|
44
44
|
# ─── Migration 003: Code chunks ───────────────────────────
|
|
45
45
|
run_migration "003_code_chunks" "$SCRIPT_DIR/003_code_chunks.sql"
|
|
46
46
|
|
|
47
|
+
# ─── Migration 004: Observation indexes ──────────────────
|
|
48
|
+
run_migration "004_observation_indexes" "$SCRIPT_DIR/004_observation_indexes.sql"
|
|
49
|
+
|
|
47
50
|
echo " Eagle Mem database ready: $DB"
|
package/db/schema.sql
CHANGED
|
@@ -20,6 +20,8 @@ CREATE TABLE IF NOT EXISTS _migrations (
|
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
-- ─── Sessions ──────────────────────────────────────────────
|
|
23
|
+
-- Sessions are parent rows for summaries, tasks, and observations (FK).
|
|
24
|
+
-- Never delete sessions — prune children instead.
|
|
23
25
|
|
|
24
26
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
25
27
|
id TEXT PRIMARY KEY,
|
package/hooks/post-tool-use.sh
CHANGED
|
@@ -19,7 +19,7 @@ session_id=$(echo "$input" | jq -r '.session_id // empty')
|
|
|
19
19
|
cwd=$(echo "$input" | jq -r '.cwd // empty')
|
|
20
20
|
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
|
|
21
21
|
|
|
22
|
-
[ -z "$session_id" ] || [ -z "$tool_name" ]
|
|
22
|
+
if [ -z "$session_id" ] || [ -z "$tool_name" ]; then exit 0; fi
|
|
23
23
|
|
|
24
24
|
# Only track file-related tools
|
|
25
25
|
case "$tool_name" in
|
|
@@ -38,17 +38,17 @@ tool_summary=""
|
|
|
38
38
|
case "$tool_name" in
|
|
39
39
|
Read)
|
|
40
40
|
fp=$(echo "$input" | jq -r '.tool_input.file_path // empty')
|
|
41
|
-
[ -n "$fp" ] && files_read
|
|
41
|
+
[ -n "$fp" ] && files_read=$(printf '%s' "$fp" | jq -Rsc '[.]')
|
|
42
42
|
tool_summary="Read $fp"
|
|
43
43
|
;;
|
|
44
44
|
Write)
|
|
45
45
|
fp=$(echo "$input" | jq -r '.tool_input.file_path // empty')
|
|
46
|
-
[ -n "$fp" ] && files_modified
|
|
46
|
+
[ -n "$fp" ] && files_modified=$(printf '%s' "$fp" | jq -Rsc '[.]')
|
|
47
47
|
tool_summary="Write $fp"
|
|
48
48
|
;;
|
|
49
49
|
Edit)
|
|
50
50
|
fp=$(echo "$input" | jq -r '.tool_input.file_path // empty')
|
|
51
|
-
[ -n "$fp" ] && files_modified
|
|
51
|
+
[ -n "$fp" ] && files_modified=$(printf '%s' "$fp" | jq -Rsc '[.]')
|
|
52
52
|
tool_summary="Edit $fp"
|
|
53
53
|
;;
|
|
54
54
|
Bash)
|
package/hooks/session-start.sh
CHANGED
|
@@ -25,7 +25,6 @@ model=$(echo "$input" | jq -r '.model // empty')
|
|
|
25
25
|
[ -z "$session_id" ] && exit 0
|
|
26
26
|
|
|
27
27
|
project=$(eagle_project_from_cwd "$cwd")
|
|
28
|
-
project_sql=$(eagle_sql_escape "$project")
|
|
29
28
|
|
|
30
29
|
eagle_log "INFO" "SessionStart: session=$session_id project=$project source=$source_type"
|
|
31
30
|
|
|
@@ -48,13 +47,7 @@ $overview
|
|
|
48
47
|
fi
|
|
49
48
|
|
|
50
49
|
# Recent summaries for this project (last 5 sessions)
|
|
51
|
-
recent=$(
|
|
52
|
-
SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at
|
|
53
|
-
FROM summaries s
|
|
54
|
-
WHERE s.project = '$project_sql'
|
|
55
|
-
ORDER BY s.created_at DESC
|
|
56
|
-
LIMIT 5;
|
|
57
|
-
")
|
|
50
|
+
recent=$(eagle_get_recent_summaries "$project" 5)
|
|
58
51
|
|
|
59
52
|
if [ -n "$recent" ]; then
|
|
60
53
|
context+="=== EAGLE MEM ===
|
|
@@ -78,13 +71,7 @@ Next steps: $next_steps"
|
|
|
78
71
|
fi
|
|
79
72
|
|
|
80
73
|
# Pending tasks from TaskAware loop
|
|
81
|
-
pending_tasks=$(
|
|
82
|
-
SELECT id, title, instructions, status
|
|
83
|
-
FROM tasks
|
|
84
|
-
WHERE project = '$project_sql' AND status IN ('pending', 'active')
|
|
85
|
-
ORDER BY ordinal ASC, id ASC
|
|
86
|
-
LIMIT 10;
|
|
87
|
-
")
|
|
74
|
+
pending_tasks=$(eagle_get_pending_tasks "$project")
|
|
88
75
|
|
|
89
76
|
if [ -n "$pending_tasks" ]; then
|
|
90
77
|
context+="
|
|
@@ -92,7 +79,7 @@ if [ -n "$pending_tasks" ]; then
|
|
|
92
79
|
Pending tasks for '$project':
|
|
93
80
|
"
|
|
94
81
|
first_pending=""
|
|
95
|
-
while IFS='|' read -r tid title instructions status; do
|
|
82
|
+
while IFS='|' read -r tid title instructions status _ordinal; do
|
|
96
83
|
[ -z "$tid" ] && continue
|
|
97
84
|
local_marker=""
|
|
98
85
|
if [ "$status" = "active" ]; then
|
|
@@ -108,22 +95,12 @@ Pending tasks for '$project':
|
|
|
108
95
|
done <<< "$pending_tasks"
|
|
109
96
|
|
|
110
97
|
# Load context snapshot for the active/next task
|
|
111
|
-
active_task=$(
|
|
112
|
-
SELECT id, title, instructions, context_snapshot
|
|
113
|
-
FROM tasks
|
|
114
|
-
WHERE project = '$project_sql' AND status = 'active'
|
|
115
|
-
ORDER BY ordinal ASC, id ASC
|
|
116
|
-
LIMIT 1;
|
|
117
|
-
")
|
|
98
|
+
active_task=$(eagle_get_active_task "$project")
|
|
118
99
|
|
|
119
100
|
if [ -z "$active_task" ] && [ -n "$first_pending" ]; then
|
|
120
101
|
# Auto-activate the next pending task
|
|
121
|
-
|
|
122
|
-
active_task=$(
|
|
123
|
-
SELECT id, title, instructions, context_snapshot
|
|
124
|
-
FROM tasks
|
|
125
|
-
WHERE id = $first_pending;
|
|
126
|
-
")
|
|
102
|
+
eagle_activate_task "$first_pending"
|
|
103
|
+
active_task=$(eagle_get_active_task "$project")
|
|
127
104
|
fi
|
|
128
105
|
|
|
129
106
|
if [ -n "$active_task" ]; then
|
package/hooks/stop.sh
CHANGED
|
@@ -29,7 +29,6 @@ agent_type=$(echo "$input" | jq -r '.agent_type // empty')
|
|
|
29
29
|
[ -n "$agent_type" ] && [ "$agent_type" != "main" ] && exit 0
|
|
30
30
|
|
|
31
31
|
project=$(eagle_project_from_cwd "$cwd")
|
|
32
|
-
project_sql=$(eagle_sql_escape "$project")
|
|
33
32
|
|
|
34
33
|
eagle_log "INFO" "Stop: session=$session_id project=$project transcript=$transcript_path"
|
|
35
34
|
|
|
@@ -137,10 +136,9 @@ fi
|
|
|
137
136
|
|
|
138
137
|
# Mark active task as done if eagle-summary mentions completion
|
|
139
138
|
if [ -n "$completed" ]; then
|
|
140
|
-
|
|
141
|
-
if [ -n "$
|
|
142
|
-
|
|
143
|
-
eagle_log "INFO" "Stop: marked task #$active_task_id as done"
|
|
139
|
+
completed_task_id=$(eagle_complete_active_task "$project")
|
|
140
|
+
if [ -n "$completed_task_id" ]; then
|
|
141
|
+
eagle_log "INFO" "Stop: marked task #$completed_task_id as done"
|
|
144
142
|
fi
|
|
145
143
|
fi
|
|
146
144
|
|
|
@@ -24,7 +24,6 @@ user_prompt=$(echo "$input" | jq -r '.prompt // empty')
|
|
|
24
24
|
[ -z "$user_prompt" ] && exit 0
|
|
25
25
|
|
|
26
26
|
project=$(eagle_project_from_cwd "$cwd")
|
|
27
|
-
project_sql=$(eagle_sql_escape "$project")
|
|
28
27
|
|
|
29
28
|
# Skip short prompts — not enough signal for meaningful search
|
|
30
29
|
word_count=$(echo "$user_prompt" | wc -w | tr -d ' ')
|
|
@@ -45,23 +44,15 @@ fts_query=$(echo "$user_prompt" | tr -cs '[:alnum:]' ' ' | tr '[:upper:]' '[:low
|
|
|
45
44
|
|
|
46
45
|
[ -z "$fts_query" ] && exit 0
|
|
47
46
|
|
|
48
|
-
fts_query_escaped=$(eagle_sql_escape "$fts_query")
|
|
49
|
-
|
|
50
47
|
# Search for relevant past summaries (cross-session)
|
|
51
|
-
results=$(
|
|
52
|
-
FROM summaries s
|
|
53
|
-
JOIN summaries_fts f ON f.rowid = s.id
|
|
54
|
-
WHERE summaries_fts MATCH '$fts_query_escaped'
|
|
55
|
-
AND s.project = '$project_sql'
|
|
56
|
-
ORDER BY rank
|
|
57
|
-
LIMIT 3;")
|
|
48
|
+
results=$(eagle_search_summaries "$fts_query" "$project" 3)
|
|
58
49
|
|
|
59
50
|
context=""
|
|
60
51
|
|
|
61
52
|
if [ -n "$results" ]; then
|
|
62
53
|
context+="=== EAGLE MEM — Relevant Memory ===
|
|
63
54
|
"
|
|
64
|
-
while IFS='|' read -r req learned
|
|
55
|
+
while IFS='|' read -r req completed learned _next_steps created_at _proj; do
|
|
65
56
|
[ -z "$req" ] && [ -z "$completed" ] && continue
|
|
66
57
|
context+="[$created_at] "
|
|
67
58
|
[ -n "$req" ] && context+="$req"
|
|
@@ -73,15 +64,9 @@ if [ -n "$results" ]; then
|
|
|
73
64
|
fi
|
|
74
65
|
|
|
75
66
|
# Search indexed code chunks (if any exist for this project)
|
|
76
|
-
has_chunks=$(
|
|
67
|
+
has_chunks=$(eagle_count_code_chunks "$project")
|
|
77
68
|
if [ "${has_chunks:-0}" -gt 0 ]; then
|
|
78
|
-
code_results=$(
|
|
79
|
-
FROM code_chunks c
|
|
80
|
-
JOIN code_chunks_fts f ON f.rowid = c.id
|
|
81
|
-
WHERE code_chunks_fts MATCH '$fts_query_escaped'
|
|
82
|
-
AND c.project = '$project_sql'
|
|
83
|
-
ORDER BY rank
|
|
84
|
-
LIMIT 5;")
|
|
69
|
+
code_results=$(eagle_search_code_chunks "$fts_query" "$project" 5)
|
|
85
70
|
|
|
86
71
|
if [ -n "$code_results" ]; then
|
|
87
72
|
context+="=== EAGLE MEM — Relevant Code ===
|
package/lib/common.sh
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
EAGLE_MEM_DIR="${EAGLE_MEM_DIR:-$HOME/.eagle-mem}"
|
|
8
8
|
EAGLE_MEM_DB="$EAGLE_MEM_DIR/memory.db"
|
|
9
9
|
EAGLE_MEM_LOG="$EAGLE_MEM_DIR/eagle-mem.log"
|
|
10
|
+
EAGLE_SETTINGS="${EAGLE_SETTINGS:-$HOME/.claude/settings.json}"
|
|
11
|
+
EAGLE_SKILLS_DIR="$HOME/.claude/skills"
|
|
10
12
|
|
|
11
13
|
eagle_log() {
|
|
12
14
|
local level="$1"
|
|
@@ -23,6 +25,17 @@ eagle_sql_escape() {
|
|
|
23
25
|
printf '%s' "$1" | sed "s/'/''/g"
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
eagle_sql_int() {
|
|
29
|
+
case "$1" in
|
|
30
|
+
''|*[!0-9]*) echo "0" ;;
|
|
31
|
+
*) printf '%s' "$1" ;;
|
|
32
|
+
esac
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
eagle_fts_sanitize() {
|
|
36
|
+
printf '%s' "$1" | sed 's/[*"(){}^~:]/ /g' | sed 's/ */ /g; s/^ //; s/ $//'
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
eagle_read_stdin() {
|
|
27
40
|
local input=""
|
|
28
41
|
if [ ! -t 0 ]; then
|
package/lib/db.sh
CHANGED
|
@@ -34,7 +34,7 @@ eagle_ensure_db() {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
eagle_upsert_session() {
|
|
37
|
-
local session_id
|
|
37
|
+
local session_id; session_id=$(eagle_sql_escape "$1")
|
|
38
38
|
local project; project=$(eagle_sql_escape "$2")
|
|
39
39
|
local cwd; cwd=$(eagle_sql_escape "${3:-}")
|
|
40
40
|
local model; model=$(eagle_sql_escape "${4:-}")
|
|
@@ -49,17 +49,17 @@ eagle_upsert_session() {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
eagle_end_session() {
|
|
52
|
-
local session_id
|
|
52
|
+
local session_id; session_id=$(eagle_sql_escape "$1")
|
|
53
53
|
eagle_db "UPDATE sessions SET status = 'completed', ended_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = '$session_id';"
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
eagle_insert_observation() {
|
|
57
|
-
local session_id
|
|
57
|
+
local session_id; session_id=$(eagle_sql_escape "$1")
|
|
58
58
|
local project; project=$(eagle_sql_escape "$2")
|
|
59
59
|
local tool_name; tool_name=$(eagle_sql_escape "$3")
|
|
60
60
|
local tool_input_summary; tool_input_summary=$(eagle_sql_escape "$4")
|
|
61
|
-
local files_read
|
|
62
|
-
local files_modified
|
|
61
|
+
local files_read; files_read=$(eagle_sql_escape "$5")
|
|
62
|
+
local files_modified; files_modified=$(eagle_sql_escape "$6")
|
|
63
63
|
|
|
64
64
|
eagle_db "INSERT INTO observations (session_id, project, tool_name, tool_input_summary, files_read, files_modified)
|
|
65
65
|
VALUES ('$session_id', '$project', '$tool_name', '$tool_input_summary', '$files_read', '$files_modified');"
|
|
@@ -96,7 +96,7 @@ SQL
|
|
|
96
96
|
|
|
97
97
|
eagle_get_recent_summaries() {
|
|
98
98
|
local project; project=$(eagle_sql_escape "$1")
|
|
99
|
-
local limit
|
|
99
|
+
local limit; limit=$(eagle_sql_int "${2:-5}")
|
|
100
100
|
|
|
101
101
|
eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at
|
|
102
102
|
FROM summaries s
|
|
@@ -108,7 +108,7 @@ eagle_get_recent_summaries() {
|
|
|
108
108
|
eagle_search_summaries() {
|
|
109
109
|
local query; query=$(eagle_sql_escape "$1")
|
|
110
110
|
local project="${2:-}"
|
|
111
|
-
local limit
|
|
111
|
+
local limit; limit=$(eagle_sql_int "${3:-10}")
|
|
112
112
|
|
|
113
113
|
local where_clause=""
|
|
114
114
|
if [ -n "$project" ]; then
|
|
@@ -146,18 +146,19 @@ eagle_get_next_task() {
|
|
|
146
146
|
|
|
147
147
|
eagle_get_active_files() {
|
|
148
148
|
local project; project=$(eagle_sql_escape "$1")
|
|
149
|
-
local limit
|
|
149
|
+
local limit; limit=$(eagle_sql_int "${2:-20}")
|
|
150
150
|
|
|
151
151
|
eagle_db "SELECT json_each.value
|
|
152
152
|
FROM observations, json_each(observations.files_modified)
|
|
153
153
|
WHERE observations.project = '$project'
|
|
154
|
+
AND observations.created_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-30 days')
|
|
154
155
|
GROUP BY json_each.value
|
|
155
156
|
ORDER BY MAX(observations.created_at) DESC
|
|
156
157
|
LIMIT $limit;"
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
eagle_observation_exists() {
|
|
160
|
-
local session_id
|
|
161
|
+
local session_id; session_id=$(eagle_sql_escape "$1")
|
|
161
162
|
local tool_name; tool_name=$(eagle_sql_escape "$2")
|
|
162
163
|
local tool_summary; tool_summary=$(eagle_sql_escape "$3")
|
|
163
164
|
|
|
@@ -184,3 +185,77 @@ eagle_get_overview() {
|
|
|
184
185
|
|
|
185
186
|
eagle_db "SELECT content FROM overviews WHERE project = '$project';"
|
|
186
187
|
}
|
|
188
|
+
|
|
189
|
+
eagle_activate_task() {
|
|
190
|
+
local task_id; task_id=$(eagle_sql_int "$1")
|
|
191
|
+
eagle_db "UPDATE tasks SET status = 'active', started_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = $task_id;"
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
eagle_complete_active_task() {
|
|
195
|
+
local project; project=$(eagle_sql_escape "$1")
|
|
196
|
+
local active_id
|
|
197
|
+
active_id=$(eagle_db "SELECT id FROM tasks WHERE project = '$project' AND status = 'active' LIMIT 1;")
|
|
198
|
+
if [ -n "$active_id" ]; then
|
|
199
|
+
local safe_id; safe_id=$(eagle_sql_int "$active_id")
|
|
200
|
+
eagle_db "UPDATE tasks SET status = 'done', completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = $safe_id;"
|
|
201
|
+
echo "$active_id"
|
|
202
|
+
fi
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
eagle_get_active_task() {
|
|
206
|
+
local project; project=$(eagle_sql_escape "$1")
|
|
207
|
+
eagle_db "SELECT id, title, instructions, context_snapshot
|
|
208
|
+
FROM tasks
|
|
209
|
+
WHERE project = '$project' AND status = 'active'
|
|
210
|
+
ORDER BY ordinal ASC, id ASC
|
|
211
|
+
LIMIT 1;"
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
eagle_search_code_chunks() {
|
|
215
|
+
local query; query=$(eagle_sql_escape "$1")
|
|
216
|
+
local project; project=$(eagle_sql_escape "$2")
|
|
217
|
+
local limit; limit=$(eagle_sql_int "${3:-5}")
|
|
218
|
+
|
|
219
|
+
eagle_db "SELECT c.file_path, c.start_line, c.end_line, c.language
|
|
220
|
+
FROM code_chunks c
|
|
221
|
+
JOIN code_chunks_fts f ON f.rowid = c.id
|
|
222
|
+
WHERE code_chunks_fts MATCH '$query'
|
|
223
|
+
AND c.project = '$project'
|
|
224
|
+
ORDER BY rank
|
|
225
|
+
LIMIT $limit;"
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
eagle_count_code_chunks() {
|
|
229
|
+
local project; project=$(eagle_sql_escape "$1")
|
|
230
|
+
eagle_db "SELECT COUNT(*) FROM code_chunks WHERE project = '$project' LIMIT 1;"
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
eagle_prune_observations() {
|
|
234
|
+
local days; days=$(eagle_sql_int "${1:-90}")
|
|
235
|
+
local project_filter=""
|
|
236
|
+
if [ -n "${2:-}" ]; then
|
|
237
|
+
local proj; proj=$(eagle_sql_escape "$2")
|
|
238
|
+
project_filter="AND session_id IN (SELECT id FROM sessions WHERE project = '$proj')"
|
|
239
|
+
fi
|
|
240
|
+
eagle_db "DELETE FROM observations WHERE created_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-$days days') $project_filter;"
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
eagle_prune_orphan_chunks() {
|
|
244
|
+
local project; project=$(eagle_sql_escape "$1")
|
|
245
|
+
local target_dir="$2"
|
|
246
|
+
|
|
247
|
+
# Get all indexed file paths for this project
|
|
248
|
+
local paths
|
|
249
|
+
paths=$(eagle_db "SELECT DISTINCT file_path FROM code_chunks WHERE project = '$project';")
|
|
250
|
+
|
|
251
|
+
local removed=0
|
|
252
|
+
while IFS= read -r fpath; do
|
|
253
|
+
[ -z "$fpath" ] && continue
|
|
254
|
+
if [ ! -f "$target_dir/$fpath" ]; then
|
|
255
|
+
local fpath_sql; fpath_sql=$(eagle_sql_escape "$fpath")
|
|
256
|
+
eagle_db "DELETE FROM code_chunks WHERE project = '$project' AND file_path = '$fpath_sql';"
|
|
257
|
+
removed=$((removed + 1))
|
|
258
|
+
fi
|
|
259
|
+
done <<< "$paths"
|
|
260
|
+
echo "$removed"
|
|
261
|
+
}
|
package/package.json
CHANGED
package/scripts/help.sh
CHANGED
|
@@ -19,20 +19,28 @@ echo -e " ${BOLD}Usage:${RESET}"
|
|
|
19
19
|
echo -e " eagle-mem ${CYAN}<command>${RESET}"
|
|
20
20
|
echo ""
|
|
21
21
|
echo -e " ${BOLD}Commands:${RESET}"
|
|
22
|
+
echo -e " ${CYAN}search${RESET} Search past sessions, files, and observations"
|
|
23
|
+
echo -e " ${CYAN}tasks${RESET} Manage tracked tasks (add, done, block, list)"
|
|
24
|
+
echo -e " ${CYAN}overview${RESET} View or set project overview"
|
|
25
|
+
echo -e " ${CYAN}scan${RESET} Analyze a project and generate an overview"
|
|
26
|
+
echo -e " ${CYAN}index${RESET} Index source files for code-level search"
|
|
27
|
+
echo -e " ${CYAN}prune${RESET} Remove old observations and orphaned chunks"
|
|
22
28
|
echo -e " ${CYAN}install${RESET} Set up hooks, database, and skills"
|
|
23
29
|
echo -e " ${CYAN}uninstall${RESET} Remove hooks and optionally delete data"
|
|
24
30
|
echo -e " ${CYAN}update${RESET} Re-deploy hooks and run new migrations"
|
|
25
|
-
echo -e " ${CYAN}scan${RESET} Analyze a project and generate an overview"
|
|
26
|
-
echo -e " ${CYAN}index${RESET} Index source files for code-level search"
|
|
27
31
|
echo -e " ${CYAN}help${RESET} Show this help message"
|
|
28
32
|
echo -e " ${CYAN}version${RESET} Show version number"
|
|
29
33
|
echo ""
|
|
30
34
|
echo -e " ${BOLD}Examples:${RESET}"
|
|
31
|
-
echo -e " ${DIM}\$${RESET} eagle-mem
|
|
32
|
-
echo -e " ${DIM}\$${RESET} eagle-mem
|
|
33
|
-
echo -e " ${DIM}\$${RESET} eagle-mem
|
|
34
|
-
echo -e " ${DIM}\$${RESET} eagle-mem
|
|
35
|
-
echo -e " ${DIM}\$${RESET} eagle-mem
|
|
35
|
+
echo -e " ${DIM}\$${RESET} eagle-mem search \"auth bug\" ${DIM}# Search memory${RESET}"
|
|
36
|
+
echo -e " ${DIM}\$${RESET} eagle-mem search --timeline ${DIM}# Recent sessions${RESET}"
|
|
37
|
+
echo -e " ${DIM}\$${RESET} eagle-mem tasks ${DIM}# List tasks${RESET}"
|
|
38
|
+
echo -e " ${DIM}\$${RESET} eagle-mem tasks add \"Fix X\" ${DIM}# Add a task${RESET}"
|
|
39
|
+
echo -e " ${DIM}\$${RESET} eagle-mem overview ${DIM}# View overview${RESET}"
|
|
40
|
+
echo -e " ${DIM}\$${RESET} eagle-mem scan . ${DIM}# Scan current project${RESET}"
|
|
41
|
+
echo -e " ${DIM}\$${RESET} eagle-mem index . ${DIM}# Index source files${RESET}"
|
|
42
|
+
echo -e " ${DIM}\$${RESET} eagle-mem prune ${DIM}# Clean old data${RESET}"
|
|
43
|
+
echo -e " ${DIM}\$${RESET} eagle-mem install ${DIM}# First-time setup${RESET}"
|
|
36
44
|
echo ""
|
|
37
45
|
echo -e " ${BOLD}What it does:${RESET}"
|
|
38
46
|
echo -e " ${DOT} Saves session summaries to a shared SQLite database"
|
|
@@ -46,5 +54,7 @@ echo -e " ${CYAN}/eagle-mem-search${RESET} Search past sessions and observ
|
|
|
46
54
|
echo -e " ${CYAN}/eagle-mem-tasks${RESET} Break work into tracked subtasks"
|
|
47
55
|
echo -e " ${CYAN}/eagle-mem-overview${RESET} Generate a persistent project summary"
|
|
48
56
|
echo ""
|
|
57
|
+
echo -e " ${DIM}Skills use these CLI commands under the hood — no raw SQL.${RESET}"
|
|
58
|
+
echo ""
|
|
49
59
|
echo -e " ${DIM}https://github.com/eagleisbatman/eagle-mem${RESET}"
|
|
50
60
|
echo ""
|
package/scripts/install.sh
CHANGED
|
@@ -7,11 +7,12 @@ set -euo pipefail
|
|
|
7
7
|
|
|
8
8
|
PACKAGE_DIR="${1:-.}"
|
|
9
9
|
SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
10
11
|
|
|
11
12
|
. "$SCRIPTS_DIR/style.sh"
|
|
13
|
+
. "$LIB_DIR/common.sh"
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
SETTINGS="$HOME/.claude/settings.json"
|
|
15
|
+
SETTINGS="$EAGLE_SETTINGS"
|
|
15
16
|
|
|
16
17
|
eagle_banner
|
|
17
18
|
eagle_header "Install"
|
|
@@ -203,22 +204,12 @@ patch_hook "UserPromptSubmit" "" \
|
|
|
203
204
|
|
|
204
205
|
# ─── Install skills ────────────────────────────────────────
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
if [ -d "$PACKAGE_DIR/skills" ]; then
|
|
208
|
+
mkdir -p "$EAGLE_SKILLS_DIR"
|
|
208
209
|
for skill_dir in "$PACKAGE_DIR"/skills/*/; do
|
|
209
210
|
[ ! -d "$skill_dir" ] && continue
|
|
210
211
|
skill_name=$(basename "$skill_dir")
|
|
211
|
-
dst="$
|
|
212
|
-
[ -L "$dst" ] && rm "$dst"
|
|
213
|
-
ln -sf "$skill_dir" "$dst"
|
|
214
|
-
eagle_ok "Skill: $skill_name"
|
|
215
|
-
done
|
|
216
|
-
elif [ -d "$PACKAGE_DIR/skills" ]; then
|
|
217
|
-
mkdir -p "$SKILLS_DIR"
|
|
218
|
-
for skill_dir in "$PACKAGE_DIR"/skills/*/; do
|
|
219
|
-
[ ! -d "$skill_dir" ] && continue
|
|
220
|
-
skill_name=$(basename "$skill_dir")
|
|
221
|
-
dst="$SKILLS_DIR/$skill_name"
|
|
212
|
+
dst="$EAGLE_SKILLS_DIR/$skill_name"
|
|
222
213
|
[ -L "$dst" ] && rm "$dst"
|
|
223
214
|
ln -sf "$skill_dir" "$dst"
|
|
224
215
|
eagle_ok "Skill: $skill_name"
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════
|
|
3
|
+
# Eagle Mem — Overview
|
|
4
|
+
# CLI wrapper for project overview management
|
|
5
|
+
# ═══════════════════════════════════════════════════════════
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
10
|
+
|
|
11
|
+
. "$SCRIPTS_DIR/style.sh"
|
|
12
|
+
. "$LIB_DIR/common.sh"
|
|
13
|
+
. "$LIB_DIR/db.sh"
|
|
14
|
+
|
|
15
|
+
eagle_ensure_db
|
|
16
|
+
|
|
17
|
+
# ─── Parse arguments ──────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
action="${1:-show}"
|
|
20
|
+
shift 2>/dev/null || true
|
|
21
|
+
|
|
22
|
+
project=""
|
|
23
|
+
json_output=false
|
|
24
|
+
args=()
|
|
25
|
+
|
|
26
|
+
while [ $# -gt 0 ]; do
|
|
27
|
+
case "$1" in
|
|
28
|
+
--project|-p) project="$2"; shift 2 ;;
|
|
29
|
+
--json|-j) json_output=true; shift ;;
|
|
30
|
+
--help|-h)
|
|
31
|
+
echo -e " ${BOLD}eagle-mem overview${RESET} — Manage project overviews"
|
|
32
|
+
echo ""
|
|
33
|
+
echo -e " ${BOLD}Usage:${RESET}"
|
|
34
|
+
echo -e " eagle-mem overview ${DIM}# show current overview${RESET}"
|
|
35
|
+
echo -e " eagle-mem overview ${CYAN}set${RESET} <text> ${DIM}# set/update overview${RESET}"
|
|
36
|
+
echo -e " eagle-mem overview ${CYAN}delete${RESET} ${DIM}# delete overview${RESET}"
|
|
37
|
+
echo -e " eagle-mem overview ${CYAN}list${RESET} ${DIM}# list all overviews${RESET}"
|
|
38
|
+
echo ""
|
|
39
|
+
echo -e " ${BOLD}Options:${RESET}"
|
|
40
|
+
echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current dir)"
|
|
41
|
+
echo -e " ${CYAN}-j, --json${RESET} Output as JSON"
|
|
42
|
+
echo ""
|
|
43
|
+
echo -e " ${BOLD}Tip:${RESET} Use ${CYAN}eagle-mem scan${RESET} to auto-generate an overview from code."
|
|
44
|
+
echo ""
|
|
45
|
+
exit 0
|
|
46
|
+
;;
|
|
47
|
+
*) args+=("$1"); shift ;;
|
|
48
|
+
esac
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
[ -z "$project" ] && project=$(eagle_project_from_cwd "$(pwd)")
|
|
52
|
+
project_sql=$(eagle_sql_escape "$project")
|
|
53
|
+
|
|
54
|
+
# ─── Show overview ────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
overview_show() {
|
|
57
|
+
if [ "$json_output" = true ]; then
|
|
58
|
+
eagle_db_json "SELECT project, content, updated_at FROM overviews WHERE project = '$project_sql';"
|
|
59
|
+
return
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
local content
|
|
63
|
+
content=$(eagle_get_overview "$project")
|
|
64
|
+
|
|
65
|
+
if [ -z "$content" ]; then
|
|
66
|
+
eagle_dim "No overview for project '$project'"
|
|
67
|
+
eagle_dim "Run 'eagle-mem scan' or 'eagle-mem overview set <text>' to create one"
|
|
68
|
+
return
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
echo ""
|
|
72
|
+
echo -e " ${BOLD}Overview${RESET} ${DIM}($project)${RESET}"
|
|
73
|
+
echo -e " ${DIM}─────────────────────────────────────${RESET}"
|
|
74
|
+
echo ""
|
|
75
|
+
echo -e " $content"
|
|
76
|
+
echo ""
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# ─── Set overview ─────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
overview_set() {
|
|
82
|
+
local content="${args[*]:-}"
|
|
83
|
+
|
|
84
|
+
if [ -z "$content" ]; then
|
|
85
|
+
eagle_err "Usage: eagle-mem overview set <text>"
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
eagle_upsert_overview "$project" "$content"
|
|
90
|
+
|
|
91
|
+
if [ "$json_output" = true ]; then
|
|
92
|
+
jq -nc --arg project "$project" '{project: $project, updated: true}'
|
|
93
|
+
return
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
eagle_ok "Overview saved for '$project'"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# ─── Delete overview ──────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
overview_delete() {
|
|
102
|
+
eagle_db "DELETE FROM overviews WHERE project = '$project_sql';"
|
|
103
|
+
|
|
104
|
+
if [ "$json_output" = true ]; then
|
|
105
|
+
jq -nc --arg project "$project" '{project: $project, deleted: true}'
|
|
106
|
+
return
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
eagle_ok "Overview deleted for '$project'"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# ─── List all overviews ──────────────────────────────────
|
|
113
|
+
|
|
114
|
+
overview_list() {
|
|
115
|
+
if [ "$json_output" = true ]; then
|
|
116
|
+
eagle_db_json "SELECT project, content, updated_at FROM overviews ORDER BY updated_at DESC;"
|
|
117
|
+
return
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
local results
|
|
121
|
+
results=$(eagle_db "SELECT project, substr(content, 1, 80), updated_at FROM overviews ORDER BY updated_at DESC;")
|
|
122
|
+
|
|
123
|
+
if [ -z "$results" ]; then
|
|
124
|
+
eagle_dim "No overviews stored"
|
|
125
|
+
return
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
echo ""
|
|
129
|
+
echo -e " ${BOLD}All project overviews${RESET}"
|
|
130
|
+
echo -e " ${DIM}─────────────────────────────────────${RESET}"
|
|
131
|
+
echo ""
|
|
132
|
+
|
|
133
|
+
while IFS='|' read -r proj snippet updated_at; do
|
|
134
|
+
[ -z "$proj" ] && continue
|
|
135
|
+
echo -e " ${BOLD}$proj${RESET} ${DIM}$updated_at${RESET}"
|
|
136
|
+
echo -e " ${DIM}$snippet...${RESET}"
|
|
137
|
+
echo ""
|
|
138
|
+
done <<< "$results"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# ─── Dispatch ─────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
case "$action" in
|
|
144
|
+
show|view) overview_show ;;
|
|
145
|
+
set|update) overview_set ;;
|
|
146
|
+
delete|rm) overview_delete ;;
|
|
147
|
+
list|ls) overview_list ;;
|
|
148
|
+
--help|-h)
|
|
149
|
+
echo -e " ${BOLD}eagle-mem overview${RESET} — Manage project overviews"
|
|
150
|
+
echo ""
|
|
151
|
+
echo -e " ${BOLD}Usage:${RESET}"
|
|
152
|
+
echo -e " eagle-mem overview ${DIM}# show current overview${RESET}"
|
|
153
|
+
echo -e " eagle-mem overview ${CYAN}set${RESET} <text> ${DIM}# set/update overview${RESET}"
|
|
154
|
+
echo -e " eagle-mem overview ${CYAN}delete${RESET} ${DIM}# delete overview${RESET}"
|
|
155
|
+
echo -e " eagle-mem overview ${CYAN}list${RESET} ${DIM}# list all overviews${RESET}"
|
|
156
|
+
echo ""
|
|
157
|
+
echo -e " ${BOLD}Options:${RESET}"
|
|
158
|
+
echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current dir)"
|
|
159
|
+
echo -e " ${CYAN}-j, --json${RESET} Output as JSON"
|
|
160
|
+
echo ""
|
|
161
|
+
echo -e " ${BOLD}Tip:${RESET} Use ${CYAN}eagle-mem scan${RESET} to auto-generate an overview from code."
|
|
162
|
+
echo ""
|
|
163
|
+
exit 0
|
|
164
|
+
;;
|
|
165
|
+
*)
|
|
166
|
+
eagle_err "Unknown action: $action"
|
|
167
|
+
eagle_dim " Run 'eagle-mem overview --help' for options"
|
|
168
|
+
exit 1
|
|
169
|
+
;;
|
|
170
|
+
esac
|