eagle-mem 4.10.10 → 4.10.11
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 +13 -0
- package/README.md +5 -0
- package/bin/eagle-mem +1 -0
- package/hooks/post-tool-use.sh +9 -6
- 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 +85 -0
- package/lib/hooks-sessionstart.sh +60 -7
- package/lib/provider.sh +187 -40
- package/package.json +1 -1
- package/scripts/config.sh +2 -0
- package/scripts/curate.sh +9 -1
- package/scripts/health.sh +5 -7
- package/scripts/help.sh +1 -0
- package/scripts/index.sh +12 -1
- package/scripts/logs.sh +84 -0
- package/scripts/scan.sh +11 -1
- package/scripts/test.sh +1 -0
- package/tests/test_reliability_guards.sh +107 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ All notable changes to the **Eagle Mem** project are documented here.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## v4.10.11 Reliability Guards and Provider Fallback
|
|
8
|
+
|
|
9
|
+
This patch closes the active reliability items that remained after the Dream Cycle hotfix:
|
|
10
|
+
|
|
11
|
+
- **Command-Scoped Logs**: `scan`, `index`, and `curate` now write per-run logs under `~/.eagle-mem/runs`, preserve normal CLI output, and print the log path on command failure. Added `eagle-mem logs list|tail|show` for inspection.
|
|
12
|
+
- **Provider Fallback Transparency**: Provider calls now use an explicit fallback chain. `agent_cli` can fall through from a failed preferred Codex call to Claude Code when available, and provider display now shows the actual chain instead of `unknown`.
|
|
13
|
+
- **Read Prediction / Token Guard Scoring**: `PreToolUse` now scores repeated, large, or recently modified reads and emits a targeted read-score nudge. A configurable `read_guard.mode=block` path is available for stricter high-confidence duplicate-read gating.
|
|
14
|
+
- **Auto-Scan Retry Reliability**: SessionStart auto-scan/index freshness markers are now cleared when the background job fails, so failed scans do not block retries for the next 24 hours.
|
|
15
|
+
- **Hook Field Parsing**: Hook JSON field extraction now uses the intended unit separator in `PreToolUse`, `UserPromptSubmit`, and `Stop`, preserving clean `tool_name`, `session_id`, and `cwd` parsing.
|
|
16
|
+
- **Regression Coverage**: Added an isolated reliability test for provider fallback, read scoring, auto-scan failure state cleanup, and run-log creation.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
7
20
|
## v4.10.10 Dream Cycle Consolidation Hardening
|
|
8
21
|
|
|
9
22
|
This patch closes the review findings from the multi-model Spectral pass:
|
package/README.md
CHANGED
|
@@ -146,6 +146,7 @@ Eagle Mem prevents Claude from repeating past mistakes:
|
|
|
146
146
|
| `eagle-mem search` | Search past sessions, memories, and code |
|
|
147
147
|
| `eagle-mem health` | Diagnose pipeline health and background automation |
|
|
148
148
|
| `eagle-mem doctor` | Verify installed runtime files, hooks, SQLite/FTS5, statusline, manifest, and drift |
|
|
149
|
+
| `eagle-mem logs` | Inspect command-scoped `scan`, `index`, and `curate` run logs |
|
|
149
150
|
| `eagle-mem config` | View or change LLM provider and token-guard settings |
|
|
150
151
|
| `eagle-mem updates` | View or change auto-update policy |
|
|
151
152
|
| `eagle-mem guard` | Manage regression guardrails for files |
|
|
@@ -346,6 +347,8 @@ eagle-mem config set agent_cli.preferred current
|
|
|
346
347
|
|
|
347
348
|
Provider preference is local-first: Ollama is auto-detected when running, then Eagle Mem can use the installed Codex/Claude CLI via `agent_cli` before falling back to explicit Anthropic/OpenAI API providers. Eagle Mem works fully without a provider — LLM features gracefully degrade to heuristic fallbacks.
|
|
348
349
|
|
|
350
|
+
Provider calls use an explicit fallback chain by default. For example, `agent_cli` can try the preferred/current agent first and then fall through to another supported local CLI when available. `eagle-mem config`, `eagle-mem health`, and `eagle-mem curate` display the resolved provider path so failed or unavailable agent CLIs are visible instead of appearing as `unknown`.
|
|
351
|
+
|
|
349
352
|
RTK is configured separately from the LLM provider:
|
|
350
353
|
|
|
351
354
|
```bash
|
|
@@ -353,6 +356,8 @@ eagle-mem config set token_guard.rtk auto # default: use RTK when available
|
|
|
353
356
|
eagle-mem config set token_guard.rtk enforce # block known raw-output commands if RTK is missing
|
|
354
357
|
eagle-mem config set token_guard.rtk off # disable RTK behavior
|
|
355
358
|
eagle-mem config set token_guard.raw_bash block
|
|
359
|
+
eagle-mem config set read_guard.mode advisory # score repeated reads and nudge
|
|
360
|
+
eagle-mem config set read_guard.mode block # optionally block high-confidence duplicate reads
|
|
356
361
|
```
|
|
357
362
|
|
|
358
363
|
## Long-term Direction
|
package/bin/eagle-mem
CHANGED
|
@@ -22,6 +22,7 @@ case "$command" in
|
|
|
22
22
|
search) bash "$SCRIPTS_DIR/search.sh" "$@" ;;
|
|
23
23
|
health) bash "$SCRIPTS_DIR/health.sh" "$@" ;;
|
|
24
24
|
doctor) bash "$SCRIPTS_DIR/doctor.sh" "$PACKAGE_DIR" "$@" ;;
|
|
25
|
+
logs) bash "$SCRIPTS_DIR/logs.sh" "$@" ;;
|
|
25
26
|
config) bash "$SCRIPTS_DIR/config.sh" "$@" ;;
|
|
26
27
|
updates) bash "$SCRIPTS_DIR/updates.sh" "$@" ;;
|
|
27
28
|
statusline) "$SCRIPTS_DIR/statusline-em.sh" "$@" ;;
|
package/hooks/post-tool-use.sh
CHANGED
|
@@ -18,7 +18,7 @@ input=$(eagle_read_stdin)
|
|
|
18
18
|
[ -z "$input" ] && exit 0
|
|
19
19
|
|
|
20
20
|
IFS=$'\x1f' read -r session_id cwd tool_name hook_event <<< \
|
|
21
|
-
"$(echo "$input" | jq -r '[.session_id, .cwd, .tool_name, .hook_event_name] | map(. // "") | join("
|
|
21
|
+
"$(echo "$input" | jq -r '[.session_id, .cwd, .tool_name, .hook_event_name] | map(. // "") | join("\u001f")')"
|
|
22
22
|
agent=$(eagle_agent_source_from_json "$input")
|
|
23
23
|
|
|
24
24
|
if [ -z "$session_id" ]; then exit 0; fi
|
|
@@ -173,12 +173,15 @@ if [ -n "$fp" ] && [ -n "$session_id" ] && eagle_validate_session_id "$session_i
|
|
|
173
173
|
mod_dir="$EAGLE_MEM_DIR/mod-tracker"
|
|
174
174
|
mkdir -p "$mod_dir" 2>/dev/null
|
|
175
175
|
mod_file="$mod_dir/${session_id}"
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
# race when parallel PostToolUse hooks fire on same session
|
|
179
|
-
if [ -f "$mod_file" ]; then
|
|
176
|
+
mod_lock="${mod_file}.lock"
|
|
177
|
+
if mkdir "$mod_lock" 2>/dev/null; then
|
|
180
178
|
_mod_tmp=$(mktemp "${mod_file}.XXXXXX" 2>/dev/null) || _mod_tmp="${mod_file}.$$"
|
|
181
|
-
|
|
179
|
+
(cat "$mod_file" 2>/dev/null; printf '%s\n' "$fp") | tail -3 > "$_mod_tmp"
|
|
180
|
+
mv "$_mod_tmp" "$mod_file" 2>/dev/null || rm -f "$_mod_tmp"
|
|
181
|
+
rmdir "$mod_lock" 2>/dev/null || true
|
|
182
|
+
else
|
|
183
|
+
# If another hook is trimming, append is still safer than losing the edit.
|
|
184
|
+
printf '%s\n' "$fp" >> "$mod_file"
|
|
182
185
|
fi
|
|
183
186
|
|
|
184
187
|
# Full edit history for stuck loop detection (not truncated)
|
package/hooks/pre-tool-use.sh
CHANGED
|
@@ -22,7 +22,7 @@ input=$(eagle_read_stdin)
|
|
|
22
22
|
[ -z "$input" ] && exit 0
|
|
23
23
|
|
|
24
24
|
IFS=$'\x1f' read -r tool_name session_id cwd <<< \
|
|
25
|
-
"$(echo "$input" | jq -r '[.tool_name, .session_id, .cwd] | map(. // "") | join("")')"
|
|
25
|
+
"$(echo "$input" | jq -r '[.tool_name, .session_id, .cwd] | map(. // "") | join("\u001f")')"
|
|
26
26
|
agent=$(eagle_agent_source_from_json "$input")
|
|
27
27
|
|
|
28
28
|
case "$tool_name" in
|
|
@@ -295,11 +295,15 @@ Edit|Write|apply_patch)
|
|
|
295
295
|
Read)
|
|
296
296
|
fp=$(echo "$input" | jq -r '.tool_input.file_path // empty')
|
|
297
297
|
if [ -n "$fp" ] && [ -n "$session_id" ] && eagle_validate_session_id "$session_id"; then
|
|
298
|
+
read_score=0
|
|
299
|
+
read_reasons=""
|
|
298
300
|
|
|
299
301
|
# ─── Read-after-modify detection ──────────────────────
|
|
300
302
|
mod_file="$EAGLE_MEM_DIR/mod-tracker/${session_id}"
|
|
301
303
|
if [ -f "$mod_file" ] && grep -qFx -- "$fp" "$mod_file" 2>/dev/null; then
|
|
302
304
|
context+="Eagle Mem recall: '$(basename "$fp")' was just edited/written — the diff is already in context from the tool output. "
|
|
305
|
+
read_score=$((read_score + 45))
|
|
306
|
+
read_reasons="${read_reasons}recently modified; "
|
|
303
307
|
fi
|
|
304
308
|
|
|
305
309
|
# ─── Read dedup tracker (soft nudge) ──────────────────
|
|
@@ -312,6 +316,69 @@ Read)
|
|
|
312
316
|
if [ "$read_count" -ge 3 ]; then
|
|
313
317
|
context+="Eagle Mem recall: '$(basename "$fp")' has been read ${read_count} times this session. Its contents are likely already in context."
|
|
314
318
|
fi
|
|
319
|
+
|
|
320
|
+
if [ "$read_count" -ge 2 ]; then
|
|
321
|
+
repeat_score=$((20 + (read_count - 2) * 10))
|
|
322
|
+
[ "$repeat_score" -gt 40 ] && repeat_score=40
|
|
323
|
+
read_score=$((read_score + repeat_score))
|
|
324
|
+
read_reasons="${read_reasons}${read_count} reads this session; "
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
hot_files=$(eagle_get_hot_files "$project" 2>/dev/null || true)
|
|
328
|
+
if [ -n "$hot_files" ]; then
|
|
329
|
+
fp_base=$(basename "$fp")
|
|
330
|
+
case ",$hot_files," in
|
|
331
|
+
*"/$fp_base,"*|*",$fp_base,"*)
|
|
332
|
+
read_score=$((read_score + 10))
|
|
333
|
+
read_reasons="${read_reasons}hot file; "
|
|
334
|
+
;;
|
|
335
|
+
esac
|
|
336
|
+
fi
|
|
337
|
+
|
|
338
|
+
full_fp="$fp"
|
|
339
|
+
if [ ! -f "$full_fp" ] && [ -n "$cwd" ] && [ -f "$cwd/$fp" ]; then
|
|
340
|
+
full_fp="$cwd/$fp"
|
|
341
|
+
fi
|
|
342
|
+
if [ -f "$full_fp" ]; then
|
|
343
|
+
file_size=$(wc -c < "$full_fp" 2>/dev/null | tr -d ' ')
|
|
344
|
+
file_size=${file_size:-0}
|
|
345
|
+
if [ "$file_size" -ge 500000 ] 2>/dev/null; then
|
|
346
|
+
read_score=$((read_score + 20))
|
|
347
|
+
read_reasons="${read_reasons}large file; "
|
|
348
|
+
elif [ "$file_size" -ge 150000 ] 2>/dev/null; then
|
|
349
|
+
read_score=$((read_score + 10))
|
|
350
|
+
read_reasons="${read_reasons}medium-large file; "
|
|
351
|
+
fi
|
|
352
|
+
fi
|
|
353
|
+
|
|
354
|
+
[ "$read_score" -gt 100 ] && read_score=100
|
|
355
|
+
score_threshold=$(eagle_read_guard_score_threshold)
|
|
356
|
+
block_threshold=$(eagle_read_guard_block_threshold)
|
|
357
|
+
read_guard_mode=$(eagle_read_guard_mode)
|
|
358
|
+
read_reasons=${read_reasons%; }
|
|
359
|
+
if [ "$read_score" -ge "$score_threshold" ] 2>/dev/null; then
|
|
360
|
+
context+=" Eagle Mem read score: ${read_score}/100 for '$(basename "$fp")'"
|
|
361
|
+
[ -n "$read_reasons" ] && context+=" (${read_reasons})"
|
|
362
|
+
context+=". Prefer the existing context, recent diff, or targeted search unless you need exact fresh lines."
|
|
363
|
+
fi
|
|
364
|
+
if [ "$read_guard_mode" = "block" ] && [ "$read_score" -ge "$block_threshold" ] 2>/dev/null && ! eagle_raw_bash_unlock_active; then
|
|
365
|
+
reason="Eagle Mem blocked this high-confidence duplicate read to save context tokens.
|
|
366
|
+
|
|
367
|
+
File: $(basename "$fp")
|
|
368
|
+
Score: ${read_score}/100
|
|
369
|
+
Reason: ${read_reasons:-repeated read}
|
|
370
|
+
|
|
371
|
+
Use the existing context, run a narrower search, or bypass once with:
|
|
372
|
+
touch $EAGLE_RAW_BASH_UNLOCK"
|
|
373
|
+
jq -nc --arg reason "$reason" '{
|
|
374
|
+
"hookSpecificOutput":{
|
|
375
|
+
"hookEventName":"PreToolUse",
|
|
376
|
+
"permissionDecision":"deny",
|
|
377
|
+
"permissionDecisionReason":$reason
|
|
378
|
+
}
|
|
379
|
+
}'
|
|
380
|
+
exit 0
|
|
381
|
+
fi
|
|
315
382
|
fi
|
|
316
383
|
;;
|
|
317
384
|
esac
|
package/hooks/stop.sh
CHANGED
|
@@ -22,7 +22,7 @@ input=$(eagle_read_stdin)
|
|
|
22
22
|
[ -z "$input" ] && exit 0
|
|
23
23
|
|
|
24
24
|
IFS=$'\x1f' read -r session_id cwd transcript_path agent_type <<< \
|
|
25
|
-
"$(echo "$input" | jq -r '[.session_id, .cwd, .transcript_path, .agent_type] | map(. // "") | join("")')"
|
|
25
|
+
"$(echo "$input" | jq -r '[.session_id, .cwd, .transcript_path, .agent_type] | map(. // "") | join("\u001f")')"
|
|
26
26
|
last_assistant_message=$(echo "$input" | jq -r '.last_assistant_message // empty')
|
|
27
27
|
agent=$(eagle_agent_source_from_json "$input")
|
|
28
28
|
|
|
@@ -19,7 +19,7 @@ input=$(eagle_read_stdin)
|
|
|
19
19
|
[ -z "$input" ] && exit 0
|
|
20
20
|
|
|
21
21
|
IFS=$'\x1f' read -r session_id cwd <<< \
|
|
22
|
-
"$(echo "$input" | jq -r '[.session_id, .cwd] | map(. // "") | join("")')"
|
|
22
|
+
"$(echo "$input" | jq -r '[.session_id, .cwd] | map(. // "") | join("\u001f")')"
|
|
23
23
|
user_prompt=$(echo "$input" | jq -r '.prompt // empty')
|
|
24
24
|
agent=$(eagle_agent_source_from_json "$input")
|
|
25
25
|
|
package/lib/common.sh
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
EAGLE_MEM_DIR="${EAGLE_MEM_DIR:-$HOME/.eagle-mem}"
|
|
8
8
|
EAGLE_MEM_DB="$EAGLE_MEM_DIR/memory.db"
|
|
9
9
|
EAGLE_MEM_LOG="$EAGLE_MEM_DIR/eagle-mem.log"
|
|
10
|
+
EAGLE_RUNS_DIR="${EAGLE_RUNS_DIR:-$EAGLE_MEM_DIR/runs}"
|
|
10
11
|
EAGLE_SETTINGS="${EAGLE_SETTINGS:-$HOME/.claude/settings.json}"
|
|
11
12
|
EAGLE_SKILLS_DIR="${EAGLE_SKILLS_DIR:-$HOME/.claude/skills}"
|
|
12
13
|
EAGLE_CLAUDE_PROJECTS_DIR="${EAGLE_CLAUDE_PROJECTS_DIR:-$HOME/.claude/projects}"
|
|
@@ -112,6 +113,60 @@ eagle_log() {
|
|
|
112
113
|
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [$level] $*" >> "$EAGLE_MEM_LOG" 2>/dev/null || true
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
eagle_run_slug() {
|
|
117
|
+
printf '%s' "${1:-command}" \
|
|
118
|
+
| tr -c 'A-Za-z0-9._-' '-' \
|
|
119
|
+
| sed 's/^-*//;s/-*$//' \
|
|
120
|
+
| cut -c1-48
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
eagle_run_start() {
|
|
124
|
+
[ "${EAGLE_RUN_ACTIVE:-0}" = "1" ] && return 0
|
|
125
|
+
|
|
126
|
+
local command_name="$1" project="${2:-}" target="${3:-}"
|
|
127
|
+
local slug
|
|
128
|
+
slug=$(eagle_run_slug "$command_name")
|
|
129
|
+
[ -n "$slug" ] || slug="command"
|
|
130
|
+
|
|
131
|
+
mkdir -p "$EAGLE_RUNS_DIR" "$EAGLE_MEM_DIR" 2>/dev/null || true
|
|
132
|
+
EAGLE_RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)-${slug}-$$"
|
|
133
|
+
EAGLE_RUN_LOG="$EAGLE_RUNS_DIR/${EAGLE_RUN_ID}.log"
|
|
134
|
+
EAGLE_RUN_COMMAND="$command_name"
|
|
135
|
+
EAGLE_RUN_PROJECT="$project"
|
|
136
|
+
EAGLE_RUN_TARGET="$target"
|
|
137
|
+
EAGLE_RUN_ACTIVE=1
|
|
138
|
+
export EAGLE_RUN_ID EAGLE_RUN_LOG EAGLE_RUN_COMMAND EAGLE_RUN_PROJECT EAGLE_RUN_TARGET EAGLE_RUN_ACTIVE
|
|
139
|
+
|
|
140
|
+
touch "$EAGLE_RUN_LOG" 2>/dev/null && chmod 600 "$EAGLE_RUN_LOG" 2>/dev/null || true
|
|
141
|
+
{
|
|
142
|
+
printf '[%s] [INFO] run_start id=%s command=%s project=%s target=%s cwd=%s\n' \
|
|
143
|
+
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$EAGLE_RUN_ID" "$command_name" "$project" "$target" "$(pwd)"
|
|
144
|
+
} >> "$EAGLE_RUN_LOG" 2>/dev/null || true
|
|
145
|
+
|
|
146
|
+
# Keep normal CLI output intact while also preserving a command-scoped log.
|
|
147
|
+
exec > >(tee -a "$EAGLE_RUN_LOG") 2> >(tee -a "$EAGLE_RUN_LOG" >&2)
|
|
148
|
+
eagle_log "INFO" "Run started: id=$EAGLE_RUN_ID command=$command_name project=$project log=$EAGLE_RUN_LOG"
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
eagle_run_step() {
|
|
152
|
+
[ "${EAGLE_RUN_ACTIVE:-0}" = "1" ] || return 0
|
|
153
|
+
printf '[%s] [STEP] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*" >> "$EAGLE_RUN_LOG" 2>/dev/null || true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
eagle_run_finish() {
|
|
157
|
+
[ "${EAGLE_RUN_ACTIVE:-0}" = "1" ] || return 0
|
|
158
|
+
local rc="${1:-0}" line="${2:-unknown}"
|
|
159
|
+
printf '[%s] [INFO] run_finish id=%s rc=%s line=%s\n' \
|
|
160
|
+
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$EAGLE_RUN_ID" "$rc" "$line" >> "$EAGLE_RUN_LOG" 2>/dev/null || true
|
|
161
|
+
if [ "$rc" -ne 0 ] 2>/dev/null; then
|
|
162
|
+
eagle_log "ERROR" "Run failed: id=$EAGLE_RUN_ID command=${EAGLE_RUN_COMMAND:-unknown} rc=$rc line=$line log=${EAGLE_RUN_LOG:-}"
|
|
163
|
+
printf '\nEagle Mem command failed: %s (exit %s, line %s)\nLog: %s\n' \
|
|
164
|
+
"${EAGLE_RUN_COMMAND:-unknown}" "$rc" "$line" "${EAGLE_RUN_LOG:-unknown}" >&2
|
|
165
|
+
else
|
|
166
|
+
eagle_log "INFO" "Run finished: id=$EAGLE_RUN_ID command=${EAGLE_RUN_COMMAND:-unknown} rc=0"
|
|
167
|
+
fi
|
|
168
|
+
}
|
|
169
|
+
|
|
115
170
|
eagle_normalize_project_path() {
|
|
116
171
|
local path="${1:-$(pwd)}"
|
|
117
172
|
|
|
@@ -925,6 +980,36 @@ eagle_token_guard_raw_bash_mode() {
|
|
|
925
980
|
fi
|
|
926
981
|
}
|
|
927
982
|
|
|
983
|
+
eagle_read_guard_mode() {
|
|
984
|
+
if declare -F eagle_config_get >/dev/null 2>&1; then
|
|
985
|
+
eagle_config_get "read_guard" "mode" "advisory"
|
|
986
|
+
else
|
|
987
|
+
eagle_config_get_light "read_guard" "mode" "advisory"
|
|
988
|
+
fi
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
eagle_read_guard_score_threshold() {
|
|
992
|
+
local threshold
|
|
993
|
+
if declare -F eagle_config_get >/dev/null 2>&1; then
|
|
994
|
+
threshold=$(eagle_config_get "read_guard" "score_threshold" "70")
|
|
995
|
+
else
|
|
996
|
+
threshold=$(eagle_config_get_light "read_guard" "score_threshold" "70")
|
|
997
|
+
fi
|
|
998
|
+
case "$threshold" in *[!0-9]*|"") threshold=70 ;; esac
|
|
999
|
+
printf '%s\n' "$threshold"
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
eagle_read_guard_block_threshold() {
|
|
1003
|
+
local threshold
|
|
1004
|
+
if declare -F eagle_config_get >/dev/null 2>&1; then
|
|
1005
|
+
threshold=$(eagle_config_get "read_guard" "block_threshold" "90")
|
|
1006
|
+
else
|
|
1007
|
+
threshold=$(eagle_config_get_light "read_guard" "block_threshold" "90")
|
|
1008
|
+
fi
|
|
1009
|
+
case "$threshold" in *[!0-9]*|"") threshold=90 ;; esac
|
|
1010
|
+
printf '%s\n' "$threshold"
|
|
1011
|
+
}
|
|
1012
|
+
|
|
928
1013
|
eagle_raw_output_command_needs_guard() {
|
|
929
1014
|
local cmd="$1"
|
|
930
1015
|
local first
|
|
@@ -12,18 +12,23 @@ _eagle_state_slug() {
|
|
|
12
12
|
printf '%s' "$1" | shasum | cut -c1-12
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
_eagle_state_file() {
|
|
16
|
+
local key="$1" project="$2"
|
|
17
|
+
local safe_project; safe_project=$(_eagle_state_slug "$project")
|
|
18
|
+
printf '%s/%s-%s\n' "$_state_dir" "$key" "$safe_project"
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
_eagle_state_fresh() {
|
|
16
22
|
local key="$1" project="$2" max_age_days="${3:-1}"
|
|
17
|
-
local
|
|
18
|
-
local state_file="$_state_dir/${key}-${safe_project}"
|
|
23
|
+
local state_file; state_file=$(_eagle_state_file "$key" "$project")
|
|
19
24
|
[ -f "$state_file" ] && [ -z "$(find "$state_file" -mtime +${max_age_days} 2>/dev/null)" ]
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
_eagle_state_touch() {
|
|
23
28
|
local key="$1" project="$2"
|
|
24
|
-
local
|
|
29
|
+
local state_file; state_file=$(_eagle_state_file "$key" "$project")
|
|
25
30
|
mkdir -p "$_state_dir" 2>/dev/null
|
|
26
|
-
touch "$
|
|
31
|
+
touch "$state_file"
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
eagle_sessionstart_auto_provision() {
|
|
@@ -51,15 +56,63 @@ eagle_sessionstart_auto_provision() {
|
|
|
51
56
|
eagle_log "INFO" "SessionStart: first-session provision — scan then index"
|
|
52
57
|
_eagle_state_touch "scan" "$project"
|
|
53
58
|
_eagle_state_touch "index" "$project"
|
|
54
|
-
|
|
59
|
+
scan_state=$(_eagle_state_file "scan" "$project")
|
|
60
|
+
index_state=$(_eagle_state_file "index" "$project")
|
|
61
|
+
nohup bash -c '
|
|
62
|
+
scripts_dir="$1"; cwd="$2"; log="$3"; scan_state="$4"; index_state="$5"
|
|
63
|
+
bash "$scripts_dir/scan.sh" "$cwd" >> "$log" 2>&1
|
|
64
|
+
scan_rc=$?
|
|
65
|
+
if [ "$scan_rc" -eq 0 ]; then
|
|
66
|
+
touch "$scan_state" 2>/dev/null || true
|
|
67
|
+
else
|
|
68
|
+
rm -f "$scan_state" 2>/dev/null || true
|
|
69
|
+
printf "[%s] [ERROR] SessionStart: auto-scan failed rc=%s\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$scan_rc" >> "$log" 2>/dev/null || true
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
bash "$scripts_dir/index.sh" "$cwd" >> "$log" 2>&1
|
|
73
|
+
index_rc=$?
|
|
74
|
+
if [ "$index_rc" -eq 0 ]; then
|
|
75
|
+
touch "$index_state" 2>/dev/null || true
|
|
76
|
+
else
|
|
77
|
+
rm -f "$index_state" 2>/dev/null || true
|
|
78
|
+
printf "[%s] [ERROR] SessionStart: auto-index failed rc=%s\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$index_rc" >> "$log" 2>/dev/null || true
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
[ "$scan_rc" -eq 0 ] && exit "$index_rc"
|
|
82
|
+
exit "$scan_rc"
|
|
83
|
+
' eagle-auto "$scripts_dir" "$cwd" "$EAGLE_MEM_LOG" "$scan_state" "$index_state" &
|
|
55
84
|
elif [ "$needs_scan" = true ]; then
|
|
56
85
|
eagle_log "INFO" "SessionStart: auto-scan triggered"
|
|
57
86
|
_eagle_state_touch "scan" "$project"
|
|
58
|
-
|
|
87
|
+
scan_state=$(_eagle_state_file "scan" "$project")
|
|
88
|
+
nohup bash -c '
|
|
89
|
+
scripts_dir="$1"; cwd="$2"; log="$3"; scan_state="$4"
|
|
90
|
+
bash "$scripts_dir/scan.sh" "$cwd" >> "$log" 2>&1
|
|
91
|
+
rc=$?
|
|
92
|
+
if [ "$rc" -eq 0 ]; then
|
|
93
|
+
touch "$scan_state" 2>/dev/null || true
|
|
94
|
+
else
|
|
95
|
+
rm -f "$scan_state" 2>/dev/null || true
|
|
96
|
+
printf "[%s] [ERROR] SessionStart: auto-scan failed rc=%s\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$rc" >> "$log" 2>/dev/null || true
|
|
97
|
+
fi
|
|
98
|
+
exit "$rc"
|
|
99
|
+
' eagle-auto "$scripts_dir" "$cwd" "$EAGLE_MEM_LOG" "$scan_state" &
|
|
59
100
|
elif [ "$needs_index" = true ]; then
|
|
60
101
|
eagle_log "INFO" "SessionStart: auto-index triggered"
|
|
61
102
|
_eagle_state_touch "index" "$project"
|
|
62
|
-
|
|
103
|
+
index_state=$(_eagle_state_file "index" "$project")
|
|
104
|
+
nohup bash -c '
|
|
105
|
+
scripts_dir="$1"; cwd="$2"; log="$3"; index_state="$4"
|
|
106
|
+
bash "$scripts_dir/index.sh" "$cwd" >> "$log" 2>&1
|
|
107
|
+
rc=$?
|
|
108
|
+
if [ "$rc" -eq 0 ]; then
|
|
109
|
+
touch "$index_state" 2>/dev/null || true
|
|
110
|
+
else
|
|
111
|
+
rm -f "$index_state" 2>/dev/null || true
|
|
112
|
+
printf "[%s] [ERROR] SessionStart: auto-index failed rc=%s\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$rc" >> "$log" 2>/dev/null || true
|
|
113
|
+
fi
|
|
114
|
+
exit "$rc"
|
|
115
|
+
' eagle-auto "$scripts_dir" "$cwd" "$EAGLE_MEM_LOG" "$index_state" &
|
|
63
116
|
fi
|
|
64
117
|
}
|
|
65
118
|
|
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,57 @@ _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
|
-
elif command -v codex &>/dev/null; then
|
|
294
|
-
echo "codex"
|
|
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
|
|
311
|
-
;;
|
|
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
|
+
*) preferred_target="$preferred" ;;
|
|
312
413
|
esac
|
|
414
|
+
|
|
415
|
+
for candidate in "$preferred_target" "$current" codex claude-code; do
|
|
416
|
+
case "$candidate" in
|
|
417
|
+
codex|claude-code) ;;
|
|
418
|
+
*) continue ;;
|
|
419
|
+
esac
|
|
420
|
+
case "$candidate" in
|
|
421
|
+
codex) command -v codex >/dev/null 2>&1 || continue ;;
|
|
422
|
+
claude-code) command -v claude >/dev/null 2>&1 || continue ;;
|
|
423
|
+
esac
|
|
424
|
+
case " $targets " in *" $candidate "*) continue ;; esac
|
|
425
|
+
targets="${targets}${targets:+ }$candidate"
|
|
426
|
+
done
|
|
427
|
+
|
|
428
|
+
printf '%s\n' "$targets" | tr ' ' '\n' | sed '/^$/d'
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
_eagle_agent_cli_target_summary() {
|
|
432
|
+
local chain summary="" sep="" target
|
|
433
|
+
chain=$(_eagle_agent_cli_target_chain)
|
|
434
|
+
while IFS= read -r target; do
|
|
435
|
+
[ -z "$target" ] && continue
|
|
436
|
+
summary="${summary}${sep}${target}"
|
|
437
|
+
sep=" -> "
|
|
438
|
+
done <<< "$chain"
|
|
439
|
+
[ -n "$summary" ] || summary="none"
|
|
440
|
+
printf '%s\n' "$summary"
|
|
313
441
|
}
|
|
314
442
|
|
|
315
443
|
_eagle_agent_cli_prompt_file() {
|
|
@@ -327,17 +455,32 @@ _eagle_agent_cli_prompt_file() {
|
|
|
327
455
|
|
|
328
456
|
_eagle_call_agent_cli() {
|
|
329
457
|
local prompt="$1" system="$2" max_tokens="$3"
|
|
330
|
-
local target
|
|
331
|
-
|
|
458
|
+
local target targets result rc tried=0
|
|
459
|
+
targets=$(_eagle_agent_cli_target_chain)
|
|
332
460
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
461
|
+
if [ -z "$targets" ]; then
|
|
462
|
+
eagle_log "ERROR" "agent_cli provider unavailable: no supported Codex or Claude CLI found"
|
|
463
|
+
return 1
|
|
464
|
+
fi
|
|
465
|
+
|
|
466
|
+
while IFS= read -r target; do
|
|
467
|
+
[ -z "$target" ] && continue
|
|
468
|
+
tried=$((tried + 1))
|
|
469
|
+
case "$target" in
|
|
470
|
+
codex) result=$(_eagle_call_codex_cli "$prompt" "$system" "$max_tokens"); rc=$? ;;
|
|
471
|
+
claude-code) result=$(_eagle_call_claude_cli "$prompt" "$system" "$max_tokens"); rc=$? ;;
|
|
472
|
+
*) rc=1; result="" ;;
|
|
473
|
+
esac
|
|
474
|
+
if [ "$rc" -eq 0 ] && [ -n "$result" ]; then
|
|
475
|
+
[ "$tried" -gt 1 ] && eagle_log "INFO" "agent_cli fallback succeeded with $target"
|
|
476
|
+
printf '%s\n' "$result"
|
|
477
|
+
return 0
|
|
478
|
+
fi
|
|
479
|
+
eagle_log "WARN" "agent_cli target failed: target=$target rc=$rc"
|
|
480
|
+
done <<< "$targets"
|
|
481
|
+
|
|
482
|
+
eagle_log "ERROR" "All agent_cli targets failed: targets=$(printf '%s' "$targets" | tr '\n' ',')"
|
|
483
|
+
return 1
|
|
341
484
|
}
|
|
342
485
|
|
|
343
486
|
_eagle_call_codex_cli() {
|
|
@@ -539,16 +682,19 @@ eagle_show_config() {
|
|
|
539
682
|
return 1
|
|
540
683
|
fi
|
|
541
684
|
|
|
542
|
-
local provider model
|
|
685
|
+
local provider model fallback
|
|
543
686
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
687
|
+
fallback=$(eagle_config_get "provider" "fallback" "auto")
|
|
544
688
|
if [ "$provider" = "agent_cli" ]; then
|
|
545
|
-
model=$(
|
|
689
|
+
model=$(_eagle_agent_cli_target_summary)
|
|
546
690
|
else
|
|
547
691
|
model=$(eagle_config_get "$provider" "model" "unknown")
|
|
548
692
|
fi
|
|
549
693
|
|
|
550
694
|
echo "Provider: $provider"
|
|
551
695
|
echo "Model: $model"
|
|
696
|
+
echo "Fallback: $fallback"
|
|
697
|
+
echo "Chain: $(eagle_llm_provider_label)"
|
|
552
698
|
|
|
553
699
|
if [ "$provider" = "ollama" ]; then
|
|
554
700
|
local url
|
|
@@ -564,6 +710,7 @@ eagle_show_config() {
|
|
|
564
710
|
fi
|
|
565
711
|
elif [ "$provider" = "agent_cli" ]; then
|
|
566
712
|
echo "Preferred: $(eagle_config_get "agent_cli" "preferred" "current")"
|
|
713
|
+
echo "Targets: $(_eagle_agent_cli_target_summary)"
|
|
567
714
|
echo "Codex: $(command -v codex 2>/dev/null || echo "not found")"
|
|
568
715
|
echo "Claude: $(command -v claude 2>/dev/null || echo "not found")"
|
|
569
716
|
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
|
@@ -100,19 +100,27 @@ fi
|
|
|
100
100
|
|
|
101
101
|
p_esc=$(eagle_sql_escape "$project")
|
|
102
102
|
|
|
103
|
+
cleanup_curate() {
|
|
104
|
+
local rc=$?
|
|
105
|
+
eagle_run_finish "$rc" "$LINENO"
|
|
106
|
+
}
|
|
107
|
+
eagle_run_start "curate" "$project" "$(pwd)"
|
|
108
|
+
trap cleanup_curate EXIT
|
|
109
|
+
|
|
103
110
|
# Verify provider is configured
|
|
104
111
|
provider=$(eagle_config_get "provider" "type" "none")
|
|
105
112
|
if [ "$provider" = "none" ]; then
|
|
106
113
|
eagle_err "No LLM provider configured. Run: eagle-mem config init"
|
|
107
114
|
exit 1
|
|
108
115
|
fi
|
|
109
|
-
eagle_info "Provider: $
|
|
116
|
+
eagle_info "Provider: $(eagle_llm_provider_label)"
|
|
110
117
|
eagle_info "Project: $project"
|
|
111
118
|
[ "$DRY_RUN" -eq 1 ] && eagle_info "Dry run — no changes will be made"
|
|
112
119
|
echo ""
|
|
113
120
|
|
|
114
121
|
# ─── 1. Analyze gotchas for promotion ─────────────────────
|
|
115
122
|
|
|
123
|
+
eagle_run_step "promote_gotchas"
|
|
116
124
|
eagle_info "Analyzing gotchas for promotion..."
|
|
117
125
|
|
|
118
126
|
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 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,84 @@
|
|
|
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] [run-id-or-path]
|
|
19
|
+
|
|
20
|
+
Commands:
|
|
21
|
+
list Show recent command-scoped run logs
|
|
22
|
+
tail [id|path] Tail a run log, or the latest run log when omitted
|
|
23
|
+
show <id|path> Print a run log
|
|
24
|
+
EOF
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
resolve_log_path() {
|
|
28
|
+
local ref="${1:-}"
|
|
29
|
+
if [ -z "$ref" ]; then
|
|
30
|
+
ls -t "$EAGLE_RUNS_DIR"/*.log 2>/dev/null | sed -n '1p'
|
|
31
|
+
return 0
|
|
32
|
+
fi
|
|
33
|
+
if [ -f "$ref" ]; then
|
|
34
|
+
printf '%s\n' "$ref"
|
|
35
|
+
return 0
|
|
36
|
+
fi
|
|
37
|
+
if [ -f "$EAGLE_RUNS_DIR/$ref" ]; then
|
|
38
|
+
printf '%s\n' "$EAGLE_RUNS_DIR/$ref"
|
|
39
|
+
return 0
|
|
40
|
+
fi
|
|
41
|
+
if [ -f "$EAGLE_RUNS_DIR/$ref.log" ]; then
|
|
42
|
+
printf '%s\n' "$EAGLE_RUNS_DIR/$ref.log"
|
|
43
|
+
return 0
|
|
44
|
+
fi
|
|
45
|
+
return 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
case "$cmd" in
|
|
49
|
+
-h|--help|help)
|
|
50
|
+
show_help
|
|
51
|
+
;;
|
|
52
|
+
list)
|
|
53
|
+
eagle_header "Run Logs"
|
|
54
|
+
if ! ls "$EAGLE_RUNS_DIR"/*.log >/dev/null 2>&1; then
|
|
55
|
+
eagle_info "No run logs found yet"
|
|
56
|
+
eagle_dim "Run eagle-mem scan, index, or curate to create command-scoped logs."
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
59
|
+
ls -t "$EAGLE_RUNS_DIR"/*.log 2>/dev/null | head -20 | while IFS= read -r log_path; do
|
|
60
|
+
first_line=$(sed -n '1p' "$log_path" 2>/dev/null)
|
|
61
|
+
run_id=$(basename "$log_path" .log)
|
|
62
|
+
printf ' %s %s\n' "$run_id" "$first_line"
|
|
63
|
+
done
|
|
64
|
+
;;
|
|
65
|
+
tail)
|
|
66
|
+
log_path=$(resolve_log_path "${1:-}") || {
|
|
67
|
+
eagle_err "Run log not found"
|
|
68
|
+
exit 1
|
|
69
|
+
}
|
|
70
|
+
tail -n "${EAGLE_LOG_TAIL_LINES:-80}" "$log_path"
|
|
71
|
+
;;
|
|
72
|
+
show)
|
|
73
|
+
log_path=$(resolve_log_path "${1:-}") || {
|
|
74
|
+
eagle_err "Run log not found"
|
|
75
|
+
exit 1
|
|
76
|
+
}
|
|
77
|
+
cat "$log_path"
|
|
78
|
+
;;
|
|
79
|
+
*)
|
|
80
|
+
eagle_err "Unknown logs command: $cmd"
|
|
81
|
+
show_help
|
|
82
|
+
exit 1
|
|
83
|
+
;;
|
|
84
|
+
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
|
package/scripts/test.sh
CHANGED
|
@@ -53,6 +53,7 @@ run_check "Installer And Updater (install / update syntax)" "bash -n \"$SCRIPTS_
|
|
|
53
53
|
run_check "Code Scan And Index (scan / index syntax)" "bash -n \"$SCRIPTS_DIR/scan.sh\" && bash -n \"$SCRIPTS_DIR/index.sh\""
|
|
54
54
|
run_check "Graph Memory Rebuild (isolated regression suite)" "bash \"$SCRIPTS_DIR/../tests/test_graph_memory.sh\""
|
|
55
55
|
run_check "Dream Cycle Memory Graph Wiring (isolated regression suite)" "bash \"$SCRIPTS_DIR/../tests/test_curate_graph_memories.sh\""
|
|
56
|
+
run_check "Reliability Guards (provider fallback, logs, autoscan, read scoring)" "bash \"$SCRIPTS_DIR/../tests/test_reliability_guards.sh\""
|
|
56
57
|
|
|
57
58
|
echo ""
|
|
58
59
|
if [ "$errors" -eq 0 ]; then
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
5
|
+
tmp_dir=$(mktemp -d)
|
|
6
|
+
trap 'rm -rf "$tmp_dir"' EXIT
|
|
7
|
+
|
|
8
|
+
assert_contains() {
|
|
9
|
+
local haystack="$1" needle="$2" message="$3"
|
|
10
|
+
case "$haystack" in
|
|
11
|
+
*"$needle"*) ;;
|
|
12
|
+
*)
|
|
13
|
+
echo "$message" >&2
|
|
14
|
+
echo "Expected to find: $needle" >&2
|
|
15
|
+
echo "Actual: $haystack" >&2
|
|
16
|
+
exit 1
|
|
17
|
+
;;
|
|
18
|
+
esac
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Provider fallback: a failed preferred Codex CLI should fall through to Claude.
|
|
22
|
+
provider_home="$tmp_dir/provider-home"
|
|
23
|
+
fake_bin="$tmp_dir/bin"
|
|
24
|
+
mkdir -p "$provider_home" "$fake_bin"
|
|
25
|
+
cat > "$fake_bin/codex" <<'SH'
|
|
26
|
+
#!/usr/bin/env bash
|
|
27
|
+
exit 42
|
|
28
|
+
SH
|
|
29
|
+
cat > "$fake_bin/claude" <<'SH'
|
|
30
|
+
#!/usr/bin/env bash
|
|
31
|
+
printf 'claude fallback ok\n'
|
|
32
|
+
SH
|
|
33
|
+
chmod +x "$fake_bin/codex" "$fake_bin/claude"
|
|
34
|
+
cat > "$provider_home/config.toml" <<'TOML'
|
|
35
|
+
[provider]
|
|
36
|
+
type = "agent_cli"
|
|
37
|
+
fallback = "auto"
|
|
38
|
+
|
|
39
|
+
[agent_cli]
|
|
40
|
+
preferred = "codex"
|
|
41
|
+
codex_model = ""
|
|
42
|
+
claude_model = ""
|
|
43
|
+
TOML
|
|
44
|
+
provider_result=$(EAGLE_MEM_DIR="$provider_home" PATH="$fake_bin:$PATH" bash -c "
|
|
45
|
+
. '$ROOT_DIR/lib/common.sh'
|
|
46
|
+
. '$ROOT_DIR/lib/provider.sh'
|
|
47
|
+
eagle_llm_call 'say ok' 'system' 20
|
|
48
|
+
")
|
|
49
|
+
assert_contains "$provider_result" "claude fallback ok" "agent_cli fallback did not use Claude after Codex failed"
|
|
50
|
+
|
|
51
|
+
# PreToolUse parsing + read scoring: repeated large read after modification should emit scored context.
|
|
52
|
+
hook_home="$tmp_dir/hook-home"
|
|
53
|
+
repo="$tmp_dir/repo"
|
|
54
|
+
mkdir -p "$hook_home/mod-tracker" "$repo"
|
|
55
|
+
touch "$hook_home/memory.db"
|
|
56
|
+
large_file="$repo/large.txt"
|
|
57
|
+
dd if=/dev/zero bs=1024 count=600 2>/dev/null | tr '\0' 'x' > "$large_file"
|
|
58
|
+
session_id="session_reliability_123"
|
|
59
|
+
printf '%s\n' "$large_file" > "$hook_home/mod-tracker/$session_id"
|
|
60
|
+
read_input=$(jq -nc --arg fp "$large_file" --arg sid "$session_id" --arg cwd "$repo" \
|
|
61
|
+
'{tool_name:"Read",session_id:$sid,cwd:$cwd,tool_input:{file_path:$fp}}')
|
|
62
|
+
EAGLE_MEM_DIR="$hook_home" EAGLE_MEM_PROJECT="project-read" bash "$ROOT_DIR/hooks/pre-tool-use.sh" <<< "$read_input" >/dev/null
|
|
63
|
+
EAGLE_MEM_DIR="$hook_home" EAGLE_MEM_PROJECT="project-read" bash "$ROOT_DIR/hooks/pre-tool-use.sh" <<< "$read_input" >/dev/null
|
|
64
|
+
read_output=$(EAGLE_MEM_DIR="$hook_home" EAGLE_MEM_PROJECT="project-read" bash "$ROOT_DIR/hooks/pre-tool-use.sh" <<< "$read_input")
|
|
65
|
+
assert_contains "$read_output" "Eagle Mem read score" "Read guard did not emit scored duplicate-read context"
|
|
66
|
+
grep -q 'mod_file}.lock' "$ROOT_DIR/hooks/post-tool-use.sh" || {
|
|
67
|
+
echo "post-tool-use modification tracker should use a lock directory" >&2
|
|
68
|
+
exit 1
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Auto-scan state race: failed background scan must clear the freshness marker.
|
|
72
|
+
state_home="$tmp_dir/state-home"
|
|
73
|
+
auto_scripts="$tmp_dir/auto-scripts"
|
|
74
|
+
auto_repo="$tmp_dir/auto-repo"
|
|
75
|
+
mkdir -p "$state_home" "$auto_scripts" "$auto_repo"
|
|
76
|
+
cat > "$auto_scripts/scan.sh" <<'SH'
|
|
77
|
+
#!/usr/bin/env bash
|
|
78
|
+
exit 9
|
|
79
|
+
SH
|
|
80
|
+
cat > "$auto_scripts/index.sh" <<'SH'
|
|
81
|
+
#!/usr/bin/env bash
|
|
82
|
+
exit 0
|
|
83
|
+
SH
|
|
84
|
+
EAGLE_MEM_DIR="$state_home" EAGLE_MEM_LOG="$state_home/eagle-mem.log" bash -c "
|
|
85
|
+
. '$ROOT_DIR/lib/common.sh'
|
|
86
|
+
. '$ROOT_DIR/lib/hooks-sessionstart.sh'
|
|
87
|
+
eagle_get_overview() { return 0; }
|
|
88
|
+
eagle_db() { printf '0\n'; }
|
|
89
|
+
eagle_sessionstart_auto_provision 'project-auto-fail' '$auto_repo' '$auto_scripts'
|
|
90
|
+
scan_state=\$(_eagle_state_file scan 'project-auto-fail')
|
|
91
|
+
for _i in 1 2 3 4 5 6 7 8 9 10; do
|
|
92
|
+
grep -q 'auto-scan failed' '$state_home/eagle-mem.log' 2>/dev/null && break
|
|
93
|
+
sleep 0.2
|
|
94
|
+
done
|
|
95
|
+
[ ! -f \"\$scan_state\" ]
|
|
96
|
+
"
|
|
97
|
+
|
|
98
|
+
# Command-scoped logs: scan should create an inspectable run log and logs list should show it.
|
|
99
|
+
log_home="$tmp_dir/log-home"
|
|
100
|
+
log_repo="$tmp_dir/log-repo"
|
|
101
|
+
mkdir -p "$log_home" "$log_repo"
|
|
102
|
+
printf '# demo\n' > "$log_repo/README.md"
|
|
103
|
+
EAGLE_MEM_DIR="$log_home" bash "$ROOT_DIR/scripts/scan.sh" "$log_repo" >/dev/null
|
|
104
|
+
log_list=$(EAGLE_MEM_DIR="$log_home" bash "$ROOT_DIR/bin/eagle-mem" logs list)
|
|
105
|
+
assert_contains "$log_list" "command=scan" "logs list did not show the scan command run"
|
|
106
|
+
|
|
107
|
+
echo "reliability guard regressions passed"
|