eagle-mem 4.6.2 → 4.7.1
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 +49 -15
- package/db/023_guardrails.sql +3 -2
- package/db/024_guardrails_unique.sql +46 -0
- package/db/025_pending_feature_verifications.sql +30 -0
- package/db/026_agent_source.sql +18 -0
- package/db/027_feature_verification_fingerprints.sql +9 -0
- package/db/028_agent_artifact_tables.sql +124 -0
- package/hooks/post-tool-use.sh +42 -13
- package/hooks/pre-tool-use.sh +107 -14
- package/hooks/session-end.sh +3 -1
- package/hooks/session-start.sh +64 -15
- package/hooks/stop.sh +115 -21
- package/hooks/user-prompt-submit.sh +14 -5
- package/lib/codex-hooks.sh +194 -0
- package/lib/common.sh +345 -0
- package/lib/db-backfill.sh +3 -3
- package/lib/db-features.sh +222 -0
- package/lib/db-guardrails.sh +2 -1
- package/lib/db-mirrors.sh +79 -43
- package/lib/db-observations.sh +3 -2
- package/lib/db-sessions.sh +11 -7
- package/lib/db-summaries.sh +9 -6
- package/lib/hooks-posttool.sh +8 -6
- package/lib/provider.sh +190 -4
- package/package.json +7 -3
- package/scripts/config.sh +2 -0
- package/scripts/feature.sh +70 -2
- package/scripts/guard.sh +4 -1
- package/scripts/health.sh +5 -1
- package/scripts/help.sh +13 -8
- package/scripts/install.sh +130 -76
- package/scripts/memories.sh +71 -45
- package/scripts/refresh.sh +3 -3
- package/scripts/search.sh +57 -47
- package/scripts/statusline-em.sh +1 -1
- package/scripts/tasks.sh +186 -15
- package/scripts/uninstall.sh +7 -0
- package/scripts/update.sh +51 -7
- package/skills/eagle-mem-memories/SKILL.md +13 -13
- package/skills/eagle-mem-tasks/SKILL.md +21 -15
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# Searches memory for relevant context and injects it
|
|
6
6
|
# ═══════════════════════════════════════════════════════════
|
|
7
7
|
set +e
|
|
8
|
+
[ "${EAGLE_MEM_DISABLE_HOOKS:-}" = "1" ] && exit 0
|
|
8
9
|
|
|
9
10
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
11
|
LIB_DIR="$SCRIPT_DIR/../lib"
|
|
@@ -20,6 +21,7 @@ input=$(eagle_read_stdin)
|
|
|
20
21
|
session_id=$(echo "$input" | jq -r '.session_id // empty')
|
|
21
22
|
cwd=$(echo "$input" | jq -r '.cwd // empty')
|
|
22
23
|
user_prompt=$(echo "$input" | jq -r '.prompt // empty')
|
|
24
|
+
agent=$(eagle_agent_source_from_json "$input")
|
|
23
25
|
|
|
24
26
|
[ -z "$user_prompt" ] && exit 0
|
|
25
27
|
|
|
@@ -60,7 +62,10 @@ fi
|
|
|
60
62
|
|
|
61
63
|
# Skip short prompts — not enough signal for meaningful search
|
|
62
64
|
word_count=$(echo "$user_prompt" | wc -w | tr -d ' ')
|
|
63
|
-
[ "$word_count" -lt 3 ]
|
|
65
|
+
if [ "$word_count" -lt 3 ]; then
|
|
66
|
+
eagle_emit_context_for_agent "$agent" "UserPromptSubmit" "$context"
|
|
67
|
+
exit 0
|
|
68
|
+
fi
|
|
64
69
|
|
|
65
70
|
# Build FTS5 query from significant words (drop stop words, take first 6)
|
|
66
71
|
fts_query=$(echo "$user_prompt" | tr -cs '[:alnum:]' ' ' | tr '[:upper:]' '[:lower:]' | \
|
|
@@ -75,7 +80,10 @@ fts_query=$(echo "$user_prompt" | tr -cs '[:alnum:]' ' ' | tr '[:upper:]' '[:low
|
|
|
75
80
|
}
|
|
76
81
|
}')
|
|
77
82
|
|
|
78
|
-
[ -z "$fts_query" ]
|
|
83
|
+
if [ -z "$fts_query" ]; then
|
|
84
|
+
eagle_emit_context_for_agent "$agent" "UserPromptSubmit" "$context"
|
|
85
|
+
exit 0
|
|
86
|
+
fi
|
|
79
87
|
|
|
80
88
|
# Search for relevant past summaries (cross-session)
|
|
81
89
|
results=$(eagle_search_summaries "$fts_query" "$project" 3)
|
|
@@ -83,9 +91,10 @@ results=$(eagle_search_summaries "$fts_query" "$project" 3)
|
|
|
83
91
|
if [ -n "$results" ]; then
|
|
84
92
|
context+="=== Eagle Mem: Relevant Recall ===
|
|
85
93
|
"
|
|
86
|
-
while IFS='|' read -r req completed learned _next_steps created_at _proj decisions gotchas key_files; do
|
|
94
|
+
while IFS='|' read -r req completed learned _next_steps created_at _proj decisions gotchas key_files summary_agent; do
|
|
87
95
|
[ -z "$req" ] && [ -z "$completed" ] && continue
|
|
88
|
-
|
|
96
|
+
origin_label=$(eagle_agent_label "$summary_agent")
|
|
97
|
+
context+="[$created_at][$origin_label] "
|
|
89
98
|
[ -n "$req" ] && context+="$req"
|
|
90
99
|
[ -n "$completed" ] && context+=" → $completed"
|
|
91
100
|
[ -n "$learned" ] && context+=" (Learned: $learned)"
|
|
@@ -126,5 +135,5 @@ IMPORTANT: When Eagle Mem finds relevant memories or code for the user's prompt,
|
|
|
126
135
|
=== Eagle Mem: Persistent Memory ===
|
|
127
136
|
"
|
|
128
137
|
|
|
129
|
-
|
|
138
|
+
eagle_emit_context_for_agent "$agent" "UserPromptSubmit" "$context"
|
|
130
139
|
exit 0
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════
|
|
3
|
+
# Eagle Mem — Codex hook registration helpers
|
|
4
|
+
# Shared by install.sh, update.sh, and uninstall.sh
|
|
5
|
+
# ═══════════════════════════════════════════════════════════
|
|
6
|
+
[ -n "${_EAGLE_CODEX_HOOKS_LOADED:-}" ] && return 0
|
|
7
|
+
_EAGLE_CODEX_HOOKS_LOADED=1
|
|
8
|
+
|
|
9
|
+
eagle_enable_codex_hooks() {
|
|
10
|
+
local config="$EAGLE_CODEX_CONFIG"
|
|
11
|
+
mkdir -p "$(dirname "$config")"
|
|
12
|
+
|
|
13
|
+
if [ ! -f "$config" ]; then
|
|
14
|
+
cat > "$config" << 'TOML'
|
|
15
|
+
[features]
|
|
16
|
+
codex_hooks = true
|
|
17
|
+
TOML
|
|
18
|
+
chmod 600 "$config" 2>/dev/null || true
|
|
19
|
+
return 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
local tmp
|
|
23
|
+
tmp=$(mktemp)
|
|
24
|
+
awk '
|
|
25
|
+
BEGIN { in_features=0; saw_features=0; saw_flag=0; inserted=0 }
|
|
26
|
+
/^[[:space:]]*\[features\][[:space:]]*$/ {
|
|
27
|
+
saw_features=1
|
|
28
|
+
in_features=1
|
|
29
|
+
print
|
|
30
|
+
next
|
|
31
|
+
}
|
|
32
|
+
/^[[:space:]]*\[/ && in_features {
|
|
33
|
+
if (!saw_flag && !inserted) {
|
|
34
|
+
print "codex_hooks = true"
|
|
35
|
+
inserted=1
|
|
36
|
+
}
|
|
37
|
+
in_features=0
|
|
38
|
+
}
|
|
39
|
+
in_features && /^[[:space:]]*codex_hooks[[:space:]]*=/ {
|
|
40
|
+
print "codex_hooks = true"
|
|
41
|
+
saw_flag=1
|
|
42
|
+
next
|
|
43
|
+
}
|
|
44
|
+
{ print }
|
|
45
|
+
END {
|
|
46
|
+
if (in_features && !saw_flag && !inserted) {
|
|
47
|
+
print "codex_hooks = true"
|
|
48
|
+
inserted=1
|
|
49
|
+
}
|
|
50
|
+
if (!saw_features) {
|
|
51
|
+
print ""
|
|
52
|
+
print "[features]"
|
|
53
|
+
print "codex_hooks = true"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
' "$config" > "$tmp" && mv "$tmp" "$config"
|
|
57
|
+
chmod 600 "$config" 2>/dev/null || true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
eagle_patch_codex_hook() {
|
|
61
|
+
local hooks_file="$1"
|
|
62
|
+
local event="$2"
|
|
63
|
+
local matcher="$3"
|
|
64
|
+
local command="$4"
|
|
65
|
+
local description="${5:-}"
|
|
66
|
+
local status_message="${6:-}"
|
|
67
|
+
local timeout="${7:-}"
|
|
68
|
+
local script_path="$command"
|
|
69
|
+
script_path="${script_path#EAGLE_AGENT_SOURCE=codex bash \"}"
|
|
70
|
+
script_path="${script_path#bash \"}"
|
|
71
|
+
script_path="${script_path%\"}"
|
|
72
|
+
|
|
73
|
+
mkdir -p "$(dirname "$hooks_file")"
|
|
74
|
+
if [ ! -f "$hooks_file" ]; then
|
|
75
|
+
printf '{"hooks":{}}\n' > "$hooks_file"
|
|
76
|
+
chmod 600 "$hooks_file" 2>/dev/null || true
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
local match_query
|
|
80
|
+
if [ -n "$matcher" ]; then
|
|
81
|
+
match_query='.hooks[$event][]? | select(.matcher == $matcher and (.hooks[]?.command == $command))'
|
|
82
|
+
else
|
|
83
|
+
match_query='.hooks[$event][]? | select((.matcher == null or .matcher == "") and (.hooks[]?.command == $command))'
|
|
84
|
+
fi
|
|
85
|
+
if jq -e --arg event "$event" --arg matcher "$matcher" --arg command "$command" "$match_query" "$hooks_file" &>/dev/null; then
|
|
86
|
+
[ -n "$description" ] && eagle_ok "$description ${DIM}(already registered)${RESET}"
|
|
87
|
+
return 0
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
if jq -e --arg event "$event" --arg matcher "$matcher" --arg script "$script_path" '
|
|
91
|
+
.hooks[$event][]?
|
|
92
|
+
| select((($matcher == "" and (.matcher == null or .matcher == "")) or .matcher == $matcher)
|
|
93
|
+
and any(.hooks[]?; (.command // "") | contains($script)))
|
|
94
|
+
' "$hooks_file" &>/dev/null; then
|
|
95
|
+
local tmp_existing
|
|
96
|
+
tmp_existing=$(mktemp)
|
|
97
|
+
jq --arg event "$event" --arg matcher "$matcher" --arg script "$script_path" --arg command "$command" '
|
|
98
|
+
.hooks[$event] |= map(
|
|
99
|
+
if ((($matcher == "" and (.matcher == null or .matcher == "")) or .matcher == $matcher)
|
|
100
|
+
and any(.hooks[]?; (.command // "") | contains($script)))
|
|
101
|
+
then .hooks |= map(if ((.command // "") | contains($script)) then .command = $command else . end)
|
|
102
|
+
else .
|
|
103
|
+
end
|
|
104
|
+
)
|
|
105
|
+
' "$hooks_file" > "$tmp_existing" && mv "$tmp_existing" "$hooks_file"
|
|
106
|
+
[ -n "$description" ] && eagle_ok "$description ${DIM}(updated)${RESET}"
|
|
107
|
+
return 0
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
local entry
|
|
111
|
+
entry=$(jq -nc \
|
|
112
|
+
--arg m "$matcher" \
|
|
113
|
+
--arg c "$command" \
|
|
114
|
+
--arg s "$status_message" \
|
|
115
|
+
--arg timeout "$timeout" '
|
|
116
|
+
{
|
|
117
|
+
hooks: [
|
|
118
|
+
{
|
|
119
|
+
type: "command",
|
|
120
|
+
command: $c
|
|
121
|
+
}
|
|
122
|
+
+ (if $s == "" then {} else {statusMessage: $s} end)
|
|
123
|
+
+ (if $timeout == "" then {} else {timeout: ($timeout | tonumber)} end)
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
+ (if $m == "" then {} else {matcher: $m} end)')
|
|
127
|
+
|
|
128
|
+
local tmp
|
|
129
|
+
tmp=$(mktemp)
|
|
130
|
+
jq --argjson entry "$entry" ".hooks.${event} = ((.hooks.${event} // []) + [\$entry])" "$hooks_file" > "$tmp" && mv "$tmp" "$hooks_file"
|
|
131
|
+
chmod 600 "$hooks_file" 2>/dev/null || true
|
|
132
|
+
[ -n "$description" ] && eagle_ok "$description"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
eagle_register_codex_hooks() {
|
|
136
|
+
eagle_enable_codex_hooks
|
|
137
|
+
|
|
138
|
+
eagle_patch_codex_hook "$EAGLE_CODEX_HOOKS" "SessionStart" "startup|resume|clear" \
|
|
139
|
+
"EAGLE_AGENT_SOURCE=codex bash \"$EAGLE_MEM_DIR/hooks/session-start.sh\"" \
|
|
140
|
+
"Codex SessionStart hook" \
|
|
141
|
+
"Loading Eagle Mem recall" \
|
|
142
|
+
"30"
|
|
143
|
+
|
|
144
|
+
eagle_patch_codex_hook "$EAGLE_CODEX_HOOKS" "UserPromptSubmit" "" \
|
|
145
|
+
"EAGLE_AGENT_SOURCE=codex bash \"$EAGLE_MEM_DIR/hooks/user-prompt-submit.sh\"" \
|
|
146
|
+
"Codex UserPromptSubmit hook" \
|
|
147
|
+
"Searching Eagle Mem" \
|
|
148
|
+
"30"
|
|
149
|
+
|
|
150
|
+
eagle_patch_codex_hook "$EAGLE_CODEX_HOOKS" "PreToolUse" "^(Bash|exec_command|shell_command|unified_exec|apply_patch|Edit|Write)$" \
|
|
151
|
+
"EAGLE_AGENT_SOURCE=codex bash \"$EAGLE_MEM_DIR/hooks/pre-tool-use.sh\"" \
|
|
152
|
+
"Codex PreToolUse hook" \
|
|
153
|
+
"Checking Eagle Mem guardrails" \
|
|
154
|
+
"30"
|
|
155
|
+
|
|
156
|
+
eagle_patch_codex_hook "$EAGLE_CODEX_HOOKS" "PostToolUse" "^(Bash|exec_command|shell_command|unified_exec|apply_patch|Edit|Write)$" \
|
|
157
|
+
"EAGLE_AGENT_SOURCE=codex bash \"$EAGLE_MEM_DIR/hooks/post-tool-use.sh\"" \
|
|
158
|
+
"Codex PostToolUse hook" \
|
|
159
|
+
"Recording Eagle Mem observation" \
|
|
160
|
+
"30"
|
|
161
|
+
|
|
162
|
+
eagle_patch_codex_hook "$EAGLE_CODEX_HOOKS" "Stop" "" \
|
|
163
|
+
"EAGLE_AGENT_SOURCE=codex bash \"$EAGLE_MEM_DIR/hooks/stop.sh\"" \
|
|
164
|
+
"Codex Stop hook" \
|
|
165
|
+
"Saving Eagle Mem summary" \
|
|
166
|
+
"30"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
eagle_remove_codex_hooks() {
|
|
170
|
+
local hooks_file="$EAGLE_CODEX_HOOKS"
|
|
171
|
+
[ -f "$hooks_file" ] || return 1
|
|
172
|
+
command -v jq &>/dev/null || return 1
|
|
173
|
+
|
|
174
|
+
local tmp
|
|
175
|
+
tmp=$(mktemp)
|
|
176
|
+
jq '
|
|
177
|
+
def without_eagle_mem_handlers:
|
|
178
|
+
.hooks = ((.hooks // [])
|
|
179
|
+
| map(select(((.command // "") | contains(".eagle-mem/hooks/")) | not)));
|
|
180
|
+
|
|
181
|
+
if .hooks then
|
|
182
|
+
.hooks |= with_entries(
|
|
183
|
+
.value = [
|
|
184
|
+
.value[]?
|
|
185
|
+
| without_eagle_mem_handlers
|
|
186
|
+
| select((.hooks // []) | length > 0)
|
|
187
|
+
]
|
|
188
|
+
| select(.value != [])
|
|
189
|
+
)
|
|
190
|
+
else . end
|
|
191
|
+
| if .hooks == {} then del(.hooks) else . end
|
|
192
|
+
' "$hooks_file" > "$tmp" && mv "$tmp" "$hooks_file"
|
|
193
|
+
chmod 600 "$hooks_file" 2>/dev/null || true
|
|
194
|
+
}
|
package/lib/common.sh
CHANGED
|
@@ -12,6 +12,13 @@ EAGLE_SKILLS_DIR="$HOME/.claude/skills"
|
|
|
12
12
|
EAGLE_CLAUDE_PROJECTS_DIR="$HOME/.claude/projects"
|
|
13
13
|
EAGLE_CLAUDE_PLANS_DIR="$HOME/.claude/plans"
|
|
14
14
|
EAGLE_CLAUDE_TASKS_DIR="$HOME/.claude/tasks"
|
|
15
|
+
EAGLE_CODEX_DIR="${EAGLE_CODEX_DIR:-$HOME/.codex}"
|
|
16
|
+
EAGLE_CODEX_CONFIG="${EAGLE_CODEX_CONFIG:-$EAGLE_CODEX_DIR/config.toml}"
|
|
17
|
+
EAGLE_CODEX_HOOKS="${EAGLE_CODEX_HOOKS:-$EAGLE_CODEX_DIR/hooks.json}"
|
|
18
|
+
EAGLE_CODEX_AGENTS_MD="${EAGLE_CODEX_AGENTS_MD:-$EAGLE_CODEX_DIR/AGENTS.md}"
|
|
19
|
+
EAGLE_CODEX_SKILLS_DIR="${EAGLE_CODEX_SKILLS_DIR:-$EAGLE_CODEX_DIR/skills}"
|
|
20
|
+
EAGLE_CODEX_MEMORIES_DIR="${EAGLE_CODEX_MEMORIES_DIR:-$EAGLE_CODEX_DIR/memories}"
|
|
21
|
+
EAGLE_RAW_BASH_UNLOCK="${EAGLE_RAW_BASH_UNLOCK:-/tmp/eagle-mem-raw-bash-unlock}"
|
|
15
22
|
|
|
16
23
|
eagle_log() {
|
|
17
24
|
local level="$1"
|
|
@@ -63,6 +70,289 @@ eagle_project_from_cwd() {
|
|
|
63
70
|
fi
|
|
64
71
|
}
|
|
65
72
|
|
|
73
|
+
eagle_project_file_path() {
|
|
74
|
+
local cwd="${1:-$(pwd)}"
|
|
75
|
+
local file_path="${2:-}"
|
|
76
|
+
|
|
77
|
+
[ -z "$file_path" ] && return 0
|
|
78
|
+
|
|
79
|
+
case "$file_path" in
|
|
80
|
+
./*) file_path="${file_path#./}" ;;
|
|
81
|
+
esac
|
|
82
|
+
|
|
83
|
+
case "$file_path" in
|
|
84
|
+
/*)
|
|
85
|
+
local git_root
|
|
86
|
+
git_root=$(git -C "$cwd" rev-parse --show-toplevel 2>/dev/null)
|
|
87
|
+
if [ -n "$git_root" ]; then
|
|
88
|
+
case "$file_path" in
|
|
89
|
+
"$git_root"/*)
|
|
90
|
+
printf '%s\n' "${file_path#$git_root/}"
|
|
91
|
+
return 0
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
94
|
+
fi
|
|
95
|
+
case "$file_path" in
|
|
96
|
+
"$cwd"/*)
|
|
97
|
+
printf '%s\n' "${file_path#$cwd/}"
|
|
98
|
+
return 0
|
|
99
|
+
;;
|
|
100
|
+
esac
|
|
101
|
+
;;
|
|
102
|
+
esac
|
|
103
|
+
|
|
104
|
+
printf '%s\n' "$file_path"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
eagle_extract_apply_patch_files() {
|
|
108
|
+
sed -n -E 's/^\*\*\* (Add|Update|Delete) File: //p'
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
eagle_agent_source() {
|
|
112
|
+
local agent="${EAGLE_AGENT_SOURCE:-${EAGLE_AGENT:-}}"
|
|
113
|
+
case "$agent" in
|
|
114
|
+
codex|openai-codex) echo "codex" ;;
|
|
115
|
+
claude|claude-code|cloud-code) echo "claude-code" ;;
|
|
116
|
+
*) echo "claude-code" ;;
|
|
117
|
+
esac
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
eagle_agent_source_from_json() {
|
|
121
|
+
local input="${1:-}"
|
|
122
|
+
local configured="${EAGLE_AGENT_SOURCE:-${EAGLE_AGENT:-}}"
|
|
123
|
+
if [ -n "$configured" ]; then
|
|
124
|
+
eagle_agent_source
|
|
125
|
+
return
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
local transcript_path turn_id tool_name
|
|
129
|
+
transcript_path=$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
130
|
+
turn_id=$(printf '%s' "$input" | jq -r '.turn_id // empty' 2>/dev/null)
|
|
131
|
+
tool_name=$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
132
|
+
|
|
133
|
+
case "$transcript_path" in
|
|
134
|
+
"$HOME/.codex/"*|*/.codex/*) echo "codex"; return ;;
|
|
135
|
+
"$HOME/.claude/"*|*/.claude/*) echo "claude-code"; return ;;
|
|
136
|
+
esac
|
|
137
|
+
[ -n "$turn_id" ] && { echo "codex"; return; }
|
|
138
|
+
[ "$tool_name" = "apply_patch" ] && { echo "codex"; return; }
|
|
139
|
+
|
|
140
|
+
echo "claude-code"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
eagle_agent_label() {
|
|
144
|
+
case "${1:-$(eagle_agent_source)}" in
|
|
145
|
+
codex) echo "Codex" ;;
|
|
146
|
+
*) echo "Claude Code" ;;
|
|
147
|
+
esac
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
eagle_is_shell_tool() {
|
|
151
|
+
case "${1:-}" in
|
|
152
|
+
Bash|exec_command|shell_command|unified_exec) return 0 ;;
|
|
153
|
+
*) return 1 ;;
|
|
154
|
+
esac
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
eagle_tool_command_from_json() {
|
|
158
|
+
local input="${1:-}"
|
|
159
|
+
printf '%s' "$input" | jq -r '
|
|
160
|
+
.tool_input.command
|
|
161
|
+
// .tool_input.cmd
|
|
162
|
+
// .tool_input.shell_command
|
|
163
|
+
// .tool_input.command_line
|
|
164
|
+
// .tool_input.cmdline
|
|
165
|
+
// (if (.tool_input.argv? | type) == "array" then (.tool_input.argv | join(" ")) else empty end)
|
|
166
|
+
// empty
|
|
167
|
+
' 2>/dev/null
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
eagle_emit_context_for_agent() {
|
|
171
|
+
local agent="${1:-$(eagle_agent_source)}"
|
|
172
|
+
local hook_event="${2:-}"
|
|
173
|
+
local context="${3:-}"
|
|
174
|
+
|
|
175
|
+
[ -z "$context" ] && return 0
|
|
176
|
+
|
|
177
|
+
if [ "$agent" = "codex" ]; then
|
|
178
|
+
jq -cn \
|
|
179
|
+
--arg event "$hook_event" \
|
|
180
|
+
--arg context "$context" \
|
|
181
|
+
'{
|
|
182
|
+
hookSpecificOutput: {
|
|
183
|
+
hookEventName: $event,
|
|
184
|
+
additionalContext: $context
|
|
185
|
+
}
|
|
186
|
+
}'
|
|
187
|
+
return 0
|
|
188
|
+
fi
|
|
189
|
+
|
|
190
|
+
printf '%s\n' "$context"
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
eagle_rtk_rewrite_command() {
|
|
194
|
+
local cmd="$1"
|
|
195
|
+
command -v rtk >/dev/null 2>&1 || return 1
|
|
196
|
+
|
|
197
|
+
case "$cmd" in
|
|
198
|
+
""|rtk\ *|*" rtk "*|*"eagle-mem "*|*"git push"*|*"gh pr create"*|*"npm publish"*|*"pnpm publish"*|*"yarn npm publish"*|*"bun publish"*)
|
|
199
|
+
return 1
|
|
200
|
+
;;
|
|
201
|
+
esac
|
|
202
|
+
|
|
203
|
+
local rewritten
|
|
204
|
+
rewritten=$(rtk rewrite "$cmd" 2>/dev/null | head -1)
|
|
205
|
+
[ -z "$rewritten" ] && return 1
|
|
206
|
+
[ "$rewritten" = "$cmd" ] && return 1
|
|
207
|
+
printf '%s\n' "$rewritten"
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
eagle_raw_bash_unlock_active() {
|
|
211
|
+
[ -f "$EAGLE_RAW_BASH_UNLOCK" ] || return 1
|
|
212
|
+
local now mtime age
|
|
213
|
+
now=$(date +%s)
|
|
214
|
+
mtime=$(stat -f %m "$EAGLE_RAW_BASH_UNLOCK" 2>/dev/null || stat -c %Y "$EAGLE_RAW_BASH_UNLOCK" 2>/dev/null || echo 0)
|
|
215
|
+
age=$((now - mtime))
|
|
216
|
+
[ "$age" -lt 600 ]
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
eagle_sha256_stream() {
|
|
220
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
221
|
+
shasum -a 256 | awk '{print $1}'
|
|
222
|
+
else
|
|
223
|
+
sha256sum | awk '{print $1}'
|
|
224
|
+
fi
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
eagle_is_release_boundary_command() {
|
|
228
|
+
local cmd="$1"
|
|
229
|
+
|
|
230
|
+
if printf '%s\n' "$cmd" \
|
|
231
|
+
| tr '\n' ';' \
|
|
232
|
+
| sed -E 's/(&&|[|][|]|;)/\
|
|
233
|
+
/g' \
|
|
234
|
+
| awk '
|
|
235
|
+
function has_dry_run_flag(line) {
|
|
236
|
+
return line ~ /(^|[[:space:]])--dry-run([[:space:]]|$|=([Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss])([[:space:]]|$))/
|
|
237
|
+
}
|
|
238
|
+
/(^|[[:space:]])gh[[:space:]]+pr[[:space:]]+create([[:space:]]|$)/ ||
|
|
239
|
+
/(^|[[:space:]])npm[[:space:]]+publish([[:space:]]|$)/ ||
|
|
240
|
+
/(^|[[:space:]])pnpm[[:space:]]+publish([[:space:]]|$)/ ||
|
|
241
|
+
/(^|[[:space:]])yarn[[:space:]]+npm[[:space:]]+publish([[:space:]]|$)/ ||
|
|
242
|
+
/(^|[[:space:]])bun[[:space:]]+publish([[:space:]]|$)/ {
|
|
243
|
+
if (!has_dry_run_flag($0)) found = 1
|
|
244
|
+
}
|
|
245
|
+
END { exit(found ? 0 : 1) }
|
|
246
|
+
'
|
|
247
|
+
then
|
|
248
|
+
return 0
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
if printf '%s\n' "$cmd" \
|
|
252
|
+
| tr '\n' ';' \
|
|
253
|
+
| sed -E 's/(&&|[|][|]|;)/\
|
|
254
|
+
/g' \
|
|
255
|
+
| awk '
|
|
256
|
+
function has_dry_run_flag(line) {
|
|
257
|
+
return line ~ /(^|[[:space:]])--dry-run([[:space:]]|$|=([Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss])([[:space:]]|$))/
|
|
258
|
+
}
|
|
259
|
+
/(^|[[:space:]])git[[:space:]]+push([[:space:]]|$)/ {
|
|
260
|
+
if (!has_dry_run_flag($0)) found = 1
|
|
261
|
+
}
|
|
262
|
+
END { exit(found ? 0 : 1) }
|
|
263
|
+
'
|
|
264
|
+
then
|
|
265
|
+
return 0
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
return 1
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
eagle_changed_files_for_release() {
|
|
272
|
+
local cwd="${1:-$(pwd)}"
|
|
273
|
+
[ -d "$cwd" ] || return 0
|
|
274
|
+
|
|
275
|
+
{
|
|
276
|
+
git -C "$cwd" diff --name-only HEAD 2>/dev/null
|
|
277
|
+
git -C "$cwd" diff --cached --name-only 2>/dev/null
|
|
278
|
+
local default_branch
|
|
279
|
+
default_branch=$(git -C "$cwd" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||')
|
|
280
|
+
if [ -n "$default_branch" ] && git -C "$cwd" rev-parse --verify "origin/$default_branch" >/dev/null 2>&1; then
|
|
281
|
+
git -C "$cwd" diff --name-only "origin/$default_branch...HEAD" 2>/dev/null
|
|
282
|
+
fi
|
|
283
|
+
if git -C "$cwd" rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' >/dev/null 2>&1; then
|
|
284
|
+
git -C "$cwd" diff --name-only '@{upstream}...HEAD' 2>/dev/null
|
|
285
|
+
fi
|
|
286
|
+
if ! git -C "$cwd" rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' >/dev/null 2>&1; then
|
|
287
|
+
if ! git -C "$cwd" symbolic-ref --quiet --short refs/remotes/origin/HEAD >/dev/null 2>&1; then
|
|
288
|
+
if git -C "$cwd" rev-parse --verify HEAD~1 >/dev/null 2>&1; then
|
|
289
|
+
git -C "$cwd" diff --name-only HEAD~1..HEAD 2>/dev/null
|
|
290
|
+
fi
|
|
291
|
+
fi
|
|
292
|
+
fi
|
|
293
|
+
} | sed '/^[[:space:]]*$/d' | sort -u
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
eagle_change_fingerprint_for_file() {
|
|
297
|
+
local cwd="${1:-$(pwd)}"
|
|
298
|
+
local file_path="${2:-}"
|
|
299
|
+
[ -z "$file_path" ] && return 0
|
|
300
|
+
[ -d "$cwd" ] || return 0
|
|
301
|
+
|
|
302
|
+
local git_root
|
|
303
|
+
git_root=$(git -C "$cwd" rev-parse --show-toplevel 2>/dev/null)
|
|
304
|
+
[ -z "$git_root" ] && return 0
|
|
305
|
+
|
|
306
|
+
local rel_path="$file_path"
|
|
307
|
+
case "$rel_path" in
|
|
308
|
+
./*) rel_path="${rel_path#./}" ;;
|
|
309
|
+
/*)
|
|
310
|
+
case "$rel_path" in
|
|
311
|
+
"$git_root"/*) rel_path="${rel_path#$git_root/}" ;;
|
|
312
|
+
"$cwd"/*) rel_path="${rel_path#$cwd/}" ;;
|
|
313
|
+
esac
|
|
314
|
+
;;
|
|
315
|
+
esac
|
|
316
|
+
|
|
317
|
+
local base_ref=""
|
|
318
|
+
local default_branch
|
|
319
|
+
default_branch=$(git -C "$git_root" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||')
|
|
320
|
+
if [ -n "$default_branch" ] && git -C "$git_root" rev-parse --verify "origin/$default_branch" >/dev/null 2>&1; then
|
|
321
|
+
base_ref=$(git -C "$git_root" merge-base HEAD "origin/$default_branch" 2>/dev/null)
|
|
322
|
+
fi
|
|
323
|
+
|
|
324
|
+
if [ -z "$base_ref" ] && git -C "$git_root" rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' >/dev/null 2>&1; then
|
|
325
|
+
base_ref=$(git -C "$git_root" merge-base HEAD '@{upstream}' 2>/dev/null)
|
|
326
|
+
fi
|
|
327
|
+
|
|
328
|
+
if [ -z "$base_ref" ]; then
|
|
329
|
+
if ! git -C "$git_root" diff --quiet HEAD -- "$rel_path" 2>/dev/null \
|
|
330
|
+
|| ! git -C "$git_root" diff --cached --quiet HEAD -- "$rel_path" 2>/dev/null \
|
|
331
|
+
|| git -C "$git_root" ls-files --others --exclude-standard -- "$rel_path" 2>/dev/null | grep -qxF "$rel_path"
|
|
332
|
+
then
|
|
333
|
+
base_ref="HEAD"
|
|
334
|
+
elif git -C "$git_root" rev-parse --verify HEAD~1 >/dev/null 2>&1; then
|
|
335
|
+
base_ref="HEAD~1"
|
|
336
|
+
else
|
|
337
|
+
base_ref="HEAD"
|
|
338
|
+
fi
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
local base_blob="missing"
|
|
342
|
+
if [ -n "$base_ref" ] && git -C "$git_root" cat-file -e "$base_ref:$rel_path" 2>/dev/null; then
|
|
343
|
+
base_blob=$(git -C "$git_root" rev-parse "$base_ref:$rel_path" 2>/dev/null)
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
local final_blob="missing"
|
|
347
|
+
if [ -f "$git_root/$rel_path" ]; then
|
|
348
|
+
final_blob=$(git -C "$git_root" hash-object -- "$rel_path" 2>/dev/null)
|
|
349
|
+
elif git -C "$git_root" cat-file -e "HEAD:$rel_path" 2>/dev/null; then
|
|
350
|
+
final_blob=$(git -C "$git_root" rev-parse "HEAD:$rel_path" 2>/dev/null)
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
printf 'file:%s\nbase:%s\nfinal:%s\n' "$rel_path" "$base_blob" "$final_blob" | eagle_sha256_stream
|
|
354
|
+
}
|
|
355
|
+
|
|
66
356
|
eagle_sql_escape() {
|
|
67
357
|
printf '%s' "$1" | sed "s/'/''/g"
|
|
68
358
|
}
|
|
@@ -189,6 +479,9 @@ next_steps: [concrete actions]
|
|
|
189
479
|
key_files: [path — role]
|
|
190
480
|
files_read: [path, ...]
|
|
191
481
|
files_modified: [path, ...]
|
|
482
|
+
affected_features: [feature, ...]
|
|
483
|
+
verified_features: [feature, ...]
|
|
484
|
+
regression_risks: [risk, ...]
|
|
192
485
|
</eagle-summary>
|
|
193
486
|
```
|
|
194
487
|
|
|
@@ -198,6 +491,7 @@ files_modified: [path, ...]
|
|
|
198
491
|
- Emit `<eagle-summary>` before your final text response, every session
|
|
199
492
|
- When Eagle Mem injects context at SessionStart, attribute it: "Eagle Mem recalls:"
|
|
200
493
|
- Do not revert decisions surfaced by PostToolUse without asking the user
|
|
494
|
+
- If Eagle Mem reports pending feature verification, verify or waive it before push/PR/publish
|
|
201
495
|
- Never put raw secrets in the summary — Eagle Mem redacts but defense in depth
|
|
202
496
|
- If you contradict a loaded memory, update the memory file
|
|
203
497
|
EAGLE_MD
|
|
@@ -233,3 +527,54 @@ eagle_patch_claude_md() {
|
|
|
233
527
|
|
|
234
528
|
_eagle_claude_md_section >> "$claude_md"
|
|
235
529
|
}
|
|
530
|
+
|
|
531
|
+
_eagle_codex_agents_section() {
|
|
532
|
+
cat << 'EAGLE_AGENTS'
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
## Eagle Mem — Persistent Memory
|
|
537
|
+
|
|
538
|
+
Eagle Mem hooks are active for Codex in this project. SessionStart and UserPromptSubmit inject project recall from the shared Eagle Mem database at `~/.eagle-mem/memory.db`. PostToolUse records observations and marks affected features for verification.
|
|
539
|
+
|
|
540
|
+
**Rule:** Before your final response in every session, emit an `<eagle-summary>` block so the Stop hook can capture a rich summary.
|
|
541
|
+
|
|
542
|
+
```
|
|
543
|
+
<eagle-summary>
|
|
544
|
+
request: [what user asked]
|
|
545
|
+
completed: [what shipped]
|
|
546
|
+
learned: [non-obvious discoveries]
|
|
547
|
+
decisions: [choice — why]
|
|
548
|
+
gotchas: [what surprised]
|
|
549
|
+
next_steps: [concrete actions]
|
|
550
|
+
key_files: [path — role]
|
|
551
|
+
files_read: [path, ...]
|
|
552
|
+
files_modified: [path, ...]
|
|
553
|
+
affected_features: [feature, ...]
|
|
554
|
+
verified_features: [feature, ...]
|
|
555
|
+
regression_risks: [risk, ...]
|
|
556
|
+
</eagle-summary>
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**How to apply:**
|
|
560
|
+
- Attribute recalled context as "Eagle Mem recalls:" when it is injected
|
|
561
|
+
- Use the Eagle Mem skills when relevant: `eagle-mem-search`, `eagle-mem-overview`, `eagle-mem-memories`, and `eagle-mem-tasks`
|
|
562
|
+
- For important decisions, preferences, gotchas, or durable project facts, explicitly include them in the `<eagle-summary>` block so Codex-originated memories become available to future Claude Code and Codex sessions
|
|
563
|
+
- Do not revert Eagle Mem-surfaced decisions without asking the user
|
|
564
|
+
- If Eagle Mem reports pending feature verification, verify or waive it before push/PR/publish
|
|
565
|
+
- Never put raw secrets in summaries
|
|
566
|
+
EAGLE_AGENTS
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
eagle_patch_codex_agents_md() {
|
|
570
|
+
local agents_md="$EAGLE_CODEX_AGENTS_MD"
|
|
571
|
+
local marker="## Eagle Mem — Persistent Memory"
|
|
572
|
+
|
|
573
|
+
mkdir -p "$(dirname "$agents_md")"
|
|
574
|
+
|
|
575
|
+
if [ -f "$agents_md" ] && grep -qF "$marker" "$agents_md" 2>/dev/null; then
|
|
576
|
+
return 1
|
|
577
|
+
fi
|
|
578
|
+
|
|
579
|
+
_eagle_codex_agents_section >> "$agents_md"
|
|
580
|
+
}
|
package/lib/db-backfill.sh
CHANGED
|
@@ -65,9 +65,9 @@ eagle_backfill_projects() {
|
|
|
65
65
|
ch=$(eagle_db_pipe <<SQL
|
|
66
66
|
BEGIN;
|
|
67
67
|
UPDATE sessions SET project = '$proj_sql' WHERE id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
68
|
-
UPDATE
|
|
69
|
-
UPDATE
|
|
70
|
-
UPDATE
|
|
68
|
+
UPDATE agent_tasks SET project = '$proj_sql' WHERE source_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
69
|
+
UPDATE agent_memories SET project = '$proj_sql' WHERE origin_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
70
|
+
UPDATE agent_plans SET project = '$proj_sql' WHERE origin_session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
71
71
|
UPDATE summaries SET project = '$proj_sql' WHERE session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
72
72
|
UPDATE observations SET project = '$proj_sql' WHERE session_id = '$sid_sql' AND (project = '' OR project != '$proj_sql');
|
|
73
73
|
SELECT total_changes();
|