eagle-mem 4.10.10 → 4.10.12
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/CHANGELOG.md +26 -0
- package/README.md +7 -0
- package/bin/eagle-mem +1 -0
- package/hooks/post-tool-use.sh +53 -17
- package/hooks/pre-tool-use.sh +68 -1
- package/hooks/stop.sh +1 -1
- package/hooks/user-prompt-submit.sh +1 -1
- package/lib/common.sh +129 -2
- package/lib/hooks-sessionstart.sh +60 -7
- package/lib/provider.sh +193 -39
- package/package.json +1 -1
- package/scripts/config.sh +2 -0
- package/scripts/curate.sh +25 -18
- package/scripts/health.sh +5 -7
- package/scripts/help.sh +1 -0
- package/scripts/index.sh +12 -1
- package/scripts/logs.sh +144 -0
- package/scripts/scan.sh +11 -1
- package/scripts/test.sh +1 -0
- package/tests/test_curate_graph_memories.sh +8 -0
- package/tests/test_reliability_guards.sh +195 -0
package/lib/provider.sh
CHANGED
|
@@ -152,6 +152,8 @@ eagle_config_init() {
|
|
|
152
152
|
# Which LLM provider to use for the curator and analysis features
|
|
153
153
|
# Options: "ollama" (free, local), "agent_cli" (Codex/Claude CLI auth), "anthropic", "openai"
|
|
154
154
|
type = "$provider"
|
|
155
|
+
# "auto" retries local/agent/API fallbacks when the primary provider fails.
|
|
156
|
+
fallback = "auto"
|
|
155
157
|
|
|
156
158
|
[ollama]
|
|
157
159
|
url = "$ollama_url"
|
|
@@ -207,6 +209,12 @@ rtk = "auto"
|
|
|
207
209
|
# "allow" keeps RTK advisory only.
|
|
208
210
|
raw_bash = "block"
|
|
209
211
|
|
|
212
|
+
[read_guard]
|
|
213
|
+
# advisory = score repeated/expensive reads and nudge; block = deny high-score rereads.
|
|
214
|
+
mode = "advisory"
|
|
215
|
+
score_threshold = "70"
|
|
216
|
+
block_threshold = "90"
|
|
217
|
+
|
|
210
218
|
[redaction]
|
|
211
219
|
# Additional secret patterns (regex) beyond built-in defaults
|
|
212
220
|
# extra_patterns = ["MY_CUSTOM_SECRET_.*"]
|
|
@@ -223,14 +231,39 @@ eagle_llm_call() {
|
|
|
223
231
|
local system_prompt="${2:-You are a helpful assistant that analyzes software development sessions.}"
|
|
224
232
|
local max_tokens="${3:-1024}"
|
|
225
233
|
|
|
226
|
-
local provider
|
|
234
|
+
local provider chain candidate result rc tried=0
|
|
227
235
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
236
|
+
chain=$(_eagle_provider_chain "$provider")
|
|
237
|
+
|
|
238
|
+
if [ -z "$chain" ]; then
|
|
239
|
+
eagle_log "ERROR" "No LLM provider configured or available. Run: eagle-mem config"
|
|
240
|
+
return 1
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
while IFS= read -r candidate; do
|
|
244
|
+
[ -z "$candidate" ] && continue
|
|
245
|
+
tried=$((tried + 1))
|
|
246
|
+
result=$(_eagle_call_provider_once "$candidate" "$prompt" "$system_prompt" "$max_tokens")
|
|
247
|
+
rc=$?
|
|
248
|
+
if [ "$rc" -eq 0 ] && [ -n "$result" ]; then
|
|
249
|
+
[ "$candidate" != "$provider" ] && eagle_log "INFO" "LLM provider fallback succeeded: primary=$provider used=$candidate"
|
|
250
|
+
printf '%s\n' "$result"
|
|
251
|
+
return 0
|
|
252
|
+
fi
|
|
253
|
+
eagle_log "WARN" "LLM provider candidate failed: primary=$provider candidate=$candidate rc=$rc"
|
|
254
|
+
done <<< "$chain"
|
|
255
|
+
|
|
256
|
+
eagle_log "ERROR" "All LLM provider candidates failed: primary=$provider tried=$tried"
|
|
257
|
+
return 1
|
|
258
|
+
}
|
|
228
259
|
|
|
260
|
+
_eagle_call_provider_once() {
|
|
261
|
+
local provider="$1" prompt="$2" system_prompt="$3" max_tokens="$4"
|
|
229
262
|
case "$provider" in
|
|
230
|
-
ollama)
|
|
263
|
+
ollama) _eagle_call_ollama "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
231
264
|
agent_cli) _eagle_call_agent_cli "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
232
265
|
anthropic) _eagle_call_anthropic "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
233
|
-
openai)
|
|
266
|
+
openai) _eagle_call_openai "$prompt" "$system_prompt" "$max_tokens" ;;
|
|
234
267
|
none)
|
|
235
268
|
eagle_log "ERROR" "No LLM provider configured. Run: eagle-mem config"
|
|
236
269
|
return 1
|
|
@@ -242,6 +275,79 @@ eagle_llm_call() {
|
|
|
242
275
|
esac
|
|
243
276
|
}
|
|
244
277
|
|
|
278
|
+
_eagle_provider_chain() {
|
|
279
|
+
local primary="${1:-none}" fallback
|
|
280
|
+
fallback=$(eagle_config_get "provider" "fallback" "auto")
|
|
281
|
+
|
|
282
|
+
local chain=""
|
|
283
|
+
_eagle_provider_chain_add() {
|
|
284
|
+
local candidate="$1" required="${2:-available}"
|
|
285
|
+
[ -n "$candidate" ] || return 0
|
|
286
|
+
case " $chain " in *" $candidate "*) return 0 ;; esac
|
|
287
|
+
if [ "$required" = "available" ] && ! _eagle_provider_available "$candidate"; then
|
|
288
|
+
return 0
|
|
289
|
+
fi
|
|
290
|
+
chain="${chain}${chain:+ }$candidate"
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if [ "$primary" != "none" ]; then
|
|
294
|
+
_eagle_provider_chain_add "$primary" "always"
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
if [ "$fallback" != "off" ]; then
|
|
298
|
+
case "$primary" in
|
|
299
|
+
ollama) _eagle_provider_chain_add "agent_cli"; _eagle_provider_chain_add "anthropic"; _eagle_provider_chain_add "openai" ;;
|
|
300
|
+
agent_cli) _eagle_provider_chain_add "ollama"; _eagle_provider_chain_add "anthropic"; _eagle_provider_chain_add "openai" ;;
|
|
301
|
+
anthropic) _eagle_provider_chain_add "ollama"; _eagle_provider_chain_add "agent_cli"; _eagle_provider_chain_add "openai" ;;
|
|
302
|
+
openai) _eagle_provider_chain_add "ollama"; _eagle_provider_chain_add "agent_cli"; _eagle_provider_chain_add "anthropic" ;;
|
|
303
|
+
none|"") _eagle_provider_chain_add "ollama"; _eagle_provider_chain_add "agent_cli"; _eagle_provider_chain_add "anthropic"; _eagle_provider_chain_add "openai" ;;
|
|
304
|
+
esac
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
printf '%s\n' "$chain" | tr ' ' '\n' | sed '/^$/d'
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
_eagle_provider_available() {
|
|
311
|
+
case "$1" in
|
|
312
|
+
ollama)
|
|
313
|
+
[ -n "$(eagle_detect_ollama "$(eagle_config_get "ollama" "url" "$EAGLE_DEFAULT_OLLAMA_URL")" 2>/dev/null || true)" ]
|
|
314
|
+
;;
|
|
315
|
+
agent_cli)
|
|
316
|
+
[ -n "$(_eagle_agent_cli_target_chain)" ]
|
|
317
|
+
;;
|
|
318
|
+
anthropic)
|
|
319
|
+
[ -n "${ANTHROPIC_API_KEY:-}" ]
|
|
320
|
+
;;
|
|
321
|
+
openai)
|
|
322
|
+
[ -n "${OPENAI_API_KEY:-}" ]
|
|
323
|
+
;;
|
|
324
|
+
*) return 1 ;;
|
|
325
|
+
esac
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
_eagle_provider_model_label() {
|
|
329
|
+
case "$1" in
|
|
330
|
+
ollama) printf 'ollama:%s' "$(eagle_config_get "ollama" "model" "mistral")" ;;
|
|
331
|
+
agent_cli) printf 'agent_cli:%s' "$(_eagle_agent_cli_target_summary)" ;;
|
|
332
|
+
anthropic) printf 'anthropic:%s' "$(eagle_config_get "anthropic" "model" "claude-haiku-4-5-20251001")" ;;
|
|
333
|
+
openai) printf 'openai:%s' "$(eagle_config_get "openai" "model" "gpt-4o-mini")" ;;
|
|
334
|
+
*) printf '%s' "$1" ;;
|
|
335
|
+
esac
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
eagle_llm_provider_label() {
|
|
339
|
+
local provider chain labels="" candidate sep=""
|
|
340
|
+
provider=$(eagle_config_get "provider" "type" "none")
|
|
341
|
+
chain=$(_eagle_provider_chain "$provider")
|
|
342
|
+
while IFS= read -r candidate; do
|
|
343
|
+
[ -z "$candidate" ] && continue
|
|
344
|
+
labels="${labels}${sep}$(_eagle_provider_model_label "$candidate")"
|
|
345
|
+
sep=" -> "
|
|
346
|
+
done <<< "$chain"
|
|
347
|
+
[ -n "$labels" ] || labels="none"
|
|
348
|
+
printf '%s\n' "$labels"
|
|
349
|
+
}
|
|
350
|
+
|
|
245
351
|
_eagle_call_ollama() {
|
|
246
352
|
local prompt="$1" system="$2" max_tokens="$3"
|
|
247
353
|
local url model
|
|
@@ -281,35 +387,60 @@ _eagle_call_ollama() {
|
|
|
281
387
|
}
|
|
282
388
|
|
|
283
389
|
_eagle_agent_cli_target() {
|
|
284
|
-
local
|
|
390
|
+
local first
|
|
391
|
+
first=$(_eagle_agent_cli_target_chain | sed -n '1p')
|
|
392
|
+
if [ -n "$first" ]; then
|
|
393
|
+
printf '%s\n' "$first"
|
|
394
|
+
else
|
|
395
|
+
printf 'none\n'
|
|
396
|
+
fi
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
_eagle_agent_cli_target_chain() {
|
|
400
|
+
local preferred current preferred_target targets="" candidate
|
|
285
401
|
preferred=$(eagle_config_get "agent_cli" "preferred" "current")
|
|
402
|
+
current=""
|
|
403
|
+
if [ -n "${EAGLE_AGENT_SOURCE:-${EAGLE_AGENT:-}}" ]; then
|
|
404
|
+
current=$(eagle_agent_source)
|
|
405
|
+
fi
|
|
286
406
|
|
|
287
407
|
case "$preferred" in
|
|
288
|
-
codex|openai-codex)
|
|
289
|
-
claude|claude-code|cloud-code)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
elif command -v claude &>/dev/null; then
|
|
296
|
-
echo "claude-code"
|
|
297
|
-
else
|
|
298
|
-
echo "none"
|
|
299
|
-
fi
|
|
300
|
-
;;
|
|
301
|
-
current|*)
|
|
302
|
-
if [ -n "${EAGLE_AGENT_SOURCE:-${EAGLE_AGENT:-}}" ]; then
|
|
303
|
-
eagle_agent_source
|
|
304
|
-
elif command -v codex &>/dev/null; then
|
|
305
|
-
echo "codex"
|
|
306
|
-
elif command -v claude &>/dev/null; then
|
|
307
|
-
echo "claude-code"
|
|
308
|
-
else
|
|
309
|
-
echo "none"
|
|
310
|
-
fi
|
|
408
|
+
codex|openai-codex) preferred_target="codex" ;;
|
|
409
|
+
claude|claude-code|cloud-code) preferred_target="claude-code" ;;
|
|
410
|
+
current) preferred_target="$current" ;;
|
|
411
|
+
auto|"") preferred_target="" ;;
|
|
412
|
+
*)
|
|
413
|
+
eagle_log "WARN" "agent_cli unsupported preferred target: $preferred"
|
|
414
|
+
preferred_target=""
|
|
311
415
|
;;
|
|
312
416
|
esac
|
|
417
|
+
|
|
418
|
+
for candidate in "$preferred_target" "$current" codex claude-code; do
|
|
419
|
+
case "$candidate" in
|
|
420
|
+
codex|claude-code) ;;
|
|
421
|
+
*) continue ;;
|
|
422
|
+
esac
|
|
423
|
+
case "$candidate" in
|
|
424
|
+
codex) command -v codex >/dev/null 2>&1 || continue ;;
|
|
425
|
+
claude-code) command -v claude >/dev/null 2>&1 || continue ;;
|
|
426
|
+
esac
|
|
427
|
+
case " $targets " in *" $candidate "*) continue ;; esac
|
|
428
|
+
targets="${targets}${targets:+ }$candidate"
|
|
429
|
+
done
|
|
430
|
+
|
|
431
|
+
printf '%s\n' "$targets" | tr ' ' '\n' | sed '/^$/d'
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
_eagle_agent_cli_target_summary() {
|
|
435
|
+
local chain summary="" sep="" target
|
|
436
|
+
chain=$(_eagle_agent_cli_target_chain)
|
|
437
|
+
while IFS= read -r target; do
|
|
438
|
+
[ -z "$target" ] && continue
|
|
439
|
+
summary="${summary}${sep}${target}"
|
|
440
|
+
sep=" -> "
|
|
441
|
+
done <<< "$chain"
|
|
442
|
+
[ -n "$summary" ] || summary="none"
|
|
443
|
+
printf '%s\n' "$summary"
|
|
313
444
|
}
|
|
314
445
|
|
|
315
446
|
_eagle_agent_cli_prompt_file() {
|
|
@@ -327,17 +458,36 @@ _eagle_agent_cli_prompt_file() {
|
|
|
327
458
|
|
|
328
459
|
_eagle_call_agent_cli() {
|
|
329
460
|
local prompt="$1" system="$2" max_tokens="$3"
|
|
330
|
-
local target
|
|
331
|
-
|
|
461
|
+
local target targets result rc tried=0
|
|
462
|
+
targets=$(_eagle_agent_cli_target_chain)
|
|
332
463
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
464
|
+
if [ -z "$targets" ]; then
|
|
465
|
+
eagle_log "ERROR" "agent_cli provider unavailable: no supported Codex or Claude CLI found"
|
|
466
|
+
return 1
|
|
467
|
+
fi
|
|
468
|
+
|
|
469
|
+
while IFS= read -r target; do
|
|
470
|
+
[ -z "$target" ] && continue
|
|
471
|
+
tried=$((tried + 1))
|
|
472
|
+
case "$target" in
|
|
473
|
+
codex) result=$(_eagle_call_codex_cli "$prompt" "$system" "$max_tokens"); rc=$? ;;
|
|
474
|
+
claude-code) result=$(_eagle_call_claude_cli "$prompt" "$system" "$max_tokens"); rc=$? ;;
|
|
475
|
+
*)
|
|
476
|
+
eagle_log "WARN" "agent_cli unsupported target: $target"
|
|
477
|
+
rc=1
|
|
478
|
+
result=""
|
|
479
|
+
;;
|
|
480
|
+
esac
|
|
481
|
+
if [ "$rc" -eq 0 ] && [ -n "$result" ]; then
|
|
482
|
+
[ "$tried" -gt 1 ] && eagle_log "INFO" "agent_cli fallback succeeded with $target"
|
|
483
|
+
printf '%s\n' "$result"
|
|
484
|
+
return 0
|
|
485
|
+
fi
|
|
486
|
+
eagle_log "WARN" "agent_cli target failed: target=$target rc=$rc"
|
|
487
|
+
done <<< "$targets"
|
|
488
|
+
|
|
489
|
+
eagle_log "ERROR" "All agent_cli targets failed: targets=$(printf '%s' "$targets" | tr '\n' ',')"
|
|
490
|
+
return 1
|
|
341
491
|
}
|
|
342
492
|
|
|
343
493
|
_eagle_call_codex_cli() {
|
|
@@ -539,16 +689,19 @@ eagle_show_config() {
|
|
|
539
689
|
return 1
|
|
540
690
|
fi
|
|
541
691
|
|
|
542
|
-
local provider model
|
|
692
|
+
local provider model fallback
|
|
543
693
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
694
|
+
fallback=$(eagle_config_get "provider" "fallback" "auto")
|
|
544
695
|
if [ "$provider" = "agent_cli" ]; then
|
|
545
|
-
model=$(
|
|
696
|
+
model=$(_eagle_agent_cli_target_summary)
|
|
546
697
|
else
|
|
547
698
|
model=$(eagle_config_get "$provider" "model" "unknown")
|
|
548
699
|
fi
|
|
549
700
|
|
|
550
701
|
echo "Provider: $provider"
|
|
551
702
|
echo "Model: $model"
|
|
703
|
+
echo "Fallback: $fallback"
|
|
704
|
+
echo "Chain: $(eagle_llm_provider_label)"
|
|
552
705
|
|
|
553
706
|
if [ "$provider" = "ollama" ]; then
|
|
554
707
|
local url
|
|
@@ -564,6 +717,7 @@ eagle_show_config() {
|
|
|
564
717
|
fi
|
|
565
718
|
elif [ "$provider" = "agent_cli" ]; then
|
|
566
719
|
echo "Preferred: $(eagle_config_get "agent_cli" "preferred" "current")"
|
|
720
|
+
echo "Targets: $(_eagle_agent_cli_target_summary)"
|
|
567
721
|
echo "Codex: $(command -v codex 2>/dev/null || echo "not found")"
|
|
568
722
|
echo "Claude: $(command -v claude 2>/dev/null || echo "not found")"
|
|
569
723
|
fi
|
package/package.json
CHANGED
package/scripts/config.sh
CHANGED
|
@@ -36,6 +36,7 @@ show_help() {
|
|
|
36
36
|
echo -e " eagle-mem config set updates.allow patch"
|
|
37
37
|
echo -e " eagle-mem config set token_guard.rtk enforce"
|
|
38
38
|
echo -e " eagle-mem config set token_guard.raw_bash block"
|
|
39
|
+
echo -e " eagle-mem config set read_guard.mode advisory"
|
|
39
40
|
echo ""
|
|
40
41
|
exit 0
|
|
41
42
|
}
|
|
@@ -72,6 +73,7 @@ case "$subcommand" in
|
|
|
72
73
|
eagle_info " eagle-mem config set anthropic.model claude-haiku-4-5-20251001"
|
|
73
74
|
eagle_info " eagle-mem config set token_guard.rtk enforce"
|
|
74
75
|
eagle_info " eagle-mem config set token_guard.raw_bash block"
|
|
76
|
+
eagle_info " eagle-mem config set read_guard.mode advisory"
|
|
75
77
|
exit 1
|
|
76
78
|
fi
|
|
77
79
|
section="${key%%.*}"
|
package/scripts/curate.sh
CHANGED
|
@@ -39,22 +39,20 @@ EOF
|
|
|
39
39
|
|
|
40
40
|
parse_consolidations_json() {
|
|
41
41
|
local result="$1"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
printf '%s' "$json_payload" | jq -c '
|
|
42
|
+
printf '%s' "$result" | jq -Rrs -c '
|
|
43
|
+
def text_trim: gsub("^\\s+|\\s+$"; "");
|
|
44
|
+
def parse_payload:
|
|
45
|
+
gsub("\r"; "")
|
|
46
|
+
| gsub("^\\s*```json\\s*\\n"; "")
|
|
47
|
+
| gsub("^\\s*```\\s*\\n"; "")
|
|
48
|
+
| gsub("\\n\\s*```\\s*$"; "")
|
|
49
|
+
| text_trim
|
|
50
|
+
| if . == "" or . == "NONE" or . == "none" or . == "null" then []
|
|
51
|
+
else
|
|
52
|
+
try fromjson catch (
|
|
53
|
+
([match("(?s)(\\{.*\\}|\\[.*\\])")? | .string][0] // "[]") | fromjson
|
|
54
|
+
)
|
|
55
|
+
end;
|
|
58
56
|
def trim: gsub("^\\s+|\\s+$"; "");
|
|
59
57
|
def names:
|
|
60
58
|
if type == "array" then map(tostring | trim) | map(select(length > 0))
|
|
@@ -66,7 +64,8 @@ parse_consolidations_json() {
|
|
|
66
64
|
elif type == "object" then (.consolidations // .items // .instructions // [])
|
|
67
65
|
else []
|
|
68
66
|
end;
|
|
69
|
-
|
|
67
|
+
parse_payload
|
|
68
|
+
| root
|
|
70
69
|
| map({
|
|
71
70
|
source_names: ((.source_names // .sourceNames // .source_memories // .sourceMemories // .original_names // .originalNames // .originals // .names) | names),
|
|
72
71
|
new_name: ((.new_name // .newName // .name // .title // "") | tostring | trim),
|
|
@@ -100,19 +99,27 @@ fi
|
|
|
100
99
|
|
|
101
100
|
p_esc=$(eagle_sql_escape "$project")
|
|
102
101
|
|
|
102
|
+
cleanup_curate() {
|
|
103
|
+
local rc=$?
|
|
104
|
+
eagle_run_finish "$rc" "$LINENO"
|
|
105
|
+
}
|
|
106
|
+
eagle_run_start "curate" "$project" "$(pwd)"
|
|
107
|
+
trap cleanup_curate EXIT
|
|
108
|
+
|
|
103
109
|
# Verify provider is configured
|
|
104
110
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
105
111
|
if [ "$provider" = "none" ]; then
|
|
106
112
|
eagle_err "No LLM provider configured. Run: eagle-mem config init"
|
|
107
113
|
exit 1
|
|
108
114
|
fi
|
|
109
|
-
eagle_info "Provider: $
|
|
115
|
+
eagle_info "Provider: $(eagle_llm_provider_label)"
|
|
110
116
|
eagle_info "Project: $project"
|
|
111
117
|
[ "$DRY_RUN" -eq 1 ] && eagle_info "Dry run — no changes will be made"
|
|
112
118
|
echo ""
|
|
113
119
|
|
|
114
120
|
# ─── 1. Analyze gotchas for promotion ─────────────────────
|
|
115
121
|
|
|
122
|
+
eagle_run_step "promote_gotchas"
|
|
116
123
|
eagle_info "Analyzing gotchas for promotion..."
|
|
117
124
|
|
|
118
125
|
recent_gotchas=$(eagle_db "SELECT gotchas, created_at
|
package/scripts/health.sh
CHANGED
|
@@ -136,14 +136,11 @@ max_score=$((max_score + 15))
|
|
|
136
136
|
|
|
137
137
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
138
138
|
if [ "$provider" != "none" ]; then
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
else
|
|
142
|
-
model=$(eagle_config_get "$provider" "model" "default")
|
|
143
|
-
fi
|
|
144
|
-
eagle_ok "Provider: ${provider} (${model})"
|
|
139
|
+
provider_chain=$(eagle_llm_provider_label)
|
|
140
|
+
eagle_ok "Provider: ${provider_chain}"
|
|
145
141
|
score=$((score + 15))
|
|
146
142
|
else
|
|
143
|
+
provider_chain="none"
|
|
147
144
|
eagle_fail "No LLM provider — curator and enrichment disabled"
|
|
148
145
|
issues+=("Configure a provider: eagle-mem config init")
|
|
149
146
|
fi
|
|
@@ -321,6 +318,7 @@ if [ "$JSON_OUT" -eq 1 ]; then
|
|
|
321
318
|
--argjson enriched_summaries "${enriched_summaries:-0}" \
|
|
322
319
|
--argjson features "${feature_count:-0}" \
|
|
323
320
|
--arg provider "$provider" \
|
|
321
|
+
--arg provider_chain "$provider_chain" \
|
|
324
322
|
--arg token_guard_rtk "$rtk_mode" \
|
|
325
323
|
--arg token_guard_raw_bash "$raw_bash_mode" \
|
|
326
324
|
--arg rtk_bin "${rtk_bin:-}" \
|
|
@@ -345,7 +343,7 @@ if [ "$JSON_OUT" -eq 1 ]; then
|
|
|
345
343
|
'{project:$project, score:$score, max:$max_score, pct:$pct, grade:$grade,
|
|
346
344
|
capture:{sessions:$total_sessions, summaries:$total_summaries, heuristic:$heuristic_summaries},
|
|
347
345
|
enrichment:$enriched_summaries,
|
|
348
|
-
features:$features, provider:$provider,
|
|
346
|
+
features:$features, provider:$provider, provider_chain:$provider_chain,
|
|
349
347
|
token_guard:{rtk:$token_guard_rtk, raw_bash:$token_guard_raw_bash, rtk_bin:$rtk_bin},
|
|
350
348
|
orchestration:{
|
|
351
349
|
route:$orchestration_route,
|
package/scripts/help.sh
CHANGED
|
@@ -23,6 +23,7 @@ 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
25
|
echo -e " ${CYAN}doctor${RESET} Show install footprint, hooks, SQLite, manifest, and runtime drift"
|
|
26
|
+
echo -e " ${CYAN}logs${RESET} Inspect/prune command-scoped scan/index/curate logs"
|
|
26
27
|
echo -e " ${CYAN}updates${RESET} Auto-update status and policy"
|
|
27
28
|
echo -e " ${CYAN}overview${RESET} Build or view project overview"
|
|
28
29
|
echo -e " ${CYAN}session${RESET} Save a manual session summary"
|
package/scripts/index.sh
CHANGED
|
@@ -40,6 +40,15 @@ TARGET_DIR="${args[0]:-.}"
|
|
|
40
40
|
TARGET_DIR="$(cd "$TARGET_DIR" && pwd)"
|
|
41
41
|
PROJECT=$(eagle_project_from_cwd "$TARGET_DIR")
|
|
42
42
|
|
|
43
|
+
TMPDIR_IDX=""
|
|
44
|
+
cleanup_index() {
|
|
45
|
+
local rc=$?
|
|
46
|
+
[ -n "${TMPDIR_IDX:-}" ] && rm -rf "$TMPDIR_IDX" 2>/dev/null || true
|
|
47
|
+
eagle_run_finish "$rc" "$LINENO"
|
|
48
|
+
}
|
|
49
|
+
eagle_run_start "index" "$PROJECT" "$TARGET_DIR"
|
|
50
|
+
trap cleanup_index EXIT
|
|
51
|
+
|
|
43
52
|
CHUNK_SIZE="${EAGLE_MEM_CHUNK_SIZE:-80}"
|
|
44
53
|
if ! [[ "$CHUNK_SIZE" =~ ^[0-9]+$ ]] || [ "$CHUNK_SIZE" -lt 1 ]; then
|
|
45
54
|
CHUNK_SIZE=80
|
|
@@ -111,10 +120,10 @@ sql_literal_expr() {
|
|
|
111
120
|
# ─── Collect files ─────────────────────────────────────────
|
|
112
121
|
|
|
113
122
|
TMPDIR_IDX=$(mktemp -d)
|
|
114
|
-
trap 'rm -rf "$TMPDIR_IDX"' EXIT
|
|
115
123
|
|
|
116
124
|
ALL_FILES="$TMPDIR_IDX/all_files"
|
|
117
125
|
|
|
126
|
+
eagle_run_step "collect_files"
|
|
118
127
|
eagle_collect_files "$TARGET_DIR" "$ALL_FILES"
|
|
119
128
|
|
|
120
129
|
# Filter to source files only, skip large files
|
|
@@ -147,6 +156,7 @@ NEEDS_INDEX="$TMPDIR_IDX/needs_index"
|
|
|
147
156
|
skipped_count=0
|
|
148
157
|
|
|
149
158
|
if [ "$force" = true ]; then
|
|
159
|
+
eagle_run_step "force_clear_index_state"
|
|
150
160
|
eagle_info "Force rebuild requested: clearing chunks, declarations, and import edges"
|
|
151
161
|
eagle_graph_clear_index_state "$PROJECT"
|
|
152
162
|
fi
|
|
@@ -183,6 +193,7 @@ if [ "$needs_count" -eq 0 ]; then
|
|
|
183
193
|
fi
|
|
184
194
|
|
|
185
195
|
eagle_info "$needs_count files to index"
|
|
196
|
+
eagle_run_step "index_files count=$needs_count"
|
|
186
197
|
|
|
187
198
|
# ─── Chunk and index files ─────────────────────────────────
|
|
188
199
|
|
package/scripts/logs.sh
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════
|
|
3
|
+
# Eagle Mem — Command Run Logs
|
|
4
|
+
# ═══════════════════════════════════════════════════════════
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
LIB_DIR="$SCRIPTS_DIR/../lib"
|
|
9
|
+
|
|
10
|
+
. "$SCRIPTS_DIR/style.sh"
|
|
11
|
+
. "$LIB_DIR/common.sh"
|
|
12
|
+
|
|
13
|
+
cmd="${1:-list}"
|
|
14
|
+
[ $# -gt 0 ] && shift || true
|
|
15
|
+
|
|
16
|
+
show_help() {
|
|
17
|
+
cat <<EOF
|
|
18
|
+
Usage: eagle-mem logs [list|tail|show|prune] [run-id-or-filename]
|
|
19
|
+
|
|
20
|
+
Commands:
|
|
21
|
+
list Show recent command-scoped run logs
|
|
22
|
+
tail [id|filename] Tail a run log, or the latest run log when omitted
|
|
23
|
+
show <id|filename> Print a run log
|
|
24
|
+
prune [--days N] [--keep N]
|
|
25
|
+
Delete old run logs (defaults: 14 days, latest 50)
|
|
26
|
+
EOF
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
resolve_log_path() {
|
|
30
|
+
local ref="${1:-}"
|
|
31
|
+
local runs_root="${EAGLE_RUNS_DIR%/}" rel_ref
|
|
32
|
+
if [ -z "$ref" ]; then
|
|
33
|
+
ls -t "$runs_root"/*.log 2>/dev/null | while IFS= read -r candidate; do
|
|
34
|
+
[ -L "$candidate" ] && continue
|
|
35
|
+
[ -f "$candidate" ] && printf '%s\n' "$candidate" && break
|
|
36
|
+
done
|
|
37
|
+
return 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
case "$ref" in
|
|
41
|
+
*$'\n'*|*..*) return 1 ;;
|
|
42
|
+
/*)
|
|
43
|
+
rel_ref="${ref#"$runs_root"/}"
|
|
44
|
+
[ "$rel_ref" != "$ref" ] || return 1
|
|
45
|
+
case "$rel_ref" in ""|*/*) return 1 ;; esac
|
|
46
|
+
[ -L "$runs_root/$rel_ref" ] && return 1
|
|
47
|
+
[ -f "$runs_root/$rel_ref" ] && printf '%s\n' "$runs_root/$rel_ref" && return 0
|
|
48
|
+
return 1
|
|
49
|
+
;;
|
|
50
|
+
*/*) return 1 ;;
|
|
51
|
+
esac
|
|
52
|
+
|
|
53
|
+
if [ ! -L "$runs_root/$ref" ] && [ -f "$runs_root/$ref" ]; then
|
|
54
|
+
printf '%s\n' "$runs_root/$ref"
|
|
55
|
+
return 0
|
|
56
|
+
fi
|
|
57
|
+
if [ ! -L "$runs_root/$ref.log" ] && [ -f "$runs_root/$ref.log" ]; then
|
|
58
|
+
printf '%s\n' "$runs_root/$ref.log"
|
|
59
|
+
return 0
|
|
60
|
+
fi
|
|
61
|
+
return 1
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
run_log_count() {
|
|
65
|
+
[ -d "$EAGLE_RUNS_DIR" ] || {
|
|
66
|
+
printf '0\n'
|
|
67
|
+
return 0
|
|
68
|
+
}
|
|
69
|
+
find "$EAGLE_RUNS_DIR" -type f -name '*.log' -print 2>/dev/null | wc -l | tr -d ' '
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
case "$cmd" in
|
|
73
|
+
-h|--help|help)
|
|
74
|
+
show_help
|
|
75
|
+
;;
|
|
76
|
+
list)
|
|
77
|
+
eagle_header "Run Logs"
|
|
78
|
+
if ! ls "$EAGLE_RUNS_DIR"/*.log >/dev/null 2>&1; then
|
|
79
|
+
eagle_info "No run logs found yet"
|
|
80
|
+
eagle_dim "Run eagle-mem scan, index, or curate to create command-scoped logs."
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
ls -t "$EAGLE_RUNS_DIR"/*.log 2>/dev/null | head -20 | while IFS= read -r log_path; do
|
|
84
|
+
[ -L "$log_path" ] && continue
|
|
85
|
+
first_line=$(sed -n '1p' "$log_path" 2>/dev/null)
|
|
86
|
+
run_id=$(basename "$log_path" .log)
|
|
87
|
+
printf ' %s %s\n' "$run_id" "$first_line"
|
|
88
|
+
done
|
|
89
|
+
;;
|
|
90
|
+
tail)
|
|
91
|
+
log_path=$(resolve_log_path "${1:-}") || {
|
|
92
|
+
eagle_err "Run log not found"
|
|
93
|
+
exit 1
|
|
94
|
+
}
|
|
95
|
+
tail -n "${EAGLE_LOG_TAIL_LINES:-80}" "$log_path"
|
|
96
|
+
;;
|
|
97
|
+
show)
|
|
98
|
+
log_path=$(resolve_log_path "${1:-}") || {
|
|
99
|
+
eagle_err "Run log not found"
|
|
100
|
+
exit 1
|
|
101
|
+
}
|
|
102
|
+
cat "$log_path"
|
|
103
|
+
;;
|
|
104
|
+
prune)
|
|
105
|
+
days="${EAGLE_RUN_LOG_RETENTION_DAYS:-14}"
|
|
106
|
+
keep="${EAGLE_RUN_LOG_MAX_COUNT:-50}"
|
|
107
|
+
while [ $# -gt 0 ]; do
|
|
108
|
+
case "$1" in
|
|
109
|
+
--days)
|
|
110
|
+
days="${2:-}"
|
|
111
|
+
shift 2
|
|
112
|
+
;;
|
|
113
|
+
--keep)
|
|
114
|
+
keep="${2:-}"
|
|
115
|
+
shift 2
|
|
116
|
+
;;
|
|
117
|
+
*)
|
|
118
|
+
eagle_err "Unknown prune option: $1"
|
|
119
|
+
show_help
|
|
120
|
+
exit 1
|
|
121
|
+
;;
|
|
122
|
+
esac
|
|
123
|
+
done
|
|
124
|
+
case "$days" in ""|*[!0-9]*)
|
|
125
|
+
eagle_err "Invalid --days value: $days"
|
|
126
|
+
exit 1
|
|
127
|
+
;;
|
|
128
|
+
esac
|
|
129
|
+
case "$keep" in ""|*[!0-9]*)
|
|
130
|
+
eagle_err "Invalid --keep value: $keep"
|
|
131
|
+
exit 1
|
|
132
|
+
;;
|
|
133
|
+
esac
|
|
134
|
+
before=$(run_log_count)
|
|
135
|
+
eagle_run_prune_logs "$days" "$keep"
|
|
136
|
+
after=$(run_log_count)
|
|
137
|
+
eagle_ok "Pruned run logs: before=$before after=$after days=$days keep=$keep"
|
|
138
|
+
;;
|
|
139
|
+
*)
|
|
140
|
+
eagle_err "Unknown logs command: $cmd"
|
|
141
|
+
show_help
|
|
142
|
+
exit 1
|
|
143
|
+
;;
|
|
144
|
+
esac
|
package/scripts/scan.sh
CHANGED
|
@@ -29,6 +29,15 @@ TARGET_DIR="${args[0]:-.}"
|
|
|
29
29
|
TARGET_DIR="$(cd "$TARGET_DIR" && pwd)"
|
|
30
30
|
PROJECT=$(eagle_project_from_cwd "$TARGET_DIR")
|
|
31
31
|
|
|
32
|
+
TMPFILE=""
|
|
33
|
+
cleanup_scan() {
|
|
34
|
+
local rc=$?
|
|
35
|
+
[ -n "${TMPFILE:-}" ] && rm -f "$TMPFILE" "${TMPFILE}.analysis" 2>/dev/null || true
|
|
36
|
+
eagle_run_finish "$rc" "$LINENO"
|
|
37
|
+
}
|
|
38
|
+
eagle_run_start "scan" "$PROJECT" "$TARGET_DIR"
|
|
39
|
+
trap cleanup_scan EXIT
|
|
40
|
+
|
|
32
41
|
eagle_header "Scan"
|
|
33
42
|
eagle_info "Scanning ${BOLD}$PROJECT${RESET} at $TARGET_DIR"
|
|
34
43
|
echo ""
|
|
@@ -52,8 +61,8 @@ if git -C "$TARGET_DIR" rev-parse --is-inside-work-tree &>/dev/null; then
|
|
|
52
61
|
fi
|
|
53
62
|
|
|
54
63
|
TMPFILE=$(mktemp)
|
|
55
|
-
trap 'rm -f "$TMPFILE"' EXIT
|
|
56
64
|
|
|
65
|
+
eagle_run_step "collect_files"
|
|
57
66
|
eagle_collect_files "$TARGET_DIR" "$TMPFILE"
|
|
58
67
|
|
|
59
68
|
total_files=$(wc -l < "$TMPFILE" | tr -d ' ')
|
|
@@ -67,6 +76,7 @@ eagle_ok "$total_files files found"
|
|
|
67
76
|
|
|
68
77
|
# ─── Language breakdown (bash 3 compatible — no assoc arrays) ──
|
|
69
78
|
|
|
79
|
+
eagle_run_step "language_breakdown"
|
|
70
80
|
while IFS= read -r file; do
|
|
71
81
|
ext="${file##*.}"
|
|
72
82
|
[ "$ext" = "$file" ] && continue
|