ScuttleBot

scuttlebot / skills / gemini-relay / hooks / scuttlebot-post.sh
Blame History Raw 149 lines
1
#!/bin/bash
2
# AfterTool hook for Gemini agents. Posts activity to scuttlebot IRC.
3
4
SCUTTLEBOT_CONFIG_FILE="${SCUTTLEBOT_CONFIG_FILE:-$HOME/.config/scuttlebot-relay.env}"
5
if [ -f "$SCUTTLEBOT_CONFIG_FILE" ]; then
6
set -a
7
. "$SCUTTLEBOT_CONFIG_FILE"
8
set +a
9
fi
10
if [ -n "${SCUTTLEBOT_CHANNEL_STATE_FILE:-}" ] && [ -f "$SCUTTLEBOT_CHANNEL_STATE_FILE" ]; then
11
set -a
12
. "$SCUTTLEBOT_CHANNEL_STATE_FILE"
13
set +a
14
fi
15
16
SCUTTLEBOT_URL="${SCUTTLEBOT_URL:-http://localhost:8080}"
17
SCUTTLEBOT_TOKEN="${SCUTTLEBOT_TOKEN}"
18
SCUTTLEBOT_CHANNEL="${SCUTTLEBOT_CHANNEL:-general}"
19
SCUTTLEBOT_HOOKS_ENABLED="${SCUTTLEBOT_HOOKS_ENABLED:-1}"
20
21
normalize_channel() {
22
local channel="$1"
23
channel="${channel//[$' \t\r\n']/}"
24
channel="${channel#\#}"
25
printf '%s' "$channel"
26
}
27
28
relay_channels() {
29
local raw="${SCUTTLEBOT_CHANNELS:-$SCUTTLEBOT_CHANNEL}"
30
local IFS=','
31
local item channel seen=""
32
read -r -a items <<< "$raw"
33
for item in "${items[@]}"; do
34
channel=$(normalize_channel "$item")
35
[ -n "$channel" ] || continue
36
case ",$seen," in
37
*,"$channel",*) ;;
38
*)
39
seen="${seen:+$seen,}$channel"
40
printf '%s\n' "$channel"
41
;;
42
esac
43
done
44
}
45
46
post_message() {
47
local text="$1"
48
local payload
49
payload="{\"text\": $(printf '%s' "$text" | jq -Rs .), \"nick\": \"$SCUTTLEBOT_NICK\"}"
50
for channel in $(relay_channels); do
51
curl -sf -X POST "$SCUTTLEBOT_URL/v1/channels/$channel/messages" \
52
--connect-timeout 1 \
53
--max-time 2 \
54
-H "Authorization: Bearer $SCUTTLEBOT_TOKEN" \
55
-H "Content-Type: application/json" \
56
-d "$payload" \
57
> /dev/null || true
58
done
59
}
60
61
sanitize() {
62
local input="$1"
63
if [ -z "$input" ]; then
64
input=$(cat)
65
fi
66
printf '%s' "$input" | tr -cs '[:alnum:]_-' '-'
67
}
68
69
input=$(cat)
70
71
tool=$(echo "$input" | jq -r '.tool_name // empty')
72
cwd=$(echo "$input" | jq -r '.cwd // empty')
73
74
if [ -z "$cwd" ]; then
75
cwd=$(pwd)
76
fi
77
base_name=$(sanitize "$(basename "$cwd")")
78
session_raw="${SCUTTLEBOT_SESSION_ID:-${GEMINI_SESSION_ID:-$PPID}}"
79
if [ -z "$session_raw" ] || [ "$session_raw" = "0" ]; then
80
session_raw=$(date +%s)
81
fi
82
session_suffix=$(printf '%s' "$session_raw" | sanitize | cut -c 1-8)
83
default_nick="gemini-${base_name}-${session_suffix}"
84
SCUTTLEBOT_NICK="${SCUTTLEBOT_NICK:-$default_nick}"
85
86
[ "$SCUTTLEBOT_HOOKS_ENABLED" = "0" ] && { echo '{}'; exit 0; }
87
[ "$SCUTTLEBOT_HOOKS_ENABLED" = "false" ] && { echo '{}'; exit 0; }
88
[ -z "$SCUTTLEBOT_TOKEN" ] && { echo '{}'; exit 0; }
89
90
case "$tool" in
91
run_shell_command|Bash)
92
cmd=$(echo "$input" | jq -r '.tool_input.command // empty' | head -c 120)
93
msg="› $cmd"
94
;;
95
read_file|Read)
96
file=$(echo "$input" | jq -r '.tool_input.file_path // empty' | sed "s|$cwd/||")
97
msg="read $file"
98
;;
99
edit|Edit)
100
file=$(echo "$input" | jq -r '.tool_input.file_path // empty' | sed "s|$cwd/||")
101
msg="edit $file"
102
;;
103
write_file|Write)
104
file=$(echo "$input" | jq -r '.tool_input.file_path // empty' | sed "s|$cwd/||")
105
msg="write $file"
106
;;
107
Glob)
108
pattern=$(echo "$input" | jq -r '.tool_input.pattern // empty')
109
msg="glob $pattern"
110
;;
111
read_many_files)
112
paths=$(echo "$input" | jq -r '.tool_input.paths[]? // empty' 2>/dev/null | head -n 3 | paste -sd ", " -)
113
[ -z "$paths" ] && paths=$(echo "$input" | jq -r '.tool_input.path // empty')
114
msg="read many ${paths:-files}"
115
;;
116
grep|search_file_content|Grep)
117
pattern=$(echo "$input" | jq -r '.tool_input.pattern // empty')
118
msg="grep \"$pattern\""
119
;;
120
list_directory)
121
path=$(echo "$input" | jq -r '.tool_input.path // empty')
122
msg="list ${path:-.}"
123
;;
124
write_todos)
125
msg="update todos"
126
;;
127
activate_skill)
128
skill=$(echo "$input" | jq -r '.tool_input.name // empty')
129
msg="activate skill ${skill:-unknown}"
130
;;
131
ask_user)
132
msg="ask user"
133
;;
134
Agent)
135
desc=$(echo "$input" | jq -r '.tool_input.description // empty' | head -c 80)
136
msg="spawn agent: $desc"
137
;;
138
*)
139
msg="$tool"
140
;;
141
esac
142
143
[ -z "$msg" ] && { echo '{}'; exit 0; }
144
145
post_message "$msg"
146
147
echo '{}'
148
exit 0
149

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button