eagle-mem 2.0.6 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -8
- package/bin/eagle-mem +3 -0
- package/db/012_enriched_summaries.sql +51 -0
- package/db/013_features.sql +69 -0
- package/db/014_command_intelligence.sql +25 -0
- package/hooks/post-tool-use.sh +131 -18
- package/hooks/pre-tool-use.sh +124 -0
- package/hooks/session-start.sh +140 -26
- package/hooks/stop.sh +19 -2
- package/hooks/user-prompt-submit.sh +7 -1
- package/lib/common.sh +22 -0
- package/lib/db.sh +158 -7
- package/lib/provider.sh +330 -0
- package/package.json +1 -1
- package/scripts/config.sh +72 -0
- package/scripts/curate.sh +349 -0
- package/scripts/feature.sh +110 -0
- package/scripts/help.sh +3 -0
- package/scripts/index.sh +1 -1
- package/scripts/install.sh +14 -0
- package/scripts/style.sh +4 -6
- package/scripts/update.sh +1 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════
|
|
3
|
+
# Eagle Mem — Curator
|
|
4
|
+
# Self-learning engine that analyzes sessions and generates:
|
|
5
|
+
# 1. Promoted gotchas → persistent memories
|
|
6
|
+
# 2. Superseded decision detection
|
|
7
|
+
# 3. Command compression rules
|
|
8
|
+
# 4. Feature auto-discovery
|
|
9
|
+
# ═══════════════════════════════════════════════════════════
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
13
|
+
LIB_DIR="$SCRIPT_DIR/../lib"
|
|
14
|
+
|
|
15
|
+
. "$LIB_DIR/common.sh"
|
|
16
|
+
. "$SCRIPT_DIR/style.sh"
|
|
17
|
+
. "$LIB_DIR/db.sh"
|
|
18
|
+
. "$LIB_DIR/provider.sh"
|
|
19
|
+
|
|
20
|
+
eagle_header "Curator"
|
|
21
|
+
|
|
22
|
+
DRY_RUN=0
|
|
23
|
+
FULL=0
|
|
24
|
+
project=""
|
|
25
|
+
|
|
26
|
+
while [ $# -gt 0 ]; do
|
|
27
|
+
case "$1" in
|
|
28
|
+
--dry-run) DRY_RUN=1; shift ;;
|
|
29
|
+
--full) FULL=1; shift ;;
|
|
30
|
+
-p|--project) project="$2"; shift 2 ;;
|
|
31
|
+
*) shift ;;
|
|
32
|
+
esac
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
if [ -z "$project" ]; then
|
|
36
|
+
project=$(eagle_project_from_cwd "$(pwd)")
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
p_esc=$(eagle_sql_escape "$project")
|
|
40
|
+
|
|
41
|
+
# Verify provider is configured
|
|
42
|
+
provider=$(eagle_config_get "provider" "type" "none")
|
|
43
|
+
if [ "$provider" = "none" ]; then
|
|
44
|
+
eagle_err "No LLM provider configured. Run: eagle-mem config init"
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
eagle_info "Provider: $provider ($(eagle_config_get "$provider" "model" "unknown"))"
|
|
48
|
+
eagle_info "Project: $project"
|
|
49
|
+
[ "$DRY_RUN" -eq 1 ] && eagle_info "Dry run — no changes will be made"
|
|
50
|
+
echo ""
|
|
51
|
+
|
|
52
|
+
# ─── 1. Analyze gotchas for promotion ─────────────────────
|
|
53
|
+
|
|
54
|
+
eagle_info "Analyzing gotchas for promotion..."
|
|
55
|
+
|
|
56
|
+
recent_gotchas=$(eagle_db "SELECT gotchas, created_at
|
|
57
|
+
FROM summaries
|
|
58
|
+
WHERE project = '$p_esc'
|
|
59
|
+
AND gotchas IS NOT NULL AND gotchas != ''
|
|
60
|
+
ORDER BY created_at DESC
|
|
61
|
+
LIMIT 20;")
|
|
62
|
+
|
|
63
|
+
if [ -n "$recent_gotchas" ]; then
|
|
64
|
+
gotcha_prompt="Analyze these gotchas from recent development sessions on project '$project'. Identify any that appear multiple times or are important enough to become permanent project knowledge.
|
|
65
|
+
|
|
66
|
+
GOTCHAS:
|
|
67
|
+
$recent_gotchas
|
|
68
|
+
|
|
69
|
+
For each gotcha worth promoting, output EXACTLY this format (one per line):
|
|
70
|
+
PROMOTE: <one-line gotcha summary>
|
|
71
|
+
|
|
72
|
+
Only promote gotchas that are:
|
|
73
|
+
1. Repeated across multiple sessions (same mistake happening again)
|
|
74
|
+
2. Non-obvious and likely to be forgotten
|
|
75
|
+
3. Specific enough to be actionable
|
|
76
|
+
|
|
77
|
+
If none qualify, output: NONE"
|
|
78
|
+
|
|
79
|
+
gotcha_result=$(eagle_llm_call "$gotcha_prompt" "You analyze software development patterns. Be concise. Only output PROMOTE lines or NONE." 512)
|
|
80
|
+
|
|
81
|
+
if [ -n "$gotcha_result" ] && ! echo "$gotcha_result" | grep -q "^NONE$"; then
|
|
82
|
+
promoted=0
|
|
83
|
+
while IFS= read -r line; do
|
|
84
|
+
case "$line" in
|
|
85
|
+
PROMOTE:*)
|
|
86
|
+
gotcha_text=$(echo "$line" | sed 's/^PROMOTE:[[:space:]]*//')
|
|
87
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
88
|
+
eagle_info " Would promote: $gotcha_text"
|
|
89
|
+
else
|
|
90
|
+
eagle_log "INFO" "Curator: promoting gotcha: $gotcha_text"
|
|
91
|
+
fi
|
|
92
|
+
promoted=$((promoted + 1))
|
|
93
|
+
;;
|
|
94
|
+
esac
|
|
95
|
+
done <<< "$gotcha_result"
|
|
96
|
+
eagle_ok "Found $promoted gotchas to promote"
|
|
97
|
+
else
|
|
98
|
+
eagle_ok "No gotchas need promotion"
|
|
99
|
+
fi
|
|
100
|
+
else
|
|
101
|
+
eagle_dim " No gotchas to analyze"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# ─── 2. Detect superseded decisions ───────────���───────────
|
|
105
|
+
|
|
106
|
+
eagle_info "Checking for superseded decisions..."
|
|
107
|
+
|
|
108
|
+
recent_decisions=$(eagle_db "SELECT decisions, key_files, created_at
|
|
109
|
+
FROM summaries
|
|
110
|
+
WHERE project = '$p_esc'
|
|
111
|
+
AND decisions IS NOT NULL AND decisions != ''
|
|
112
|
+
ORDER BY created_at DESC
|
|
113
|
+
LIMIT 20;")
|
|
114
|
+
|
|
115
|
+
if [ -n "$recent_decisions" ]; then
|
|
116
|
+
decision_prompt="Analyze these decisions from recent sessions on project '$project'. Identify any that CONTRADICT or SUPERSEDE earlier decisions (e.g., session 5 decided to use approach A, but session 8 switched to approach B).
|
|
117
|
+
|
|
118
|
+
DECISIONS (newest first):
|
|
119
|
+
$recent_decisions
|
|
120
|
+
|
|
121
|
+
For each superseded decision, output EXACTLY:
|
|
122
|
+
SUPERSEDED: <old decision> → <new decision> | file: <affected file if known>
|
|
123
|
+
|
|
124
|
+
If none are superseded, output: NONE"
|
|
125
|
+
|
|
126
|
+
decision_result=$(eagle_llm_call "$decision_prompt" "You detect contradicting software decisions. Be precise." 512)
|
|
127
|
+
|
|
128
|
+
if [ -n "$decision_result" ] && ! echo "$decision_result" | grep -q "^NONE$"; then
|
|
129
|
+
superseded=0
|
|
130
|
+
while IFS= read -r line; do
|
|
131
|
+
case "$line" in
|
|
132
|
+
SUPERSEDED:*)
|
|
133
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
134
|
+
eagle_info " $line"
|
|
135
|
+
else
|
|
136
|
+
eagle_log "INFO" "Curator: $line"
|
|
137
|
+
fi
|
|
138
|
+
superseded=$((superseded + 1))
|
|
139
|
+
;;
|
|
140
|
+
esac
|
|
141
|
+
done <<< "$decision_result"
|
|
142
|
+
eagle_ok "Found $superseded superseded decisions"
|
|
143
|
+
else
|
|
144
|
+
eagle_ok "No superseded decisions found"
|
|
145
|
+
fi
|
|
146
|
+
else
|
|
147
|
+
eagle_dim " No decisions to analyze"
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# ─── 3. Generate command compression rules ────────────────
|
|
151
|
+
|
|
152
|
+
eagle_info "Analyzing command patterns..."
|
|
153
|
+
|
|
154
|
+
command_stats=$(eagle_db "SELECT command_category,
|
|
155
|
+
COUNT(*) as count,
|
|
156
|
+
AVG(output_bytes) as avg_bytes,
|
|
157
|
+
MAX(output_bytes) as max_bytes,
|
|
158
|
+
AVG(output_lines) as avg_lines
|
|
159
|
+
FROM observations
|
|
160
|
+
WHERE project = '$p_esc'
|
|
161
|
+
AND tool_name = 'Bash'
|
|
162
|
+
AND command_category IS NOT NULL
|
|
163
|
+
AND output_bytes IS NOT NULL
|
|
164
|
+
AND output_bytes > 0
|
|
165
|
+
GROUP BY command_category
|
|
166
|
+
HAVING count > 5
|
|
167
|
+
ORDER BY avg_bytes DESC
|
|
168
|
+
LIMIT 10;")
|
|
169
|
+
|
|
170
|
+
if [ -n "$command_stats" ]; then
|
|
171
|
+
# Get specific noisy commands
|
|
172
|
+
noisy_commands=$(eagle_db "SELECT tool_input_summary,
|
|
173
|
+
COUNT(*) as count,
|
|
174
|
+
CAST(AVG(output_bytes) AS INTEGER) as avg_bytes,
|
|
175
|
+
CAST(AVG(output_lines) AS INTEGER) as avg_lines
|
|
176
|
+
FROM observations
|
|
177
|
+
WHERE project = '$p_esc'
|
|
178
|
+
AND tool_name = 'Bash'
|
|
179
|
+
AND output_bytes > 1000
|
|
180
|
+
GROUP BY tool_input_summary
|
|
181
|
+
HAVING count >= 3
|
|
182
|
+
ORDER BY avg_bytes DESC
|
|
183
|
+
LIMIT 15;")
|
|
184
|
+
|
|
185
|
+
if [ -n "$noisy_commands" ]; then
|
|
186
|
+
cmd_prompt="Analyze these frequently-run commands and their output sizes for project '$project'. Suggest compression rules for commands that produce consistently large output.
|
|
187
|
+
|
|
188
|
+
COMMAND STATS (command | times_run | avg_output_bytes | avg_output_lines):
|
|
189
|
+
$noisy_commands
|
|
190
|
+
|
|
191
|
+
For each command that deserves a compression rule, output EXACTLY:
|
|
192
|
+
RULE: <pattern> | <strategy: summary or truncate> | <max_lines or -> | <reason>
|
|
193
|
+
|
|
194
|
+
Where:
|
|
195
|
+
- pattern: the base command to match (e.g., 'npm', 'git log', 'pnpm test')
|
|
196
|
+
- strategy: 'summary' (only show result) or 'truncate' (keep first N lines)
|
|
197
|
+
- max_lines: for truncate strategy, how many lines to keep; '-' for summary
|
|
198
|
+
- reason: one-line explanation
|
|
199
|
+
|
|
200
|
+
If no rules needed, output: NONE"
|
|
201
|
+
|
|
202
|
+
cmd_result=$(eagle_llm_call "$cmd_prompt" "You optimize CLI output for AI assistants. Be conservative — only suggest rules for genuinely noisy commands." 512)
|
|
203
|
+
|
|
204
|
+
if [ -n "$cmd_result" ] && ! echo "$cmd_result" | grep -q "^NONE$"; then
|
|
205
|
+
rules_count=0
|
|
206
|
+
while IFS= read -r line; do
|
|
207
|
+
case "$line" in
|
|
208
|
+
RULE:*)
|
|
209
|
+
rule_data=$(echo "$line" | sed 's/^RULE:[[:space:]]*//')
|
|
210
|
+
IFS='|' read -r pattern strategy max_lines reason <<< "$rule_data"
|
|
211
|
+
pattern=$(echo "$pattern" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
212
|
+
strategy=$(echo "$strategy" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
213
|
+
max_lines=$(echo "$max_lines" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
214
|
+
reason=$(echo "$reason" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
215
|
+
|
|
216
|
+
[ "$max_lines" = "-" ] && max_lines=""
|
|
217
|
+
|
|
218
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
219
|
+
eagle_info " Rule: $pattern → $strategy ($reason)"
|
|
220
|
+
else
|
|
221
|
+
pattern_esc=$(eagle_sql_escape "$pattern")
|
|
222
|
+
reason_esc=$(eagle_sql_escape "$reason")
|
|
223
|
+
ml_val="${max_lines:-NULL}"
|
|
224
|
+
[ "$ml_val" != "NULL" ] && ml_val=$(eagle_sql_int "$ml_val")
|
|
225
|
+
|
|
226
|
+
eagle_db "INSERT INTO command_rules (project, pattern, strategy, max_lines, reason)
|
|
227
|
+
VALUES ('$p_esc', '$pattern_esc', '$strategy', $ml_val, '$reason_esc')
|
|
228
|
+
ON CONFLICT(project, pattern) DO UPDATE SET
|
|
229
|
+
strategy = excluded.strategy,
|
|
230
|
+
max_lines = excluded.max_lines,
|
|
231
|
+
reason = excluded.reason,
|
|
232
|
+
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');"
|
|
233
|
+
eagle_log "INFO" "Curator: added command rule: $pattern → $strategy"
|
|
234
|
+
fi
|
|
235
|
+
rules_count=$((rules_count + 1))
|
|
236
|
+
;;
|
|
237
|
+
esac
|
|
238
|
+
done <<< "$cmd_result"
|
|
239
|
+
eagle_ok "$rules_count command rules generated"
|
|
240
|
+
else
|
|
241
|
+
eagle_ok "No command rules needed"
|
|
242
|
+
fi
|
|
243
|
+
else
|
|
244
|
+
eagle_dim " Not enough command data yet"
|
|
245
|
+
fi
|
|
246
|
+
else
|
|
247
|
+
eagle_dim " No command metrics collected yet (need more sessions)"
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
# ─── 4. Feature auto-discovery ────────────────────────────
|
|
251
|
+
|
|
252
|
+
eagle_info "Discovering features from session data..."
|
|
253
|
+
|
|
254
|
+
feature_data=$(eagle_db "SELECT s.request, s.key_files, s.decisions, s.completed
|
|
255
|
+
FROM summaries s
|
|
256
|
+
WHERE s.project = '$p_esc'
|
|
257
|
+
AND (s.key_files IS NOT NULL AND s.key_files != ''
|
|
258
|
+
OR s.decisions IS NOT NULL AND s.decisions != '')
|
|
259
|
+
ORDER BY s.created_at DESC
|
|
260
|
+
LIMIT 15;")
|
|
261
|
+
|
|
262
|
+
existing_features=$(eagle_db "SELECT name FROM features WHERE project = '$p_esc' AND status = 'active';")
|
|
263
|
+
|
|
264
|
+
if [ -n "$feature_data" ]; then
|
|
265
|
+
feature_prompt="Analyze these session summaries from project '$project' and identify distinct FEATURES (user-facing capabilities, not implementation details).
|
|
266
|
+
|
|
267
|
+
SESSION DATA (request | key_files | decisions | completed):
|
|
268
|
+
$feature_data
|
|
269
|
+
|
|
270
|
+
EXISTING FEATURES (already tracked):
|
|
271
|
+
${existing_features:-none}
|
|
272
|
+
|
|
273
|
+
For each NEW feature discovered (not already in existing list), output EXACTLY:
|
|
274
|
+
FEATURE: <name> | <one-line description> | <comma-separated key files>
|
|
275
|
+
|
|
276
|
+
Rules:
|
|
277
|
+
- Feature names should be short, kebab-case (e.g., 'title-generation', 'auth-middleware')
|
|
278
|
+
- Only discover features with clear file evidence (at least 2 related files)
|
|
279
|
+
- Don't re-discover existing features
|
|
280
|
+
- If no new features found, output: NONE"
|
|
281
|
+
|
|
282
|
+
feature_result=$(eagle_llm_call "$feature_prompt" "You identify software features from development session data. Be specific and evidence-based." 512)
|
|
283
|
+
|
|
284
|
+
if [ -n "$feature_result" ] && ! echo "$feature_result" | grep -q "^NONE$"; then
|
|
285
|
+
features_count=0
|
|
286
|
+
while IFS= read -r line; do
|
|
287
|
+
case "$line" in
|
|
288
|
+
FEATURE:*)
|
|
289
|
+
feat_data=$(echo "$line" | sed 's/^FEATURE:[[:space:]]*//')
|
|
290
|
+
IFS='|' read -r fname fdesc ffiles <<< "$feat_data"
|
|
291
|
+
fname=$(echo "$fname" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
292
|
+
fdesc=$(echo "$fdesc" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
293
|
+
ffiles=$(echo "$ffiles" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
294
|
+
|
|
295
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
296
|
+
eagle_info " Feature: $fname — $fdesc"
|
|
297
|
+
eagle_info " Files: $ffiles"
|
|
298
|
+
else
|
|
299
|
+
eagle_upsert_feature "$project" "$fname" "$fdesc"
|
|
300
|
+
fid=$(eagle_get_feature_id "$project" "$fname")
|
|
301
|
+
if [ -n "$fid" ] && [ -n "$ffiles" ]; then
|
|
302
|
+
IFS=',' read -ra file_arr <<< "$ffiles"
|
|
303
|
+
for f in "${file_arr[@]}"; do
|
|
304
|
+
f=$(echo "$f" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
305
|
+
[ -n "$f" ] && eagle_add_feature_file "$fid" "$f" ""
|
|
306
|
+
done
|
|
307
|
+
fi
|
|
308
|
+
eagle_log "INFO" "Curator: discovered feature: $fname"
|
|
309
|
+
fi
|
|
310
|
+
features_count=$((features_count + 1))
|
|
311
|
+
;;
|
|
312
|
+
esac
|
|
313
|
+
done <<< "$feature_result"
|
|
314
|
+
eagle_ok "$features_count features discovered"
|
|
315
|
+
else
|
|
316
|
+
eagle_ok "No new features discovered"
|
|
317
|
+
fi
|
|
318
|
+
else
|
|
319
|
+
eagle_dim " Not enough session data for feature discovery"
|
|
320
|
+
fi
|
|
321
|
+
|
|
322
|
+
# ─── 5. Session compression (--full only) ─────────────────
|
|
323
|
+
|
|
324
|
+
if [ "$FULL" -eq 1 ]; then
|
|
325
|
+
eagle_info "Compressing old sessions..."
|
|
326
|
+
|
|
327
|
+
old_count=$(eagle_db "SELECT COUNT(*) FROM summaries
|
|
328
|
+
WHERE project = '$p_esc'
|
|
329
|
+
AND created_at < strftime('%Y-%m-%dT%H:%M:%fZ', 'now', '-30 days');")
|
|
330
|
+
|
|
331
|
+
if [ "${old_count:-0}" -gt 10 ]; then
|
|
332
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
333
|
+
eagle_info " Would analyze $old_count old sessions for compression"
|
|
334
|
+
else
|
|
335
|
+
eagle_info " $old_count sessions older than 30 days (compression not yet implemented)"
|
|
336
|
+
fi
|
|
337
|
+
else
|
|
338
|
+
eagle_dim " Not enough old sessions to compress"
|
|
339
|
+
fi
|
|
340
|
+
fi
|
|
341
|
+
|
|
342
|
+
# ──��� Summary ──────────────────────────────────────────────
|
|
343
|
+
|
|
344
|
+
echo ""
|
|
345
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
346
|
+
eagle_footer "Dry run complete. Run without --dry-run to apply changes."
|
|
347
|
+
else
|
|
348
|
+
eagle_footer "Curation complete for '$project'."
|
|
349
|
+
fi
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════
|
|
3
|
+
# Eagle Mem — Feature management
|
|
4
|
+
# eagle-mem feature [list|show|verify|add]
|
|
5
|
+
# ═══════════════════════════════════════════════════════════
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
LIB_DIR="$SCRIPT_DIR/../lib"
|
|
10
|
+
|
|
11
|
+
. "$LIB_DIR/common.sh"
|
|
12
|
+
. "$SCRIPT_DIR/style.sh"
|
|
13
|
+
. "$LIB_DIR/db.sh"
|
|
14
|
+
|
|
15
|
+
eagle_ensure_db
|
|
16
|
+
eagle_header "Features"
|
|
17
|
+
|
|
18
|
+
project=$(eagle_project_from_cwd "$(pwd)")
|
|
19
|
+
subcommand="${1:-list}"
|
|
20
|
+
shift 2>/dev/null || true
|
|
21
|
+
|
|
22
|
+
case "$subcommand" in
|
|
23
|
+
list|ls)
|
|
24
|
+
results=$(eagle_list_features "$project")
|
|
25
|
+
if [ -z "$results" ]; then
|
|
26
|
+
eagle_dim "No features tracked for '$project'"
|
|
27
|
+
eagle_dim "Run 'eagle-mem curate' to auto-discover features"
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
while IFS='|' read -r name desc status verified dep_count file_count test_count; do
|
|
32
|
+
[ -z "$name" ] && continue
|
|
33
|
+
verified_label=""
|
|
34
|
+
if [ -n "$verified" ]; then
|
|
35
|
+
verified_label=" ${DIM}(verified: ${verified})${RESET}"
|
|
36
|
+
else
|
|
37
|
+
verified_label=" ${DIM}(never verified)${RESET}"
|
|
38
|
+
fi
|
|
39
|
+
echo -e " ${CYAN}${name}${RESET}${verified_label}"
|
|
40
|
+
[ -n "$desc" ] && echo -e " ${desc}"
|
|
41
|
+
echo -e " ${DIM}${file_count} files, ${dep_count} deps, ${test_count} smoke tests${RESET}"
|
|
42
|
+
done <<< "$results"
|
|
43
|
+
;;
|
|
44
|
+
|
|
45
|
+
show)
|
|
46
|
+
name="${1:-}"
|
|
47
|
+
[ -z "$name" ] && { eagle_err "Usage: eagle-mem feature show <name>"; exit 1; }
|
|
48
|
+
eagle_show_feature "$project" "$name"
|
|
49
|
+
;;
|
|
50
|
+
|
|
51
|
+
verify)
|
|
52
|
+
name="${1:-}"
|
|
53
|
+
[ -z "$name" ] && { eagle_err "Usage: eagle-mem feature verify <name> [--notes <text>]"; exit 1; }
|
|
54
|
+
shift
|
|
55
|
+
notes=""
|
|
56
|
+
while [ $# -gt 0 ]; do
|
|
57
|
+
case "$1" in
|
|
58
|
+
--notes) notes="$2"; shift 2 ;;
|
|
59
|
+
*) notes="$1"; shift ;;
|
|
60
|
+
esac
|
|
61
|
+
done
|
|
62
|
+
eagle_verify_feature "$project" "$name" "$notes"
|
|
63
|
+
eagle_ok "Feature '$name' marked as verified"
|
|
64
|
+
;;
|
|
65
|
+
|
|
66
|
+
add)
|
|
67
|
+
name="${1:-}"
|
|
68
|
+
[ -z "$name" ] && { eagle_err "Usage: eagle-mem feature add <name> [--desc <text>] [--file <path>] [--requires <target:name>] [--smoke <command>]"; exit 1; }
|
|
69
|
+
shift
|
|
70
|
+
desc=""
|
|
71
|
+
files=()
|
|
72
|
+
deps=()
|
|
73
|
+
smokes=()
|
|
74
|
+
|
|
75
|
+
while [ $# -gt 0 ]; do
|
|
76
|
+
case "$1" in
|
|
77
|
+
--desc|-d) desc="$2"; shift 2 ;;
|
|
78
|
+
--file|-f) files+=("$2"); shift 2 ;;
|
|
79
|
+
--requires|-r) deps+=("$2"); shift 2 ;;
|
|
80
|
+
--smoke|-s) smokes+=("$2"); shift 2 ;;
|
|
81
|
+
*) shift ;;
|
|
82
|
+
esac
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
eagle_upsert_feature "$project" "$name" "$desc"
|
|
86
|
+
fid=$(eagle_get_feature_id "$project" "$name")
|
|
87
|
+
|
|
88
|
+
for f in "${files[@]+"${files[@]}"}"; do
|
|
89
|
+
eagle_add_feature_file "$fid" "$f" ""
|
|
90
|
+
done
|
|
91
|
+
|
|
92
|
+
for d in "${deps[@]+"${deps[@]}"}"; do
|
|
93
|
+
target="${d%%:*}"
|
|
94
|
+
dep_name="${d#*:}"
|
|
95
|
+
eagle_add_feature_dependency "$fid" "env_var" "$target" "$dep_name" ""
|
|
96
|
+
done
|
|
97
|
+
|
|
98
|
+
for s in "${smokes[@]+"${smokes[@]}"}"; do
|
|
99
|
+
eagle_add_feature_smoke_test "$fid" "$s" ""
|
|
100
|
+
done
|
|
101
|
+
|
|
102
|
+
eagle_ok "Feature '$name' created"
|
|
103
|
+
;;
|
|
104
|
+
|
|
105
|
+
*)
|
|
106
|
+
eagle_err "Unknown feature command: $subcommand"
|
|
107
|
+
eagle_info "Usage: eagle-mem feature [list|show|verify|add]"
|
|
108
|
+
exit 1
|
|
109
|
+
;;
|
|
110
|
+
esac
|
package/scripts/help.sh
CHANGED
|
@@ -24,6 +24,9 @@ echo -e " ${CYAN}memories${RESET} View/sync mirrored Claude Code memories"
|
|
|
24
24
|
echo -e " ${CYAN}tasks${RESET} View mirrored Claude Code tasks"
|
|
25
25
|
echo -e " ${CYAN}refresh${RESET} Full project sync: scan (if needed) + index + memories"
|
|
26
26
|
echo -e " ${CYAN}prune${RESET} Remove old observations and orphaned chunks"
|
|
27
|
+
echo -e " ${CYAN}config${RESET} View or change LLM provider settings"
|
|
28
|
+
echo -e " ${CYAN}curate${RESET} Run the self-learning curator (LLM-powered analysis)"
|
|
29
|
+
echo -e " ${CYAN}feature${RESET} Manage feature graph (list/show/verify/add)"
|
|
27
30
|
echo -e " ${CYAN}install${RESET} First-time setup: hooks, database, skills"
|
|
28
31
|
echo -e " ${CYAN}update${RESET} Re-deploy hooks and run migrations"
|
|
29
32
|
echo -e " ${CYAN}uninstall${RESET} Remove hooks and optionally delete data"
|
package/scripts/index.sh
CHANGED
|
@@ -162,7 +162,7 @@ DELETE FROM code_chunks WHERE project = '$project_sql' AND file_path = '$file_sq
|
|
|
162
162
|
end=$((start + CHUNK_SIZE - 1))
|
|
163
163
|
[ "$end" -gt "$total_lines" ] && end="$total_lines"
|
|
164
164
|
|
|
165
|
-
content=$(sed -n "${start},${end}p" "$full_path")
|
|
165
|
+
content=$(sed -n "${start},${end}p" "$full_path" | eagle_redact)
|
|
166
166
|
content_sql=$(eagle_sql_escape "$content")
|
|
167
167
|
|
|
168
168
|
txn_sql+="
|
package/scripts/install.sh
CHANGED
|
@@ -181,6 +181,10 @@ eagle_patch_hook "$SETTINGS" "UserPromptSubmit" "" \
|
|
|
181
181
|
"$EAGLE_MEM_DIR/hooks/user-prompt-submit.sh" \
|
|
182
182
|
"UserPromptSubmit hook"
|
|
183
183
|
|
|
184
|
+
eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash" \
|
|
185
|
+
"$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
|
|
186
|
+
"PreToolUse hook"
|
|
187
|
+
|
|
184
188
|
# ─── Install skills ────────────────────────────────────────
|
|
185
189
|
|
|
186
190
|
if [ -d "$PACKAGE_DIR/skills" ]; then
|
|
@@ -242,6 +246,16 @@ else
|
|
|
242
246
|
fi
|
|
243
247
|
fi
|
|
244
248
|
|
|
249
|
+
# ─── Initialize config ────────────────────────────────────
|
|
250
|
+
|
|
251
|
+
. "$LIB_DIR/provider.sh"
|
|
252
|
+
if [ ! -f "$EAGLE_CONFIG_FILE" ]; then
|
|
253
|
+
eagle_config_init
|
|
254
|
+
eagle_ok "Config created ${DIM}(auto-detected provider)${RESET}"
|
|
255
|
+
else
|
|
256
|
+
eagle_ok "Config ${DIM}(already exists)${RESET}"
|
|
257
|
+
fi
|
|
258
|
+
|
|
245
259
|
# ─── Save installed version ───────────────────────────────
|
|
246
260
|
|
|
247
261
|
version=$(jq -r .version "$PACKAGE_DIR/package.json" 2>/dev/null || echo "unknown")
|
package/scripts/style.sh
CHANGED
|
@@ -43,12 +43,10 @@ eagle_footer() {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
eagle_banner() {
|
|
46
|
-
echo -e "${CYAN}"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
BANNER
|
|
51
|
-
echo -e "${RESET}"
|
|
46
|
+
echo -e " ${CYAN}======================================${RESET}"
|
|
47
|
+
echo -e " ${CYAN}${BOLD} Eagle Mem${RESET}"
|
|
48
|
+
echo -e " ${CYAN}======================================${RESET}"
|
|
49
|
+
echo ""
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
eagle_is_tty() {
|
package/scripts/update.sh
CHANGED
|
@@ -69,6 +69,7 @@ if [ -f "$SETTINGS" ] && command -v jq &>/dev/null; then
|
|
|
69
69
|
eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskCreate|TaskUpdate" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
70
70
|
eagle_patch_hook "$SETTINGS" "SessionEnd" "" "$EAGLE_MEM_DIR/hooks/session-end.sh"
|
|
71
71
|
eagle_patch_hook "$SETTINGS" "UserPromptSubmit" "" "$EAGLE_MEM_DIR/hooks/user-prompt-submit.sh"
|
|
72
|
+
eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
72
73
|
|
|
73
74
|
eagle_ok "Hooks registered"
|
|
74
75
|
fi
|