eagle-mem 4.6.1 → 4.7.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 +40 -8
- 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/hooks/post-tool-use.sh +41 -13
- package/hooks/pre-tool-use.sh +106 -14
- package/hooks/session-end.sh +2 -1
- package/hooks/session-start.sh +54 -13
- package/hooks/stop.sh +114 -21
- package/hooks/user-prompt-submit.sh +13 -5
- package/lib/codex-hooks.sh +194 -0
- package/lib/common.sh +341 -0
- package/lib/db-features.sh +222 -0
- package/lib/db-guardrails.sh +2 -1
- package/lib/db-mirrors.sh +24 -15
- package/lib/db-observations.sh +3 -2
- package/lib/db-sessions.sh +6 -2
- package/lib/db-summaries.sh +6 -3
- package/lib/hooks-posttool.sh +8 -6
- package/package.json +7 -3
- package/scripts/curate.sh +35 -25
- package/scripts/feature.sh +70 -2
- package/scripts/guard.sh +4 -1
- package/scripts/help.sh +7 -2
- package/scripts/install.sh +118 -76
- package/scripts/memories.sh +21 -18
- package/scripts/search.sh +36 -28
- package/scripts/uninstall.sh +7 -0
- package/scripts/update.sh +31 -6
package/lib/db-mirrors.sh
CHANGED
|
@@ -9,6 +9,7 @@ eagle_capture_claude_memory() {
|
|
|
9
9
|
local file_path="$1"
|
|
10
10
|
local session_id="${2:-}"
|
|
11
11
|
local project="${3:-}"
|
|
12
|
+
local agent="${4:-$(eagle_agent_source)}"
|
|
12
13
|
|
|
13
14
|
[ ! -f "$file_path" ] && return 0
|
|
14
15
|
|
|
@@ -28,7 +29,7 @@ eagle_capture_claude_memory() {
|
|
|
28
29
|
morigin=$(_fm_field "originSessionId")
|
|
29
30
|
[ -z "$morigin" ] && morigin="$session_id"
|
|
30
31
|
|
|
31
|
-
local fp_sql proj_sql name_sql desc_sql type_sql content_sql hash_sql origin_sql
|
|
32
|
+
local fp_sql proj_sql name_sql desc_sql type_sql content_sql hash_sql origin_sql agent_sql
|
|
32
33
|
fp_sql=$(eagle_sql_escape "$file_path")
|
|
33
34
|
proj_sql=$(eagle_sql_escape "$project")
|
|
34
35
|
name_sql=$(eagle_sql_escape "$mname")
|
|
@@ -37,10 +38,11 @@ eagle_capture_claude_memory() {
|
|
|
37
38
|
content_sql=$(eagle_sql_escape "$body")
|
|
38
39
|
hash_sql=$(eagle_sql_escape "$chash")
|
|
39
40
|
origin_sql=$(eagle_sql_escape "$morigin")
|
|
41
|
+
agent_sql=$(eagle_sql_escape "$agent")
|
|
40
42
|
|
|
41
43
|
eagle_db_pipe <<SQL
|
|
42
|
-
INSERT INTO claude_memories (project, file_path, memory_name, description, memory_type, content, content_hash, origin_session_id)
|
|
43
|
-
VALUES ('$proj_sql', '$fp_sql', '$name_sql', '$desc_sql', '$type_sql', '$content_sql', '$hash_sql', '$origin_sql')
|
|
44
|
+
INSERT INTO claude_memories (project, file_path, memory_name, description, memory_type, content, content_hash, origin_session_id, origin_agent)
|
|
45
|
+
VALUES ('$proj_sql', '$fp_sql', '$name_sql', '$desc_sql', '$type_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql')
|
|
44
46
|
ON CONFLICT(file_path) DO UPDATE SET
|
|
45
47
|
memory_name = excluded.memory_name,
|
|
46
48
|
description = excluded.description,
|
|
@@ -48,6 +50,7 @@ ON CONFLICT(file_path) DO UPDATE SET
|
|
|
48
50
|
content = excluded.content,
|
|
49
51
|
content_hash = excluded.content_hash,
|
|
50
52
|
origin_session_id = excluded.origin_session_id,
|
|
53
|
+
origin_agent = excluded.origin_agent,
|
|
51
54
|
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
52
55
|
WHERE claude_memories.content_hash != excluded.content_hash;
|
|
53
56
|
SQL
|
|
@@ -71,7 +74,7 @@ eagle_search_claude_memories() {
|
|
|
71
74
|
|
|
72
75
|
eagle_db "SELECT m.memory_name, m.memory_type, m.description,
|
|
73
76
|
replace(substr(m.content, 1, 200), char(10), ' '),
|
|
74
|
-
m.file_path, m.updated_at
|
|
77
|
+
m.file_path, m.updated_at, m.origin_agent
|
|
75
78
|
FROM claude_memories m
|
|
76
79
|
JOIN claude_memories_fts f ON f.rowid = m.id
|
|
77
80
|
WHERE claude_memories_fts MATCH '$query'
|
|
@@ -90,7 +93,7 @@ eagle_list_claude_memories() {
|
|
|
90
93
|
where_clause="WHERE project = '$project'"
|
|
91
94
|
fi
|
|
92
95
|
|
|
93
|
-
eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at
|
|
96
|
+
eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_agent
|
|
94
97
|
FROM claude_memories
|
|
95
98
|
$where_clause
|
|
96
99
|
ORDER BY updated_at DESC
|
|
@@ -101,6 +104,7 @@ eagle_capture_claude_plan() {
|
|
|
101
104
|
local file_path="$1"
|
|
102
105
|
local session_id="${2:-}"
|
|
103
106
|
local project="${3:-}"
|
|
107
|
+
local agent="${4:-$(eagle_agent_source)}"
|
|
104
108
|
|
|
105
109
|
[ ! -f "$file_path" ] && return 0
|
|
106
110
|
|
|
@@ -111,22 +115,24 @@ eagle_capture_claude_plan() {
|
|
|
111
115
|
title=$(awk '/^# /{print; exit}' "$file_path" | sed 's/^# //')
|
|
112
116
|
content=$(cat "$file_path")
|
|
113
117
|
|
|
114
|
-
local fp_sql proj_sql title_sql content_sql hash_sql origin_sql
|
|
118
|
+
local fp_sql proj_sql title_sql content_sql hash_sql origin_sql agent_sql
|
|
115
119
|
fp_sql=$(eagle_sql_escape "$file_path")
|
|
116
120
|
proj_sql=$(eagle_sql_escape "$project")
|
|
117
121
|
title_sql=$(eagle_sql_escape "$title")
|
|
118
122
|
content_sql=$(eagle_sql_escape "$content")
|
|
119
123
|
hash_sql=$(eagle_sql_escape "$chash")
|
|
120
124
|
origin_sql=$(eagle_sql_escape "$session_id")
|
|
125
|
+
agent_sql=$(eagle_sql_escape "$agent")
|
|
121
126
|
|
|
122
127
|
eagle_db_pipe <<SQL
|
|
123
|
-
INSERT INTO claude_plans (project, file_path, title, content, content_hash, origin_session_id)
|
|
124
|
-
VALUES ('$proj_sql', '$fp_sql', '$title_sql', '$content_sql', '$hash_sql', '$origin_sql')
|
|
128
|
+
INSERT INTO claude_plans (project, file_path, title, content, content_hash, origin_session_id, origin_agent)
|
|
129
|
+
VALUES ('$proj_sql', '$fp_sql', '$title_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql')
|
|
125
130
|
ON CONFLICT(file_path) DO UPDATE SET
|
|
126
131
|
title = excluded.title,
|
|
127
132
|
content = excluded.content,
|
|
128
133
|
content_hash = excluded.content_hash,
|
|
129
134
|
origin_session_id = COALESCE(NULLIF(excluded.origin_session_id, ''), claude_plans.origin_session_id),
|
|
135
|
+
origin_agent = COALESCE(NULLIF(excluded.origin_agent, ''), claude_plans.origin_agent),
|
|
130
136
|
project = CASE WHEN excluded.project != '' THEN excluded.project ELSE claude_plans.project END,
|
|
131
137
|
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
132
138
|
WHERE claude_plans.content_hash != excluded.content_hash;
|
|
@@ -151,7 +157,7 @@ eagle_search_claude_plans() {
|
|
|
151
157
|
|
|
152
158
|
eagle_db "SELECT p.title, p.project,
|
|
153
159
|
replace(substr(p.content, 1, 200), char(10), ' '),
|
|
154
|
-
p.file_path, p.updated_at
|
|
160
|
+
p.file_path, p.updated_at, p.origin_agent
|
|
155
161
|
FROM claude_plans p
|
|
156
162
|
JOIN claude_plans_fts f ON f.rowid = p.id
|
|
157
163
|
WHERE claude_plans_fts MATCH '$query'
|
|
@@ -170,7 +176,7 @@ eagle_list_claude_plans() {
|
|
|
170
176
|
where_clause="WHERE project = '$project'"
|
|
171
177
|
fi
|
|
172
178
|
|
|
173
|
-
eagle_db "SELECT title, project, file_path, updated_at
|
|
179
|
+
eagle_db "SELECT title, project, file_path, updated_at, origin_agent
|
|
174
180
|
FROM claude_plans
|
|
175
181
|
$where_clause
|
|
176
182
|
ORDER BY updated_at DESC
|
|
@@ -181,6 +187,7 @@ eagle_capture_claude_task() {
|
|
|
181
187
|
local file_path="$1"
|
|
182
188
|
local session_id="${2:-}"
|
|
183
189
|
local project="${3:-}"
|
|
190
|
+
local agent="${4:-$(eagle_agent_source)}"
|
|
184
191
|
|
|
185
192
|
[ ! -f "$file_path" ] && return 0
|
|
186
193
|
|
|
@@ -201,7 +208,7 @@ eagle_capture_claude_task() {
|
|
|
201
208
|
|
|
202
209
|
[ -z "$task_id" ] && return 0
|
|
203
210
|
|
|
204
|
-
local fp_sql proj_sql sid_sql tid_sql subj_sql desc_sql af_sql status_sql blocks_sql bb_sql hash_sql
|
|
211
|
+
local fp_sql proj_sql sid_sql tid_sql subj_sql desc_sql af_sql status_sql blocks_sql bb_sql hash_sql agent_sql
|
|
205
212
|
fp_sql=$(eagle_sql_escape "$file_path")
|
|
206
213
|
proj_sql=$(eagle_sql_escape "$project")
|
|
207
214
|
sid_sql=$(eagle_sql_escape "$session_id")
|
|
@@ -213,10 +220,11 @@ eagle_capture_claude_task() {
|
|
|
213
220
|
blocks_sql=$(eagle_sql_escape "$blocks")
|
|
214
221
|
bb_sql=$(eagle_sql_escape "$blocked_by")
|
|
215
222
|
hash_sql=$(eagle_sql_escape "$chash")
|
|
223
|
+
agent_sql=$(eagle_sql_escape "$agent")
|
|
216
224
|
|
|
217
225
|
eagle_db_pipe <<SQL
|
|
218
|
-
INSERT INTO claude_tasks (project, source_session_id, source_task_id, file_path, subject, description, active_form, status, blocks, blocked_by, content_hash)
|
|
219
|
-
VALUES ('$proj_sql', '$sid_sql', '$tid_sql', '$fp_sql', '$subj_sql', '$desc_sql', '$af_sql', '$status_sql', '$blocks_sql', '$bb_sql', '$hash_sql')
|
|
226
|
+
INSERT INTO claude_tasks (project, source_session_id, source_task_id, file_path, subject, description, active_form, status, blocks, blocked_by, content_hash, origin_agent)
|
|
227
|
+
VALUES ('$proj_sql', '$sid_sql', '$tid_sql', '$fp_sql', '$subj_sql', '$desc_sql', '$af_sql', '$status_sql', '$blocks_sql', '$bb_sql', '$hash_sql', '$agent_sql')
|
|
220
228
|
ON CONFLICT(file_path) DO UPDATE SET
|
|
221
229
|
subject = excluded.subject,
|
|
222
230
|
description = excluded.description,
|
|
@@ -225,6 +233,7 @@ ON CONFLICT(file_path) DO UPDATE SET
|
|
|
225
233
|
blocks = excluded.blocks,
|
|
226
234
|
blocked_by = excluded.blocked_by,
|
|
227
235
|
content_hash = excluded.content_hash,
|
|
236
|
+
origin_agent = excluded.origin_agent,
|
|
228
237
|
project = CASE WHEN excluded.project != '' THEN excluded.project ELSE claude_tasks.project END,
|
|
229
238
|
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
230
239
|
WHERE claude_tasks.content_hash != excluded.content_hash;
|
|
@@ -241,7 +250,7 @@ eagle_list_claude_tasks() {
|
|
|
241
250
|
where_clause="WHERE project = '$project'"
|
|
242
251
|
fi
|
|
243
252
|
|
|
244
|
-
eagle_db "SELECT subject, status, source_session_id, source_task_id, updated_at
|
|
253
|
+
eagle_db "SELECT subject, status, source_session_id, source_task_id, updated_at, origin_agent
|
|
245
254
|
FROM claude_tasks
|
|
246
255
|
$where_clause
|
|
247
256
|
ORDER BY updated_at DESC
|
|
@@ -266,7 +275,7 @@ eagle_search_claude_tasks() {
|
|
|
266
275
|
|
|
267
276
|
eagle_db "SELECT t.subject, t.status,
|
|
268
277
|
replace(substr(t.description, 1, 200), char(10), ' '),
|
|
269
|
-
t.source_session_id, t.source_task_id, t.updated_at
|
|
278
|
+
t.source_session_id, t.source_task_id, t.updated_at, t.origin_agent
|
|
270
279
|
FROM claude_tasks t
|
|
271
280
|
JOIN claude_tasks_fts f ON f.rowid = t.id
|
|
272
281
|
WHERE claude_tasks_fts MATCH '$query'
|
package/lib/db-observations.sh
CHANGED
|
@@ -15,6 +15,7 @@ eagle_insert_observation() {
|
|
|
15
15
|
local output_bytes="${7:-}"
|
|
16
16
|
local output_lines="${8:-}"
|
|
17
17
|
local command_category; command_category=$(eagle_sql_escape "${9:-}")
|
|
18
|
+
local agent; agent=$(eagle_sql_escape "${10:-$(eagle_agent_source)}")
|
|
18
19
|
|
|
19
20
|
local extra_cols=""
|
|
20
21
|
local extra_vals=""
|
|
@@ -23,8 +24,8 @@ eagle_insert_observation() {
|
|
|
23
24
|
extra_vals=", $(eagle_sql_int "$output_bytes"), $(eagle_sql_int "$output_lines"), '$command_category'"
|
|
24
25
|
fi
|
|
25
26
|
|
|
26
|
-
eagle_db "INSERT INTO observations (session_id, project, tool_name, tool_input_summary, files_read, files_modified${extra_cols})
|
|
27
|
-
SELECT '$session_id', '$project', '$tool_name', '$tool_input_summary', '$files_read', '$files_modified'${extra_vals}
|
|
27
|
+
eagle_db "INSERT INTO observations (session_id, project, agent, tool_name, tool_input_summary, files_read, files_modified${extra_cols})
|
|
28
|
+
SELECT '$session_id', '$project', '$agent', '$tool_name', '$tool_input_summary', '$files_read', '$files_modified'${extra_vals}
|
|
28
29
|
WHERE NOT EXISTS (
|
|
29
30
|
SELECT 1 FROM observations
|
|
30
31
|
WHERE session_id = '$session_id'
|
package/lib/db-sessions.sh
CHANGED
|
@@ -11,13 +11,15 @@ eagle_upsert_session() {
|
|
|
11
11
|
local cwd; cwd=$(eagle_sql_escape "${3:-}")
|
|
12
12
|
local model; model=$(eagle_sql_escape "${4:-}")
|
|
13
13
|
local source; source=$(eagle_sql_escape "${5:-}")
|
|
14
|
+
local agent; agent=$(eagle_sql_escape "${6:-$(eagle_agent_source)}")
|
|
14
15
|
|
|
15
|
-
eagle_db "INSERT INTO sessions (id, project, cwd, model, source, last_activity_at)
|
|
16
|
-
VALUES ('$session_id', '$project', '$cwd', '$model', '$source', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
16
|
+
eagle_db "INSERT INTO sessions (id, project, cwd, model, source, agent, last_activity_at)
|
|
17
|
+
VALUES ('$session_id', '$project', '$cwd', '$model', '$source', '$agent', strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
17
18
|
ON CONFLICT(id) DO UPDATE SET
|
|
18
19
|
cwd = COALESCE(NULLIF(excluded.cwd, ''), sessions.cwd),
|
|
19
20
|
model = COALESCE(NULLIF(excluded.model, ''), sessions.model),
|
|
20
21
|
source = COALESCE(NULLIF(excluded.source, ''), sessions.source),
|
|
22
|
+
agent = COALESCE(NULLIF(excluded.agent, ''), sessions.agent),
|
|
21
23
|
status = 'active',
|
|
22
24
|
last_activity_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');"
|
|
23
25
|
}
|
|
@@ -43,6 +45,8 @@ eagle_get_project_stats() {
|
|
|
43
45
|
local project; project=$(eagle_sql_escape "$1")
|
|
44
46
|
eagle_db_pipe <<SQL
|
|
45
47
|
SELECT 'sessions|' || COUNT(*) FROM sessions WHERE project = '$project';
|
|
48
|
+
SELECT 'sessions_claude|' || COUNT(*) FROM sessions WHERE project = '$project' AND agent = 'claude-code';
|
|
49
|
+
SELECT 'sessions_codex|' || COUNT(*) FROM sessions WHERE project = '$project' AND agent = 'codex';
|
|
46
50
|
SELECT 'summaries|' || COUNT(*) FROM summaries WHERE project = '$project';
|
|
47
51
|
SELECT 'with_summaries|' || COUNT(*) FROM summaries WHERE project = '$project' AND request IS NOT NULL AND request != '';
|
|
48
52
|
SELECT 'memories|' || COUNT(*) FROM claude_memories WHERE project = '$project';
|
package/lib/db-summaries.sh
CHANGED
|
@@ -19,12 +19,14 @@ eagle_insert_summary() {
|
|
|
19
19
|
local decisions; decisions=$(eagle_sql_escape "${11:-}")
|
|
20
20
|
local gotchas; gotchas=$(eagle_sql_escape "${12:-}")
|
|
21
21
|
local key_files; key_files=$(eagle_sql_escape "${13:-}")
|
|
22
|
+
local agent; agent=$(eagle_sql_escape "${14:-$(eagle_agent_source)}")
|
|
22
23
|
|
|
23
24
|
eagle_db_pipe <<SQL
|
|
24
|
-
INSERT INTO summaries (session_id, project, request, investigated, learned, completed, next_steps, files_read, files_modified, notes, decisions, gotchas, key_files)
|
|
25
|
+
INSERT INTO summaries (session_id, project, agent, request, investigated, learned, completed, next_steps, files_read, files_modified, notes, decisions, gotchas, key_files)
|
|
25
26
|
VALUES (
|
|
26
27
|
'$session_id',
|
|
27
28
|
'$project',
|
|
29
|
+
'$agent',
|
|
28
30
|
'$request',
|
|
29
31
|
'$investigated',
|
|
30
32
|
'$learned',
|
|
@@ -39,6 +41,7 @@ VALUES (
|
|
|
39
41
|
)
|
|
40
42
|
ON CONFLICT(session_id) DO UPDATE SET
|
|
41
43
|
project = excluded.project,
|
|
44
|
+
agent = COALESCE(NULLIF(excluded.agent, ''), summaries.agent),
|
|
42
45
|
request = COALESCE(NULLIF(excluded.request, ''), summaries.request),
|
|
43
46
|
investigated = COALESCE(NULLIF(excluded.investigated, ''), summaries.investigated),
|
|
44
47
|
learned = COALESCE(NULLIF(excluded.learned, ''), summaries.learned),
|
|
@@ -57,7 +60,7 @@ eagle_get_recent_summaries() {
|
|
|
57
60
|
local project; project=$(eagle_sql_escape "$1")
|
|
58
61
|
local limit; limit=$(eagle_sql_int "${2:-5}")
|
|
59
62
|
|
|
60
|
-
eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at, s.decisions, s.gotchas, s.key_files
|
|
63
|
+
eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at, s.decisions, s.gotchas, s.key_files, s.agent
|
|
61
64
|
FROM summaries s
|
|
62
65
|
WHERE s.project = '$project'
|
|
63
66
|
AND s.request NOT LIKE '%<local-command-caveat>%'
|
|
@@ -77,7 +80,7 @@ eagle_search_summaries() {
|
|
|
77
80
|
where_clause="AND s.project = '$project'"
|
|
78
81
|
fi
|
|
79
82
|
|
|
80
|
-
eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at, s.project, s.decisions, s.gotchas, s.key_files
|
|
83
|
+
eagle_db "SELECT s.request, s.completed, s.learned, s.next_steps, s.created_at, s.project, s.decisions, s.gotchas, s.key_files, s.agent
|
|
81
84
|
FROM summaries s
|
|
82
85
|
JOIN summaries_fts f ON f.rowid = s.id
|
|
83
86
|
WHERE summaries_fts MATCH '$query'
|
package/lib/hooks-posttool.sh
CHANGED
|
@@ -8,9 +8,10 @@ _EAGLE_HOOKS_POSTTOOL_LOADED=1
|
|
|
8
8
|
|
|
9
9
|
eagle_posttool_mirror_writes() {
|
|
10
10
|
local tool_name="$1" fp="$2" session_id="$3" project="$4"
|
|
11
|
+
local agent="${5:-$(eagle_agent_source)}"
|
|
11
12
|
|
|
12
13
|
case "$tool_name" in
|
|
13
|
-
Write|Edit)
|
|
14
|
+
Write|Edit|apply_patch)
|
|
14
15
|
if [ -n "$fp" ]; then
|
|
15
16
|
case "$fp" in
|
|
16
17
|
*..*) ;; # path traversal — skip
|
|
@@ -18,12 +19,12 @@ eagle_posttool_mirror_writes() {
|
|
|
18
19
|
local mem_base
|
|
19
20
|
mem_base=$(basename "$fp")
|
|
20
21
|
if [ "$mem_base" != "MEMORY.md" ] && [ -f "$fp" ]; then
|
|
21
|
-
eagle_capture_claude_memory "$fp" "$session_id" "$project"
|
|
22
|
+
eagle_capture_claude_memory "$fp" "$session_id" "$project" "$agent"
|
|
22
23
|
fi
|
|
23
24
|
;;
|
|
24
25
|
"$EAGLE_CLAUDE_PLANS_DIR/"*.md)
|
|
25
26
|
if [ -f "$fp" ]; then
|
|
26
|
-
eagle_capture_claude_plan "$fp" "$session_id" "$project"
|
|
27
|
+
eagle_capture_claude_plan "$fp" "$session_id" "$project" "$agent"
|
|
27
28
|
fi
|
|
28
29
|
;;
|
|
29
30
|
esac
|
|
@@ -34,6 +35,7 @@ eagle_posttool_mirror_writes() {
|
|
|
34
35
|
|
|
35
36
|
eagle_posttool_mirror_tasks() {
|
|
36
37
|
local tool_name="$1" session_id="$2" project="$3" input="$4"
|
|
38
|
+
local agent="${5:-$(eagle_agent_source)}"
|
|
37
39
|
|
|
38
40
|
case "$tool_name" in
|
|
39
41
|
TaskCreate|TaskUpdate)
|
|
@@ -45,10 +47,10 @@ eagle_posttool_mirror_tasks() {
|
|
|
45
47
|
if [ -z "$task_id" ]; then
|
|
46
48
|
local newest
|
|
47
49
|
newest=$(ls -t "$task_dir"/*.json 2>/dev/null | head -1)
|
|
48
|
-
[ -n "$newest" ] && [ -f "$newest" ] && eagle_capture_claude_task "$newest" "$session_id" "$project"
|
|
50
|
+
[ -n "$newest" ] && [ -f "$newest" ] && eagle_capture_claude_task "$newest" "$session_id" "$project" "$agent"
|
|
49
51
|
elif eagle_validate_session_id "$task_id"; then
|
|
50
52
|
local task_json="$task_dir/$task_id.json"
|
|
51
|
-
[ -f "$task_json" ] && eagle_capture_claude_task "$task_json" "$session_id" "$project"
|
|
53
|
+
[ -f "$task_json" ] && eagle_capture_claude_task "$task_json" "$session_id" "$project" "$agent"
|
|
52
54
|
fi
|
|
53
55
|
fi
|
|
54
56
|
fi
|
|
@@ -60,7 +62,7 @@ eagle_posttool_stale_hint() {
|
|
|
60
62
|
local tool_name="$1" fp="$2" project="$3"
|
|
61
63
|
|
|
62
64
|
case "$tool_name" in
|
|
63
|
-
Write|Edit)
|
|
65
|
+
Write|Edit|apply_patch)
|
|
64
66
|
if [ -n "$fp" ]; then
|
|
65
67
|
local fname fname_stem
|
|
66
68
|
fname=$(basename "$fp")
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eagle-mem",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Context that survives /compact for Claude Code — SQLite + FTS5, no daemon, no bloat",
|
|
3
|
+
"version": "4.7.0",
|
|
4
|
+
"description": "Context that survives /compact for Claude Code and Codex — SQLite + FTS5, no daemon, no bloat",
|
|
5
5
|
"bin": {
|
|
6
6
|
"eagle-mem": "bin/eagle-mem"
|
|
7
7
|
},
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
],
|
|
16
16
|
"keywords": [
|
|
17
17
|
"claude-code",
|
|
18
|
+
"codex",
|
|
18
19
|
"memory",
|
|
19
20
|
"sqlite",
|
|
20
21
|
"fts5",
|
|
@@ -27,5 +28,8 @@
|
|
|
27
28
|
"type": "git",
|
|
28
29
|
"url": "git+https://github.com/eagleisbatman/eagle-mem.git"
|
|
29
30
|
},
|
|
30
|
-
"homepage": "https://github.
|
|
31
|
+
"homepage": "https://eagleisbatman.github.io/eagle-mem/",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/eagleisbatman/eagle-mem/issues"
|
|
34
|
+
}
|
|
31
35
|
}
|
package/scripts/curate.sh
CHANGED
|
@@ -398,46 +398,56 @@ if [ -n "$co_edit_data" ]; then
|
|
|
398
398
|
eagle_info " Co-edit pairs found:"
|
|
399
399
|
fi
|
|
400
400
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
401
|
+
# Use awk for associative map (Bash 3.2 compatible)
|
|
402
|
+
co_map_output=$(printf '%s\n' "$co_edit_data" | awk -F'|' '
|
|
403
|
+
$1 && $2 {
|
|
404
|
+
m[$1] = (m[$1] ? m[$1] "," : "") $2
|
|
405
|
+
m[$2] = (m[$2] ? m[$2] "," : "") $1
|
|
406
|
+
n++
|
|
407
|
+
}
|
|
408
|
+
END {
|
|
409
|
+
for (f in m) print f "|" m[f]
|
|
410
|
+
print "__co_edit_count__|" n "|" length(m) > "/dev/stderr"
|
|
411
|
+
}
|
|
412
|
+
' 2>&1 1>/dev/null)
|
|
413
|
+
# stderr has the count line, stdout has the map lines — reverse:
|
|
414
|
+
co_map_output=$(printf '%s\n' "$co_edit_data" | awk -F'|' '
|
|
415
|
+
$1 && $2 {
|
|
416
|
+
m[$1] = (m[$1] ? m[$1] "," : "") $2
|
|
417
|
+
m[$2] = (m[$2] ? m[$2] "," : "") $1
|
|
418
|
+
n++
|
|
419
|
+
}
|
|
420
|
+
END {
|
|
421
|
+
for (f in m) print f "|" m[f]
|
|
422
|
+
printf "%s\n", "__META__|" n "|" length(m) > "/dev/stderr"
|
|
423
|
+
}
|
|
424
|
+
' 2>/tmp/eagle_co_edit_meta)
|
|
425
|
+
co_edit_count=$(awk -F'|' '{print $2}' /tmp/eagle_co_edit_meta)
|
|
426
|
+
co_map_file_count=$(awk -F'|' '{print $3}' /tmp/eagle_co_edit_meta)
|
|
427
|
+
rm -f /tmp/eagle_co_edit_meta
|
|
420
428
|
|
|
421
429
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
422
|
-
|
|
423
|
-
|
|
430
|
+
printf '%s\n' "$co_map_output" | while IFS='|' read -r f partners; do
|
|
431
|
+
[ -z "$f" ] && continue
|
|
432
|
+
eagle_info " $(basename "$f") → $partners"
|
|
424
433
|
done
|
|
425
434
|
else
|
|
426
435
|
{
|
|
427
436
|
echo "BEGIN;"
|
|
428
437
|
echo "DELETE FROM file_hints WHERE project = '$(eagle_sql_escape "$project")' AND hint_type = 'co_edit';"
|
|
429
|
-
|
|
438
|
+
printf '%s\n' "$co_map_output" | while IFS='|' read -r f partners; do
|
|
439
|
+
[ -z "$f" ] && continue
|
|
430
440
|
local_f=$(eagle_sql_escape "$f")
|
|
431
|
-
local_v=$(eagle_sql_escape "$
|
|
441
|
+
local_v=$(eagle_sql_escape "$partners")
|
|
432
442
|
echo "INSERT INTO file_hints (project, hint_type, file_path, hint_value) VALUES ('$(eagle_sql_escape "$project")', 'co_edit', '$local_f', '$local_v') ON CONFLICT(project, hint_type, file_path) DO UPDATE SET hint_value = excluded.hint_value, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now');"
|
|
433
443
|
done
|
|
434
444
|
echo "COMMIT;"
|
|
435
445
|
} | eagle_db_pipe
|
|
436
446
|
_proj_hash=$(printf '%s' "$project" | shasum | cut -c1-8)
|
|
437
447
|
touch "$EAGLE_MEM_DIR/.co-edit-active.${_proj_hash}"
|
|
438
|
-
eagle_log "INFO" "Curator: stored $
|
|
448
|
+
eagle_log "INFO" "Curator: stored $co_map_file_count co-edit hints from $co_edit_count pairs"
|
|
439
449
|
fi
|
|
440
|
-
eagle_ok "$co_edit_count co-edit pairs found ($
|
|
450
|
+
eagle_ok "$co_edit_count co-edit pairs found ($co_map_file_count files)"
|
|
441
451
|
else
|
|
442
452
|
if [ "$DRY_RUN" -eq 0 ]; then
|
|
443
453
|
eagle_delete_file_hints "$project" "co_edit"
|
package/scripts/feature.sh
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ═══════════════════════════════════════════════════════════
|
|
3
3
|
# Eagle Mem — Feature management
|
|
4
|
-
# eagle-mem feature [list|show|verify|add]
|
|
4
|
+
# eagle-mem feature [list|show|verify|pending|waive|add]
|
|
5
5
|
# ═══════════════════════════════════════════════════════════
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
@@ -59,8 +59,76 @@ case "$subcommand" in
|
|
|
59
59
|
*) notes="$1"; shift ;;
|
|
60
60
|
esac
|
|
61
61
|
done
|
|
62
|
+
fid=$(eagle_get_feature_id "$project" "$name")
|
|
63
|
+
if [ -z "$fid" ]; then
|
|
64
|
+
eagle_err "Feature not found: $name"
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
62
67
|
eagle_verify_feature "$project" "$name" "$notes"
|
|
68
|
+
resolved=$(eagle_resolve_pending_feature_verifications "$project" "$name" "verified" "$notes" | tail -1)
|
|
63
69
|
eagle_ok "Feature '$name' marked as verified"
|
|
70
|
+
if [ "${resolved:-0}" -gt 0 ] 2>/dev/null; then
|
|
71
|
+
eagle_info "Resolved pending verification records: $resolved"
|
|
72
|
+
fi
|
|
73
|
+
;;
|
|
74
|
+
|
|
75
|
+
pending)
|
|
76
|
+
results=$(eagle_list_pending_feature_verifications "$project" 50)
|
|
77
|
+
if [ -z "$results" ]; then
|
|
78
|
+
eagle_ok "No pending feature verifications for '$project'"
|
|
79
|
+
exit 0
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
echo -e " ${BOLD}ID Feature File Trigger Diff${RESET}"
|
|
83
|
+
echo -e " ${DIM}──── ────────────────────────────── ──────────────────────────── ───────── ────────────${RESET}"
|
|
84
|
+
while IFS='|' read -r id feat file reason trigger created smoke fingerprint; do
|
|
85
|
+
[ -z "$id" ] && continue
|
|
86
|
+
feat_display="${feat:0:30}"
|
|
87
|
+
file_display="${file:0:28}"
|
|
88
|
+
trigger_display="${trigger:-hook}"
|
|
89
|
+
fingerprint_display="${fingerprint:-unknown}"
|
|
90
|
+
printf " %-4s %-30s %-28s %-9s %-12s\n" "$id" "$feat_display" "$file_display" "$trigger_display" "$fingerprint_display"
|
|
91
|
+
[ -n "$reason" ] && echo -e " ${DIM}${reason}${RESET}"
|
|
92
|
+
[ -n "$smoke" ] && echo -e " ${CYAN}smoke:${RESET} $smoke"
|
|
93
|
+
[ -n "$created" ] && echo -e " ${DIM}created: $created${RESET}"
|
|
94
|
+
done <<< "$results"
|
|
95
|
+
echo ""
|
|
96
|
+
eagle_info "Verify after testing: eagle-mem feature verify <name> --notes \"what passed\""
|
|
97
|
+
eagle_info "Waive intentionally: eagle-mem feature waive <id> --reason \"why safe\""
|
|
98
|
+
;;
|
|
99
|
+
|
|
100
|
+
waive)
|
|
101
|
+
id="${1:-}"
|
|
102
|
+
[ -z "$id" ] && { eagle_err "Usage: eagle-mem feature waive <id> --reason <text>"; exit 1; }
|
|
103
|
+
case "$id" in
|
|
104
|
+
*[!0-9]*)
|
|
105
|
+
eagle_err "Invalid ID: '$id' (must be numeric)"
|
|
106
|
+
exit 1
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
109
|
+
shift
|
|
110
|
+
reason=""
|
|
111
|
+
while [ $# -gt 0 ]; do
|
|
112
|
+
case "$1" in
|
|
113
|
+
--reason|--notes)
|
|
114
|
+
if [ $# -lt 2 ] || [ -z "${2:-}" ]; then
|
|
115
|
+
eagle_err "$1 requires a value"
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
118
|
+
reason="$2"
|
|
119
|
+
shift 2
|
|
120
|
+
;;
|
|
121
|
+
*) reason="$1"; shift ;;
|
|
122
|
+
esac
|
|
123
|
+
done
|
|
124
|
+
[ -z "$reason" ] && { eagle_err "Usage: eagle-mem feature waive <id> --reason <text>"; exit 1; }
|
|
125
|
+
waived=$(eagle_waive_pending_feature_verification "$project" "$id" "$reason" | tail -1)
|
|
126
|
+
if [ "${waived:-0}" -gt 0 ] 2>/dev/null; then
|
|
127
|
+
eagle_ok "Pending verification #$id waived"
|
|
128
|
+
else
|
|
129
|
+
eagle_err "No pending verification found with ID $id"
|
|
130
|
+
exit 1
|
|
131
|
+
fi
|
|
64
132
|
;;
|
|
65
133
|
|
|
66
134
|
add)
|
|
@@ -104,7 +172,7 @@ case "$subcommand" in
|
|
|
104
172
|
|
|
105
173
|
*)
|
|
106
174
|
eagle_err "Unknown feature command: $subcommand"
|
|
107
|
-
eagle_info "Usage: eagle-mem feature [list|show|verify|add]"
|
|
175
|
+
eagle_info "Usage: eagle-mem feature [list|show|verify|pending|waive|add]"
|
|
108
176
|
exit 1
|
|
109
177
|
;;
|
|
110
178
|
esac
|
package/scripts/guard.sh
CHANGED
|
@@ -55,7 +55,10 @@ case "$subcommand" in
|
|
|
55
55
|
esac
|
|
56
56
|
done
|
|
57
57
|
|
|
58
|
-
eagle_add_guardrail "$project" "$rule" "$file_pattern" "manual"
|
|
58
|
+
if ! eagle_add_guardrail "$project" "$rule" "$file_pattern" "manual"; then
|
|
59
|
+
eagle_err "Failed to add guardrail. Check $EAGLE_MEM_LOG for SQLite details."
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
59
62
|
eagle_ok "Guardrail added for project: $project"
|
|
60
63
|
if [ -n "$file_pattern" ]; then
|
|
61
64
|
eagle_info "File pattern: $file_pattern"
|
package/scripts/help.sh
CHANGED
|
@@ -13,7 +13,7 @@ version=$(jq -r .version "$PACKAGE_DIR/package.json" 2>/dev/null || echo "unknow
|
|
|
13
13
|
eagle_banner
|
|
14
14
|
|
|
15
15
|
echo -e " ${BOLD}Eagle Mem${RESET} ${DIM}v${version}${RESET}"
|
|
16
|
-
echo -e " ${DIM}Context that survives /compact for Claude Code${RESET}"
|
|
16
|
+
echo -e " ${DIM}Context that survives /compact for Claude Code and Codex${RESET}"
|
|
17
17
|
echo ""
|
|
18
18
|
echo -e " ${BOLD}Commands:${RESET}"
|
|
19
19
|
echo -e " ${CYAN}install${RESET} First-time setup: hooks, database, skills"
|
|
@@ -27,7 +27,7 @@ echo -e " ${CYAN}overview${RESET} Build or view project overview"
|
|
|
27
27
|
echo -e " ${CYAN}memories${RESET} View/sync Claude Code memories"
|
|
28
28
|
echo -e " ${CYAN}tasks${RESET} View mirrored tasks"
|
|
29
29
|
echo -e " ${CYAN}curate${RESET} Run curator (co-edits, hot files, guardrails)"
|
|
30
|
-
echo -e " ${CYAN}feature${RESET} Track and
|
|
30
|
+
echo -e " ${CYAN}feature${RESET} Track, verify, and unblock features"
|
|
31
31
|
echo -e " ${CYAN}prune${RESET} Clean old sessions and stale data"
|
|
32
32
|
echo ""
|
|
33
33
|
echo -e " ${BOLD}Search modes:${RESET}"
|
|
@@ -39,6 +39,11 @@ echo -e " ${DIM}\$${RESET} eagle-mem search --tasks ${DIM}# in-flight
|
|
|
39
39
|
echo -e " ${DIM}\$${RESET} eagle-mem search --files ${DIM}# hot files${RESET}"
|
|
40
40
|
echo -e " ${DIM}\$${RESET} eagle-mem search --stats ${DIM}# project stats${RESET}"
|
|
41
41
|
echo ""
|
|
42
|
+
echo -e " ${BOLD}Anti-regression:${RESET}"
|
|
43
|
+
echo -e " ${DIM}\$${RESET} eagle-mem feature pending ${DIM}# pending release blockers${RESET}"
|
|
44
|
+
echo -e " ${DIM}\$${RESET} eagle-mem feature verify NAME ${DIM}# verify current diff after testing${RESET}"
|
|
45
|
+
echo -e " ${DIM}\$${RESET} eagle-mem feature waive ID ${DIM}# intentional exception${RESET}"
|
|
46
|
+
echo ""
|
|
42
47
|
echo -e " ${BOLD}Skills${RESET} ${DIM}(inside Claude Code sessions):${RESET}"
|
|
43
48
|
echo -e " ${CYAN}/eagle-mem-search${RESET} Search memory and past sessions"
|
|
44
49
|
echo -e " ${CYAN}/eagle-mem-overview${RESET} Build or update project overview"
|