eagle-mem 1.1.0 → 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 +1 -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 +2 -0
- package/scripts/install.sh +6 -15
- package/scripts/overview.sh +17 -3
- package/scripts/prune.sh +154 -0
- package/scripts/search.sh +14 -5
- package/scripts/tasks.sh +67 -16
- package/scripts/uninstall.sh +5 -5
- package/scripts/update.sh +5 -5
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
|
+
prune) bash "$SCRIPTS_DIR/prune.sh" "$@" ;;
|
|
27
28
|
help|--help|-h)
|
|
28
29
|
bash "$SCRIPTS_DIR/help.sh" ;;
|
|
29
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
|
@@ -24,6 +24,7 @@ echo -e " ${CYAN}tasks${RESET} Manage tracked tasks (add, done, block,
|
|
|
24
24
|
echo -e " ${CYAN}overview${RESET} View or set project overview"
|
|
25
25
|
echo -e " ${CYAN}scan${RESET} Analyze a project and generate an overview"
|
|
26
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"
|
|
27
28
|
echo -e " ${CYAN}install${RESET} Set up hooks, database, and skills"
|
|
28
29
|
echo -e " ${CYAN}uninstall${RESET} Remove hooks and optionally delete data"
|
|
29
30
|
echo -e " ${CYAN}update${RESET} Re-deploy hooks and run new migrations"
|
|
@@ -38,6 +39,7 @@ echo -e " ${DIM}\$${RESET} eagle-mem tasks add \"Fix X\" ${DIM}# Add a task$
|
|
|
38
39
|
echo -e " ${DIM}\$${RESET} eagle-mem overview ${DIM}# View overview${RESET}"
|
|
39
40
|
echo -e " ${DIM}\$${RESET} eagle-mem scan . ${DIM}# Scan current project${RESET}"
|
|
40
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}"
|
|
41
43
|
echo -e " ${DIM}\$${RESET} eagle-mem install ${DIM}# First-time setup${RESET}"
|
|
42
44
|
echo ""
|
|
43
45
|
echo -e " ${BOLD}What it does:${RESET}"
|
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"
|
package/scripts/overview.sh
CHANGED
|
@@ -89,7 +89,7 @@ overview_set() {
|
|
|
89
89
|
eagle_upsert_overview "$project" "$content"
|
|
90
90
|
|
|
91
91
|
if [ "$json_output" = true ]; then
|
|
92
|
-
|
|
92
|
+
jq -nc --arg project "$project" '{project: $project, updated: true}'
|
|
93
93
|
return
|
|
94
94
|
fi
|
|
95
95
|
|
|
@@ -102,7 +102,7 @@ overview_delete() {
|
|
|
102
102
|
eagle_db "DELETE FROM overviews WHERE project = '$project_sql';"
|
|
103
103
|
|
|
104
104
|
if [ "$json_output" = true ]; then
|
|
105
|
-
|
|
105
|
+
jq -nc --arg project "$project" '{project: $project, deleted: true}'
|
|
106
106
|
return
|
|
107
107
|
fi
|
|
108
108
|
|
|
@@ -146,7 +146,21 @@ case "$action" in
|
|
|
146
146
|
delete|rm) overview_delete ;;
|
|
147
147
|
list|ls) overview_list ;;
|
|
148
148
|
--help|-h)
|
|
149
|
-
echo -e "
|
|
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
|
|
150
164
|
;;
|
|
151
165
|
*)
|
|
152
166
|
eagle_err "Unknown action: $action"
|
package/scripts/prune.sh
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════
|
|
3
|
+
# Eagle Mem — Prune
|
|
4
|
+
# Removes old observations and orphaned code chunks
|
|
5
|
+
# to keep the database lean
|
|
6
|
+
# ═══════════════════════════════════════════════════════════
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
11
|
+
|
|
12
|
+
. "$SCRIPTS_DIR/style.sh"
|
|
13
|
+
. "$LIB_DIR/common.sh"
|
|
14
|
+
. "$LIB_DIR/db.sh"
|
|
15
|
+
|
|
16
|
+
eagle_ensure_db
|
|
17
|
+
|
|
18
|
+
# ─── Parse arguments ──────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
days=90
|
|
21
|
+
project=""
|
|
22
|
+
dry_run=false
|
|
23
|
+
|
|
24
|
+
while [ $# -gt 0 ]; do
|
|
25
|
+
case "$1" in
|
|
26
|
+
--days|-d) days="$2"; shift 2 ;;
|
|
27
|
+
--project|-p) project="$2"; shift 2 ;;
|
|
28
|
+
--dry-run|-n) dry_run=true; shift ;;
|
|
29
|
+
--help|-h)
|
|
30
|
+
echo -e " ${BOLD}eagle-mem prune${RESET} — Clean up old data"
|
|
31
|
+
echo ""
|
|
32
|
+
echo -e " ${BOLD}Usage:${RESET}"
|
|
33
|
+
echo -e " eagle-mem prune ${DIM}# prune observations > 90 days${RESET}"
|
|
34
|
+
echo -e " eagle-mem prune ${CYAN}--days 30${RESET} ${DIM}# prune observations > 30 days${RESET}"
|
|
35
|
+
echo -e " eagle-mem prune ${CYAN}--dry-run${RESET} ${DIM}# show what would be pruned${RESET}"
|
|
36
|
+
echo ""
|
|
37
|
+
echo -e " ${BOLD}What gets pruned:${RESET}"
|
|
38
|
+
echo -e " ${DOT} Observations older than --days (default: 90)"
|
|
39
|
+
echo -e " ${DOT} Code chunks for files that no longer exist"
|
|
40
|
+
echo ""
|
|
41
|
+
echo -e " ${BOLD}What is preserved:${RESET}"
|
|
42
|
+
echo -e " ${DOT} All sessions and summaries (your session history)"
|
|
43
|
+
echo -e " ${DOT} All tasks"
|
|
44
|
+
echo -e " ${DOT} Project overviews"
|
|
45
|
+
echo ""
|
|
46
|
+
echo -e " ${BOLD}Options:${RESET}"
|
|
47
|
+
echo -e " ${CYAN}-d, --days${RESET} <N> Age threshold (default: 90)"
|
|
48
|
+
echo -e " ${CYAN}-p, --project${RESET} <name> Only prune for this project"
|
|
49
|
+
echo -e " ${CYAN}-n, --dry-run${RESET} Show counts without deleting"
|
|
50
|
+
echo ""
|
|
51
|
+
exit 0
|
|
52
|
+
;;
|
|
53
|
+
*)
|
|
54
|
+
eagle_err "Unknown option: $1"
|
|
55
|
+
exit 1
|
|
56
|
+
;;
|
|
57
|
+
esac
|
|
58
|
+
done
|
|
59
|
+
|
|
60
|
+
if ! [[ "$days" =~ ^[0-9]+$ ]]; then
|
|
61
|
+
eagle_err "Days must be a number, got: $days"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
eagle_header "Prune"
|
|
66
|
+
|
|
67
|
+
# ─── Count before ────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
total_obs=$(eagle_db "SELECT COUNT(*) FROM observations;")
|
|
70
|
+
total_chunks=$(eagle_db "SELECT COUNT(*) FROM code_chunks;")
|
|
71
|
+
eagle_info "Database: ${total_obs:-0} observations, ${total_chunks:-0} chunks"
|
|
72
|
+
echo ""
|
|
73
|
+
|
|
74
|
+
# ─── Old observations ───────────────────────────────────
|
|
75
|
+
|
|
76
|
+
obs_project_filter=""
|
|
77
|
+
if [ -n "$project" ]; then
|
|
78
|
+
obs_project_filter="AND session_id IN (SELECT id FROM sessions WHERE project = '$(eagle_sql_escape "$project")')"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
old_obs_count=$(eagle_db "SELECT COUNT(*) FROM observations WHERE created_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-$days days') $obs_project_filter;")
|
|
82
|
+
|
|
83
|
+
if [ "${old_obs_count:-0}" -gt 0 ]; then
|
|
84
|
+
if [ "$dry_run" = true ]; then
|
|
85
|
+
eagle_dim "Would prune $old_obs_count observations older than $days days"
|
|
86
|
+
else
|
|
87
|
+
eagle_prune_observations "$days" "$project"
|
|
88
|
+
eagle_ok "Pruned $old_obs_count observations older than $days days"
|
|
89
|
+
fi
|
|
90
|
+
else
|
|
91
|
+
eagle_ok "No observations older than $days days"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# ─── Orphaned code chunks ───────────────────────────────
|
|
95
|
+
|
|
96
|
+
if [ -n "$project" ]; then
|
|
97
|
+
projects="$project"
|
|
98
|
+
else
|
|
99
|
+
projects=$(eagle_db "SELECT DISTINCT project FROM code_chunks;")
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
orphan_total=0
|
|
103
|
+
if [ -n "$projects" ]; then
|
|
104
|
+
while IFS= read -r proj; do
|
|
105
|
+
[ -z "$proj" ] && continue
|
|
106
|
+
|
|
107
|
+
# Try to find the project directory
|
|
108
|
+
proj_cwd=$(eagle_db "SELECT cwd FROM sessions WHERE project = '$(eagle_sql_escape "$proj")' ORDER BY started_at DESC LIMIT 1;")
|
|
109
|
+
|
|
110
|
+
if [ -n "$proj_cwd" ] && [ -d "$proj_cwd" ]; then
|
|
111
|
+
if [ "$dry_run" = true ]; then
|
|
112
|
+
orphan_count=$(eagle_db "SELECT COUNT(DISTINCT file_path) FROM code_chunks WHERE project = '$(eagle_sql_escape "$proj")';")
|
|
113
|
+
# Count files that no longer exist
|
|
114
|
+
orphans=0
|
|
115
|
+
paths=$(eagle_db "SELECT DISTINCT file_path FROM code_chunks WHERE project = '$(eagle_sql_escape "$proj")';")
|
|
116
|
+
while IFS= read -r fpath; do
|
|
117
|
+
[ -z "$fpath" ] && continue
|
|
118
|
+
[ ! -f "$proj_cwd/$fpath" ] && orphans=$((orphans + 1))
|
|
119
|
+
done <<< "$paths"
|
|
120
|
+
if [ "$orphans" -gt 0 ]; then
|
|
121
|
+
eagle_dim "Would prune $orphans orphaned files from '$proj'"
|
|
122
|
+
orphan_total=$((orphan_total + orphans))
|
|
123
|
+
fi
|
|
124
|
+
else
|
|
125
|
+
removed=$(eagle_prune_orphan_chunks "$proj" "$proj_cwd")
|
|
126
|
+
if [ "${removed:-0}" -gt 0 ]; then
|
|
127
|
+
eagle_ok "Pruned $removed orphaned files from '$proj'"
|
|
128
|
+
orphan_total=$((orphan_total + removed))
|
|
129
|
+
fi
|
|
130
|
+
fi
|
|
131
|
+
fi
|
|
132
|
+
done <<< "$projects"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
if [ "$orphan_total" -eq 0 ]; then
|
|
136
|
+
eagle_ok "No orphaned code chunks found"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# ─── Summary ────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
new_obs=$(eagle_db "SELECT COUNT(*) FROM observations;")
|
|
142
|
+
new_chunks=$(eagle_db "SELECT COUNT(*) FROM code_chunks;")
|
|
143
|
+
|
|
144
|
+
echo ""
|
|
145
|
+
if [ "$dry_run" = true ]; then
|
|
146
|
+
eagle_footer "Dry run complete (no changes made)."
|
|
147
|
+
else
|
|
148
|
+
eagle_footer "Prune complete."
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
eagle_kv "Observations:" "${new_obs:-0} (was ${total_obs:-0})"
|
|
152
|
+
eagle_kv "Code chunks:" "${new_chunks:-0} (was ${total_chunks:-0})"
|
|
153
|
+
eagle_kv "Database:" "$EAGLE_MEM_DB"
|
|
154
|
+
echo ""
|
package/scripts/search.sh
CHANGED
|
@@ -62,11 +62,13 @@ while [ $# -gt 0 ]; do
|
|
|
62
62
|
done
|
|
63
63
|
|
|
64
64
|
[ -z "$project" ] && project=$(eagle_project_from_cwd "$(pwd)")
|
|
65
|
+
limit=$(eagle_sql_int "$limit")
|
|
66
|
+
[ "$limit" -eq 0 ] && limit=10
|
|
65
67
|
|
|
66
68
|
# ─── Keyword search ──────────────────────────────────────
|
|
67
69
|
|
|
68
70
|
search_keyword() {
|
|
69
|
-
local q; q=$(eagle_sql_escape "$query")
|
|
71
|
+
local q; q=$(eagle_sql_escape "$(eagle_fts_sanitize "$query")")
|
|
70
72
|
local p; p=$(eagle_sql_escape "$project")
|
|
71
73
|
|
|
72
74
|
local where_project=""
|
|
@@ -158,10 +160,12 @@ search_timeline() {
|
|
|
158
160
|
# ─── Session details ──────────────────────────────────────
|
|
159
161
|
|
|
160
162
|
search_session() {
|
|
163
|
+
local sid_sql; sid_sql=$(eagle_sql_escape "$session_id")
|
|
164
|
+
|
|
161
165
|
if [ "$json_output" = true ]; then
|
|
162
166
|
eagle_db_json "SELECT o.tool_name, o.tool_input_summary, o.files_read, o.files_modified, o.created_at
|
|
163
167
|
FROM observations o
|
|
164
|
-
WHERE o.session_id = '$
|
|
168
|
+
WHERE o.session_id = '$sid_sql'
|
|
165
169
|
ORDER BY o.created_at ASC;"
|
|
166
170
|
return
|
|
167
171
|
fi
|
|
@@ -169,7 +173,7 @@ search_session() {
|
|
|
169
173
|
local results
|
|
170
174
|
results=$(eagle_db "SELECT o.tool_name, o.tool_input_summary, o.files_read, o.files_modified, o.created_at
|
|
171
175
|
FROM observations o
|
|
172
|
-
WHERE o.session_id = '$
|
|
176
|
+
WHERE o.session_id = '$sid_sql'
|
|
173
177
|
ORDER BY o.created_at ASC;")
|
|
174
178
|
|
|
175
179
|
if [ -z "$results" ]; then
|
|
@@ -250,8 +254,13 @@ search_stats() {
|
|
|
250
254
|
chunks=$(eagle_db "SELECT COUNT(*) FROM code_chunks WHERE project = '$p';")
|
|
251
255
|
|
|
252
256
|
if [ "$json_output" = true ]; then
|
|
253
|
-
|
|
254
|
-
|
|
257
|
+
jq -nc --arg project "$project" \
|
|
258
|
+
--argjson sessions "${sessions:-0}" \
|
|
259
|
+
--argjson summaries "${summaries:-0}" \
|
|
260
|
+
--argjson observations "${observations:-0}" \
|
|
261
|
+
--argjson tasks "${tasks:-0}" \
|
|
262
|
+
--argjson code_chunks "${chunks:-0}" \
|
|
263
|
+
'{project: $project, sessions: $sessions, summaries: $summaries, observations: $observations, tasks: $tasks, code_chunks: $code_chunks}'
|
|
255
264
|
return
|
|
256
265
|
fi
|
|
257
266
|
|
package/scripts/tasks.sh
CHANGED
|
@@ -16,7 +16,7 @@ eagle_ensure_db
|
|
|
16
16
|
|
|
17
17
|
# ─── Parse arguments ──────────────────────────────────────
|
|
18
18
|
|
|
19
|
-
action="${1:-
|
|
19
|
+
action="${1:-pending}"
|
|
20
20
|
shift 2>/dev/null || true
|
|
21
21
|
|
|
22
22
|
project=""
|
|
@@ -123,14 +123,14 @@ tasks_add() {
|
|
|
123
123
|
max_ord=$(eagle_db "SELECT COALESCE(MAX(ordinal), 0) FROM tasks WHERE project = '$project_sql';")
|
|
124
124
|
local next_ord=$((max_ord + 1))
|
|
125
125
|
|
|
126
|
-
eagle_db "INSERT INTO tasks (project, title, instructions, ordinal)
|
|
127
|
-
VALUES ('$project_sql', '$title_sql', '$instr_sql', $next_ord);"
|
|
128
|
-
|
|
129
126
|
local new_id
|
|
130
|
-
new_id=$(eagle_db "
|
|
127
|
+
new_id=$(eagle_db "INSERT INTO tasks (project, title, instructions, ordinal)
|
|
128
|
+
VALUES ('$project_sql', '$title_sql', '$instr_sql', $next_ord);
|
|
129
|
+
SELECT last_insert_rowid();")
|
|
131
130
|
|
|
132
131
|
if [ "$json_output" = true ]; then
|
|
133
|
-
|
|
132
|
+
jq -nc --arg id "$new_id" --arg title "$title" --argjson ord "$next_ord" \
|
|
133
|
+
'{id: ($id | tonumber), title: $title, ordinal: $ord}'
|
|
134
134
|
return
|
|
135
135
|
fi
|
|
136
136
|
|
|
@@ -147,14 +147,26 @@ tasks_done() {
|
|
|
147
147
|
exit 1
|
|
148
148
|
fi
|
|
149
149
|
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
if ! [[ "$task_id" =~ ^[0-9]+$ ]]; then
|
|
151
|
+
eagle_err "Task ID must be a number, got: $task_id"
|
|
152
|
+
exit 1
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
local changed
|
|
156
|
+
changed=$(eagle_db "UPDATE tasks SET status = 'done', completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
157
|
+
WHERE id = $task_id AND project = '$project_sql';
|
|
158
|
+
SELECT changes();")
|
|
159
|
+
|
|
160
|
+
if [ "${changed:-0}" -eq 0 ]; then
|
|
161
|
+
eagle_err "Task #$task_id not found in project '$project'"
|
|
162
|
+
exit 1
|
|
163
|
+
fi
|
|
152
164
|
|
|
153
165
|
local title
|
|
154
|
-
title=$(eagle_db "SELECT title FROM tasks WHERE id = $task_id;")
|
|
166
|
+
title=$(eagle_db "SELECT title FROM tasks WHERE id = $task_id AND project = '$project_sql';")
|
|
155
167
|
|
|
156
168
|
if [ "$json_output" = true ]; then
|
|
157
|
-
|
|
169
|
+
jq -nc --arg id "$task_id" '{id: ($id | tonumber), status: "done"}'
|
|
158
170
|
return
|
|
159
171
|
fi
|
|
160
172
|
|
|
@@ -171,10 +183,22 @@ tasks_block() {
|
|
|
171
183
|
exit 1
|
|
172
184
|
fi
|
|
173
185
|
|
|
174
|
-
|
|
186
|
+
if ! [[ "$task_id" =~ ^[0-9]+$ ]]; then
|
|
187
|
+
eagle_err "Task ID must be a number, got: $task_id"
|
|
188
|
+
exit 1
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
local changed
|
|
192
|
+
changed=$(eagle_db "UPDATE tasks SET status = 'blocked' WHERE id = $task_id AND project = '$project_sql';
|
|
193
|
+
SELECT changes();")
|
|
194
|
+
|
|
195
|
+
if [ "${changed:-0}" -eq 0 ]; then
|
|
196
|
+
eagle_err "Task #$task_id not found in project '$project'"
|
|
197
|
+
exit 1
|
|
198
|
+
fi
|
|
175
199
|
|
|
176
200
|
if [ "$json_output" = true ]; then
|
|
177
|
-
|
|
201
|
+
jq -nc --arg id "$task_id" '{id: ($id | tonumber), status: "blocked"}'
|
|
178
202
|
return
|
|
179
203
|
fi
|
|
180
204
|
|
|
@@ -192,11 +216,23 @@ tasks_context() {
|
|
|
192
216
|
exit 1
|
|
193
217
|
fi
|
|
194
218
|
|
|
219
|
+
if ! [[ "$task_id" =~ ^[0-9]+$ ]]; then
|
|
220
|
+
eagle_err "Task ID must be a number, got: $task_id"
|
|
221
|
+
exit 1
|
|
222
|
+
fi
|
|
223
|
+
|
|
195
224
|
local snap_sql; snap_sql=$(eagle_sql_escape "$snapshot")
|
|
196
|
-
|
|
225
|
+
local changed
|
|
226
|
+
changed=$(eagle_db "UPDATE tasks SET context_snapshot = '$snap_sql' WHERE id = $task_id AND project = '$project_sql';
|
|
227
|
+
SELECT changes();")
|
|
228
|
+
|
|
229
|
+
if [ "${changed:-0}" -eq 0 ]; then
|
|
230
|
+
eagle_err "Task #$task_id not found in project '$project'"
|
|
231
|
+
exit 1
|
|
232
|
+
fi
|
|
197
233
|
|
|
198
234
|
if [ "$json_output" = true ]; then
|
|
199
|
-
|
|
235
|
+
jq -nc --arg id "$task_id" '{id: ($id | tonumber), context_snapshot: true}'
|
|
200
236
|
return
|
|
201
237
|
fi
|
|
202
238
|
|
|
@@ -212,7 +248,7 @@ tasks_clear() {
|
|
|
212
248
|
eagle_db "DELETE FROM tasks WHERE project = '$project_sql' AND status = 'done';"
|
|
213
249
|
|
|
214
250
|
if [ "$json_output" = true ]; then
|
|
215
|
-
|
|
251
|
+
jq -nc --argjson cleared "${count:-0}" '{cleared: $cleared}'
|
|
216
252
|
return
|
|
217
253
|
fi
|
|
218
254
|
|
|
@@ -230,7 +266,22 @@ case "$action" in
|
|
|
230
266
|
context) tasks_context ;;
|
|
231
267
|
clear) tasks_clear ;;
|
|
232
268
|
--help|-h)
|
|
233
|
-
echo -e "
|
|
269
|
+
echo -e " ${BOLD}eagle-mem tasks${RESET} — Manage tracked tasks"
|
|
270
|
+
echo ""
|
|
271
|
+
echo -e " ${BOLD}Usage:${RESET}"
|
|
272
|
+
echo -e " eagle-mem tasks ${DIM}# list pending tasks${RESET}"
|
|
273
|
+
echo -e " eagle-mem tasks ${CYAN}list${RESET} ${DIM}# list all tasks${RESET}"
|
|
274
|
+
echo -e " eagle-mem tasks ${CYAN}add${RESET} <title> [instructions] ${DIM}# add a task${RESET}"
|
|
275
|
+
echo -e " eagle-mem tasks ${CYAN}done${RESET} <id> ${DIM}# mark task complete${RESET}"
|
|
276
|
+
echo -e " eagle-mem tasks ${CYAN}block${RESET} <id> ${DIM}# mark task blocked${RESET}"
|
|
277
|
+
echo -e " eagle-mem tasks ${CYAN}context${RESET} <id> <snapshot> ${DIM}# set task context${RESET}"
|
|
278
|
+
echo -e " eagle-mem tasks ${CYAN}clear${RESET} ${DIM}# remove all done tasks${RESET}"
|
|
279
|
+
echo ""
|
|
280
|
+
echo -e " ${BOLD}Options:${RESET}"
|
|
281
|
+
echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current dir)"
|
|
282
|
+
echo -e " ${CYAN}-j, --json${RESET} Output as JSON"
|
|
283
|
+
echo ""
|
|
284
|
+
exit 0
|
|
234
285
|
;;
|
|
235
286
|
*)
|
|
236
287
|
eagle_err "Unknown action: $action"
|
package/scripts/uninstall.sh
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
8
8
|
SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
9
10
|
|
|
10
11
|
. "$SCRIPTS_DIR/style.sh"
|
|
12
|
+
. "$LIB_DIR/common.sh"
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
SETTINGS="$HOME/.claude/settings.json"
|
|
14
|
-
SKILLS_DIR="$HOME/.claude/skills"
|
|
14
|
+
SETTINGS="$EAGLE_SETTINGS"
|
|
15
15
|
|
|
16
16
|
eagle_header "Uninstall"
|
|
17
17
|
|
|
@@ -35,9 +35,9 @@ fi
|
|
|
35
35
|
|
|
36
36
|
# ─── Remove skill symlinks ────────────────────────────────
|
|
37
37
|
|
|
38
|
-
if [ -d "$
|
|
38
|
+
if [ -d "$EAGLE_SKILLS_DIR" ]; then
|
|
39
39
|
for skill in eagle-mem-search eagle-mem-tasks eagle-mem-overview; do
|
|
40
|
-
target="$
|
|
40
|
+
target="$EAGLE_SKILLS_DIR/$skill"
|
|
41
41
|
if [ -L "$target" ]; then
|
|
42
42
|
rm "$target"
|
|
43
43
|
eagle_ok "Skill removed: $skill"
|
package/scripts/update.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_header "Update"
|
|
17
18
|
|
|
@@ -87,13 +88,12 @@ fi
|
|
|
87
88
|
|
|
88
89
|
# ─── Update skill symlinks ────────────────────────────────
|
|
89
90
|
|
|
90
|
-
SKILLS_DIR="$HOME/.claude/skills"
|
|
91
91
|
if [ -d "$PACKAGE_DIR/skills" ]; then
|
|
92
|
-
mkdir -p "$
|
|
92
|
+
mkdir -p "$EAGLE_SKILLS_DIR"
|
|
93
93
|
for skill_dir in "$PACKAGE_DIR"/skills/*/; do
|
|
94
94
|
[ ! -d "$skill_dir" ] && continue
|
|
95
95
|
skill_name=$(basename "$skill_dir")
|
|
96
|
-
dst="$
|
|
96
|
+
dst="$EAGLE_SKILLS_DIR/$skill_name"
|
|
97
97
|
[ -L "$dst" ] && rm "$dst"
|
|
98
98
|
ln -sf "$skill_dir" "$dst"
|
|
99
99
|
done
|