eagle-mem 4.7.0 → 4.8.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/lib/db-mirrors.sh CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env bash
2
2
  # ═══════════════════════════════════════════════════════════
3
- # Eagle Mem — Claude Code memory/plan/task mirror helpers
3
+ # Eagle Mem — Agent memory/plan/task mirror helpers
4
4
  # ═══════════════════════════════════════════════════════════
5
5
  [ -n "${_EAGLE_DB_MIRRORS_LOADED:-}" ] && return 0
6
6
  _EAGLE_DB_MIRRORS_LOADED=1
7
7
 
8
- eagle_capture_claude_memory() {
8
+ eagle_capture_agent_memory() {
9
9
  local file_path="$1"
10
10
  local session_id="${2:-}"
11
11
  local project="${3:-}"
@@ -19,6 +19,9 @@ eagle_capture_claude_memory() {
19
19
  local fm body
20
20
  fm=$(awk '/^---$/{c++; next} c==1' "$file_path")
21
21
  body=$(awk '/^---$/{c++; next} c>=2' "$file_path")
22
+ if [ -z "$body" ] && [ -z "$fm" ]; then
23
+ body=$(cat "$file_path")
24
+ fi
22
25
 
23
26
  _fm_field() { printf '%s\n' "$fm" | awk -F': *' -v k="$1" '$1==k{sub(/^[^:]+: */,""); gsub(/^"|"$/,""); print; exit}'; }
24
27
 
@@ -29,6 +32,18 @@ eagle_capture_claude_memory() {
29
32
  morigin=$(_fm_field "originSessionId")
30
33
  [ -z "$morigin" ] && morigin="$session_id"
31
34
 
35
+ if [ -z "$mname" ]; then
36
+ case "$(basename "$file_path")" in
37
+ MEMORY.md) mname="Codex Memory Registry" ;;
38
+ memory_summary.md) mname="Codex Memory Summary" ;;
39
+ *) mname=$(basename "$file_path" .md) ;;
40
+ esac
41
+ fi
42
+ [ -z "$mtype" ] && mtype="$agent"
43
+ if [ -z "$mdesc" ]; then
44
+ mdesc=$(printf '%s\n' "$body" | sed '/^[[:space:]]*$/d' | head -1 | cut -c1-200)
45
+ fi
46
+
32
47
  local fp_sql proj_sql name_sql desc_sql type_sql content_sql hash_sql origin_sql agent_sql
33
48
  fp_sql=$(eagle_sql_escape "$file_path")
34
49
  proj_sql=$(eagle_sql_escape "$project")
@@ -41,7 +56,7 @@ eagle_capture_claude_memory() {
41
56
  agent_sql=$(eagle_sql_escape "$agent")
42
57
 
43
58
  eagle_db_pipe <<SQL
44
- INSERT INTO claude_memories (project, file_path, memory_name, description, memory_type, content, content_hash, origin_session_id, origin_agent)
59
+ INSERT INTO agent_memories (project, file_path, memory_name, description, memory_type, content, content_hash, origin_session_id, origin_agent)
45
60
  VALUES ('$proj_sql', '$fp_sql', '$name_sql', '$desc_sql', '$type_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql')
46
61
  ON CONFLICT(file_path) DO UPDATE SET
47
62
  memory_name = excluded.memory_name,
@@ -52,11 +67,11 @@ ON CONFLICT(file_path) DO UPDATE SET
52
67
  origin_session_id = excluded.origin_session_id,
53
68
  origin_agent = excluded.origin_agent,
54
69
  updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
55
- WHERE claude_memories.content_hash != excluded.content_hash;
70
+ WHERE agent_memories.content_hash != excluded.content_hash;
56
71
  SQL
57
72
  }
58
73
 
59
- eagle_search_claude_memories() {
74
+ eagle_search_agent_memories() {
60
75
  local query; query=$(eagle_fts_sanitize "$1")
61
76
  if [ -z "$query" ]; then
62
77
  echo "Search query is empty after sanitization. Try a different search term." >&2
@@ -75,15 +90,15 @@ eagle_search_claude_memories() {
75
90
  eagle_db "SELECT m.memory_name, m.memory_type, m.description,
76
91
  replace(substr(m.content, 1, 200), char(10), ' '),
77
92
  m.file_path, m.updated_at, m.origin_agent
78
- FROM claude_memories m
79
- JOIN claude_memories_fts f ON f.rowid = m.id
80
- WHERE claude_memories_fts MATCH '$query'
93
+ FROM agent_memories m
94
+ JOIN agent_memories_fts f ON f.rowid = m.id
95
+ WHERE agent_memories_fts MATCH '$query'
81
96
  $where_clause
82
97
  ORDER BY rank
83
98
  LIMIT $limit;"
84
99
  }
85
100
 
86
- eagle_list_claude_memories() {
101
+ eagle_list_agent_memories() {
87
102
  local project="${1:-}"
88
103
  local limit; limit=$(eagle_sql_int "${2:-20}")
89
104
 
@@ -94,13 +109,13 @@ eagle_list_claude_memories() {
94
109
  fi
95
110
 
96
111
  eagle_db "SELECT memory_name, memory_type, description, file_path, updated_at, origin_agent
97
- FROM claude_memories
112
+ FROM agent_memories
98
113
  $where_clause
99
114
  ORDER BY updated_at DESC
100
115
  LIMIT $limit;"
101
116
  }
102
117
 
103
- eagle_capture_claude_plan() {
118
+ eagle_capture_agent_plan() {
104
119
  local file_path="$1"
105
120
  local session_id="${2:-}"
106
121
  local project="${3:-}"
@@ -125,21 +140,21 @@ eagle_capture_claude_plan() {
125
140
  agent_sql=$(eagle_sql_escape "$agent")
126
141
 
127
142
  eagle_db_pipe <<SQL
128
- INSERT INTO claude_plans (project, file_path, title, content, content_hash, origin_session_id, origin_agent)
143
+ INSERT INTO agent_plans (project, file_path, title, content, content_hash, origin_session_id, origin_agent)
129
144
  VALUES ('$proj_sql', '$fp_sql', '$title_sql', '$content_sql', '$hash_sql', '$origin_sql', '$agent_sql')
130
145
  ON CONFLICT(file_path) DO UPDATE SET
131
146
  title = excluded.title,
132
147
  content = excluded.content,
133
148
  content_hash = excluded.content_hash,
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),
136
- project = CASE WHEN excluded.project != '' THEN excluded.project ELSE claude_plans.project END,
149
+ origin_session_id = COALESCE(NULLIF(excluded.origin_session_id, ''), agent_plans.origin_session_id),
150
+ origin_agent = COALESCE(NULLIF(excluded.origin_agent, ''), agent_plans.origin_agent),
151
+ project = CASE WHEN excluded.project != '' THEN excluded.project ELSE agent_plans.project END,
137
152
  updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
138
- WHERE claude_plans.content_hash != excluded.content_hash;
153
+ WHERE agent_plans.content_hash != excluded.content_hash;
139
154
  SQL
140
155
  }
141
156
 
142
- eagle_search_claude_plans() {
157
+ eagle_search_agent_plans() {
143
158
  local query; query=$(eagle_fts_sanitize "$1")
144
159
  if [ -z "$query" ]; then
145
160
  echo "Search query is empty after sanitization. Try a different search term." >&2
@@ -158,15 +173,15 @@ eagle_search_claude_plans() {
158
173
  eagle_db "SELECT p.title, p.project,
159
174
  replace(substr(p.content, 1, 200), char(10), ' '),
160
175
  p.file_path, p.updated_at, p.origin_agent
161
- FROM claude_plans p
162
- JOIN claude_plans_fts f ON f.rowid = p.id
163
- WHERE claude_plans_fts MATCH '$query'
176
+ FROM agent_plans p
177
+ JOIN agent_plans_fts f ON f.rowid = p.id
178
+ WHERE agent_plans_fts MATCH '$query'
164
179
  $where_clause
165
180
  ORDER BY rank
166
181
  LIMIT $limit;"
167
182
  }
168
183
 
169
- eagle_list_claude_plans() {
184
+ eagle_list_agent_plans() {
170
185
  local project="${1:-}"
171
186
  local limit; limit=$(eagle_sql_int "${2:-20}")
172
187
 
@@ -177,13 +192,13 @@ eagle_list_claude_plans() {
177
192
  fi
178
193
 
179
194
  eagle_db "SELECT title, project, file_path, updated_at, origin_agent
180
- FROM claude_plans
195
+ FROM agent_plans
181
196
  $where_clause
182
197
  ORDER BY updated_at DESC
183
198
  LIMIT $limit;"
184
199
  }
185
200
 
186
- eagle_capture_claude_task() {
201
+ eagle_capture_agent_task() {
187
202
  local file_path="$1"
188
203
  local session_id="${2:-}"
189
204
  local project="${3:-}"
@@ -223,7 +238,7 @@ eagle_capture_claude_task() {
223
238
  agent_sql=$(eagle_sql_escape "$agent")
224
239
 
225
240
  eagle_db_pipe <<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)
241
+ INSERT INTO agent_tasks (project, source_session_id, source_task_id, file_path, subject, description, active_form, status, blocks, blocked_by, content_hash, origin_agent)
227
242
  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')
228
243
  ON CONFLICT(file_path) DO UPDATE SET
229
244
  subject = excluded.subject,
@@ -234,13 +249,13 @@ ON CONFLICT(file_path) DO UPDATE SET
234
249
  blocked_by = excluded.blocked_by,
235
250
  content_hash = excluded.content_hash,
236
251
  origin_agent = excluded.origin_agent,
237
- project = CASE WHEN excluded.project != '' THEN excluded.project ELSE claude_tasks.project END,
252
+ project = CASE WHEN excluded.project != '' THEN excluded.project ELSE agent_tasks.project END,
238
253
  updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
239
- WHERE claude_tasks.content_hash != excluded.content_hash;
254
+ WHERE agent_tasks.content_hash != excluded.content_hash;
240
255
  SQL
241
256
  }
242
257
 
243
- eagle_list_claude_tasks() {
258
+ eagle_list_agent_tasks() {
244
259
  local project="${1:-}"
245
260
  local limit; limit=$(eagle_sql_int "${2:-20}")
246
261
 
@@ -251,13 +266,13 @@ eagle_list_claude_tasks() {
251
266
  fi
252
267
 
253
268
  eagle_db "SELECT subject, status, source_session_id, source_task_id, updated_at, origin_agent
254
- FROM claude_tasks
269
+ FROM agent_tasks
255
270
  $where_clause
256
271
  ORDER BY updated_at DESC
257
272
  LIMIT $limit;"
258
273
  }
259
274
 
260
- eagle_search_claude_tasks() {
275
+ eagle_search_agent_tasks() {
261
276
  local query; query=$(eagle_fts_sanitize "$1")
262
277
  if [ -z "$query" ]; then
263
278
  echo "Search query is empty after sanitization. Try a different search term." >&2
@@ -276,10 +291,22 @@ eagle_search_claude_tasks() {
276
291
  eagle_db "SELECT t.subject, t.status,
277
292
  replace(substr(t.description, 1, 200), char(10), ' '),
278
293
  t.source_session_id, t.source_task_id, t.updated_at, t.origin_agent
279
- FROM claude_tasks t
280
- JOIN claude_tasks_fts f ON f.rowid = t.id
281
- WHERE claude_tasks_fts MATCH '$query'
294
+ FROM agent_tasks t
295
+ JOIN agent_tasks_fts f ON f.rowid = t.id
296
+ WHERE agent_tasks_fts MATCH '$query'
282
297
  $where_clause
283
298
  ORDER BY rank
284
299
  LIMIT $limit;"
285
300
  }
301
+
302
+ # Backward-compatible helper names for older installed hooks/scripts that source
303
+ # this library during an update window. Runtime code should use agent_* helpers.
304
+ eagle_capture_claude_memory() { eagle_capture_agent_memory "$@"; }
305
+ eagle_search_claude_memories() { eagle_search_agent_memories "$@"; }
306
+ eagle_list_claude_memories() { eagle_list_agent_memories "$@"; }
307
+ eagle_capture_claude_plan() { eagle_capture_agent_plan "$@"; }
308
+ eagle_search_claude_plans() { eagle_search_agent_plans "$@"; }
309
+ eagle_list_claude_plans() { eagle_list_agent_plans "$@"; }
310
+ eagle_capture_claude_task() { eagle_capture_agent_task "$@"; }
311
+ eagle_list_claude_tasks() { eagle_list_agent_tasks "$@"; }
312
+ eagle_search_claude_tasks() { eagle_search_agent_tasks "$@"; }
@@ -70,6 +70,13 @@ eagle_search_code_chunks() {
70
70
  JOIN code_chunks_fts f ON f.rowid = c.id
71
71
  WHERE code_chunks_fts MATCH '$query'
72
72
  AND c.project = '$project'
73
+ AND c.file_path NOT LIKE '.playwright-mcp/%'
74
+ AND c.file_path NOT LIKE '.eagle-worktrees/%'
75
+ AND c.file_path NOT LIKE '.git/%'
76
+ AND c.file_path NOT LIKE 'node_modules/%'
77
+ AND c.file_path NOT LIKE 'dist/%'
78
+ AND c.file_path NOT LIKE 'build/%'
79
+ AND c.file_path NOT LIKE 'coverage/%'
73
80
  ORDER BY rank
74
81
  LIMIT $limit;"
75
82
  }
@@ -49,15 +49,21 @@ SELECT 'sessions_claude|' || COUNT(*) FROM sessions WHERE project = '$project' A
49
49
  SELECT 'sessions_codex|' || COUNT(*) FROM sessions WHERE project = '$project' AND agent = 'codex';
50
50
  SELECT 'summaries|' || COUNT(*) FROM summaries WHERE project = '$project';
51
51
  SELECT 'with_summaries|' || COUNT(*) FROM summaries WHERE project = '$project' AND request IS NOT NULL AND request != '';
52
- SELECT 'memories|' || COUNT(*) FROM claude_memories WHERE project = '$project';
53
- SELECT 'plans|' || COUNT(*) FROM claude_plans WHERE project = '$project';
54
- SELECT 'tasks_pending|' || COUNT(*) FROM claude_tasks WHERE project = '$project' AND status = 'pending';
55
- SELECT 'tasks_progress|' || COUNT(*) FROM claude_tasks WHERE project = '$project' AND status = 'in_progress';
56
- SELECT 'tasks_done|' || COUNT(*) FROM claude_tasks WHERE project = '$project' AND status = 'completed';
52
+ SELECT 'memories|' || COUNT(*) FROM agent_memories WHERE project = '$project';
53
+ SELECT 'plans|' || COUNT(*) FROM agent_plans WHERE project = '$project';
54
+ SELECT 'tasks_pending|' || COUNT(*) FROM agent_tasks WHERE project = '$project' AND status = 'pending';
55
+ SELECT 'tasks_progress|' || COUNT(*) FROM agent_tasks WHERE project = '$project' AND status = 'in_progress';
56
+ SELECT 'tasks_done|' || COUNT(*) FROM agent_tasks WHERE project = '$project' AND status = 'completed';
57
57
  SELECT 'chunks|' || COUNT(*) FROM code_chunks WHERE project = '$project';
58
58
  SELECT 'observations|' || COUNT(*) FROM observations WHERE session_id IN (SELECT id FROM sessions WHERE project = '$project');
59
59
  SELECT 'last_active|' || COALESCE(MAX(date(COALESCE(last_activity_at, started_at))), 'never') FROM sessions WHERE project = '$project';
60
- SELECT 'last_summary|' || COALESCE((SELECT substr(request, 1, 60) FROM summaries WHERE project = '$project' ORDER BY created_at DESC LIMIT 1), '');
60
+ SELECT 'last_summary|' || COALESCE((SELECT substr(request, 1, 60)
61
+ FROM summaries
62
+ WHERE project = '$project'
63
+ AND COALESCE(request, '') NOT LIKE '# AGENTS.md instructions%'
64
+ AND COALESCE(request, '') NOT LIKE '<environment_context>%'
65
+ ORDER BY created_at DESC
66
+ LIMIT 1), '');
61
67
  SQL
62
68
  }
63
69
 
@@ -63,7 +63,9 @@ eagle_get_recent_summaries() {
63
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
64
64
  FROM summaries s
65
65
  WHERE s.project = '$project'
66
- AND s.request NOT LIKE '%<local-command-caveat>%'
66
+ AND COALESCE(s.request, '') NOT LIKE '%<local-command-caveat>%'
67
+ AND COALESCE(s.request, '') NOT LIKE '# AGENTS.md instructions%'
68
+ AND COALESCE(s.request, '') NOT LIKE '<environment_context>%'
67
69
  ORDER BY s.created_at DESC
68
70
  LIMIT $limit;"
69
71
  }
@@ -84,7 +86,9 @@ eagle_search_summaries() {
84
86
  FROM summaries s
85
87
  JOIN summaries_fts f ON f.rowid = s.id
86
88
  WHERE summaries_fts MATCH '$query'
87
- AND s.request NOT LIKE '%<local-command-caveat>%'
89
+ AND COALESCE(s.request, '') NOT LIKE '%<local-command-caveat>%'
90
+ AND COALESCE(s.request, '') NOT LIKE '# AGENTS.md instructions%'
91
+ AND COALESCE(s.request, '') NOT LIKE '<environment_context>%'
88
92
  $where_clause
89
93
  ORDER BY rank
90
94
  LIMIT $limit;"
@@ -163,9 +167,9 @@ eagle_search_stale_memories() {
163
167
  local project; project=$(eagle_sql_escape "$1")
164
168
  local fts_query; fts_query=$(eagle_sql_escape "$2")
165
169
  eagle_db "SELECT m.memory_name
166
- FROM claude_memories m
167
- JOIN claude_memories_fts f ON f.rowid = m.id
168
- WHERE claude_memories_fts MATCH '$fts_query'
170
+ FROM agent_memories m
171
+ JOIN agent_memories_fts f ON f.rowid = m.id
172
+ WHERE agent_memories_fts MATCH '$fts_query'
169
173
  AND m.project = '$project'
170
174
  LIMIT 1;"
171
175
  }
@@ -19,12 +19,12 @@ eagle_posttool_mirror_writes() {
19
19
  local mem_base
20
20
  mem_base=$(basename "$fp")
21
21
  if [ "$mem_base" != "MEMORY.md" ] && [ -f "$fp" ]; then
22
- eagle_capture_claude_memory "$fp" "$session_id" "$project" "$agent"
22
+ eagle_capture_agent_memory "$fp" "$session_id" "$project" "$agent"
23
23
  fi
24
24
  ;;
25
25
  "$EAGLE_CLAUDE_PLANS_DIR/"*.md)
26
26
  if [ -f "$fp" ]; then
27
- eagle_capture_claude_plan "$fp" "$session_id" "$project" "$agent"
27
+ eagle_capture_agent_plan "$fp" "$session_id" "$project" "$agent"
28
28
  fi
29
29
  ;;
30
30
  esac
@@ -47,10 +47,10 @@ eagle_posttool_mirror_tasks() {
47
47
  if [ -z "$task_id" ]; then
48
48
  local newest
49
49
  newest=$(ls -t "$task_dir"/*.json 2>/dev/null | head -1)
50
- [ -n "$newest" ] && [ -f "$newest" ] && eagle_capture_claude_task "$newest" "$session_id" "$project" "$agent"
50
+ [ -n "$newest" ] && [ -f "$newest" ] && eagle_capture_agent_task "$newest" "$session_id" "$project" "$agent"
51
51
  elif eagle_validate_session_id "$task_id"; then
52
52
  local task_json="$task_dir/$task_id.json"
53
- [ -f "$task_json" ] && eagle_capture_claude_task "$task_json" "$session_id" "$project" "$agent"
53
+ [ -f "$task_json" ] && eagle_capture_agent_task "$task_json" "$session_id" "$project" "$agent"
54
54
  fi
55
55
  fi
56
56
  fi
package/lib/provider.sh CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  # ═══════════════════════════════════════════════════════════
3
3
  # Eagle Mem — LLM Provider Abstraction
4
- # Config parsing + unified eagle_llm_call for Ollama/Anthropic/OpenAI
4
+ # Config parsing + unified eagle_llm_call for Ollama/agent CLI/API providers
5
5
  # ═══════════════════════════════════════════════════════════
6
6
 
7
7
  EAGLE_CONFIG_FILE="${EAGLE_MEM_DIR}/config.toml"
@@ -121,12 +121,17 @@ eagle_config_init() {
121
121
  local ollama_url="$EAGLE_DEFAULT_OLLAMA_URL"
122
122
  local provider="none"
123
123
  local model=""
124
+ local ollama_model="mistral"
124
125
 
125
126
  local ollama_response
126
127
  ollama_response=$(eagle_detect_ollama "$ollama_url")
127
128
  if [ -n "$ollama_response" ]; then
128
129
  provider="ollama"
129
130
  model=$(eagle_ollama_best_model "$ollama_url")
131
+ ollama_model="$model"
132
+ elif command -v codex &>/dev/null || command -v claude &>/dev/null; then
133
+ provider="agent_cli"
134
+ model="native"
130
135
  elif [ -n "${ANTHROPIC_API_KEY:-}" ]; then
131
136
  provider="anthropic"
132
137
  model="claude-haiku-4-5-20251001"
@@ -138,18 +143,38 @@ eagle_config_init() {
138
143
  # Create config with restrictive permissions from the start (no TOCTOU window)
139
144
  (
140
145
  umask 077
146
+ mkdir -p "$EAGLE_MEM_DIR"
141
147
  cat > "$EAGLE_CONFIG_FILE" << TOML
142
148
  # Eagle Mem configuration
143
149
  # Docs: https://github.com/eagleisbatman/eagle-mem
144
150
 
145
151
  [provider]
146
152
  # Which LLM provider to use for the curator and analysis features
147
- # Options: "ollama" (free, local), "anthropic", "openai"
153
+ # Options: "ollama" (free, local), "agent_cli" (Codex/Claude CLI auth), "anthropic", "openai"
148
154
  type = "$provider"
149
155
 
150
156
  [ollama]
151
157
  url = "$ollama_url"
152
- model = "${model:-mistral}"
158
+ model = "$ollama_model"
159
+
160
+ [agent_cli]
161
+ # Uses the already-installed Codex/Claude CLI instead of direct API keys.
162
+ # preferred = "current" uses EAGLE_AGENT_SOURCE when hooks invoke Eagle Mem.
163
+ # If run manually with no agent source, it prefers Codex when available.
164
+ preferred = "current"
165
+ codex_model = ""
166
+ claude_model = ""
167
+
168
+ [orchestration]
169
+ # route = "opposite" means Codex coordinates Claude workers and Claude
170
+ # coordinates Codex workers by default.
171
+ route = "opposite"
172
+ auto_worktree = "true"
173
+ worktree_root = ""
174
+ codex_worker_model = "gpt-5.5"
175
+ codex_worker_effort = "xhigh"
176
+ claude_worker_model = "claude-opus-4-7"
177
+ claude_worker_effort = "xhigh"
153
178
 
154
179
  [anthropic]
155
180
  # Uses ANTHROPIC_API_KEY env var for authentication
@@ -164,11 +189,20 @@ model = "gpt-4o-mini"
164
189
  schedule = "auto"
165
190
  min_sessions = 5
166
191
 
192
+ [token_guard]
193
+ # rtk: "off" disables RTK help, "auto" uses RTK when found,
194
+ # "enforce" blocks known raw-output shell commands when RTK is unavailable.
195
+ rtk = "auto"
196
+ # raw_bash: "block" blocks raw Codex shell output when an RTK rewrite exists.
197
+ # "allow" keeps RTK advisory only.
198
+ raw_bash = "block"
199
+
167
200
  [redaction]
168
201
  # Additional secret patterns (regex) beyond built-in defaults
169
202
  # extra_patterns = ["MY_CUSTOM_SECRET_.*"]
170
203
  TOML
171
204
  )
205
+ chmod 700 "$EAGLE_MEM_DIR" 2>/dev/null || true
172
206
  eagle_log "INFO" "Config initialized: provider=$provider model=$model"
173
207
  }
174
208
 
@@ -184,6 +218,7 @@ eagle_llm_call() {
184
218
 
185
219
  case "$provider" in
186
220
  ollama) _eagle_call_ollama "$prompt" "$system_prompt" "$max_tokens" ;;
221
+ agent_cli) _eagle_call_agent_cli "$prompt" "$system_prompt" "$max_tokens" ;;
187
222
  anthropic) _eagle_call_anthropic "$prompt" "$system_prompt" "$max_tokens" ;;
188
223
  openai) _eagle_call_openai "$prompt" "$system_prompt" "$max_tokens" ;;
189
224
  none)
@@ -235,6 +270,170 @@ _eagle_call_ollama() {
235
270
  echo "$response" | jq -r '.message.content // empty'
236
271
  }
237
272
 
273
+ _eagle_agent_cli_target() {
274
+ local preferred
275
+ preferred=$(eagle_config_get "agent_cli" "preferred" "current")
276
+
277
+ case "$preferred" in
278
+ codex|openai-codex) echo "codex"; return 0 ;;
279
+ claude|claude-code|cloud-code) echo "claude-code"; return 0 ;;
280
+ auto)
281
+ if [ -n "${EAGLE_AGENT_SOURCE:-${EAGLE_AGENT:-}}" ]; then
282
+ eagle_agent_source
283
+ elif command -v codex &>/dev/null; then
284
+ echo "codex"
285
+ elif command -v claude &>/dev/null; then
286
+ echo "claude-code"
287
+ else
288
+ echo "none"
289
+ fi
290
+ ;;
291
+ current|*)
292
+ if [ -n "${EAGLE_AGENT_SOURCE:-${EAGLE_AGENT:-}}" ]; then
293
+ eagle_agent_source
294
+ elif command -v codex &>/dev/null; then
295
+ echo "codex"
296
+ elif command -v claude &>/dev/null; then
297
+ echo "claude-code"
298
+ else
299
+ echo "none"
300
+ fi
301
+ ;;
302
+ esac
303
+ }
304
+
305
+ _eagle_agent_cli_prompt_file() {
306
+ local prompt="$1" system="$2" max_tokens="$3" file="$4"
307
+ {
308
+ printf 'System instruction:\n%s\n\n' "$system"
309
+ printf 'Task:\n%s\n\n' "$prompt"
310
+ printf 'Output contract:\n'
311
+ printf -- '- Return only the requested curator text.\n'
312
+ printf -- '- Do not use markdown fences unless the task explicitly asks for them.\n'
313
+ printf -- '- Do not edit files, run project commands, or inspect the repository; all needed data is in this prompt.\n'
314
+ printf -- '- Keep the response within roughly %s tokens.\n' "$max_tokens"
315
+ } > "$file"
316
+ }
317
+
318
+ _eagle_call_agent_cli() {
319
+ local prompt="$1" system="$2" max_tokens="$3"
320
+ local target
321
+ target=$(_eagle_agent_cli_target)
322
+
323
+ case "$target" in
324
+ codex) _eagle_call_codex_cli "$prompt" "$system" "$max_tokens" ;;
325
+ claude-code) _eagle_call_claude_cli "$prompt" "$system" "$max_tokens" ;;
326
+ *)
327
+ eagle_log "ERROR" "agent_cli provider unavailable: no Codex or Claude CLI found"
328
+ return 1
329
+ ;;
330
+ esac
331
+ }
332
+
333
+ _eagle_call_codex_cli() {
334
+ local prompt="$1" system="$2" max_tokens="$3"
335
+ command -v codex &>/dev/null || {
336
+ eagle_log "ERROR" "agent_cli provider selected Codex, but codex command was not found"
337
+ return 1
338
+ }
339
+
340
+ mkdir -p "$EAGLE_MEM_DIR/tmp"
341
+ local prompt_file out_file
342
+ prompt_file=$(mktemp "$EAGLE_MEM_DIR/tmp/codex-provider-prompt.XXXXXX")
343
+ out_file=$(mktemp "$EAGLE_MEM_DIR/tmp/codex-provider-output.XXXXXX")
344
+ _eagle_agent_cli_prompt_file "$prompt" "$system" "$max_tokens" "$prompt_file"
345
+
346
+ local model
347
+ model=$(eagle_config_get "agent_cli" "codex_model" "")
348
+
349
+ local rc _had_errexit=0
350
+ case "$-" in *e*) _had_errexit=1; set +e ;; esac
351
+ if [ -n "$model" ]; then
352
+ EAGLE_MEM_DISABLE_HOOKS=1 codex exec \
353
+ --ephemeral \
354
+ --skip-git-repo-check \
355
+ --ignore-rules \
356
+ -c features.codex_hooks=false \
357
+ --sandbox read-only \
358
+ --cd "${EAGLE_AGENT_CWD:-$(pwd)}" \
359
+ --model "$model" \
360
+ --output-last-message "$out_file" \
361
+ - < "$prompt_file" >> "$EAGLE_MEM_LOG" 2>&1
362
+ rc=$?
363
+ else
364
+ EAGLE_MEM_DISABLE_HOOKS=1 codex exec \
365
+ --ephemeral \
366
+ --skip-git-repo-check \
367
+ --ignore-rules \
368
+ -c features.codex_hooks=false \
369
+ --sandbox read-only \
370
+ --cd "${EAGLE_AGENT_CWD:-$(pwd)}" \
371
+ --output-last-message "$out_file" \
372
+ - < "$prompt_file" >> "$EAGLE_MEM_LOG" 2>&1
373
+ rc=$?
374
+ fi
375
+ [ "$_had_errexit" -eq 1 ] && set -e
376
+
377
+ rm -f "$prompt_file"
378
+ if [ "$rc" -ne 0 ] || [ ! -s "$out_file" ]; then
379
+ rm -f "$out_file"
380
+ eagle_log "ERROR" "Codex agent_cli provider call failed"
381
+ return 1
382
+ fi
383
+
384
+ cat "$out_file"
385
+ rm -f "$out_file"
386
+ }
387
+
388
+ _eagle_call_claude_cli() {
389
+ local prompt="$1" system="$2" max_tokens="$3"
390
+ command -v claude &>/dev/null || {
391
+ eagle_log "ERROR" "agent_cli provider selected Claude Code, but claude command was not found"
392
+ return 1
393
+ }
394
+
395
+ mkdir -p "$EAGLE_MEM_DIR/tmp"
396
+ local prompt_file out_file model rc
397
+ prompt_file=$(mktemp "$EAGLE_MEM_DIR/tmp/claude-provider-prompt.XXXXXX")
398
+ out_file=$(mktemp "$EAGLE_MEM_DIR/tmp/claude-provider-output.XXXXXX")
399
+ _eagle_agent_cli_prompt_file "$prompt" "$system" "$max_tokens" "$prompt_file"
400
+ model=$(eagle_config_get "agent_cli" "claude_model" "")
401
+
402
+ local _had_errexit=0
403
+ case "$-" in *e*) _had_errexit=1; set +e ;; esac
404
+ if [ -n "$model" ]; then
405
+ EAGLE_MEM_DISABLE_HOOKS=1 CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 claude -p \
406
+ --no-session-persistence \
407
+ --disable-slash-commands \
408
+ --permission-mode dontAsk \
409
+ --tools "" \
410
+ --output-format text \
411
+ --model "$model" \
412
+ "$(cat "$prompt_file")" > "$out_file" 2>> "$EAGLE_MEM_LOG"
413
+ rc=$?
414
+ else
415
+ EAGLE_MEM_DISABLE_HOOKS=1 CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 claude -p \
416
+ --no-session-persistence \
417
+ --disable-slash-commands \
418
+ --permission-mode dontAsk \
419
+ --tools "" \
420
+ --output-format text \
421
+ "$(cat "$prompt_file")" > "$out_file" 2>> "$EAGLE_MEM_LOG"
422
+ rc=$?
423
+ fi
424
+ [ "$_had_errexit" -eq 1 ] && set -e
425
+
426
+ rm -f "$prompt_file"
427
+ if [ "$rc" -ne 0 ] || [ ! -s "$out_file" ]; then
428
+ rm -f "$out_file"
429
+ eagle_log "ERROR" "Claude agent_cli provider call failed"
430
+ return 1
431
+ fi
432
+
433
+ cat "$out_file"
434
+ rm -f "$out_file"
435
+ }
436
+
238
437
  _eagle_call_anthropic() {
239
438
  local prompt="$1" system="$2" max_tokens="$3"
240
439
  local model api_key
@@ -332,7 +531,11 @@ eagle_show_config() {
332
531
 
333
532
  local provider model
334
533
  provider=$(eagle_config_get "provider" "type" "none")
335
- model=$(eagle_config_get "$provider" "model" "unknown")
534
+ if [ "$provider" = "agent_cli" ]; then
535
+ model=$(_eagle_agent_cli_target)
536
+ else
537
+ model=$(eagle_config_get "$provider" "model" "unknown")
538
+ fi
336
539
 
337
540
  echo "Provider: $provider"
338
541
  echo "Model: $model"
@@ -349,8 +552,25 @@ eagle_show_config() {
349
552
  else
350
553
  echo "Status: not running"
351
554
  fi
555
+ elif [ "$provider" = "agent_cli" ]; then
556
+ echo "Preferred: $(eagle_config_get "agent_cli" "preferred" "current")"
557
+ echo "Codex: $(command -v codex 2>/dev/null || echo "not found")"
558
+ echo "Claude: $(command -v claude 2>/dev/null || echo "not found")"
352
559
  fi
353
560
 
561
+ echo ""
562
+ echo "Orchestration:"
563
+ echo " Route: $(eagle_config_get "orchestration" "route" "opposite")"
564
+ echo " Worktrees: $(eagle_config_get "orchestration" "auto_worktree" "true")"
565
+ echo " Codex: $(eagle_config_get "orchestration" "codex_worker_model" "gpt-5.5") / $(eagle_config_get "orchestration" "codex_worker_effort" "xhigh")"
566
+ echo " Claude: $(eagle_config_get "orchestration" "claude_worker_model" "claude-opus-4-7") / $(eagle_config_get "orchestration" "claude_worker_effort" "xhigh")"
567
+
568
+ echo ""
569
+ echo "Token guard:"
570
+ echo " RTK mode: $(eagle_config_get "token_guard" "rtk" "auto")"
571
+ echo " Raw bash: $(eagle_config_get "token_guard" "raw_bash" "block")"
572
+ echo " RTK bin: $(command -v rtk 2>/dev/null || echo "not found")"
573
+
354
574
  echo ""
355
575
  echo "Config: $EAGLE_CONFIG_FILE"
356
576
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eagle-mem",
3
- "version": "4.7.0",
3
+ "version": "4.8.0",
4
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"
@@ -20,6 +20,8 @@
20
20
  "sqlite",
21
21
  "fts5",
22
22
  "hooks",
23
+ "orchestration",
24
+ "rtk",
23
25
  "persistent-memory"
24
26
  ],
25
27
  "author": "eagleisbatman",