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 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,
@@ -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" ] && exit 0
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="[\"$fp\"]"
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="[\"$fp\"]"
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="[\"$fp\"]"
51
+ [ -n "$fp" ] && files_modified=$(printf '%s' "$fp" | jq -Rsc '[.]')
52
52
  tool_summary="Edit $fp"
53
53
  ;;
54
54
  Bash)
@@ -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=$(eagle_db "
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=$(eagle_db "
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=$(eagle_db "
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
- eagle_db "UPDATE tasks SET status = 'active', started_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = $first_pending;"
122
- active_task=$(eagle_db "
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
- active_task_id=$(eagle_db "SELECT id FROM tasks WHERE project = '$project_sql' AND status = 'active' LIMIT 1;")
141
- if [ -n "$active_task_id" ]; then
142
- eagle_db "UPDATE tasks SET status = 'done', completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = $active_task_id;"
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=$(eagle_db "SELECT s.request, s.learned, s.completed, s.created_at
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 completed created_at; do
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=$(eagle_db "SELECT COUNT(*) FROM code_chunks WHERE project = '$project_sql' LIMIT 1;")
67
+ has_chunks=$(eagle_count_code_chunks "$project")
77
68
  if [ "${has_chunks:-0}" -gt 0 ]; then
78
- code_results=$(eagle_db "SELECT c.file_path, c.start_line, c.end_line, c.language
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="$1"
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="$1"
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="$1"
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="$5"
62
- local files_modified="$6"
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="${2:-5}"
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="${3:-10}"
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="${2:-20}"
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="$1"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eagle-mem",
3
- "version": "1.0.3",
3
+ "version": "1.2.0",
4
4
  "description": "Lightweight persistent memory for Claude Code — SQLite + FTS5, no daemon, no bloat",
5
5
  "bin": {
6
6
  "eagle-mem": "bin/eagle-mem"
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 install ${DIM}# First-time setup${RESET}"
32
- echo -e " ${DIM}\$${RESET} eagle-mem update ${DIM}# After npm update${RESET}"
33
- echo -e " ${DIM}\$${RESET} eagle-mem scan . ${DIM}# Scan current project${RESET}"
34
- echo -e " ${DIM}\$${RESET} eagle-mem index . ${DIM}# Index source files${RESET}"
35
- echo -e " ${DIM}\$${RESET} eagle-mem uninstall ${DIM}# Clean removal${RESET}"
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 ""
@@ -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
- EAGLE_MEM_DIR="${EAGLE_MEM_DIR:-$HOME/.eagle-mem}"
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
- SKILLS_DIR="$HOME/.claude/skills"
207
- if [ -d "$SKILLS_DIR" ] && [ -d "$PACKAGE_DIR/skills" ]; then
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="$SKILLS_DIR/$skill_name"
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