eagle-mem 4.9.8 → 4.9.10
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/hooks/post-tool-use.sh +15 -10
- package/hooks/pre-tool-use.sh +2 -10
- package/hooks/stop.sh +2 -6
- package/hooks/user-prompt-submit.sh +2 -2
- package/lib/common.sh +9 -0
- package/lib/db-features.sh +10 -10
- package/lib/hooks-posttool.sh +0 -25
- package/lib/hooks.sh +13 -0
- package/package.json +1 -1
- package/scripts/feature.sh +22 -15
- package/scripts/install.sh +7 -15
- package/scripts/update.sh +5 -10
- package/skills/eagle-mem-feature/SKILL.md +123 -0
package/hooks/post-tool-use.sh
CHANGED
|
@@ -17,13 +17,10 @@ LIB_DIR="$SCRIPT_DIR/../lib"
|
|
|
17
17
|
input=$(eagle_read_stdin)
|
|
18
18
|
[ -z "$input" ] && exit 0
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
|
|
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("")')"
|
|
23
22
|
agent=$(eagle_agent_source_from_json "$input")
|
|
24
23
|
|
|
25
|
-
hook_event=$(echo "$input" | jq -r '.hook_event_name // empty')
|
|
26
|
-
|
|
27
24
|
if [ -z "$session_id" ]; then exit 0; fi
|
|
28
25
|
|
|
29
26
|
# TaskCreated/TaskCompleted dedicated events — parse top-level fields and exit
|
|
@@ -73,7 +70,7 @@ esac
|
|
|
73
70
|
|
|
74
71
|
# Only track relevant tools
|
|
75
72
|
case "$tool_name" in
|
|
76
|
-
Read|Write|Edit|
|
|
73
|
+
Read|Write|Edit|apply_patch|TaskUpdate) ;;
|
|
77
74
|
*) eagle_is_shell_tool "$tool_name" || exit 0 ;;
|
|
78
75
|
esac
|
|
79
76
|
|
|
@@ -153,9 +150,18 @@ case "$tool_name" in
|
|
|
153
150
|
*) command_category="other" ;;
|
|
154
151
|
esac
|
|
155
152
|
;;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
TaskUpdate)
|
|
154
|
+
task_id=$(echo "$input" | jq -r '.tool_input.taskId // empty')
|
|
155
|
+
task_status=$(echo "$input" | jq -r '.tool_input.status // empty')
|
|
156
|
+
tool_summary="TaskUpdate: ${task_id} → ${task_status}"
|
|
157
|
+
if [ -n "$task_id" ] && [ -n "$task_status" ]; then
|
|
158
|
+
fp_sql=$(eagle_sql_escape "event://${session_id}/${task_id}")
|
|
159
|
+
stat_sql=$(eagle_sql_escape "$task_status")
|
|
160
|
+
eagle_db_pipe <<SQL
|
|
161
|
+
UPDATE agent_tasks SET status = '$stat_sql', updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
162
|
+
WHERE file_path = '$fp_sql';
|
|
163
|
+
SQL
|
|
164
|
+
fi
|
|
159
165
|
;;
|
|
160
166
|
esac
|
|
161
167
|
|
|
@@ -199,7 +205,6 @@ esac
|
|
|
199
205
|
# ─── Dispatch to extracted responsibilities ───────────────
|
|
200
206
|
|
|
201
207
|
eagle_posttool_mirror_writes "$tool_name" "$fp" "$session_id" "$project" "$agent"
|
|
202
|
-
eagle_posttool_mirror_tasks "$tool_name" "$session_id" "$project" "$input" "$agent"
|
|
203
208
|
eagle_posttool_stale_hint "$tool_name" "$fp" "$project" "$agent"
|
|
204
209
|
eagle_posttool_decision_surface "$tool_name" "$fp" "$project" "$agent"
|
|
205
210
|
|
package/hooks/pre-tool-use.sh
CHANGED
|
@@ -21,7 +21,8 @@ LIB_DIR="$SCRIPT_DIR/../lib"
|
|
|
21
21
|
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
26
|
agent=$(eagle_agent_source_from_json "$input")
|
|
26
27
|
|
|
27
28
|
case "$tool_name" in
|
|
@@ -30,9 +31,6 @@ case "$tool_name" in
|
|
|
30
31
|
esac
|
|
31
32
|
|
|
32
33
|
[ ! -f "$EAGLE_MEM_DB" ] && exit 0
|
|
33
|
-
|
|
34
|
-
session_id=$(echo "$input" | jq -r '.session_id // empty')
|
|
35
|
-
cwd=$(echo "$input" | jq -r '.cwd // empty')
|
|
36
34
|
project=$(eagle_project_from_hook_input "$input")
|
|
37
35
|
[ -z "$project" ] && exit 0
|
|
38
36
|
|
|
@@ -77,8 +75,6 @@ Pending checks:"
|
|
|
77
75
|
done <<< "$pending_rows"
|
|
78
76
|
|
|
79
77
|
jq -nc --arg reason "$block_reason" '{
|
|
80
|
-
"decision":"block",
|
|
81
|
-
"reason":$reason,
|
|
82
78
|
"hookSpecificOutput":{
|
|
83
79
|
"hookEventName":"PreToolUse",
|
|
84
80
|
"permissionDecision":"deny",
|
|
@@ -104,8 +100,6 @@ Recommended compact command:
|
|
|
104
100
|
One-off developer bypass:
|
|
105
101
|
touch $EAGLE_RAW_BASH_UNLOCK"
|
|
106
102
|
jq -nc --arg reason "$reason" '{
|
|
107
|
-
"decision":"block",
|
|
108
|
-
"reason":$reason,
|
|
109
103
|
"hookSpecificOutput":{
|
|
110
104
|
"hookEventName":"PreToolUse",
|
|
111
105
|
"permissionDecision":"deny",
|
|
@@ -131,8 +125,6 @@ Install RTK or switch enforcement to auto:
|
|
|
131
125
|
One-off developer bypass:
|
|
132
126
|
touch $EAGLE_RAW_BASH_UNLOCK"
|
|
133
127
|
jq -nc --arg reason "$reason" '{
|
|
134
|
-
"decision":"block",
|
|
135
|
-
"reason":$reason,
|
|
136
128
|
"hookSpecificOutput":{
|
|
137
129
|
"hookEventName":"PreToolUse",
|
|
138
130
|
"permissionDecision":"deny",
|
package/hooks/stop.sh
CHANGED
|
@@ -21,16 +21,12 @@ eagle_ensure_db
|
|
|
21
21
|
input=$(eagle_read_stdin)
|
|
22
22
|
[ -z "$input" ] && exit 0
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
transcript_path=$(echo "$input" | jq -r '.transcript_path // empty')
|
|
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("")')"
|
|
27
26
|
last_assistant_message=$(echo "$input" | jq -r '.last_assistant_message // empty')
|
|
28
27
|
agent=$(eagle_agent_source_from_json "$input")
|
|
29
28
|
|
|
30
29
|
[ -z "$session_id" ] && exit 0
|
|
31
|
-
|
|
32
|
-
# Skip subagent contexts
|
|
33
|
-
agent_type=$(echo "$input" | jq -r '.agent_type // empty')
|
|
34
30
|
[ -n "$agent_type" ] && [ "$agent_type" != "main" ] && exit 0
|
|
35
31
|
|
|
36
32
|
project=$(eagle_project_from_hook_input "$input")
|
|
@@ -18,8 +18,8 @@ LIB_DIR="$SCRIPT_DIR/../lib"
|
|
|
18
18
|
input=$(eagle_read_stdin)
|
|
19
19
|
[ -z "$input" ] && exit 0
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
IFS=$'\x1f' read -r session_id cwd <<< \
|
|
22
|
+
"$(echo "$input" | jq -r '[.session_id, .cwd] | map(. // "") | join("")')"
|
|
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
|
@@ -672,6 +672,15 @@ eagle_project_from_hook_input() {
|
|
|
672
672
|
cwd=$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null)
|
|
673
673
|
transcript_path=$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
674
674
|
|
|
675
|
+
if [ -n "${CLAUDE_PROJECT_DIR:-}" ]; then
|
|
676
|
+
project=$(eagle_project_from_workspace_path "$CLAUDE_PROJECT_DIR")
|
|
677
|
+
if [ -n "$project" ]; then
|
|
678
|
+
[ -n "$session_id" ] && eagle_remember_session_project "$session_id" "$project" 1 >/dev/null 2>&1
|
|
679
|
+
printf '%s\n' "$project"
|
|
680
|
+
return
|
|
681
|
+
fi
|
|
682
|
+
fi
|
|
683
|
+
|
|
675
684
|
workspace_project=$(printf '%s' "$input" | jq -r '.workspace.project_dir // empty' 2>/dev/null)
|
|
676
685
|
if [ -n "$workspace_project" ]; then
|
|
677
686
|
project=$(eagle_project_from_workspace_path "$workspace_project")
|
package/lib/db-features.sh
CHANGED
|
@@ -110,16 +110,16 @@ eagle_record_pending_feature_verifications() {
|
|
|
110
110
|
local fid; fid=$(eagle_sql_int "$feature_id")
|
|
111
111
|
local name_esc; name_esc=$(eagle_sql_escape "$feature_name")
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
113
|
+
already_resolved=$(eagle_db "SELECT 1 FROM pending_feature_verifications
|
|
114
|
+
WHERE project = '$p_esc'
|
|
115
|
+
AND feature_id = $fid
|
|
116
|
+
AND file_path = '$fp_esc'
|
|
117
|
+
AND (
|
|
118
|
+
(change_fingerprint = '$fp_hash_esc' AND status = 'verified')
|
|
119
|
+
OR status = 'waived'
|
|
120
|
+
)
|
|
121
|
+
LIMIT 1;")
|
|
122
|
+
[ -n "$already_resolved" ] && continue
|
|
123
123
|
|
|
124
124
|
eagle_db "INSERT INTO pending_feature_verifications
|
|
125
125
|
(project, feature_id, feature_name, file_path, reason, source_session_id, trigger_tool, change_fingerprint)
|
package/lib/hooks-posttool.sh
CHANGED
|
@@ -33,31 +33,6 @@ eagle_posttool_mirror_writes() {
|
|
|
33
33
|
esac
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
eagle_posttool_mirror_tasks() {
|
|
37
|
-
local tool_name="$1" session_id="$2" project="$3" input="$4"
|
|
38
|
-
local agent="${5:-$(eagle_agent_source)}"
|
|
39
|
-
|
|
40
|
-
case "$tool_name" in
|
|
41
|
-
TaskCreate|TaskUpdate)
|
|
42
|
-
if eagle_validate_session_id "$session_id"; then
|
|
43
|
-
local task_dir="$EAGLE_CLAUDE_TASKS_DIR/$session_id"
|
|
44
|
-
if [ -d "$task_dir" ]; then
|
|
45
|
-
local task_id
|
|
46
|
-
task_id=$(echo "$input" | jq -r '.tool_input.id // empty')
|
|
47
|
-
if [ -z "$task_id" ]; then
|
|
48
|
-
local newest
|
|
49
|
-
newest=$(ls -t "$task_dir"/*.json 2>/dev/null | head -1)
|
|
50
|
-
[ -n "$newest" ] && [ -f "$newest" ] && eagle_capture_agent_task "$newest" "$session_id" "$project" "$agent"
|
|
51
|
-
elif eagle_validate_session_id "$task_id"; then
|
|
52
|
-
local task_json="$task_dir/$task_id.json"
|
|
53
|
-
[ -f "$task_json" ] && eagle_capture_agent_task "$task_json" "$session_id" "$project" "$agent"
|
|
54
|
-
fi
|
|
55
|
-
fi
|
|
56
|
-
fi
|
|
57
|
-
;;
|
|
58
|
-
esac
|
|
59
|
-
}
|
|
60
|
-
|
|
61
36
|
eagle_posttool_stale_hint() {
|
|
62
37
|
local tool_name="$1" fp="$2" project="$3"
|
|
63
38
|
local agent="${4:-$(eagle_agent_source)}"
|
package/lib/hooks.sh
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
# Shared by install.sh and update.sh
|
|
5
5
|
# ═══════════════════════════════════════════════════════════
|
|
6
6
|
|
|
7
|
+
eagle_clean_hook_entries() {
|
|
8
|
+
local settings="$1"
|
|
9
|
+
local event="$2"
|
|
10
|
+
local command="$3"
|
|
11
|
+
|
|
12
|
+
local tmp
|
|
13
|
+
tmp=$(mktemp)
|
|
14
|
+
jq --arg cmd "$command" \
|
|
15
|
+
".hooks.${event} = ((.hooks.${event} // []) | map(select(.hooks | all(.command != \$cmd))))" \
|
|
16
|
+
"$settings" > "$tmp" && mv "$tmp" "$settings"
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
eagle_patch_hook() {
|
|
8
20
|
local settings="$1"
|
|
9
21
|
local event="$2"
|
|
@@ -35,4 +47,5 @@ eagle_patch_hook() {
|
|
|
35
47
|
tmp=$(mktemp)
|
|
36
48
|
jq --argjson entry "$entry" ".hooks.${event} = ((.hooks.${event} // []) + [\$entry])" "$settings" > "$tmp" && mv "$tmp" "$settings"
|
|
37
49
|
[ -n "$description" ] && eagle_ok "$description"
|
|
50
|
+
return 0
|
|
38
51
|
}
|
package/package.json
CHANGED
package/scripts/feature.sh
CHANGED
|
@@ -125,13 +125,7 @@ case "$subcommand" in
|
|
|
125
125
|
|
|
126
126
|
waive)
|
|
127
127
|
id="${1:-}"
|
|
128
|
-
[ -z "$id" ] && { eagle_err "Usage: eagle-mem feature waive <id> --reason <text>"; exit 1; }
|
|
129
|
-
case "$id" in
|
|
130
|
-
*[!0-9]*)
|
|
131
|
-
eagle_err "Invalid ID: '$id' (must be numeric)"
|
|
132
|
-
exit 1
|
|
133
|
-
;;
|
|
134
|
-
esac
|
|
128
|
+
[ -z "$id" ] && { eagle_err "Usage: eagle-mem feature waive <id|name> --reason <text>"; exit 1; }
|
|
135
129
|
shift
|
|
136
130
|
reason=""
|
|
137
131
|
while [ $# -gt 0 ]; do
|
|
@@ -147,14 +141,27 @@ case "$subcommand" in
|
|
|
147
141
|
*) reason="$1"; shift ;;
|
|
148
142
|
esac
|
|
149
143
|
done
|
|
150
|
-
[ -z "$reason" ] && { eagle_err "Usage: eagle-mem feature waive <id> --reason <text>"; exit 1; }
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
144
|
+
[ -z "$reason" ] && { eagle_err "Usage: eagle-mem feature waive <id|name> --reason <text>"; exit 1; }
|
|
145
|
+
case "$id" in
|
|
146
|
+
*[!0-9]*)
|
|
147
|
+
waived=$(eagle_resolve_pending_feature_verifications "$project" "$id" "waived" "$reason" | tail -1)
|
|
148
|
+
if [ "${waived:-0}" -gt 0 ] 2>/dev/null; then
|
|
149
|
+
eagle_ok "Waived $waived pending verification(s) for '$id'"
|
|
150
|
+
else
|
|
151
|
+
eagle_err "No pending verifications found for feature '$id'"
|
|
152
|
+
exit 1
|
|
153
|
+
fi
|
|
154
|
+
;;
|
|
155
|
+
*)
|
|
156
|
+
waived=$(eagle_waive_pending_feature_verification "$project" "$id" "$reason" | tail -1)
|
|
157
|
+
if [ "${waived:-0}" -gt 0 ] 2>/dev/null; then
|
|
158
|
+
eagle_ok "Pending verification #$id waived"
|
|
159
|
+
else
|
|
160
|
+
eagle_err "No pending verification found with ID $id"
|
|
161
|
+
exit 1
|
|
162
|
+
fi
|
|
163
|
+
;;
|
|
164
|
+
esac
|
|
158
165
|
;;
|
|
159
166
|
|
|
160
167
|
add)
|
package/scripts/install.sh
CHANGED
|
@@ -226,7 +226,11 @@ if [ "$claude_found" = true ]; then
|
|
|
226
226
|
"$EAGLE_MEM_DIR/hooks/stop.sh" \
|
|
227
227
|
"Stop hook"
|
|
228
228
|
|
|
229
|
-
|
|
229
|
+
# Clean old registrations before re-registering (handles matcher changes across versions)
|
|
230
|
+
eagle_clean_hook_entries "$SETTINGS" "PostToolUse" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
231
|
+
eagle_clean_hook_entries "$SETTINGS" "PreToolUse" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
232
|
+
|
|
233
|
+
eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskUpdate" \
|
|
230
234
|
"$EAGLE_MEM_DIR/hooks/post-tool-use.sh" \
|
|
231
235
|
"PostToolUse hook"
|
|
232
236
|
|
|
@@ -246,21 +250,9 @@ if [ "$claude_found" = true ]; then
|
|
|
246
250
|
"$EAGLE_MEM_DIR/hooks/user-prompt-submit.sh" \
|
|
247
251
|
"UserPromptSubmit hook"
|
|
248
252
|
|
|
249
|
-
eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash" \
|
|
250
|
-
"$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
|
|
251
|
-
"PreToolUse hook (Bash)"
|
|
252
|
-
|
|
253
|
-
eagle_patch_hook "$SETTINGS" "PreToolUse" "Read" \
|
|
254
|
-
"$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
|
|
255
|
-
"PreToolUse hook (Read)"
|
|
256
|
-
|
|
257
|
-
eagle_patch_hook "$SETTINGS" "PreToolUse" "Edit" \
|
|
258
|
-
"$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
|
|
259
|
-
"PreToolUse hook (Edit)"
|
|
260
|
-
|
|
261
|
-
eagle_patch_hook "$SETTINGS" "PreToolUse" "Write" \
|
|
253
|
+
eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash|Read|Edit|Write" \
|
|
262
254
|
"$EAGLE_MEM_DIR/hooks/pre-tool-use.sh" \
|
|
263
|
-
"PreToolUse hook
|
|
255
|
+
"PreToolUse hook"
|
|
264
256
|
else
|
|
265
257
|
eagle_info "Claude hooks skipped ${DIM}(Claude Code not detected)${RESET}"
|
|
266
258
|
fi
|
package/scripts/update.sh
CHANGED
|
@@ -76,23 +76,18 @@ fi
|
|
|
76
76
|
# ─── Re-register hooks (idempotent) ───────────────────────
|
|
77
77
|
|
|
78
78
|
if [ "$claude_found" = true ] && [ -f "$SETTINGS" ] && command -v jq &>/dev/null; then
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
jq '(.hooks.PostToolUse[] | select(.matcher == "Read|Write|Edit|Bash")).matcher = "Read|Write|Edit|Bash|TaskCreate|TaskUpdate"' "$SETTINGS" > "$_tmp" && mv "$_tmp" "$SETTINGS"
|
|
83
|
-
fi
|
|
79
|
+
# Clean old registrations before re-registering (handles matcher changes across versions)
|
|
80
|
+
eagle_clean_hook_entries "$SETTINGS" "PostToolUse" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
81
|
+
eagle_clean_hook_entries "$SETTINGS" "PreToolUse" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
84
82
|
|
|
85
83
|
eagle_patch_hook "$SETTINGS" "SessionStart" "" "$EAGLE_MEM_DIR/hooks/session-start.sh"
|
|
86
84
|
eagle_patch_hook "$SETTINGS" "Stop" "" "$EAGLE_MEM_DIR/hooks/stop.sh"
|
|
87
|
-
eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|
|
|
85
|
+
eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskUpdate" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
88
86
|
eagle_patch_hook "$SETTINGS" "TaskCreated" "" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
89
87
|
eagle_patch_hook "$SETTINGS" "TaskCompleted" "" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
|
|
90
88
|
eagle_patch_hook "$SETTINGS" "SessionEnd" "" "$EAGLE_MEM_DIR/hooks/session-end.sh"
|
|
91
89
|
eagle_patch_hook "$SETTINGS" "UserPromptSubmit" "" "$EAGLE_MEM_DIR/hooks/user-prompt-submit.sh"
|
|
92
|
-
eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
93
|
-
eagle_patch_hook "$SETTINGS" "PreToolUse" "Read" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
94
|
-
eagle_patch_hook "$SETTINGS" "PreToolUse" "Edit" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
95
|
-
eagle_patch_hook "$SETTINGS" "PreToolUse" "Write" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
90
|
+
eagle_patch_hook "$SETTINGS" "PreToolUse" "Bash|Read|Edit|Write" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
|
|
96
91
|
|
|
97
92
|
eagle_ok "Hooks registered"
|
|
98
93
|
fi
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eagle-mem-feature
|
|
3
|
+
description: >
|
|
4
|
+
Manage Eagle Mem feature verification and anti-regression guardrails. Use when:
|
|
5
|
+
'feature pending', 'verify feature', 'waive feature', 'feature verification',
|
|
6
|
+
'pending verifications', 'release blocked', 'feature guard', 'smoke test',
|
|
7
|
+
'what needs verification', 'mark feature verified', 'skip verification'.
|
|
8
|
+
Uses the eagle-mem CLI.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Eagle Mem — Feature Verification
|
|
12
|
+
|
|
13
|
+
## Purpose
|
|
14
|
+
|
|
15
|
+
**For the user:** Every tracked feature has files, dependencies, and smoke tests. When you edit a tracked file, Eagle Mem automatically flags the feature as needing re-verification. Release-boundary commands stay blocked until all pending verifications are resolved. This prevents shipping regressions.
|
|
16
|
+
|
|
17
|
+
**For you:** The pending verification list tells you what's at risk after your edits. Resolve each one: run smoke tests and `verify`, or explain why it's safe and `waive`. Don't leave pending items unresolved — they block the user's workflow.
|
|
18
|
+
|
|
19
|
+
## Core Concepts
|
|
20
|
+
|
|
21
|
+
### Verify vs Waive
|
|
22
|
+
|
|
23
|
+
These are semantically different operations:
|
|
24
|
+
|
|
25
|
+
**Verify** = "I tested this exact change and it works." Fingerprint-specific — tied to the current diff hash. If the file changes again, a new pending verification appears.
|
|
26
|
+
|
|
27
|
+
**Waive** = "I accept changes to this feature+file pair." Fingerprint-agnostic — covers the current change AND all future changes to that file for that feature. Use when the change is known-safe (e.g., comment-only edit, unrelated code path).
|
|
28
|
+
|
|
29
|
+
**Decision rule:** Did you run the smoke test or manually confirm behavior? Use `verify`. Is the change structurally irrelevant to the feature? Use `waive`.
|
|
30
|
+
|
|
31
|
+
### Pending Verifications
|
|
32
|
+
|
|
33
|
+
When you Edit/Write a file tracked by a feature, PostToolUse automatically creates a pending verification record. Each record is keyed by (project, feature, file, fingerprint). The `pending` list shows what needs attention before release.
|
|
34
|
+
|
|
35
|
+
### Release Boundary
|
|
36
|
+
|
|
37
|
+
Release-boundary commands (publish, deploy) check for unresolved pending verifications. If any exist, the command is blocked with a list of what's pending. This is the enforcement mechanism — it only gates release, not development.
|
|
38
|
+
|
|
39
|
+
## Steps
|
|
40
|
+
|
|
41
|
+
### 1. Check what's pending
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
eagle-mem feature pending # current project
|
|
45
|
+
eagle-mem feature pending --raw # include trigger, fingerprint, timestamp
|
|
46
|
+
eagle-mem feature pending --limit 100 # show more than default 50
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Each entry shows: feature name, file path, reason, and smoke test command (if registered).
|
|
50
|
+
|
|
51
|
+
### 2. Resolve by verifying
|
|
52
|
+
|
|
53
|
+
After running smoke tests or confirming the feature works:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
eagle-mem feature verify <feature-name> --notes "smoke tests pass, tested login flow"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This marks ALL pending verifications for that feature as verified (for the current fingerprints) and updates the feature's `last_verified_at` timestamp.
|
|
60
|
+
|
|
61
|
+
### 3. Resolve by waiving
|
|
62
|
+
|
|
63
|
+
When a change is known-safe without testing — waive by feature name:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
eagle-mem feature waive <feature-name> --reason "comment-only change, no behavior impact"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Or waive a single pending record by ID:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
eagle-mem feature waive <id> --reason "unrelated code path"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Prefer waive-by-name** over waive-by-ID. IDs are ephemeral (new edits create new IDs), but names are stable. Waiving by name resolves all pending records for that feature at once.
|
|
76
|
+
|
|
77
|
+
A reason is always required — it's the audit trail for why verification was skipped.
|
|
78
|
+
|
|
79
|
+
### 4. Register a new feature
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
eagle-mem feature add auth-flow \
|
|
83
|
+
--desc "User authentication and session management" \
|
|
84
|
+
--file src/auth/login.ts \
|
|
85
|
+
--file src/auth/session.ts \
|
|
86
|
+
--smoke "npm test -- --grep auth" \
|
|
87
|
+
--requires env_var:AUTH_SECRET
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 5. List and inspect features
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
eagle-mem feature list # all active features
|
|
94
|
+
eagle-mem feature show <name> # files, deps, smoke tests, last verified
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Judgment
|
|
98
|
+
|
|
99
|
+
**Always verify when:**
|
|
100
|
+
- You edited core logic in a tracked file
|
|
101
|
+
- The smoke test exists and is quick to run
|
|
102
|
+
- Multiple features depend on the changed file
|
|
103
|
+
|
|
104
|
+
**Waive when:**
|
|
105
|
+
- The edit is cosmetic (formatting, comments, imports)
|
|
106
|
+
- The changed code path is unrelated to the feature's functionality
|
|
107
|
+
- The feature's smoke test would test something you already tested another way
|
|
108
|
+
|
|
109
|
+
**Don't ignore pending verifications.** If you see them in the recall block, address them before your session ends. The user will be blocked on release otherwise.
|
|
110
|
+
|
|
111
|
+
## Reference
|
|
112
|
+
|
|
113
|
+
| Command | What it does |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `feature list` | All active features with file/dep/test counts |
|
|
116
|
+
| `feature show <name>` | Full detail: files, dependencies, smoke tests |
|
|
117
|
+
| `feature pending` | All unresolved pending verifications |
|
|
118
|
+
| `feature verify <name>` | Mark feature verified (fingerprint-specific) |
|
|
119
|
+
| `feature waive <name\|id>` | Waive verification (fingerprint-agnostic for name) |
|
|
120
|
+
| `feature add <name>` | Register a new feature with files/deps/tests |
|
|
121
|
+
| `--notes "text"` | Attach notes to verify/waive (audit trail) |
|
|
122
|
+
| `--reason "text"` | Required for waive — explains why safe |
|
|
123
|
+
| `--raw` | Show trigger, fingerprint, and timestamp in pending |
|