eagle-mem 4.10.13 → 4.11.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +20 -20
  3. package/architecture.html +26 -14
  4. package/bin/eagle-mem +4 -0
  5. package/db/039_recall_events.sql +27 -0
  6. package/db/040_graph_decision_nodes.sql +21 -0
  7. package/db/041_graph_semantic_edge_types.sql +21 -0
  8. package/db/042_orchestration_auto_events.sql +23 -0
  9. package/db/043_eagle_events.sql +22 -0
  10. package/docs/agent-compatibility/README.md +38 -0
  11. package/docs/agent-compatibility/claude-code.md +50 -0
  12. package/docs/agent-compatibility/codex.md +51 -0
  13. package/docs/agent-compatibility/opencode.md +71 -0
  14. package/hooks/post-tool-use.sh +8 -0
  15. package/hooks/pre-tool-use.sh +10 -1
  16. package/hooks/session-end.sh +3 -0
  17. package/hooks/session-start.sh +7 -0
  18. package/hooks/stop.sh +10 -1
  19. package/hooks/user-prompt-submit.sh +79 -6
  20. package/integrations/opencode_eagle_mem_plugin.js +387 -0
  21. package/lib/codex-hooks.sh +13 -6
  22. package/lib/common.sh +63 -0
  23. package/lib/db-events.sh +89 -0
  24. package/lib/db-graph.sh +154 -0
  25. package/lib/db-observations.sh +34 -0
  26. package/lib/db-orchestration.sh +149 -0
  27. package/lib/db.sh +2 -0
  28. package/lib/hooks.sh +12 -7
  29. package/lib/opencode-hooks.sh +105 -0
  30. package/lib/provider.sh +2 -2
  31. package/package.json +5 -2
  32. package/scripts/compaction.sh +108 -8
  33. package/scripts/dashboard.sh +372 -0
  34. package/scripts/doctor.sh +30 -3
  35. package/scripts/health.sh +40 -2
  36. package/scripts/help.sh +10 -2
  37. package/scripts/inspect.sh +285 -0
  38. package/scripts/install.sh +31 -7
  39. package/scripts/memories.sh +13 -0
  40. package/scripts/repair.sh +187 -0
  41. package/scripts/replay.sh +248 -0
  42. package/scripts/search.sh +44 -3
  43. package/scripts/statusline-em.sh +34 -7
  44. package/scripts/tasks.sh +34 -0
  45. package/scripts/test.sh +13 -0
  46. package/scripts/uninstall.sh +9 -0
  47. package/scripts/update.sh +18 -2
  48. package/tests/fixtures/agent-hooks/claude-statusline.json +32 -0
  49. package/tests/fixtures/agent-hooks/claude-user-prompt-submit.json +9 -0
  50. package/tests/fixtures/agent-hooks/codex-pre-tool-use.json +10 -0
  51. package/tests/fixtures/agent-hooks/codex-user-prompt-submit.json +7 -0
  52. package/tests/fixtures/agent-hooks/opencode-chat-message.json +36 -0
  53. package/tests/fixtures/agent-hooks/opencode-session-compacting.json +9 -0
  54. package/tests/fixtures/agent-hooks/opencode-todo-updated.json +13 -0
  55. package/tests/fixtures/agent-hooks/opencode-tool-execute-after.json +15 -0
  56. package/tests/fixtures/agent-hooks/opencode-tool-execute-before.json +12 -0
  57. package/tests/test_agent_compatibility_docs_gate.sh +123 -0
  58. package/tests/test_auto_orchestration_detection.sh +109 -0
  59. package/tests/test_claude_stop_hook_registration.sh +56 -0
  60. package/tests/test_codex_hooks_config.sh +73 -0
  61. package/tests/test_compaction_survival_matrix.sh +237 -0
  62. package/tests/test_dashboard.sh +96 -0
  63. package/tests/test_eagle_events.sh +96 -0
  64. package/tests/test_opencode_hooks_config.sh +56 -0
  65. package/tests/test_opencode_plugin_adapter.sh +202 -0
  66. package/tests/test_recall_observability.sh +144 -0
  67. package/tests/test_repair.sh +63 -0
  68. package/tests/test_rust_migration_plan.sh +75 -0
  69. package/tests/test_trust_surfaces.sh +123 -0
@@ -11,6 +11,7 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
11
11
  . "$SCRIPTS_DIR/style.sh"
12
12
  . "$LIB_DIR/common.sh"
13
13
  . "$LIB_DIR/codex-hooks.sh"
14
+ . "$LIB_DIR/opencode-hooks.sh"
14
15
 
15
16
  SETTINGS="$EAGLE_SETTINGS"
16
17
  dry_run=false
@@ -66,6 +67,12 @@ else
66
67
  eagle_warn "Could not patch Codex hooks.json (jq not found or file missing)"
67
68
  fi
68
69
 
70
+ if eagle_remove_opencode_plugin; then
71
+ eagle_ok "OpenCode plugin removed"
72
+ else
73
+ eagle_info "OpenCode plugin not present or not Eagle Mem-owned"
74
+ fi
75
+
69
76
  # ─── Remove instruction blocks and statusline integration ───
70
77
 
71
78
  claude_md="$HOME/.claude/CLAUDE.md"
@@ -132,6 +139,8 @@ if [ -d "$EAGLE_GROK_SKILLS_DIR" ]; then
132
139
  done
133
140
  fi
134
141
 
142
+ eagle_remove_opencode_skills >/dev/null 2>&1 || true
143
+
135
144
  # ─── Optionally wipe data ─────────────────────────────────
136
145
 
137
146
  if [ -d "$EAGLE_MEM_DIR" ]; then
package/scripts/update.sh CHANGED
@@ -16,11 +16,13 @@ LIB_DIR="$SCRIPTS_DIR/../lib"
16
16
  . "$LIB_DIR/updater.sh"
17
17
  . "$LIB_DIR/hooks.sh"
18
18
  . "$LIB_DIR/codex-hooks.sh"
19
+ . "$LIB_DIR/opencode-hooks.sh"
19
20
 
20
21
  SETTINGS="$EAGLE_SETTINGS"
21
22
  claude_found=false
22
23
  codex_found=false
23
24
  grok_found=false
25
+ opencode_found=false
24
26
 
25
27
  eagle_header "Update"
26
28
 
@@ -43,8 +45,11 @@ fi
43
45
  if [ -d "$EAGLE_GROK_DIR" ]; then
44
46
  grok_found=true
45
47
  fi
48
+ if eagle_opencode_detected; then
49
+ opencode_found=true
50
+ fi
46
51
 
47
- eagle_runtime_change_plan "update" "$PACKAGE_DIR" "$claude_found" "$codex_found"
52
+ eagle_runtime_change_plan "update" "$PACKAGE_DIR" "$claude_found" "$codex_found" "$opencode_found"
48
53
 
49
54
  # ─── Update files ──────────────────────────────────────────
50
55
 
@@ -82,11 +87,12 @@ fi
82
87
 
83
88
  if [ "$claude_found" = true ] && [ -f "$SETTINGS" ] && command -v jq &>/dev/null; then
84
89
  # Clean old registrations before re-registering (handles matcher changes across versions)
90
+ eagle_clean_hook_entries "$SETTINGS" "Stop" "$EAGLE_MEM_DIR/hooks/stop.sh"
85
91
  eagle_clean_hook_entries "$SETTINGS" "PostToolUse" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
86
92
  eagle_clean_hook_entries "$SETTINGS" "PreToolUse" "$EAGLE_MEM_DIR/hooks/pre-tool-use.sh"
87
93
 
88
94
  eagle_patch_hook "$SETTINGS" "SessionStart" "" "$EAGLE_MEM_DIR/hooks/session-start.sh"
89
- eagle_patch_hook "$SETTINGS" "Stop" "" "$EAGLE_MEM_DIR/hooks/stop.sh"
95
+ eagle_patch_hook "$SETTINGS" "Stop" "" "bash \"$EAGLE_MEM_DIR/hooks/stop.sh\""
90
96
  eagle_patch_hook "$SETTINGS" "PostToolUse" "Read|Write|Edit|Bash|TaskUpdate" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
91
97
  eagle_patch_hook "$SETTINGS" "TaskCreated" "" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
92
98
  eagle_patch_hook "$SETTINGS" "TaskCompleted" "" "$EAGLE_MEM_DIR/hooks/post-tool-use.sh"
@@ -104,6 +110,12 @@ elif [ "$codex_found" = false ]; then
104
110
  eagle_info "Codex hooks skipped ${DIM}(Codex not detected)${RESET}"
105
111
  fi
106
112
 
113
+ if [ "$opencode_found" = true ]; then
114
+ eagle_install_opencode_plugin "$PACKAGE_DIR" "0"
115
+ else
116
+ eagle_info "OpenCode plugin skipped ${DIM}(OpenCode not detected)${RESET}"
117
+ fi
118
+
107
119
  # ─── Update skill symlinks ────────────────────────────────
108
120
 
109
121
  if [ "$claude_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
@@ -164,6 +176,10 @@ if [ "$grok_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
164
176
  eagle_ok "Grok skills updated"
165
177
  fi
166
178
 
179
+ if [ "$opencode_found" = true ] && [ -d "$PACKAGE_DIR/skills" ]; then
180
+ eagle_install_opencode_skills "$PACKAGE_DIR" "0"
181
+ fi
182
+
167
183
  # ─── Refresh generated Claude statusline wrapper ───────────
168
184
 
169
185
  statusline_wrapper="$EAGLE_MEM_DIR/scripts/statusline-wrapper.sh"
@@ -0,0 +1,32 @@
1
+ {
2
+ "hook_event_name": "Status",
3
+ "session_id": "claude-status-fixture",
4
+ "transcript_path": "/tmp/eagle-mem/claude-status-fixture.jsonl",
5
+ "cwd": "/tmp/eagle-mem/project",
6
+ "model": {
7
+ "id": "claude-opus-4-1",
8
+ "display_name": "Opus"
9
+ },
10
+ "workspace": {
11
+ "current_dir": "/tmp/eagle-mem/project",
12
+ "project_dir": "/tmp/eagle-mem/project"
13
+ },
14
+ "version": "2.1.145",
15
+ "output_style": {
16
+ "name": "default"
17
+ },
18
+ "cost": {
19
+ "total_cost_usd": 0.01234,
20
+ "total_duration_ms": 45000,
21
+ "total_api_duration_ms": 2300,
22
+ "total_lines_added": 12,
23
+ "total_lines_removed": 2
24
+ },
25
+ "thinking": {
26
+ "enabled": true
27
+ },
28
+ "effort": {
29
+ "level": "xhigh"
30
+ }
31
+ }
32
+
@@ -0,0 +1,9 @@
1
+ {
2
+ "session_id": "claude-session-fixture",
3
+ "transcript_path": "/tmp/eagle-mem/claude-session-fixture.jsonl",
4
+ "cwd": "/tmp/eagle-mem/project",
5
+ "permission_mode": "default",
6
+ "hook_event_name": "UserPromptSubmit",
7
+ "prompt": "Review the OAuth memory before editing auth files"
8
+ }
9
+
@@ -0,0 +1,10 @@
1
+ {
2
+ "session_id": "codex-session-fixture",
3
+ "cwd": "/tmp/eagle-mem/project",
4
+ "hook_event_name": "PreToolUse",
5
+ "tool_name": "Bash",
6
+ "tool_input": {
7
+ "command": "git status --short"
8
+ }
9
+ }
10
+
@@ -0,0 +1,7 @@
1
+ {
2
+ "session_id": "codex-session-fixture",
3
+ "cwd": "/tmp/eagle-mem/project",
4
+ "hook_event_name": "UserPromptSubmit",
5
+ "prompt": "Review the OAuth memory before editing auth files"
6
+ }
7
+
@@ -0,0 +1,36 @@
1
+ {
2
+ "sessionID": "opencode-session-fixture",
3
+ "input": {
4
+ "sessionID": "opencode-session-fixture",
5
+ "agent": "build",
6
+ "model": {
7
+ "providerID": "openai",
8
+ "modelID": "gpt-5"
9
+ },
10
+ "messageID": "opencode-user-message-fixture"
11
+ },
12
+ "output": {
13
+ "message": {
14
+ "id": "opencode-user-message-fixture",
15
+ "sessionID": "opencode-session-fixture",
16
+ "role": "user",
17
+ "time": {
18
+ "created": 1780372800000
19
+ },
20
+ "agent": "build",
21
+ "model": {
22
+ "providerID": "openai",
23
+ "modelID": "gpt-5"
24
+ }
25
+ },
26
+ "parts": [
27
+ {
28
+ "id": "opencode-text-part-fixture",
29
+ "sessionID": "opencode-session-fixture",
30
+ "messageID": "opencode-user-message-fixture",
31
+ "type": "text",
32
+ "text": "Review the OAuth memory before editing auth files"
33
+ }
34
+ ]
35
+ }
36
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "input": {
3
+ "sessionID": "opencode-session-fixture"
4
+ },
5
+ "output": {
6
+ "context": [],
7
+ "prompt": null
8
+ }
9
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "type": "todo.updated",
3
+ "properties": {
4
+ "sessionID": "opencode-session-fixture",
5
+ "todos": [
6
+ {
7
+ "content": "Verify OAuth token refresh after OpenCode compact",
8
+ "status": "in_progress",
9
+ "priority": "high"
10
+ }
11
+ ]
12
+ }
13
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "input": {
3
+ "tool": "read",
4
+ "sessionID": "opencode-session-fixture",
5
+ "callID": "opencode-call-fixture",
6
+ "args": {
7
+ "filePath": "/tmp/eagle-mem/project/lib/auth.ts"
8
+ }
9
+ },
10
+ "output": {
11
+ "title": "Read lib/auth.ts",
12
+ "output": "export function refreshToken() {}",
13
+ "metadata": {}
14
+ }
15
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "input": {
3
+ "tool": "bash",
4
+ "sessionID": "opencode-session-fixture",
5
+ "callID": "opencode-call-fixture"
6
+ },
7
+ "output": {
8
+ "args": {
9
+ "command": "git push origin main"
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env bash
2
+ # Guard against stale agent lifecycle assumptions when editing hook, statusline,
3
+ # config-installation, or instruction-file integration code.
4
+ set -euo pipefail
5
+
6
+ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
7
+ DOC_DIR="$ROOT_DIR/docs/agent-compatibility"
8
+ FIXTURE_DIR="$ROOT_DIR/tests/fixtures/agent-hooks"
9
+
10
+ fail() {
11
+ echo "agent compatibility docs gate failed: $*" >&2
12
+ exit 1
13
+ }
14
+
15
+ require_file() {
16
+ local file="$1"
17
+ [ -f "$file" ] || fail "missing required file: ${file#$ROOT_DIR/}"
18
+ }
19
+
20
+ require_contains() {
21
+ local file="$1"
22
+ local pattern="$2"
23
+ local label="$3"
24
+ if ! grep -Eq "$pattern" "$file"; then
25
+ fail "${file#$ROOT_DIR/} does not mention $label"
26
+ fi
27
+ }
28
+
29
+ require_json() {
30
+ local file="$1"
31
+ jq -e . "$file" >/dev/null || fail "invalid JSON fixture: ${file#$ROOT_DIR/}"
32
+ }
33
+
34
+ require_file "$DOC_DIR/README.md"
35
+ require_file "$DOC_DIR/claude-code.md"
36
+ require_file "$DOC_DIR/codex.md"
37
+ require_file "$DOC_DIR/opencode.md"
38
+ require_file "$FIXTURE_DIR/claude-user-prompt-submit.json"
39
+ require_file "$FIXTURE_DIR/claude-statusline.json"
40
+ require_file "$FIXTURE_DIR/codex-user-prompt-submit.json"
41
+ require_file "$FIXTURE_DIR/codex-pre-tool-use.json"
42
+ require_file "$FIXTURE_DIR/opencode-chat-message.json"
43
+ require_file "$FIXTURE_DIR/opencode-tool-execute-before.json"
44
+ require_file "$FIXTURE_DIR/opencode-tool-execute-after.json"
45
+ require_file "$FIXTURE_DIR/opencode-todo-updated.json"
46
+ require_file "$FIXTURE_DIR/opencode-session-compacting.json"
47
+
48
+ require_json "$FIXTURE_DIR/claude-user-prompt-submit.json"
49
+ require_json "$FIXTURE_DIR/claude-statusline.json"
50
+ require_json "$FIXTURE_DIR/codex-user-prompt-submit.json"
51
+ require_json "$FIXTURE_DIR/codex-pre-tool-use.json"
52
+ require_json "$FIXTURE_DIR/opencode-chat-message.json"
53
+ require_json "$FIXTURE_DIR/opencode-tool-execute-before.json"
54
+ require_json "$FIXTURE_DIR/opencode-tool-execute-after.json"
55
+ require_json "$FIXTURE_DIR/opencode-todo-updated.json"
56
+ require_json "$FIXTURE_DIR/opencode-session-compacting.json"
57
+
58
+ require_contains "$DOC_DIR/README.md" "Before modifying" "the required pre-edit workflow"
59
+ require_contains "$DOC_DIR/README.md" "official documentation" "official documentation"
60
+ require_contains "$DOC_DIR/README.md" "fixture|golden-test" "fixture or golden-test coverage"
61
+
62
+ require_contains "$DOC_DIR/claude-code.md" "Last verified: [0-9]{4}-[0-9]{2}-[0-9]{2}" "a Last verified date"
63
+ require_contains "$DOC_DIR/claude-code.md" "https://code\\.claude\\.com/docs/en/hooks" "Claude Code hooks source"
64
+ require_contains "$DOC_DIR/claude-code.md" "https://code\\.claude\\.com/docs/en/statusline" "Claude Code statusline source"
65
+ require_contains "$DOC_DIR/claude-code.md" "UserPromptSubmit" "Claude UserPromptSubmit"
66
+ require_contains "$DOC_DIR/claude-code.md" "PreCompact" "Claude PreCompact"
67
+ require_contains "$DOC_DIR/claude-code.md" "PostCompact" "Claude PostCompact"
68
+ require_contains "$DOC_DIR/claude-code.md" "statusLine" "Claude statusLine config"
69
+ require_contains "$DOC_DIR/claude-code.md" "tests/fixtures/agent-hooks/claude-statusline\\.json" "Claude statusline fixture"
70
+
71
+ require_contains "$DOC_DIR/codex.md" "Last verified: [0-9]{4}-[0-9]{2}-[0-9]{2}" "a Last verified date"
72
+ require_contains "$DOC_DIR/codex.md" "https://developers\\.openai\\.com/codex/codex-manual\\.md" "Codex manual source"
73
+ require_contains "$DOC_DIR/codex.md" "https://developers\\.openai\\.com/codex/hooks\\.md" "Codex hooks source"
74
+ require_contains "$DOC_DIR/codex.md" "AGENTS\\.md" "Codex AGENTS.md behavior"
75
+ require_contains "$DOC_DIR/codex.md" "features\\.hooks" "canonical Codex hooks feature flag"
76
+ require_contains "$DOC_DIR/codex.md" "deprecated alias" "deprecated Codex hook alias"
77
+ require_contains "$DOC_DIR/codex.md" "UserPromptSubmit" "Codex UserPromptSubmit"
78
+ require_contains "$DOC_DIR/codex.md" "tests/fixtures/agent-hooks/codex-pre-tool-use\\.json" "Codex PreToolUse fixture"
79
+
80
+ require_contains "$DOC_DIR/opencode.md" "Last verified: [0-9]{4}-[0-9]{2}-[0-9]{2}" "an OpenCode Last verified date"
81
+ require_contains "$DOC_DIR/opencode.md" "https://opencode\\.ai/docs/plugins/" "OpenCode plugins source"
82
+ require_contains "$DOC_DIR/opencode.md" "https://opencode\\.ai/docs/config/" "OpenCode config source"
83
+ require_contains "$DOC_DIR/opencode.md" "https://opencode\\.ai/config\\.json" "OpenCode config schema source"
84
+ require_contains "$DOC_DIR/opencode.md" "~/.config/opencode/plugins/" "OpenCode global local plugin directory"
85
+ require_contains "$DOC_DIR/opencode.md" "chat\\.message" "OpenCode chat.message hook"
86
+ require_contains "$DOC_DIR/opencode.md" "tool\\.execute\\.before" "OpenCode tool.execute.before hook"
87
+ require_contains "$DOC_DIR/opencode.md" "tool\\.execute\\.after" "OpenCode tool.execute.after hook"
88
+ require_contains "$DOC_DIR/opencode.md" "todo\\.updated" "OpenCode todo.updated event"
89
+ require_contains "$DOC_DIR/opencode.md" "experimental\\.session\\.compacting" "OpenCode compaction hook"
90
+ require_contains "$DOC_DIR/opencode.md" "opencode --pure" "OpenCode pure-mode caveat"
91
+ require_contains "$DOC_DIR/opencode.md" "tests/fixtures/agent-hooks/opencode-tool-execute-before\\.json" "OpenCode PreToolUse fixture"
92
+ require_contains "$DOC_DIR/opencode.md" "tests/test_opencode_plugin_adapter\\.sh" "OpenCode adapter regression test"
93
+
94
+ if grep -R "features\\.codex_hooks" "$ROOT_DIR/lib" "$ROOT_DIR/scripts" "$ROOT_DIR/hooks" "$ROOT_DIR/bin" >/dev/null 2>&1; then
95
+ fail "implementation still uses deprecated Codex features.codex_hooks instead of features.hooks"
96
+ fi
97
+
98
+ if grep -R "codex_hooks[[:space:]]*=[[:space:]]*true" "$ROOT_DIR/lib" "$ROOT_DIR/scripts" "$ROOT_DIR/hooks" "$ROOT_DIR/bin" >/dev/null 2>&1; then
99
+ fail "implementation still writes deprecated Codex codex_hooks config instead of hooks"
100
+ fi
101
+
102
+ changed_sensitive=$(
103
+ {
104
+ git -C "$ROOT_DIR" diff --name-only HEAD -- 2>/dev/null || true
105
+ git -C "$ROOT_DIR" ls-files --others --exclude-standard 2>/dev/null || true
106
+ } | awk '
107
+ /^(hooks\/.*\.sh|lib\/hooks\.sh|lib\/codex-hooks\.sh|lib\/opencode-hooks\.sh|integrations\/opencode_eagle_mem_plugin\.js|scripts\/install\.sh|scripts\/update\.sh|scripts\/uninstall\.sh|scripts\/doctor\.sh|scripts\/statusline-em\.sh|lib\/common\.sh|AGENTS\.md|CLAUDE\.md)$/ { print }
108
+ ' | sort -u
109
+ )
110
+
111
+ if [ -n "$changed_sensitive" ]; then
112
+ changed_evidence=$(
113
+ {
114
+ git -C "$ROOT_DIR" diff --name-only HEAD -- 2>/dev/null || true
115
+ git -C "$ROOT_DIR" ls-files --others --exclude-standard 2>/dev/null || true
116
+ } | awk '
117
+ /^(docs\/agent-compatibility\/.*\.md|tests\/fixtures\/agent-hooks\/.*\.json|tests\/test_agent_compatibility_docs_gate\.sh)$/ { print }
118
+ ' | sort -u
119
+ )
120
+ [ -n "$changed_evidence" ] || fail "sensitive integration files changed without updated compatibility docs or fixtures: $changed_sensitive"
121
+ fi
122
+
123
+ echo "agent compatibility docs gate passed"
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env bash
2
+ # Broad prompts should create durable orchestration state automatically. This
3
+ # proves Eagle detects the need for lanes instead of relying only on agent
4
+ # obedience to instructions.
5
+ set -euo pipefail
6
+
7
+ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
8
+ EAGLE_BIN="$ROOT_DIR/bin/eagle-mem"
9
+
10
+ tmp_dir=$(mktemp -d "$ROOT_DIR/.tmp-auto-orchestration.XXXXXX")
11
+ trap 'rm -rf "$tmp_dir"' EXIT
12
+
13
+ export HOME="$tmp_dir/home"
14
+ export EAGLE_MEM_DIR="$tmp_dir/eagle-mem"
15
+ mkdir -p "$HOME" "$EAGLE_MEM_DIR"
16
+
17
+ . "$ROOT_DIR/lib/common.sh"
18
+ "$ROOT_DIR/db/migrate.sh" >/dev/null
19
+ . "$ROOT_DIR/lib/db.sh"
20
+
21
+ assert_json() {
22
+ local json="$1" filter="$2" message="$3"
23
+ if ! printf '%s' "$json" | jq -e "$filter" >/dev/null; then
24
+ echo "$message" >&2
25
+ echo "$json" >&2
26
+ exit 1
27
+ fi
28
+ }
29
+
30
+ assert_contains() {
31
+ local haystack="$1" needle="$2" message="$3"
32
+ case "$haystack" in
33
+ *"$needle"*) ;;
34
+ *)
35
+ echo "$message" >&2
36
+ echo "Expected to find: $needle" >&2
37
+ echo "$haystack" >&2
38
+ exit 1
39
+ ;;
40
+ esac
41
+ }
42
+
43
+ project="project-auto-orchestration"
44
+ repo="$HOME/$project"
45
+ mkdir -p "$repo"
46
+
47
+ hook_input=$(jq -nc \
48
+ --arg sid "auto-orchestration-session" \
49
+ --arg cwd "$repo" \
50
+ --arg prompt "Please plan and get started on a broad full codebase release. Split the work into lanes, coordinate workers, implement the fixes, and verify the ship path." \
51
+ '{session_id:$sid, cwd:$cwd, prompt:$prompt, transcript_path:"/tmp/transcript.jsonl"}')
52
+
53
+ hook_output=$(EAGLE_MEM_PROJECT="$project" EAGLE_MEM_DIR="$EAGLE_MEM_DIR" EAGLE_AGENT_SOURCE="codex" bash "$ROOT_DIR/hooks/user-prompt-submit.sh" <<< "$hook_input")
54
+ assert_contains "$hook_output" "created durable orchestration 'auto'" "broad prompt did not report auto orchestration creation"
55
+ assert_contains "$hook_output" "Continue from the durable lanes" "hook did not tell the agent to continue from durable lanes"
56
+
57
+ orchestration_json=$(cd "$repo" && EAGLE_MEM_PROJECT="$project" EAGLE_MEM_DIR="$EAGLE_MEM_DIR" "$EAGLE_BIN" orchestrate --name auto --json)
58
+ assert_json "$orchestration_json" '
59
+ length == 3
60
+ and ([.[].lane_key] | sort == ["implementation","scope","verification"])
61
+ and all(.[]; .status == "pending")
62
+ ' "auto orchestration did not create the expected pending lanes"
63
+
64
+ task_json=$(cd "$repo" && EAGLE_MEM_PROJECT="$project" EAGLE_MEM_DIR="$EAGLE_MEM_DIR" "$EAGLE_BIN" tasks --json)
65
+ assert_json "$task_json" '
66
+ length >= 3
67
+ and ([.[] | select(.source_task_id | startswith("lane-auto-"))] | length == 3)
68
+ ' "auto orchestration did not mirror lanes into durable tasks"
69
+
70
+ event_json=$(eagle_db_json "SELECT session_id, project, agent, prompt_snippet, orchestration_name, lanes, status
71
+ FROM orchestration_auto_events
72
+ WHERE project = '$project'
73
+ ORDER BY id DESC;")
74
+ assert_json "$event_json" '
75
+ length == 1
76
+ and .[0].session_id == "auto-orchestration-session"
77
+ and .[0].orchestration_name == "auto"
78
+ and .[0].status == "created"
79
+ and ((.[0].lanes | fromjson) | length == 3)
80
+ ' "auto orchestration event was not recorded"
81
+
82
+ hook_output_again=$(EAGLE_MEM_PROJECT="$project" EAGLE_MEM_DIR="$EAGLE_MEM_DIR" EAGLE_AGENT_SOURCE="codex" bash "$ROOT_DIR/hooks/user-prompt-submit.sh" <<< "$hook_input")
83
+ assert_contains "$hook_output_again" "created durable orchestration 'auto'" "repeated broad prompt did not reuse auto orchestration"
84
+
85
+ lane_count=$(eagle_db "SELECT COUNT(*)
86
+ FROM orchestration_lanes l
87
+ JOIN orchestrations o ON o.id = l.orchestration_id
88
+ WHERE o.project = '$project'
89
+ AND o.name = 'auto';")
90
+ task_count=$(eagle_db "SELECT COUNT(*)
91
+ FROM agent_tasks
92
+ WHERE project = '$project'
93
+ AND source_task_id LIKE 'lane-auto-%';")
94
+ event_count=$(eagle_db "SELECT COUNT(*) FROM orchestration_auto_events WHERE project = '$project';")
95
+
96
+ [ "$lane_count" = "3" ] || { echo "expected 3 auto lanes after repeat, got $lane_count" >&2; exit 1; }
97
+ [ "$task_count" = "3" ] || { echo "expected 3 auto tasks after repeat, got $task_count" >&2; exit 1; }
98
+ [ "$event_count" = "2" ] || { echo "expected 2 auto events after repeat, got $event_count" >&2; exit 1; }
99
+
100
+ quiet_input=$(jq -nc \
101
+ --arg sid "quiet-session" \
102
+ --arg cwd "$repo" \
103
+ --arg prompt "Please inspect the config value." \
104
+ '{session_id:$sid, cwd:$cwd, prompt:$prompt}')
105
+ EAGLE_MEM_PROJECT="$project" EAGLE_MEM_DIR="$EAGLE_MEM_DIR" EAGLE_AGENT_SOURCE="codex" bash "$ROOT_DIR/hooks/user-prompt-submit.sh" <<< "$quiet_input" >/dev/null
106
+ quiet_event_count=$(eagle_db "SELECT COUNT(*) FROM orchestration_auto_events WHERE project = '$project';")
107
+ [ "$quiet_event_count" = "2" ] || { echo "quiet prompt should not create auto orchestration event" >&2; exit 1; }
108
+
109
+ echo "auto orchestration detection regressions passed"
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env bash
2
+ # Regression coverage for Claude Stop hook registration.
3
+ set -euo pipefail
4
+
5
+ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
6
+ tmp_dir=$(mktemp -d "$ROOT_DIR/.tmp-claude-stop.XXXXXX")
7
+ trap 'rm -rf "$tmp_dir"' EXIT
8
+
9
+ export HOME="$tmp_dir/home"
10
+ export EAGLE_MEM_DIR="$tmp_dir/eagle-mem"
11
+ mkdir -p "$HOME" "$EAGLE_MEM_DIR/hooks"
12
+
13
+ . "$ROOT_DIR/scripts/style.sh"
14
+ . "$ROOT_DIR/lib/common.sh"
15
+ . "$ROOT_DIR/lib/hooks.sh"
16
+
17
+ settings="$tmp_dir/settings.json"
18
+ old_stop="$EAGLE_MEM_DIR/hooks/stop.sh"
19
+ new_stop="bash \"$EAGLE_MEM_DIR/hooks/stop.sh\""
20
+
21
+ fail() {
22
+ echo "claude stop hook registration test failed: $*" >&2
23
+ exit 1
24
+ }
25
+
26
+ cat > "$settings" <<JSON
27
+ {
28
+ "hooks": {
29
+ "Stop": [
30
+ {
31
+ "hooks": [
32
+ {
33
+ "type": "command",
34
+ "command": "$old_stop"
35
+ }
36
+ ]
37
+ }
38
+ ]
39
+ }
40
+ }
41
+ JSON
42
+
43
+ eagle_clean_hook_entries "$settings" "Stop" "$old_stop"
44
+ eagle_patch_hook "$settings" "Stop" "" "$new_stop" >/dev/null
45
+
46
+ jq -e --arg cmd "$old_stop" 'all(.hooks.Stop[]?.hooks[]?; .command != $cmd)' "$settings" >/dev/null \
47
+ || fail "path-only Stop hook registration remained"
48
+
49
+ jq -e --arg cmd "$new_stop" '.hooks.Stop[]? | select(.matcher == null and (.hooks[]?.command == $cmd))' "$settings" >/dev/null \
50
+ || fail "bash-wrapped Stop hook registration missing"
51
+
52
+ eagle_patch_hook "$settings" "Stop" "" "$new_stop" >/dev/null
53
+ count=$(jq --arg cmd "$new_stop" '[.hooks.Stop[]?.hooks[]? | select(.command == $cmd)] | length' "$settings")
54
+ [ "$count" -eq 1 ] || fail "bash-wrapped Stop hook registration duplicated: $count"
55
+
56
+ echo "claude stop hook registration test passed"
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bash
2
+ # Codex hook enablement should use the current canonical features.hooks flag
3
+ # while cleaning up the older codex_hooks alias when encountered.
4
+ set -euo pipefail
5
+
6
+ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
7
+
8
+ tmp_dir=$(mktemp -d "$ROOT_DIR/.tmp-codex-hooks-config.XXXXXX")
9
+ trap 'rm -rf "$tmp_dir"' EXIT
10
+
11
+ export HOME="$tmp_dir/home"
12
+ export EAGLE_MEM_DIR="$tmp_dir/eagle-mem"
13
+ export EAGLE_CODEX_DIR="$HOME/.codex"
14
+ export EAGLE_CODEX_CONFIG="$EAGLE_CODEX_DIR/config.toml"
15
+ export EAGLE_CODEX_HOOKS="$EAGLE_CODEX_DIR/hooks.json"
16
+
17
+ mkdir -p "$HOME" "$EAGLE_MEM_DIR" "$EAGLE_CODEX_DIR"
18
+
19
+ . "$ROOT_DIR/lib/common.sh"
20
+ . "$ROOT_DIR/lib/codex-hooks.sh"
21
+
22
+ fail() {
23
+ echo "codex hooks config regression failed: $*" >&2
24
+ exit 1
25
+ }
26
+
27
+ assert_has_hooks_true() {
28
+ grep -q '^hooks = true$' "$EAGLE_CODEX_CONFIG" || fail "missing canonical hooks = true"
29
+ }
30
+
31
+ assert_no_legacy_write() {
32
+ if grep -q '^codex_hooks[[:space:]]*=' "$EAGLE_CODEX_CONFIG"; then
33
+ fail "deprecated codex_hooks key was left in generated config"
34
+ fi
35
+ }
36
+
37
+ rm -f "$EAGLE_CODEX_CONFIG"
38
+ eagle_enable_codex_hooks
39
+ assert_has_hooks_true
40
+ assert_no_legacy_write
41
+
42
+ cat > "$EAGLE_CODEX_CONFIG" <<'TOML'
43
+ [other]
44
+ name = "preserve-me"
45
+
46
+ [features]
47
+ codex_hooks = false
48
+ memories = true
49
+
50
+ [sandbox]
51
+ mode = "workspace-write"
52
+ TOML
53
+
54
+ eagle_enable_codex_hooks
55
+ assert_has_hooks_true
56
+ assert_no_legacy_write
57
+ grep -q '^memories = true$' "$EAGLE_CODEX_CONFIG" || fail "features table values were not preserved"
58
+ grep -q '^name = "preserve-me"$' "$EAGLE_CODEX_CONFIG" || fail "other config sections were not preserved"
59
+ grep -q '^mode = "workspace-write"$' "$EAGLE_CODEX_CONFIG" || fail "later config sections were not preserved"
60
+
61
+ cat > "$EAGLE_CODEX_CONFIG" <<'TOML'
62
+ [features]
63
+ hooks = false
64
+ TOML
65
+
66
+ eagle_enable_codex_hooks
67
+ assert_has_hooks_true
68
+ assert_no_legacy_write
69
+ hook_lines=$(grep -c '^hooks = true$' "$EAGLE_CODEX_CONFIG")
70
+ [ "$hook_lines" -eq 1 ] || fail "expected one hooks = true line, found $hook_lines"
71
+
72
+ echo "codex hooks config regressions passed"
73
+