eagle-mem 1.5.1 → 1.6.1

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
@@ -29,7 +29,7 @@ case "$command" in
29
29
  help|--help|-h)
30
30
  bash "$SCRIPTS_DIR/help.sh" ;;
31
31
  version|--version|-v|-V)
32
- version=$(node -e "console.log(require('$PACKAGE_DIR/package.json').version)" 2>/dev/null || echo "unknown")
32
+ version=$(jq -r .version "$PACKAGE_DIR/package.json" 2>/dev/null || echo "unknown")
33
33
  echo -e " ${BOLD}Eagle Mem${RESET} v${version}"
34
34
  ;;
35
35
  *)
@@ -0,0 +1,29 @@
1
+ -- ═══════════════════════════════════════════════════════════
2
+ -- Migration 009: Drop dead tasks table
3
+ -- The original tasks table (from schema.sql / migration 001)
4
+ -- was replaced by claude_tasks (migration 007). No code
5
+ -- references the old table. This cleans up the vestigial
6
+ -- schema: 3 FTS triggers, the FTS virtual table, 3 indexes,
7
+ -- and the table itself.
8
+ --
9
+ -- NOTE: schema.sql (migration 001) still contains the CREATE
10
+ -- TABLE tasks definition. We do NOT edit applied migrations.
11
+ -- Fresh installs will CREATE (001) then DROP (009). Existing
12
+ -- installs just run 009. Verified 0 rows in production.
13
+ -- ═══════════════════════════════════════════════════════════
14
+
15
+ -- Drop FTS sync triggers first (they reference the tasks table)
16
+ DROP TRIGGER IF EXISTS tasks_ai;
17
+ DROP TRIGGER IF EXISTS tasks_ad;
18
+ DROP TRIGGER IF EXISTS tasks_au;
19
+
20
+ -- Drop the FTS virtual table
21
+ DROP TABLE IF EXISTS tasks_fts;
22
+
23
+ -- Drop indexes (implicit in DROP TABLE, but explicit for clarity)
24
+ DROP INDEX IF EXISTS idx_tasks_project;
25
+ DROP INDEX IF EXISTS idx_tasks_status;
26
+ DROP INDEX IF EXISTS idx_tasks_parent;
27
+
28
+ -- Drop the dead tasks table
29
+ DROP TABLE IF EXISTS tasks;
package/db/migrate.sh CHANGED
@@ -25,7 +25,8 @@ run_migration() {
25
25
  body=$(grep -v -E '^[[:space:]]*PRAGMA ' "$file")
26
26
 
27
27
  # Set connection PRAGMAs, then run migration body + tracking insert atomically
28
- { echo "PRAGMA trusted_schema=ON;"; echo "PRAGMA foreign_keys=ON;"; echo "PRAGMA busy_timeout=5000;"; echo "BEGIN;"; echo "$body"; echo "INSERT INTO _migrations (name) VALUES ('$name');"; echo "COMMIT;"; } | sqlite3 "$DB"
28
+ # .bail on ensures sqlite3 stops on the first error instead of continuing
29
+ { echo ".bail on"; echo "PRAGMA trusted_schema=ON;"; echo "PRAGMA foreign_keys=ON;"; echo "PRAGMA busy_timeout=5000;"; echo "BEGIN;"; echo "$body"; echo "INSERT INTO _migrations (name) VALUES ('$name');"; echo "COMMIT;"; } | sqlite3 "$DB"
29
30
  echo " applied: $name"
30
31
  fi
31
32
  }
@@ -64,4 +65,7 @@ run_migration "007_claude_tasks" "$SCRIPT_DIR/007_claude_tasks.sql"
64
65
  # ─── Migration 008: Summary UPSERT (unique session_id) ───
65
66
  run_migration "008_summary_upsert" "$SCRIPT_DIR/008_summary_upsert.sql"
66
67
 
68
+ # ─── Migration 009: Drop dead tasks table ────────────────
69
+ run_migration "009_drop_dead_tasks" "$SCRIPT_DIR/009_drop_dead_tasks.sql"
70
+
67
71
  echo " Eagle Mem database ready: $DB"
package/db/schema.sql CHANGED
@@ -75,28 +75,6 @@ CREATE TABLE IF NOT EXISTS summaries (
75
75
  CREATE INDEX IF NOT EXISTS idx_summaries_session ON summaries(session_id);
76
76
  CREATE INDEX IF NOT EXISTS idx_summaries_project ON summaries(project);
77
77
 
78
- -- ─── Tasks (TaskAware Compact Loop) ───────────────────────
79
- -- Subtasks for multi-step work with compaction between each
80
-
81
- CREATE TABLE IF NOT EXISTS tasks (
82
- id INTEGER PRIMARY KEY AUTOINCREMENT,
83
- project TEXT NOT NULL,
84
- session_id TEXT REFERENCES sessions(id),
85
- parent_id INTEGER REFERENCES tasks(id),
86
- title TEXT NOT NULL,
87
- instructions TEXT,
88
- context_snapshot TEXT,
89
- status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'active', 'done', 'blocked', 'cancelled')),
90
- ordinal INTEGER NOT NULL DEFAULT 0,
91
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
92
- started_at TEXT,
93
- completed_at TEXT
94
- );
95
-
96
- CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project);
97
- CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
98
- CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_id);
99
-
100
78
  -- ─── FTS5: Full-text search on summaries ───────────────────
101
79
 
102
80
  CREATE VIRTUAL TABLE IF NOT EXISTS summaries_fts USING fts5(
@@ -127,30 +105,3 @@ CREATE TRIGGER IF NOT EXISTS summaries_au AFTER UPDATE ON summaries BEGIN
127
105
  INSERT INTO summaries_fts(rowid, request, investigated, learned, completed, next_steps, notes)
128
106
  VALUES (new.id, new.request, new.investigated, new.learned, new.completed, new.next_steps, new.notes);
129
107
  END;
130
-
131
- -- ─── FTS5: Full-text search on tasks ──────────────────────
132
-
133
- CREATE VIRTUAL TABLE IF NOT EXISTS tasks_fts USING fts5(
134
- title,
135
- instructions,
136
- context_snapshot,
137
- content='tasks',
138
- content_rowid='id'
139
- );
140
-
141
- CREATE TRIGGER IF NOT EXISTS tasks_ai AFTER INSERT ON tasks BEGIN
142
- INSERT INTO tasks_fts(rowid, title, instructions, context_snapshot)
143
- VALUES (new.id, new.title, new.instructions, new.context_snapshot);
144
- END;
145
-
146
- CREATE TRIGGER IF NOT EXISTS tasks_ad AFTER DELETE ON tasks BEGIN
147
- INSERT INTO tasks_fts(tasks_fts, rowid, title, instructions, context_snapshot)
148
- VALUES ('delete', old.id, old.title, old.instructions, old.context_snapshot);
149
- END;
150
-
151
- CREATE TRIGGER IF NOT EXISTS tasks_au AFTER UPDATE ON tasks BEGIN
152
- INSERT INTO tasks_fts(tasks_fts, rowid, title, instructions, context_snapshot)
153
- VALUES ('delete', old.id, old.title, old.instructions, old.context_snapshot);
154
- INSERT INTO tasks_fts(rowid, title, instructions, context_snapshot)
155
- VALUES (new.id, new.title, new.instructions, new.context_snapshot);
156
- END;
@@ -83,7 +83,11 @@ esac
83
83
  case "$tool_name" in
84
84
  Write|Edit)
85
85
  if [ -n "$fp" ]; then
86
+ # Reject path traversal: bash case `*` matches `/`, so
87
+ # patterns like projects/*/memory/*.md would match paths
88
+ # containing /../ segments. Block any path with `..` first.
86
89
  case "$fp" in
90
+ *..*) ;; # path traversal — skip
87
91
  "$HOME/.claude/projects"/*/memory/*.md)
88
92
  mem_base=$(basename "$fp")
89
93
  if [ "$mem_base" != "MEMORY.md" ] && [ -f "$fp" ]; then
@@ -31,8 +31,10 @@ eagle_log "INFO" "SessionStart: session=$session_id project=$project source=$sou
31
31
  eagle_upsert_session "$session_id" "$project" "$cwd" "$model" "$source_type"
32
32
 
33
33
  # ─── Sweep stuck sessions (older than 4 hours) ─────────────
34
+ # Exclude the current session — it may be a resumed session older than 4h
34
35
  eagle_db "UPDATE sessions SET status = 'abandoned'
35
36
  WHERE status = 'active'
37
+ AND id != '$(eagle_sql_escape "$session_id")'
36
38
  AND started_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-4 hours');"
37
39
 
38
40
  # ─── Build context injection ────────────────────────────────
@@ -80,57 +82,6 @@ Next steps: $next_steps"
80
82
  "
81
83
  fi
82
84
 
83
- # Pending tasks from TaskAware loop
84
- pending_tasks=$(eagle_get_pending_tasks "$project")
85
-
86
- if [ -n "$pending_tasks" ]; then
87
- context+="
88
- === EAGLE MEM — Tasks ===
89
- Pending tasks for '$project':
90
- "
91
- first_pending=""
92
- while IFS='|' read -r tid title instructions status _ordinal; do
93
- [ -z "$tid" ] && continue
94
- local_marker=""
95
- if [ "$status" = "active" ]; then
96
- local_marker=" [ACTIVE]"
97
- elif [ -z "$first_pending" ]; then
98
- local_marker=" [NEXT]"
99
- first_pending="$tid"
100
- fi
101
- context+=" $tid. $title$local_marker"
102
- [ -n "$instructions" ] && context+=" — $instructions"
103
- context+="
104
- "
105
- done <<< "$pending_tasks"
106
-
107
- # Load context snapshot for the active/next task
108
- active_task=$(eagle_get_active_task "$project")
109
-
110
- if [ -z "$active_task" ] && [ -n "$first_pending" ]; then
111
- # Auto-activate the next pending task (atomic to prevent races
112
- # when multiple sessions start simultaneously)
113
- eagle_db "UPDATE tasks SET status = 'active', started_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
114
- WHERE id = $first_pending
115
- AND status = 'pending'
116
- AND NOT EXISTS (SELECT 1 FROM tasks WHERE project = '$(eagle_sql_escape "$project")' AND status = 'active');"
117
- active_task=$(eagle_get_active_task "$project")
118
- fi
119
-
120
- if [ -n "$active_task" ]; then
121
- IFS='|' read -r atid atitle ainstructions asnapshot <<< "$active_task"
122
- context+="
123
- Current task (#$atid): $atitle
124
- "
125
- [ -n "$ainstructions" ] && context+="Instructions: $ainstructions
126
- "
127
- [ -n "$asnapshot" ] && context+="Context: $asnapshot
128
- "
129
- context+="When done, tell the user to run /compact so Eagle Mem can save progress and load the next task.
130
- "
131
- fi
132
- fi
133
-
134
85
  # ─── Mirrored Claude memories ──────────────────────────────
135
86
 
136
87
  memories=$(eagle_list_claude_memories "$project" 5)
@@ -161,21 +112,28 @@ Recent plans for '$project':
161
112
  done <<< "$plans"
162
113
  fi
163
114
 
164
- # ─── Mirrored Claude tasks (synced) ──────────────────────
115
+ # ─── Claude Code tasks ───────────────────────────────────
165
116
 
166
- synced_tasks=$(eagle_db "SELECT subject, status FROM claude_tasks
117
+ synced_tasks=$(eagle_db "SELECT subject, status, blocked_by FROM claude_tasks
167
118
  WHERE project = '$(eagle_sql_escape "$project")'
168
119
  AND status IN ('in_progress', 'pending')
169
- AND updated_at > datetime('now', '-7 days')
170
- ORDER BY updated_at DESC LIMIT 5;")
120
+ AND updated_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-7 days')
121
+ ORDER BY
122
+ CASE status WHEN 'in_progress' THEN 0 ELSE 1 END,
123
+ updated_at DESC
124
+ LIMIT 10;")
171
125
  if [ -n "$synced_tasks" ]; then
172
126
  context+="
173
- === EAGLE MEM — Synced Tasks ===
174
- In-progress/pending tasks for '$project':
127
+ === EAGLE MEM — Tasks ===
128
+ Tasks for '$project':
175
129
  "
176
- while IFS='|' read -r tsubject tstatus; do
130
+ while IFS='|' read -r tsubject tstatus tblocked; do
177
131
  [ -z "$tsubject" ] && continue
178
- context+=" - [$tstatus] $tsubject
132
+ local_marker=""
133
+ if [ "$tblocked" != "[]" ] && [ -n "$tblocked" ]; then
134
+ local_marker=" (blocked)"
135
+ fi
136
+ context+=" - [$tstatus] $tsubject$local_marker
179
137
  "
180
138
  done <<< "$synced_tasks"
181
139
  fi
@@ -188,8 +146,7 @@ You have persistent memory powered by Eagle Mem. When you recall context from a
188
146
  IMPORTANT: At the start of your VERY NEXT response (this fires on session start, /clear, AND context compaction — always show this block, even if you think you showed it before, because prior context may have been compressed away). Show the user what Eagle Mem loaded using this exact format:
189
147
 
190
148
  \`\`\`
191
- █▀▀ ▄▀█ █▀▀ █ █▀▀ █▀▄▀█ █▀▀ █▀▄▀█
192
- ██▄ █▀█ █▄█ █▄▄ ██▄ █ ▀ █ ██▄ █ ▀ █
149
+ $eagle_logo
193
150
 
194
151
  Project: <project name>
195
152
  Sessions: N recent | Memories: N | Tasks: N pending
package/hooks/stop.sh CHANGED
@@ -141,12 +141,4 @@ if [ -n "$request" ] || [ -n "$completed" ] || [ -n "$learned" ]; then
141
141
  eagle_log "INFO" "Stop: summary saved for session=$session_id"
142
142
  fi
143
143
 
144
- # Mark active task as done only when Claude explicitly provided a summary
145
- if [ -n "$summary_block" ] && [ -n "$completed" ]; then
146
- completed_task_id=$(eagle_complete_active_task "$project")
147
- if [ -n "$completed_task_id" ]; then
148
- eagle_log "INFO" "Stop: marked task #$completed_task_id as done"
149
- fi
150
- fi
151
-
152
144
  exit 0
package/lib/common.sh CHANGED
@@ -46,6 +46,8 @@ eagle_fts_sanitize() {
46
46
  # Claude Code session IDs are UUIDs or hex strings — reject anything else.
47
47
  eagle_validate_session_id() {
48
48
  local sid="$1"
49
+ # Length cap: Claude Code IDs are UUIDs/hex (36-64 chars). Reject oversized input.
50
+ [ ${#sid} -gt 128 ] && return 1
49
51
  [[ "$sid" =~ ^[A-Za-z0-9_-]+$ ]]
50
52
  }
51
53
 
package/lib/db.sh CHANGED
@@ -18,7 +18,7 @@ eagle_db() {
18
18
  }
19
19
 
20
20
  eagle_db_pipe() {
21
- { echo "$EAGLE_DB_SETUP"; cat; } | sqlite3 "$EAGLE_MEM_DB" 2>>"$EAGLE_MEM_LOG"
21
+ { echo "$EAGLE_DB_SETUP"; echo ".bail on"; cat; } | sqlite3 "$EAGLE_MEM_DB" 2>>"$EAGLE_MEM_LOG"
22
22
  }
23
23
 
24
24
  eagle_db_json() {
@@ -45,7 +45,8 @@ eagle_upsert_session() {
45
45
  ON CONFLICT(id) DO UPDATE SET
46
46
  cwd = COALESCE(excluded.cwd, sessions.cwd),
47
47
  model = COALESCE(excluded.model, sessions.model),
48
- source = COALESCE(excluded.source, sessions.source);"
48
+ source = COALESCE(excluded.source, sessions.source),
49
+ status = 'active';"
49
50
  }
50
51
 
51
52
  eagle_end_session() {
@@ -78,7 +79,7 @@ eagle_insert_summary() {
78
79
  local notes; notes=$(eagle_sql_escape "${10:-}")
79
80
 
80
81
  eagle_db_pipe <<SQL
81
- INSERT OR REPLACE INTO summaries (session_id, project, request, investigated, learned, completed, next_steps, files_read, files_modified, notes)
82
+ INSERT INTO summaries (session_id, project, request, investigated, learned, completed, next_steps, files_read, files_modified, notes)
82
83
  VALUES (
83
84
  '$session_id',
84
85
  '$project',
@@ -90,7 +91,17 @@ VALUES (
90
91
  '$files_read',
91
92
  '$files_modified',
92
93
  '$notes'
93
- );
94
+ )
95
+ ON CONFLICT(session_id) DO UPDATE SET
96
+ project = excluded.project,
97
+ request = COALESCE(NULLIF(excluded.request, ''), summaries.request),
98
+ investigated = COALESCE(NULLIF(excluded.investigated, ''), summaries.investigated),
99
+ learned = COALESCE(NULLIF(excluded.learned, ''), summaries.learned),
100
+ completed = COALESCE(NULLIF(excluded.completed, ''), summaries.completed),
101
+ next_steps = COALESCE(NULLIF(excluded.next_steps, ''), summaries.next_steps),
102
+ files_read = COALESCE(NULLIF(excluded.files_read, '[]'), summaries.files_read),
103
+ files_modified = COALESCE(NULLIF(excluded.files_modified, '[]'), summaries.files_modified),
104
+ notes = COALESCE(NULLIF(excluded.notes, ''), summaries.notes);
94
105
  SQL
95
106
  }
96
107
 
@@ -107,7 +118,8 @@ eagle_get_recent_summaries() {
107
118
  }
108
119
 
109
120
  eagle_search_summaries() {
110
- local query; query=$(eagle_sql_escape "$1")
121
+ local query; query=$(eagle_fts_sanitize "$1")
122
+ query=$(eagle_sql_escape "$query")
111
123
  local project="${2:-}"
112
124
  local limit; limit=$(eagle_sql_int "${3:-10}")
113
125
 
@@ -126,25 +138,6 @@ eagle_search_summaries() {
126
138
  LIMIT $limit;"
127
139
  }
128
140
 
129
- eagle_get_pending_tasks() {
130
- local project; project=$(eagle_sql_escape "$1")
131
-
132
- eagle_db "SELECT id, title, instructions, status, ordinal
133
- FROM tasks
134
- WHERE project = '$project' AND status IN ('pending', 'active')
135
- ORDER BY ordinal ASC, id ASC;"
136
- }
137
-
138
- eagle_get_next_task() {
139
- local project; project=$(eagle_sql_escape "$1")
140
-
141
- eagle_db "SELECT id, title, instructions, context_snapshot
142
- FROM tasks
143
- WHERE project = '$project' AND status = 'pending'
144
- ORDER BY ordinal ASC, id ASC
145
- LIMIT 1;"
146
- }
147
-
148
141
  eagle_observation_exists() {
149
142
  local session_id; session_id=$(eagle_sql_escape "$1")
150
143
  local tool_name; tool_name=$(eagle_sql_escape "$2")
@@ -174,33 +167,9 @@ eagle_get_overview() {
174
167
  eagle_db "SELECT content FROM overviews WHERE project = '$project';"
175
168
  }
176
169
 
177
- eagle_activate_task() {
178
- local task_id; task_id=$(eagle_sql_int "$1")
179
- eagle_db "UPDATE tasks SET status = 'active', started_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = $task_id;"
180
- }
181
-
182
- eagle_complete_active_task() {
183
- local project; project=$(eagle_sql_escape "$1")
184
- local active_id
185
- active_id=$(eagle_db "SELECT id FROM tasks WHERE project = '$project' AND status = 'active' LIMIT 1;")
186
- if [ -n "$active_id" ]; then
187
- local safe_id; safe_id=$(eagle_sql_int "$active_id")
188
- eagle_db "UPDATE tasks SET status = 'done', completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = $safe_id;"
189
- echo "$active_id"
190
- fi
191
- }
192
-
193
- eagle_get_active_task() {
194
- local project; project=$(eagle_sql_escape "$1")
195
- eagle_db "SELECT id, title, instructions, context_snapshot
196
- FROM tasks
197
- WHERE project = '$project' AND status = 'active'
198
- ORDER BY ordinal ASC, id ASC
199
- LIMIT 1;"
200
- }
201
-
202
170
  eagle_search_code_chunks() {
203
- local query; query=$(eagle_sql_escape "$1")
171
+ local query; query=$(eagle_fts_sanitize "$1")
172
+ query=$(eagle_sql_escape "$query")
204
173
  local project; project=$(eagle_sql_escape "$2")
205
174
  local limit; limit=$(eagle_sql_int "${3:-5}")
206
175
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eagle-mem",
3
- "version": "1.5.1",
3
+ "version": "1.6.1",
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
@@ -8,7 +8,7 @@ PACKAGE_DIR="$(cd "$SCRIPTS_DIR/.." && pwd)"
8
8
 
9
9
  . "$SCRIPTS_DIR/style.sh"
10
10
 
11
- version=$(node -e "console.log(require('$PACKAGE_DIR/package.json').version)" 2>/dev/null || echo "unknown")
11
+ version=$(jq -r .version "$PACKAGE_DIR/package.json" 2>/dev/null || echo "unknown")
12
12
 
13
13
  eagle_banner
14
14
 
@@ -20,7 +20,7 @@ echo -e " eagle-mem ${CYAN}<command>${RESET}"
20
20
  echo ""
21
21
  echo -e " ${BOLD}Commands:${RESET}"
22
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)"
23
+ echo -e " ${CYAN}tasks${RESET} View mirrored Claude Code tasks"
24
24
  echo -e " ${CYAN}overview${RESET} View or set project overview"
25
25
  echo -e " ${CYAN}memories${RESET} Browse and search Claude Code memories, plans, and tasks"
26
26
  echo -e " ${CYAN}scan${RESET} Analyze a project and generate an overview"
@@ -35,8 +35,8 @@ echo ""
35
35
  echo -e " ${BOLD}Examples:${RESET}"
36
36
  echo -e " ${DIM}\$${RESET} eagle-mem search \"auth bug\" ${DIM}# Search memory${RESET}"
37
37
  echo -e " ${DIM}\$${RESET} eagle-mem search --timeline ${DIM}# Recent sessions${RESET}"
38
- echo -e " ${DIM}\$${RESET} eagle-mem tasks ${DIM}# List tasks${RESET}"
39
- echo -e " ${DIM}\$${RESET} eagle-mem tasks add \"Fix X\" ${DIM}# Add a task${RESET}"
38
+ echo -e " ${DIM}\$${RESET} eagle-mem tasks ${DIM}# View pending tasks${RESET}"
39
+ echo -e " ${DIM}\$${RESET} eagle-mem tasks search \"X\" ${DIM}# Search task history${RESET}"
40
40
  echo -e " ${DIM}\$${RESET} eagle-mem overview ${DIM}# View overview${RESET}"
41
41
  echo -e " ${DIM}\$${RESET} eagle-mem scan . ${DIM}# Scan current project${RESET}"
42
42
  echo -e " ${DIM}\$${RESET} eagle-mem index . ${DIM}# Index source files${RESET}"
@@ -53,11 +53,11 @@ echo -e " ${DOT} Injects relevant memory at session start"
53
53
  echo -e " ${DOT} Searches past sessions when you ask related questions"
54
54
  echo -e " ${DOT} Tracks file operations across sessions"
55
55
  echo -e " ${DOT} Mirrors Claude Code memories, plans, and tasks into FTS5-searchable storage"
56
- echo -e " ${DOT} Provides task management for complex multi-step work"
56
+ echo -e " ${DOT} Uses Claude Code's native task system (TaskCreate/TaskUpdate) for multi-step work"
57
57
  echo ""
58
58
  echo -e " ${BOLD}Skills${RESET} ${DIM}(available inside Claude Code):${RESET}"
59
59
  echo -e " ${CYAN}/eagle-mem-search${RESET} Search past sessions and observations"
60
- echo -e " ${CYAN}/eagle-mem-tasks${RESET} Break work into tracked subtasks"
60
+ echo -e " ${CYAN}/eagle-mem-tasks${RESET} Break work into Claude Code tasks with dependencies"
61
61
  echo -e " ${CYAN}/eagle-mem-overview${RESET} Generate a persistent project summary"
62
62
  echo -e " ${CYAN}/eagle-mem-index${RESET} Index source files for code search"
63
63
  echo -e " ${CYAN}/eagle-mem-scan${RESET} Scan and analyze a project"
package/scripts/index.sh CHANGED
@@ -104,7 +104,6 @@ fi
104
104
  project_sql=$(eagle_sql_escape "$PROJECT")
105
105
  NEEDS_INDEX="$TMPDIR_IDX/needs_index"
106
106
 
107
- indexed_count=0
108
107
  skipped_count=0
109
108
 
110
109
  while IFS= read -r file; do
package/scripts/prune.sh CHANGED
@@ -15,6 +15,33 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
15
15
 
16
16
  eagle_ensure_db
17
17
 
18
+ # ─── Help ────────────────────────────────────────────────
19
+
20
+ show_help() {
21
+ echo -e " ${BOLD}eagle-mem prune${RESET} — Clean up old data"
22
+ echo ""
23
+ echo -e " ${BOLD}Usage:${RESET}"
24
+ echo -e " eagle-mem prune ${DIM}# prune observations > 90 days${RESET}"
25
+ echo -e " eagle-mem prune ${CYAN}--days 30${RESET} ${DIM}# prune observations > 30 days${RESET}"
26
+ echo -e " eagle-mem prune ${CYAN}--dry-run${RESET} ${DIM}# show what would be pruned${RESET}"
27
+ echo ""
28
+ echo -e " ${BOLD}What gets pruned:${RESET}"
29
+ echo -e " ${DOT} Observations older than --days (default: 90)"
30
+ echo -e " ${DOT} Code chunks for files that no longer exist"
31
+ echo ""
32
+ echo -e " ${BOLD}What is preserved:${RESET}"
33
+ echo -e " ${DOT} All sessions and summaries (your session history)"
34
+ echo -e " ${DOT} All tasks"
35
+ echo -e " ${DOT} Project overviews"
36
+ echo ""
37
+ echo -e " ${BOLD}Options:${RESET}"
38
+ echo -e " ${CYAN}-d, --days${RESET} <N> Age threshold (default: 90)"
39
+ echo -e " ${CYAN}-p, --project${RESET} <name> Only prune for this project"
40
+ echo -e " ${CYAN}-n, --dry-run${RESET} Show counts without deleting"
41
+ echo ""
42
+ exit 0
43
+ }
44
+
18
45
  # ─── Parse arguments ──────────────────────────────────────
19
46
 
20
47
  days=90
@@ -26,30 +53,7 @@ while [ $# -gt 0 ]; do
26
53
  --days|-d) days="$2"; shift 2 ;;
27
54
  --project|-p) project="$2"; shift 2 ;;
28
55
  --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
- ;;
56
+ --help|-h) show_help ;;
53
57
  *)
54
58
  eagle_err "Unknown option: $1"
55
59
  exit 1
@@ -109,7 +113,6 @@ if [ -n "$projects" ]; then
109
113
 
110
114
  if [ -n "$proj_cwd" ] && [ -d "$proj_cwd" ]; then
111
115
  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
116
  # Count files that no longer exist
114
117
  orphans=0
115
118
  paths=$(eagle_db "SELECT DISTINCT file_path FROM code_chunks WHERE project = '$(eagle_sql_escape "$proj")';")
package/scripts/search.sh CHANGED
@@ -14,6 +14,27 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
14
14
 
15
15
  eagle_ensure_db
16
16
 
17
+ # ─── Help ────────────────────────────────────────────────
18
+
19
+ show_help() {
20
+ echo -e " ${BOLD}eagle-mem search${RESET} — Search persistent memory"
21
+ echo ""
22
+ echo -e " ${BOLD}Usage:${RESET}"
23
+ echo -e " eagle-mem search ${CYAN}<query>${RESET} ${DIM}# keyword search${RESET}"
24
+ echo -e " eagle-mem search ${CYAN}--timeline${RESET} ${DIM}# recent sessions${RESET}"
25
+ echo -e " eagle-mem search ${CYAN}--session <id>${RESET} ${DIM}# session details${RESET}"
26
+ echo -e " eagle-mem search ${CYAN}--files${RESET} ${DIM}# frequently modified files${RESET}"
27
+ echo -e " eagle-mem search ${CYAN}--stats${RESET} ${DIM}# project statistics${RESET}"
28
+ echo ""
29
+ echo -e " ${BOLD}Options:${RESET}"
30
+ echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current dir)"
31
+ echo -e " ${CYAN}-n, --limit${RESET} <N> Max results (default: 10)"
32
+ echo -e " ${CYAN}-a, --all${RESET} Search across all projects"
33
+ echo -e " ${CYAN}-j, --json${RESET} Output as JSON"
34
+ echo ""
35
+ exit 0
36
+ }
37
+
17
38
  # ─── Parse arguments ──────────────────────────────────────
18
39
 
19
40
  mode="keyword"
@@ -34,24 +55,7 @@ while [ $# -gt 0 ]; do
34
55
  --limit|-n) limit="$2"; shift 2 ;;
35
56
  --all|-a) cross_project=true; shift ;;
36
57
  --json|-j) json_output=true; shift ;;
37
- --help|-h)
38
- echo -e " ${BOLD}eagle-mem search${RESET} — Search persistent memory"
39
- echo ""
40
- echo -e " ${BOLD}Usage:${RESET}"
41
- echo -e " eagle-mem search ${CYAN}<query>${RESET} ${DIM}# keyword search${RESET}"
42
- echo -e " eagle-mem search ${CYAN}--timeline${RESET} ${DIM}# recent sessions${RESET}"
43
- echo -e " eagle-mem search ${CYAN}--session <id>${RESET} ${DIM}# session details${RESET}"
44
- echo -e " eagle-mem search ${CYAN}--files${RESET} ${DIM}# frequently modified files${RESET}"
45
- echo -e " eagle-mem search ${CYAN}--stats${RESET} ${DIM}# project statistics${RESET}"
46
- echo ""
47
- echo -e " ${BOLD}Options:${RESET}"
48
- echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current dir)"
49
- echo -e " ${CYAN}-n, --limit${RESET} <N> Max results (default: 10)"
50
- echo -e " ${CYAN}-a, --all${RESET} Search across all projects"
51
- echo -e " ${CYAN}-j, --json${RESET} Output as JSON"
52
- echo ""
53
- exit 0
54
- ;;
58
+ --help|-h) show_help ;;
55
59
  -*)
56
60
  eagle_err "Unknown option: $1"
57
61
  exit 1
@@ -249,7 +253,7 @@ search_stats() {
249
253
  sessions=$(eagle_db "SELECT COUNT(*) FROM sessions WHERE project = '$p';")
250
254
  summaries=$(eagle_db "SELECT COUNT(*) FROM summaries WHERE project = '$p';")
251
255
  observations=$(eagle_db "SELECT COUNT(*) FROM observations o JOIN sessions s ON s.id = o.session_id WHERE s.project = '$p';")
252
- tasks=$(eagle_db "SELECT COUNT(*) FROM tasks WHERE project = '$p';")
256
+ tasks=$(eagle_db "SELECT COUNT(*) FROM claude_tasks WHERE project = '$p';")
253
257
  local chunks
254
258
  chunks=$(eagle_db "SELECT COUNT(*) FROM code_chunks WHERE project = '$p';")
255
259
 
package/scripts/tasks.sh CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env bash
2
2
  # ═══════════════════════════════════════════════════════════
3
- # Eagle Mem — Tasks
4
- # CLI wrapper for task management (replaces raw SQL in skills)
3
+ # Eagle Mem — Tasks (read-only viewer of Claude Code tasks)
4
+ # Claude Code manages task state via TaskCreate/TaskUpdate;
5
+ # Eagle Mem mirrors it and displays it here.
5
6
  # ═══════════════════════════════════════════════════════════
6
7
  set -euo pipefail
7
8
 
@@ -22,28 +23,26 @@ shift 2>/dev/null || true
22
23
  project=""
23
24
  json_output=false
24
25
 
25
- # Extract global options from remaining args
26
- args=()
27
-
28
26
  show_help() {
29
- echo -e " ${BOLD}eagle-mem tasks${RESET} — Manage tracked tasks"
27
+ echo -e " ${BOLD}eagle-mem tasks${RESET} — View mirrored Claude Code tasks"
30
28
  echo ""
31
29
  echo -e " ${BOLD}Usage:${RESET}"
32
- echo -e " eagle-mem tasks ${DIM}# list pending tasks${RESET}"
33
- echo -e " eagle-mem tasks ${CYAN}list${RESET} ${DIM}# list all tasks${RESET}"
34
- echo -e " eagle-mem tasks ${CYAN}add${RESET} <title> [instructions] ${DIM}# add a task${RESET}"
35
- echo -e " eagle-mem tasks ${CYAN}done${RESET} <id> ${DIM}# mark task complete${RESET}"
36
- echo -e " eagle-mem tasks ${CYAN}block${RESET} <id> ${DIM}# mark task blocked${RESET}"
37
- echo -e " eagle-mem tasks ${CYAN}context${RESET} <id> <snapshot> ${DIM}# set task context${RESET}"
38
- echo -e " eagle-mem tasks ${CYAN}clear${RESET} ${DIM}# remove all done tasks${RESET}"
30
+ echo -e " eagle-mem tasks ${DIM}# list pending/in-progress tasks${RESET}"
31
+ echo -e " eagle-mem tasks ${CYAN}list${RESET} ${DIM}# list all tasks${RESET}"
32
+ echo -e " eagle-mem tasks ${CYAN}completed${RESET} ${DIM}# list completed tasks${RESET}"
33
+ echo -e " eagle-mem tasks ${CYAN}search${RESET} <query> ${DIM}# search tasks by keyword${RESET}"
39
34
  echo ""
40
35
  echo -e " ${BOLD}Options:${RESET}"
41
36
  echo -e " ${CYAN}-p, --project${RESET} <name> Project name (default: current dir)"
42
37
  echo -e " ${CYAN}-j, --json${RESET} Output as JSON"
43
38
  echo ""
39
+ echo -e " ${DIM}Tasks are managed by Claude Code (TaskCreate/TaskUpdate).${RESET}"
40
+ echo -e " ${DIM}Eagle Mem automatically mirrors them for cross-session recall.${RESET}"
41
+ echo ""
44
42
  exit 0
45
43
  }
46
44
 
45
+ args=()
47
46
  while [ $# -gt 0 ]; do
48
47
  case "$1" in
49
48
  --project|-p) project="$2"; shift 2 ;;
@@ -63,24 +62,28 @@ tasks_list() {
63
62
 
64
63
  local where_status=""
65
64
  case "$filter" in
66
- pending) where_status="AND status IN ('pending', 'active')" ;;
67
- done) where_status="AND status = 'done'" ;;
68
- all) where_status="" ;;
65
+ pending) where_status="AND status IN ('pending', 'in_progress')" ;;
66
+ completed) where_status="AND status = 'completed'" ;;
67
+ all) where_status="" ;;
69
68
  esac
70
69
 
71
70
  if [ "$json_output" = true ]; then
72
- eagle_db_json "SELECT id, title, instructions, status, ordinal, context_snapshot, completed_at, created_at
73
- FROM tasks
71
+ eagle_db_json "SELECT source_task_id, subject, description, status, blocks, blocked_by, updated_at
72
+ FROM claude_tasks
74
73
  WHERE project = '$project_sql' $where_status
75
- ORDER BY ordinal ASC, id ASC;"
74
+ ORDER BY updated_at DESC
75
+ LIMIT 20;"
76
76
  return
77
77
  fi
78
78
 
79
79
  local results
80
- results=$(eagle_db "SELECT id, title, status, ordinal, instructions
81
- FROM tasks
80
+ results=$(eagle_db "SELECT source_task_id, subject, status, blocked_by, description
81
+ FROM claude_tasks
82
82
  WHERE project = '$project_sql' $where_status
83
- ORDER BY ordinal ASC, id ASC;")
83
+ ORDER BY
84
+ CASE status WHEN 'in_progress' THEN 0 WHEN 'pending' THEN 1 ELSE 2 END,
85
+ updated_at DESC
86
+ LIMIT 20;")
84
87
 
85
88
  if [ -z "$results" ]; then
86
89
  eagle_dim "No tasks for project '$project'"
@@ -92,170 +95,74 @@ tasks_list() {
92
95
  echo -e " ${DIM}─────────────────────────────────────${RESET}"
93
96
  echo ""
94
97
 
95
- while IFS='|' read -r tid title status ordinal instructions; do
98
+ while IFS='|' read -r tid subject status blocked_by desc; do
96
99
  [ -z "$tid" ] && continue
97
100
  local icon marker
98
101
  case "$status" in
99
- active) icon="${CYAN}▶${RESET}"; marker=" ${CYAN}[ACTIVE]${RESET}" ;;
100
- pending) icon="${DIM}○${RESET}"; marker="" ;;
101
- done) icon="${GREEN}✓${RESET}"; marker=" ${DIM}[DONE]${RESET}" ;;
102
- blocked) icon="${RED}✗${RESET}"; marker=" ${RED}[BLOCKED]${RESET}" ;;
103
- *) icon="$DOT"; marker="" ;;
102
+ in_progress) icon="${CYAN}>${RESET}"; marker=" ${CYAN}[in_progress]${RESET}" ;;
103
+ pending) icon="${DIM}o${RESET}"; marker="" ;;
104
+ completed) icon="${GREEN}+${RESET}"; marker=" ${DIM}[completed]${RESET}" ;;
105
+ deleted) icon="${RED}x${RESET}"; marker=" ${RED}[deleted]${RESET}" ;;
106
+ *) icon="$DOT"; marker="" ;;
104
107
  esac
105
- echo -e " ${icon} ${BOLD}#$tid${RESET} $title$marker"
106
- [ -n "$instructions" ] && echo -e " ${DIM}$instructions${RESET}"
108
+ if [ "$blocked_by" != "[]" ] && [ -n "$blocked_by" ]; then
109
+ marker+=" ${DIM}(blocked)${RESET}"
110
+ fi
111
+ echo -e " ${icon} ${BOLD}$tid${RESET} $subject$marker"
112
+ [ -n "$desc" ] && echo -e " ${DIM}$(echo "$desc" | cut -c1-80)${RESET}"
107
113
  done <<< "$results"
108
114
  echo ""
109
115
  }
110
116
 
111
- # ─── Add task ─────────────────────────────────────────────
112
-
113
- tasks_add() {
114
- local title="${args[0]:-}"
115
- local instructions="${args[1]:-}"
116
-
117
- if [ -z "$title" ]; then
118
- eagle_err "Usage: eagle-mem tasks add <title> [instructions]"
119
- exit 1
120
- fi
121
-
122
- local title_sql; title_sql=$(eagle_sql_escape "$title")
123
- local instr_sql; instr_sql=$(eagle_sql_escape "$instructions")
124
-
125
- local max_ord
126
- max_ord=$(eagle_db "SELECT COALESCE(MAX(ordinal), 0) FROM tasks WHERE project = '$project_sql';")
127
- local next_ord=$((max_ord + 1))
128
-
129
- local new_id
130
- new_id=$(eagle_db "INSERT INTO tasks (project, title, instructions, ordinal)
131
- VALUES ('$project_sql', '$title_sql', '$instr_sql', $next_ord);
132
- SELECT last_insert_rowid();")
133
-
134
- if [ "$json_output" = true ]; then
135
- jq -nc --arg id "$new_id" --arg title "$title" --argjson ord "$next_ord" \
136
- '{id: ($id | tonumber), title: $title, ordinal: $ord}'
137
- return
138
- fi
139
-
140
- eagle_ok "Task #$new_id added: $title"
141
- }
142
-
143
- # ─── Done ─────────────────────────────────────────────────
144
-
145
- tasks_done() {
146
- local task_id="${args[0]:-}"
117
+ # ─── Search tasks ─────────────────────────────────────────
147
118
 
148
- if [ -z "$task_id" ]; then
149
- eagle_err "Usage: eagle-mem tasks done <id>"
119
+ tasks_search() {
120
+ local query="${args[0]:-}"
121
+ if [ -z "$query" ]; then
122
+ eagle_err "Usage: eagle-mem tasks search <query>"
150
123
  exit 1
151
124
  fi
152
125
 
153
- if ! [[ "$task_id" =~ ^[0-9]+$ ]]; then
154
- eagle_err "Task ID must be a number, got: $task_id"
155
- exit 1
156
- fi
157
-
158
- local changed
159
- changed=$(eagle_db "UPDATE tasks SET status = 'done', completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
160
- WHERE id = $task_id AND project = '$project_sql';
161
- SELECT changes();")
162
-
163
- if [ "${changed:-0}" -eq 0 ]; then
164
- eagle_err "Task #$task_id not found in project '$project'"
165
- exit 1
166
- fi
167
-
168
- local title
169
- title=$(eagle_db "SELECT title FROM tasks WHERE id = $task_id AND project = '$project_sql';")
170
-
171
126
  if [ "$json_output" = true ]; then
172
- jq -nc --arg id "$task_id" '{id: ($id | tonumber), status: "done"}'
127
+ local query_sql; query_sql=$(eagle_fts_sanitize "$query")
128
+ query_sql=$(eagle_sql_escape "$query_sql")
129
+ eagle_db_json "SELECT t.source_task_id, t.subject, t.status, t.description, t.updated_at
130
+ FROM claude_tasks t
131
+ JOIN claude_tasks_fts f ON f.rowid = t.id
132
+ WHERE claude_tasks_fts MATCH '$query_sql'
133
+ AND t.project = '$project_sql'
134
+ ORDER BY rank
135
+ LIMIT 10;"
173
136
  return
174
137
  fi
175
138
 
176
- eagle_ok "Task #$task_id done: $title"
177
- }
178
-
179
- # ─── Block ────────────────────────────────────────────────
180
-
181
- tasks_block() {
182
- local task_id="${args[0]:-}"
183
-
184
- if [ -z "$task_id" ]; then
185
- eagle_err "Usage: eagle-mem tasks block <id>"
186
- exit 1
187
- fi
188
-
189
- if ! [[ "$task_id" =~ ^[0-9]+$ ]]; then
190
- eagle_err "Task ID must be a number, got: $task_id"
191
- exit 1
192
- fi
193
-
194
- local changed
195
- changed=$(eagle_db "UPDATE tasks SET status = 'blocked' WHERE id = $task_id AND project = '$project_sql';
196
- SELECT changes();")
197
-
198
- if [ "${changed:-0}" -eq 0 ]; then
199
- eagle_err "Task #$task_id not found in project '$project'"
200
- exit 1
201
- fi
202
-
203
- if [ "$json_output" = true ]; then
204
- jq -nc --arg id "$task_id" '{id: ($id | tonumber), status: "blocked"}'
205
- return
206
- fi
207
-
208
- eagle_ok "Task #$task_id blocked"
209
- }
210
-
211
- # ─── Context snapshot ─────────────────────────────────────
212
-
213
- tasks_context() {
214
- local task_id="${args[0]:-}"
215
- local snapshot="${args[1]:-}"
216
-
217
- if [ -z "$task_id" ] || [ -z "$snapshot" ]; then
218
- eagle_err "Usage: eagle-mem tasks context <id> <snapshot>"
219
- exit 1
220
- fi
221
-
222
- if ! [[ "$task_id" =~ ^[0-9]+$ ]]; then
223
- eagle_err "Task ID must be a number, got: $task_id"
224
- exit 1
225
- fi
226
-
227
- local snap_sql; snap_sql=$(eagle_sql_escape "$snapshot")
228
- local changed
229
- changed=$(eagle_db "UPDATE tasks SET context_snapshot = '$snap_sql' WHERE id = $task_id AND project = '$project_sql';
230
- SELECT changes();")
231
-
232
- if [ "${changed:-0}" -eq 0 ]; then
233
- eagle_err "Task #$task_id not found in project '$project'"
234
- exit 1
235
- fi
139
+ local results
140
+ local query_sql; query_sql=$(eagle_fts_sanitize "$query")
141
+ query_sql=$(eagle_sql_escape "$query_sql")
142
+ results=$(eagle_db "SELECT t.source_task_id, t.subject, t.status, t.description
143
+ FROM claude_tasks t
144
+ JOIN claude_tasks_fts f ON f.rowid = t.id
145
+ WHERE claude_tasks_fts MATCH '$query_sql'
146
+ AND t.project = '$project_sql'
147
+ ORDER BY rank
148
+ LIMIT 10;")
236
149
 
237
- if [ "$json_output" = true ]; then
238
- jq -nc --arg id "$task_id" '{id: ($id | tonumber), context_snapshot: true}'
150
+ if [ -z "$results" ]; then
151
+ eagle_dim "No tasks matching '$query'"
239
152
  return
240
153
  fi
241
154
 
242
- eagle_ok "Context saved for task #$task_id"
243
- }
244
-
245
- # ─── Clear done tasks ────────────────────────────────────
246
-
247
- tasks_clear() {
248
- local count
249
- count=$(eagle_db "SELECT COUNT(*) FROM tasks WHERE project = '$project_sql' AND status = 'done';")
250
-
251
- eagle_db "DELETE FROM tasks WHERE project = '$project_sql' AND status = 'done';"
252
-
253
- if [ "$json_output" = true ]; then
254
- jq -nc --argjson cleared "${count:-0}" '{cleared: $cleared}'
255
- return
256
- fi
155
+ echo ""
156
+ echo -e " ${BOLD}Task search:${RESET} $query"
157
+ echo -e " ${DIM}─────────────────────────────────────${RESET}"
158
+ echo ""
257
159
 
258
- eagle_ok "Cleared ${count:-0} completed tasks"
160
+ while IFS='|' read -r tid subject status desc; do
161
+ [ -z "$tid" ] && continue
162
+ echo -e " ${BOLD}$tid${RESET} [$status] $subject"
163
+ [ -n "$desc" ] && echo -e " ${DIM}$(echo "$desc" | cut -c1-80)${RESET}"
164
+ done <<< "$results"
165
+ echo ""
259
166
  }
260
167
 
261
168
  # ─── Dispatch ─────────────────────────────────────────────
@@ -263,12 +170,9 @@ tasks_clear() {
263
170
  case "$action" in
264
171
  list) tasks_list "all" ;;
265
172
  pending) tasks_list "pending" ;;
266
- add) tasks_add ;;
267
- done) tasks_done ;;
268
- block) tasks_block ;;
269
- context) tasks_context ;;
270
- clear) tasks_clear ;;
271
- --help|-h) show_help ;;
173
+ completed) tasks_list "completed" ;;
174
+ search) tasks_search ;;
175
+ --help|-h) show_help ;;
272
176
  *)
273
177
  eagle_err "Unknown action: $action"
274
178
  eagle_dim " Run 'eagle-mem tasks --help' for options"
package/scripts/update.sh CHANGED
@@ -117,5 +117,5 @@ fi
117
117
 
118
118
  # ─── Summary ───────────────────────────────────────────────
119
119
 
120
- version=$(node -e "console.log(require('$PACKAGE_DIR/package.json').version)" 2>/dev/null || echo "unknown")
120
+ version=$(jq -r .version "$PACKAGE_DIR/package.json" 2>/dev/null || echo "unknown")
121
121
  eagle_footer "Eagle Mem updated to v${version}."
@@ -1,100 +1,92 @@
1
1
  ---
2
2
  name: eagle-mem-tasks
3
3
  description: >
4
- TaskAware Compact Loop — break complex work into database-tracked subtasks with compaction
5
- between each. Use when: 'eagle tasks', 'break this into tasks', 'create task plan',
6
- 'task loop', 'compact loop', 'eagle mem tasks'. Uses the eagle-mem CLI — never run raw sqlite3 queries.
4
+ TaskAware Compact Loop — break complex work into Claude Code tasks with dependencies,
5
+ compact between each. Use when: 'eagle tasks', 'break this into tasks', 'create task plan',
6
+ 'task loop', 'compact loop', 'eagle mem tasks'. Uses Claude Code's native TaskCreate/TaskUpdate.
7
7
  ---
8
8
 
9
9
  # Eagle Mem — TaskAware Compact Loop
10
10
 
11
- Break complex work into subtasks stored in Eagle Mem's database. Execute one task at a time, compact between each, and let Eagle Mem re-inject context for the next task.
11
+ Break complex work into subtasks using Claude Code's native task system. Execute one task at a time, compact between each, and let Eagle Mem re-inject task state for the next task.
12
12
 
13
13
  ## How it works
14
14
 
15
15
  1. **Plan**: Break the user's request into ordered subtasks
16
- 2. **Store**: Add each subtask via the CLI
17
- 3. **Execute**: Work on the current task (marked `[ACTIVE]`)
18
- 4. **Compact**: When done, tell the user to run `/compact`
19
- 5. **Resume**: After compact, SessionStart re-injects memory + loads the next task
20
- 6. **Repeat**: Until all tasks are done
16
+ 2. **Create**: Add each subtask via `TaskCreate` with dependencies via `addBlockedBy`
17
+ 3. **Execute**: Work on the current task (mark `in_progress` with `TaskUpdate`)
18
+ 4. **Complete**: Mark done with `TaskUpdate(completed)`, emit `<eagle-summary>`
19
+ 5. **Compact**: Tell the user to run `/compact`
20
+ 6. **Resume**: After compact, SessionStart re-injects memory + loads task state from Eagle Mem's mirror
21
+ 7. **Repeat**: Until all tasks are done
21
22
 
22
- ## Commands
23
+ ## Creating tasks
23
24
 
24
- ### List tasks
25
+ Use Claude Code's `TaskCreate` tool. Each task gets `pending` status automatically.
25
26
 
26
- ```bash
27
- eagle-mem tasks
28
- eagle-mem tasks list
29
- ```
30
-
31
- ### Add tasks
32
-
33
- When the user invokes `/eagle-mem-tasks` or asks to break work into tasks:
34
-
35
- 1. Analyze the request and break it into 3-8 focused subtasks
36
- 2. Each task should be completable in one context window
37
- 3. Add tasks using the CLI:
27
+ For tasks that depend on earlier work, use `addBlockedBy` in `TaskUpdate`:
38
28
 
39
- ```bash
40
- eagle-mem tasks add "Set up project structure" "Install deps, create folders, init config"
41
- eagle-mem tasks add "Implement auth middleware" "JWT validation, role checks, error responses"
42
- eagle-mem tasks add "Build CRUD endpoints" "Users and posts REST API with validation"
43
29
  ```
30
+ TaskCreate({ subject: "Set up project structure", description: "Install deps, create folders, init config" })
31
+ TaskCreate({ subject: "Implement auth middleware", description: "JWT validation, role checks, error responses" })
32
+ TaskCreate({ subject: "Build CRUD endpoints", description: "Users and posts REST API with validation" })
44
33
 
45
- 4. Show the task plan to the user for confirmation
46
- 5. After confirmation, start working on task #1
47
-
48
- ### Complete a task
49
-
50
- ```bash
51
- eagle-mem tasks done <id>
34
+ // Then set dependencies:
35
+ TaskUpdate({ taskId: "3", addBlockedBy: ["1", "2"] })
52
36
  ```
53
37
 
54
- After marking done:
55
- 1. Emit your `<eagle-summary>` block
56
- 2. Tell the user: **"Task #N complete. Run `/compact` to save progress and load the next task."**
38
+ ## Working on a task
57
39
 
58
- ### Block a task
40
+ 1. Call `TaskUpdate({ taskId: "N", status: "in_progress" })` before starting work
41
+ 2. Do the work
42
+ 3. Call `TaskUpdate({ taskId: "N", status: "completed" })` when done
43
+ 4. Emit your `<eagle-summary>` block
44
+ 5. Tell the user: **"Task complete. Run `/compact` to save progress and load the next task."**
59
45
 
60
- ```bash
61
- eagle-mem tasks block <id>
62
- ```
63
-
64
- ### Set context snapshot
46
+ ## Viewing tasks
65
47
 
66
- For tasks that depend on decisions from earlier tasks:
48
+ Use `TaskList` to see all tasks, or the CLI for cross-session history:
67
49
 
68
50
  ```bash
69
- eagle-mem tasks context <id> "Using JWT with RS256, sessions stored in Redis"
51
+ eagle-mem tasks # pending/in-progress (from mirror)
52
+ eagle-mem tasks list # all tasks
53
+ eagle-mem tasks search <q> # FTS5 search across task history
70
54
  ```
71
55
 
72
- ### Clear completed tasks
56
+ ## Cross-session context
73
57
 
74
- ```bash
75
- eagle-mem tasks clear
76
- ```
58
+ When a task depends on decisions made in earlier tasks, put that context in the task's `description` field — it persists across compactions via Eagle Mem's mirror.
77
59
 
78
- ## Options
60
+ For example, if task 1 decides to use JWT with RS256:
79
61
 
80
- | Flag | Description |
81
- |------|-------------|
82
- | `-p, --project <name>` | Target a specific project (default: current directory) |
83
- | `-j, --json` | Output as JSON |
62
+ ```
63
+ TaskUpdate({ taskId: "2", description: "Implement auth middleware. Decision from task 1: using JWT with RS256, sessions stored in Redis." })
64
+ ```
84
65
 
85
66
  ## Task design guidelines
86
67
 
87
- - Each task should be **self-contained** — completable without mid-execution context from a previous task
88
- - Include instructions with enough detail that a fresh context window can pick it up
89
- - Use context snapshots for tasks that depend on decisions from earlier tasks
90
- - Order tasks so that foundational work comes first (schema before API, API before UI)
68
+ - Each task should be **self-contained** — completable in one context window
69
+ - Include enough detail in `description` that a fresh context window can pick it up
70
+ - Use `addBlockedBy` for tasks that depend on earlier tasks
71
+ - Order tasks so foundational work comes first (schema before API, API before UI)
72
+ - Keep tasks focused: 3-8 tasks per plan
91
73
 
92
74
  ## The compact cycle
93
75
 
94
76
  ```
95
- User request Plan tasks Execute task 1 /compact
96
- Eagle Mem saves summary Context cleared → Memory re-injected
97
- Task 2 loaded as [ACTIVE] Execute task 2 /compact
98
- ... repeat until all tasks done ...
99
- Final summary: "All N tasks complete."
77
+ User request -> Plan tasks (TaskCreate) -> Execute task 1 (TaskUpdate: in_progress)
78
+ -> Complete task 1 (TaskUpdate: completed) -> /compact
79
+ -> Eagle Mem saves summary -> Context cleared -> Task state re-injected
80
+ -> Execute task 2 -> /compact
81
+ -> ... repeat until all tasks done ...
82
+ -> Final summary: "All N tasks complete."
100
83
  ```
84
+
85
+ ## Status reference
86
+
87
+ | Status | Meaning |
88
+ |---|---|
89
+ | `pending` | Not started yet |
90
+ | `in_progress` | Currently being worked on |
91
+ | `completed` | Done |
92
+ | `deleted` | Removed |