eagle-mem 4.9.5 → 4.9.7

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.
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env bash
2
+ # ═══════════════════════════════════════════════════════════
3
+ # Eagle Mem — Doctor
4
+ # Read-only trust and install footprint checks.
5
+ # ═══════════════════════════════════════════════════════════
6
+ set -euo pipefail
7
+
8
+ PACKAGE_DIR="${1:-.}"
9
+ shift 2>/dev/null || true
10
+
11
+ SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
12
+ LIB_DIR="$SCRIPTS_DIR/../lib"
13
+
14
+ . "$SCRIPTS_DIR/style.sh"
15
+ . "$LIB_DIR/common.sh"
16
+
17
+ mode="install-footprint"
18
+ json_output=false
19
+
20
+ while [ $# -gt 0 ]; do
21
+ case "$1" in
22
+ --json|-j) json_output=true; shift ;;
23
+ --help|-h)
24
+ echo -e " ${BOLD}eagle-mem doctor${RESET} — Trust and install diagnostics"
25
+ echo ""
26
+ echo -e " ${BOLD}Usage:${RESET}"
27
+ echo -e " eagle-mem doctor"
28
+ echo -e " eagle-mem doctor install-footprint"
29
+ echo ""
30
+ echo -e " ${BOLD}Options:${RESET}"
31
+ echo -e " ${CYAN}-j, --json${RESET} Output structured JSON"
32
+ exit 0
33
+ ;;
34
+ install-footprint|footprint)
35
+ mode="$1"
36
+ shift
37
+ ;;
38
+ *) shift ;;
39
+ esac
40
+ done
41
+
42
+ case "$mode" in
43
+ install-footprint|footprint|"") ;;
44
+ *)
45
+ eagle_err "Unknown doctor check: $mode"
46
+ eagle_dim "Run: eagle-mem doctor --help"
47
+ exit 1
48
+ ;;
49
+ esac
50
+
51
+ package_version=$(jq -r .version "$PACKAGE_DIR/package.json" 2>/dev/null || echo "unknown")
52
+ installed_version=$(tr -d '[:space:]' < "$EAGLE_MEM_DIR/.version" 2>/dev/null || true)
53
+ [ -z "$installed_version" ] && installed_version="not installed"
54
+
55
+ sqlite_bin=$(eagle_sqlite_path)
56
+ sqlite_version=$(eagle_sqlite_version)
57
+ sqlite_fts5=false
58
+ if eagle_sqlite_supports_fts5; then
59
+ sqlite_fts5=true
60
+ fi
61
+
62
+ runtime_exists=false
63
+ db_exists=false
64
+ [ -d "$EAGLE_MEM_DIR" ] && runtime_exists=true
65
+ [ -f "$EAGLE_MEM_DB" ] && db_exists=true
66
+
67
+ doctor_compare_group() {
68
+ local group="$1"
69
+ local checked=0 missing=0 drift=0
70
+ local src dst
71
+ for src in "$PACKAGE_DIR/$group"/*; do
72
+ [ -f "$src" ] || continue
73
+ case "$group" in
74
+ db) ;;
75
+ *)
76
+ case "$src" in
77
+ *.sh|*/eagle-mem) ;;
78
+ *) continue ;;
79
+ esac
80
+ ;;
81
+ esac
82
+ checked=$((checked + 1))
83
+ dst="$EAGLE_MEM_DIR/$group/$(basename "$src")"
84
+ if [ ! -f "$dst" ]; then
85
+ missing=$((missing + 1))
86
+ elif ! cmp -s "$src" "$dst"; then
87
+ drift=$((drift + 1))
88
+ fi
89
+ done
90
+ printf '%s|%s|%s\n' "$checked" "$missing" "$drift"
91
+ }
92
+
93
+ hooks_cmp=$(doctor_compare_group hooks)
94
+ lib_cmp=$(doctor_compare_group lib)
95
+ db_cmp=$(doctor_compare_group db)
96
+ scripts_cmp=$(doctor_compare_group scripts)
97
+ manifest_path=$(eagle_runtime_manifest_path)
98
+ manifest_check=$(eagle_runtime_manifest_check)
99
+ manifest_status="${manifest_check%%|*}"
100
+ manifest_checked=$(printf '%s' "$manifest_check" | cut -d'|' -f2)
101
+ manifest_missing=$(printf '%s' "$manifest_check" | cut -d'|' -f3)
102
+ manifest_drift=$(printf '%s' "$manifest_check" | cut -d'|' -f4)
103
+ manifest_version=$(eagle_runtime_manifest_field '.package.version' 2>/dev/null || true)
104
+ manifest_action=$(eagle_runtime_manifest_field '.action' 2>/dev/null || true)
105
+ manifest_generated_at=$(eagle_runtime_manifest_field '.generated_at' 2>/dev/null || true)
106
+ manifest_package_dir=$(eagle_runtime_manifest_field '.package.dir' 2>/dev/null || true)
107
+
108
+ sum_missing=0
109
+ sum_drift=0
110
+ for row in "$hooks_cmp" "$lib_cmp" "$db_cmp" "$scripts_cmp"; do
111
+ IFS='|' read -r _checked missing drift <<< "$row"
112
+ sum_missing=$((sum_missing + missing))
113
+ sum_drift=$((sum_drift + drift))
114
+ done
115
+
116
+ claude_hooks="not found"
117
+ if [ -f "$EAGLE_SETTINGS" ] && command -v jq >/dev/null 2>&1; then
118
+ if jq -e '.. | objects | .command? // empty | select(test("eagle-mem|\\.eagle-mem"))' "$EAGLE_SETTINGS" >/dev/null 2>&1; then
119
+ claude_hooks="registered"
120
+ else
121
+ claude_hooks="not registered"
122
+ fi
123
+ fi
124
+
125
+ codex_hooks="not found"
126
+ if [ -f "$EAGLE_CODEX_HOOKS" ] && command -v jq >/dev/null 2>&1; then
127
+ if jq -e '.. | objects | .command? // empty | select(test("eagle-mem|\\.eagle-mem"))' "$EAGLE_CODEX_HOOKS" >/dev/null 2>&1; then
128
+ codex_hooks="registered"
129
+ else
130
+ codex_hooks="not registered"
131
+ fi
132
+ fi
133
+
134
+ statusline_state="not configured"
135
+ statusline_command=""
136
+ if [ -f "$EAGLE_SETTINGS" ] && command -v jq >/dev/null 2>&1; then
137
+ statusline_command=$(jq -r '.statusLine.command // .statusline.command // empty' "$EAGLE_SETTINGS" 2>/dev/null)
138
+ if [ -n "$statusline_command" ]; then
139
+ if printf '%s' "$statusline_command" | grep -qE 'eagle-mem|\.eagle-mem'; then
140
+ statusline_state="registered"
141
+ else
142
+ sl_file=$(eagle_statusline_script_from_command "$statusline_command" 2>/dev/null || true)
143
+ if [ -n "$sl_file" ] && grep -qE 'eagle_mem_statusline|\.eagle-mem/scripts/statusline-em' "$sl_file" 2>/dev/null; then
144
+ statusline_state="registered"
145
+ else
146
+ statusline_state="custom"
147
+ fi
148
+ fi
149
+ fi
150
+ fi
151
+
152
+ overall="Healthy"
153
+ if [ "$runtime_exists" != true ] || [ "$db_exists" != true ]; then
154
+ overall="Not installed"
155
+ elif [ "$sqlite_fts5" != true ] || [ "$sum_missing" -gt 0 ] || [ "$sum_drift" -gt 0 ] || [ "$manifest_status" != "ok" ]; then
156
+ overall="Needs attention"
157
+ fi
158
+
159
+ if [ "$json_output" = true ]; then
160
+ jq -nc \
161
+ --arg overall "$overall" \
162
+ --arg package_dir "$PACKAGE_DIR" \
163
+ --arg runtime_dir "$EAGLE_MEM_DIR" \
164
+ --arg db "$EAGLE_MEM_DB" \
165
+ --arg package_version "$package_version" \
166
+ --arg installed_version "$installed_version" \
167
+ --arg sqlite_bin "${sqlite_bin:-}" \
168
+ --arg sqlite_version "${sqlite_version:-}" \
169
+ --argjson sqlite_fts5 "$sqlite_fts5" \
170
+ --arg claude_hooks "$claude_hooks" \
171
+ --arg codex_hooks "$codex_hooks" \
172
+ --arg statusline "$statusline_state" \
173
+ --arg hooks_cmp "$hooks_cmp" \
174
+ --arg lib_cmp "$lib_cmp" \
175
+ --arg db_cmp "$db_cmp" \
176
+ --arg scripts_cmp "$scripts_cmp" \
177
+ --arg manifest_path "$manifest_path" \
178
+ --arg manifest_status "$manifest_status" \
179
+ --argjson manifest_checked "${manifest_checked:-0}" \
180
+ --argjson manifest_missing "${manifest_missing:-0}" \
181
+ --argjson manifest_drift "${manifest_drift:-0}" \
182
+ --arg manifest_version "${manifest_version:-}" \
183
+ --arg manifest_action "${manifest_action:-}" \
184
+ --arg manifest_generated_at "${manifest_generated_at:-}" \
185
+ --arg manifest_package_dir "${manifest_package_dir:-}" \
186
+ '{overall:$overall, package_dir:$package_dir, runtime_dir:$runtime_dir, db:$db,
187
+ versions:{package:$package_version, installed:$installed_version},
188
+ sqlite:{path:$sqlite_bin, version:$sqlite_version, fts5:$sqlite_fts5},
189
+ hooks:{claude:$claude_hooks, codex:$codex_hooks, statusline:$statusline},
190
+ runtime_drift:{hooks:$hooks_cmp, lib:$lib_cmp, db:$db_cmp, scripts:$scripts_cmp},
191
+ manifest:{path:$manifest_path, status:$manifest_status, checked:$manifest_checked,
192
+ missing:$manifest_missing, drift:$manifest_drift, version:$manifest_version,
193
+ action:$manifest_action, generated_at:$manifest_generated_at,
194
+ package_dir:$manifest_package_dir}}'
195
+ exit 0
196
+ fi
197
+
198
+ eagle_header "Doctor"
199
+ echo -e " ${BOLD}Overall:${RESET} $overall"
200
+ echo ""
201
+ echo -e " ${BOLD}Install footprint${RESET}"
202
+ eagle_kv "Package:" "$PACKAGE_DIR"
203
+ eagle_kv "Runtime:" "$EAGLE_MEM_DIR"
204
+ eagle_kv "Database:" "$EAGLE_MEM_DB"
205
+ eagle_kv "Version:" "package $package_version, installed $installed_version"
206
+ echo ""
207
+
208
+ echo -e " ${BOLD}SQLite${RESET}"
209
+ if [ -n "$sqlite_bin" ]; then
210
+ eagle_kv "Binary:" "$sqlite_bin"
211
+ eagle_kv "Version:" "${sqlite_version:-unknown}"
212
+ if [ "$sqlite_fts5" = true ]; then
213
+ eagle_ok "FTS5 available"
214
+ else
215
+ eagle_fail "FTS5 unavailable"
216
+ fi
217
+ else
218
+ eagle_fail "SQLite not found"
219
+ fi
220
+ echo ""
221
+
222
+ echo -e " ${BOLD}Hooks${RESET}"
223
+ eagle_kv "Claude Code:" "$claude_hooks"
224
+ eagle_kv "Codex:" "$codex_hooks"
225
+ eagle_kv "Statusline:" "$statusline_state"
226
+ echo ""
227
+
228
+ echo -e " ${BOLD}Install manifest${RESET}"
229
+ eagle_kv "Path:" "$manifest_path"
230
+ if [ "$manifest_status" = "ok" ]; then
231
+ eagle_ok "${manifest_checked:-0} files match manifest"
232
+ else
233
+ eagle_warn "status=$manifest_status, checked=${manifest_checked:-0}, missing=${manifest_missing:-0}, drift=${manifest_drift:-0}"
234
+ fi
235
+ [ -n "$manifest_version" ] && eagle_kv "Version:" "$manifest_version"
236
+ [ -n "$manifest_action" ] && eagle_kv "Action:" "$manifest_action"
237
+ [ -n "$manifest_generated_at" ] && eagle_kv "Generated:" "$manifest_generated_at"
238
+ echo ""
239
+
240
+ echo -e " ${BOLD}Runtime drift${RESET}"
241
+ for label_row in "hooks:$hooks_cmp" "lib:$lib_cmp" "db:$db_cmp" "scripts:$scripts_cmp"; do
242
+ label="${label_row%%:*}"
243
+ row="${label_row#*:}"
244
+ IFS='|' read -r checked missing drift <<< "$row"
245
+ if [ "$missing" -eq 0 ] && [ "$drift" -eq 0 ] 2>/dev/null; then
246
+ eagle_ok "$label: $checked checked, no drift"
247
+ else
248
+ eagle_warn "$label: $checked checked, $missing missing, $drift drifted"
249
+ fi
250
+ done
251
+ echo ""
252
+
253
+ echo -e " ${BOLD}Next${RESET}"
254
+ if [ "$overall" = "Healthy" ]; then
255
+ eagle_ok "Installed runtime matches this package."
256
+ else
257
+ [ "$runtime_exists" != true ] && eagle_info "Run: eagle-mem install"
258
+ [ "$manifest_status" != "ok" ] && eagle_info "Run: eagle-mem update to refresh the install manifest."
259
+ [ "$sum_missing" -gt 0 ] || [ "$sum_drift" -gt 0 ] && eagle_info "Run: eagle-mem update"
260
+ [ "$sqlite_fts5" != true ] && eagle_info "Set EAGLE_SQLITE_BIN to an FTS5-capable sqlite3."
261
+ fi
262
+ echo ""
@@ -18,6 +18,7 @@ eagle_header "Features"
18
18
  project=$(eagle_project_from_cwd "$(pwd)")
19
19
  subcommand="${1:-list}"
20
20
  shift 2>/dev/null || true
21
+ raw_output=false
21
22
 
22
23
  case "$subcommand" in
23
24
  list|ls)
@@ -73,28 +74,53 @@ case "$subcommand" in
73
74
  ;;
74
75
 
75
76
  pending)
77
+ limit=50
78
+ while [ $# -gt 0 ]; do
79
+ case "$1" in
80
+ --raw|--debug) raw_output=true; shift ;;
81
+ --limit|-n) limit="$2"; shift 2 ;;
82
+ --project|-p) project="$2"; shift 2 ;;
83
+ *) shift ;;
84
+ esac
85
+ done
86
+ limit=$(eagle_sql_int "$limit")
87
+ [ "$limit" -eq 0 ] && limit=50
76
88
  results=$(eagle_list_pending_feature_verifications "$project" 50)
77
89
  if [ -z "$results" ]; then
78
90
  eagle_ok "No pending feature verifications for '$project'"
79
91
  exit 0
80
92
  fi
81
93
 
82
- echo -e " ${BOLD}ID Feature File Trigger Diff${RESET}"
83
- echo -e " ${DIM}──── ────────────────────────────── ──────────────────────────── ───────── ────────────${RESET}"
94
+ pending_count=$(printf '%s\n' "$results" | sed '/^[[:space:]]*$/d' | wc -l | tr -d ' ')
95
+ echo -e " ${BOLD}Pending verification${RESET} ${DIM}($project)${RESET}"
96
+ echo -e " ${DIM}${pending_count} check(s) must be verified or waived before release-boundary commands.${RESET}"
97
+ echo ""
98
+
99
+ shown=0
84
100
  while IFS='|' read -r id feat file reason trigger created smoke fingerprint; do
85
101
  [ -z "$id" ] && continue
86
- feat_display="${feat:0:30}"
87
- file_display="${file:0:28}"
88
- trigger_display="${trigger:-hook}"
89
- fingerprint_display="${fingerprint:-unknown}"
90
- printf " %-4s %-30s %-28s %-9s %-12s\n" "$id" "$feat_display" "$file_display" "$trigger_display" "$fingerprint_display"
91
- [ -n "$reason" ] && echo -e " ${DIM}${reason}${RESET}"
92
- [ -n "$smoke" ] && echo -e " ${CYAN}smoke:${RESET} $smoke"
93
- [ -n "$created" ] && echo -e " ${DIM}created: $created${RESET}"
102
+ [ "$shown" -ge "$limit" ] && break
103
+ shown=$((shown + 1))
104
+ echo -e " ${BOLD}${shown}. ${feat}${RESET} ${DIM}#${id}${RESET}"
105
+ [ -n "$file" ] && echo -e " ${DIM}File:${RESET} $file"
106
+ [ -n "$reason" ] && echo -e " ${DIM}Why:${RESET} $reason"
107
+ [ -n "$smoke" ] && echo -e " ${CYAN}Smoke:${RESET} $smoke"
108
+ if [ "$raw_output" = true ]; then
109
+ [ -n "$trigger" ] && echo -e " ${DIM}Trigger:${RESET} $trigger"
110
+ [ -n "$fingerprint" ] && echo -e " ${DIM}Diff:${RESET} $fingerprint"
111
+ [ -n "$created" ] && echo -e " ${DIM}Created:${RESET} $created"
112
+ fi
113
+ echo ""
94
114
  done <<< "$results"
95
- echo ""
115
+ if [ "$pending_count" -gt "$shown" ] 2>/dev/null; then
116
+ eagle_dim "$((pending_count - shown)) more pending; run with --limit $pending_count to show all."
117
+ echo ""
118
+ fi
96
119
  eagle_info "Verify after testing: eagle-mem feature verify <name> --notes \"what passed\""
97
120
  eagle_info "Waive intentionally: eagle-mem feature waive <id> --reason \"why safe\""
121
+ if [ "$raw_output" = false ]; then
122
+ eagle_dim "Run with --raw to show trigger, diff fingerprint, and created timestamp."
123
+ fi
98
124
  ;;
99
125
 
100
126
  waive)
package/scripts/health.sh CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env bash
2
- # ════════��══════════��═══════════════════════════════════════
2
+ # ═══════════════════════════════════════════════════════════
3
3
  # Eagle Mem — Health Check
4
4
  # Diagnoses how well the self-learning pipeline is working
5
- # ════════���══════════════════════════════════════════════════
5
+ # ═══════════════════════════════════════════════════════════
6
6
  set -euo pipefail
7
7
 
8
8
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
@@ -51,7 +51,7 @@ score=0
51
51
  max_score=0
52
52
  issues=()
53
53
 
54
- # ─��─ 1. Summary capture rate (25 pts) ───────────────────
54
+ # ─── 1. Summary capture rate (25 pts) ───────────────────
55
55
 
56
56
  max_score=$((max_score + 25))
57
57
 
@@ -263,10 +263,10 @@ else
263
263
  issues+=("Run: eagle-mem curate --dry-run")
264
264
  fi
265
265
 
266
- # ─── Score ───────���────────────────────────────────────────
266
+ # ─── Score ────────────────────────────────────────────────
267
267
 
268
268
  echo ""
269
- echo -e " ${DIM}────���────────────────────────────────${RESET}"
269
+ echo -e " ${DIM}─────────────────────────────────────${RESET}"
270
270
 
271
271
  pct=$((score * 100 / max_score))
272
272
  if [ "$pct" -ge 80 ]; then
@@ -280,14 +280,17 @@ else
280
280
  grade="Unhealthy"
281
281
  fi
282
282
 
283
- echo -e " ${BOLD}Score: ${color}${score}/${max_score} (${pct}%)${RESET} ${color}${grade}${RESET}"
283
+ echo -e " ${BOLD}Overall:${RESET} ${color}${grade}${RESET} ${DIM}${score}/${max_score} (${pct}%)${RESET}"
284
284
 
285
285
  if [ ${#issues[@]} -gt 0 ]; then
286
286
  echo ""
287
- echo -e " ${BOLD}Issues:${RESET}"
287
+ echo -e " ${BOLD}Next:${RESET}"
288
288
  for issue in "${issues[@]}"; do
289
289
  echo -e " ${YELLOW}!${RESET} $issue"
290
290
  done
291
+ else
292
+ echo ""
293
+ eagle_ok "No immediate action needed."
291
294
  fi
292
295
 
293
296
  eagle_footer "Health check complete."
package/scripts/help.sh CHANGED
@@ -22,6 +22,7 @@ echo -e " ${CYAN}update${RESET} Re-deploy hooks and run migrations"
22
22
  echo -e " ${CYAN}uninstall${RESET} Remove hooks and optionally delete data"
23
23
  echo -e " ${CYAN}search${RESET} Search past sessions, memories, and code"
24
24
  echo -e " ${CYAN}health${RESET} Diagnose pipeline health and background automation"
25
+ echo -e " ${CYAN}doctor${RESET} Show install footprint, hooks, SQLite, manifest, and runtime drift"
25
26
  echo -e " ${CYAN}updates${RESET} Auto-update status and policy"
26
27
  echo -e " ${CYAN}overview${RESET} Build or view project overview"
27
28
  echo -e " ${CYAN}session${RESET} Save a manual session summary"
@@ -180,6 +180,8 @@ if [ "$prereqs_ok" = false ]; then
180
180
  exit 1
181
181
  fi
182
182
 
183
+ eagle_runtime_change_plan "install" "$PACKAGE_DIR" "$claude_found" "$codex_found"
184
+
183
185
  echo ""
184
186
 
185
187
  # ─── Copy files to ~/.eagle-mem/ ───────────────────────────
@@ -334,8 +336,7 @@ WRAPPER
334
336
  eagle_dim " # ── Eagle Mem ──"
335
337
  eagle_dim " em_section=\"\""
336
338
  eagle_dim " if [ -f \"\$HOME/.eagle-mem/scripts/statusline-em.sh\" ]; then"
337
- eagle_dim " source \"\$HOME/.eagle-mem/scripts/statusline-em.sh\""
338
- eagle_dim " em_section=\$(eagle_mem_statusline \"\$project_dir\" \"\$session_id\" \"\$input\")"
339
+ eagle_dim " em_section=\$(printf '%s' \"\$input\" | bash \"\$HOME/.eagle-mem/scripts/statusline-em.sh\" --hud)"
339
340
  eagle_dim " fi"
340
341
  echo ""
341
342
  eagle_ok "Statusline ${DIM}(manual patch needed — instructions above)${RESET}"
@@ -365,7 +366,7 @@ eagle_ok "Auto-updates ${DIM}(mode=$(eagle_update_config_mode), allow=$(eagle_up
365
366
 
366
367
  if [ "$claude_found" = true ]; then
367
368
  if eagle_patch_claude_md; then
368
- eagle_ok "CLAUDE.md ${DIM}(eagle-summary instructions added)${RESET}"
369
+ eagle_ok "CLAUDE.md ${DIM}(Eagle Mem guidance added)${RESET}"
369
370
  else
370
371
  eagle_ok "CLAUDE.md ${DIM}(already has Eagle Mem section)${RESET}"
371
372
  fi
@@ -384,6 +385,11 @@ fi
384
385
  version=$(jq -r .version "$PACKAGE_DIR/package.json" 2>/dev/null || echo "unknown")
385
386
  echo "$version" > "$EAGLE_MEM_DIR/.version"
386
387
  echo "$version" > "$EAGLE_MEM_DIR/.latest-version"
388
+ if eagle_runtime_manifest_write "$PACKAGE_DIR" "install"; then
389
+ eagle_ok "Install manifest written"
390
+ else
391
+ eagle_warn "Install manifest could not be written"
392
+ fi
387
393
 
388
394
  # ─── Summary ───────────────────────────────────────────────
389
395