ScuttleBot
| 606670e… | lmata | 1 | #!/bin/bash |
| 606670e… | lmata | 2 | # Stop hook for Codex agents. Posts final assistant reply to scuttlebot IRC. |
| 606670e… | lmata | 3 | |
| 606670e… | lmata | 4 | SCUTTLEBOT_CONFIG_FILE="${SCUTTLEBOT_CONFIG_FILE:-$HOME/.config/scuttlebot-relay.env}" |
| 606670e… | lmata | 5 | if [ -f "$SCUTTLEBOT_CONFIG_FILE" ]; then |
| 606670e… | lmata | 6 | set -a |
| 606670e… | lmata | 7 | . "$SCUTTLEBOT_CONFIG_FILE" |
| 606670e… | lmata | 8 | set +a |
| 606670e… | lmata | 9 | fi |
| 606670e… | lmata | 10 | if [ -n "${SCUTTLEBOT_CHANNEL_STATE_FILE:-}" ] && [ -f "$SCUTTLEBOT_CHANNEL_STATE_FILE" ]; then |
| 606670e… | lmata | 11 | set -a |
| 606670e… | lmata | 12 | . "$SCUTTLEBOT_CHANNEL_STATE_FILE" |
| 606670e… | lmata | 13 | set +a |
| 606670e… | lmata | 14 | fi |
| 606670e… | lmata | 15 | |
| 606670e… | lmata | 16 | SCUTTLEBOT_URL="${SCUTTLEBOT_URL:-http://localhost:8080}" |
| 606670e… | lmata | 17 | SCUTTLEBOT_TOKEN="${SCUTTLEBOT_TOKEN}" |
| 606670e… | lmata | 18 | SCUTTLEBOT_CHANNEL="${SCUTTLEBOT_CHANNEL:-general}" |
| 606670e… | lmata | 19 | SCUTTLEBOT_HOOKS_ENABLED="${SCUTTLEBOT_HOOKS_ENABLED:-1}" |
| 606670e… | lmata | 20 | |
| 606670e… | lmata | 21 | normalize_channel() { |
| 606670e… | lmata | 22 | local channel="$1" |
| 606670e… | lmata | 23 | channel="${channel//[$' \t\r\n']/}" |
| 606670e… | lmata | 24 | channel="${channel#\#}" |
| 606670e… | lmata | 25 | printf '%s' "$channel" |
| 606670e… | lmata | 26 | } |
| 606670e… | lmata | 27 | |
| 606670e… | lmata | 28 | relay_channels() { |
| 606670e… | lmata | 29 | local raw="${SCUTTLEBOT_CHANNELS:-$SCUTTLEBOT_CHANNEL}" |
| 606670e… | lmata | 30 | local IFS=',' |
| 606670e… | lmata | 31 | local item channel seen="" |
| 606670e… | lmata | 32 | read -r -a items <<< "$raw" |
| 606670e… | lmata | 33 | for item in "${items[@]}"; do |
| 606670e… | lmata | 34 | channel=$(normalize_channel "$item") |
| 606670e… | lmata | 35 | [ -n "$channel" ] || continue |
| 606670e… | lmata | 36 | case ",$seen," in |
| 606670e… | lmata | 37 | *,"$channel",*) ;; |
| 606670e… | lmata | 38 | *) |
| 606670e… | lmata | 39 | seen="${seen:+$seen,}$channel" |
| 606670e… | lmata | 40 | printf '%s\n' "$channel" |
| 606670e… | lmata | 41 | ;; |
| 606670e… | lmata | 42 | esac |
| 606670e… | lmata | 43 | done |
| 606670e… | lmata | 44 | } |
| 606670e… | lmata | 45 | |
| 606670e… | lmata | 46 | sanitize() { |
| 606670e… | lmata | 47 | printf '%s' "$1" | tr -cs '[:alnum:]_-' '-' |
| 606670e… | lmata | 48 | } |
| 606670e… | lmata | 49 | |
| 606670e… | lmata | 50 | input=$(cat) |
| 606670e… | lmata | 51 | |
| 606670e… | lmata | 52 | cwd=$(echo "$input" | jq -r '.cwd // empty') |
| 606670e… | lmata | 53 | [ -z "$cwd" ] && cwd=$(pwd) |
| 606670e… | lmata | 54 | base_name=$(sanitize "$(basename "$cwd")") |
| 606670e… | lmata | 55 | session_suffix=$(echo "$input" | jq -r '.session_id // empty' | head -c 8) |
| 606670e… | lmata | 56 | [ -z "$session_suffix" ] && session_suffix=$PPID |
| 606670e… | lmata | 57 | default_nick="codex-${base_name}-$(sanitize "$session_suffix")" |
| 606670e… | lmata | 58 | SCUTTLEBOT_NICK="${SCUTTLEBOT_NICK:-$default_nick}" |
| 606670e… | lmata | 59 | |
| 606670e… | lmata | 60 | [ "$SCUTTLEBOT_HOOKS_ENABLED" = "0" ] && exit 0 |
| 606670e… | lmata | 61 | [ "$SCUTTLEBOT_HOOKS_ENABLED" = "false" ] && exit 0 |
| 606670e… | lmata | 62 | [ -z "$SCUTTLEBOT_TOKEN" ] && exit 0 |
| 606670e… | lmata | 63 | |
| 606670e… | lmata | 64 | response=$(echo "$input" | jq -r '.last_assistant_message // empty') |
| 606670e… | lmata | 65 | [ -z "$response" ] && exit 0 |
| 606670e… | lmata | 66 | |
| 606670e… | lmata | 67 | # Truncate long responses. |
| 606670e… | lmata | 68 | response=$(printf '%s' "$response" | head -c 360) |
| 606670e… | lmata | 69 | |
| 606670e… | lmata | 70 | payload="{\"text\": $(printf '%s' "$response" | jq -Rs .), \"nick\": \"$SCUTTLEBOT_NICK\"}" |
| 606670e… | lmata | 71 | for channel in $(relay_channels); do |
| 606670e… | lmata | 72 | curl -sf -X POST "$SCUTTLEBOT_URL/v1/channels/$channel/messages" \ |
| 606670e… | lmata | 73 | --connect-timeout 1 \ |
| 606670e… | lmata | 74 | --max-time 2 \ |
| 606670e… | lmata | 75 | -H "Authorization: Bearer $SCUTTLEBOT_TOKEN" \ |
| 606670e… | lmata | 76 | -H "Content-Type: application/json" \ |
| 606670e… | lmata | 77 | -d "$payload" \ |
| 606670e… | lmata | 78 | > /dev/null || true |
| 606670e… | lmata | 79 | done |
| 606670e… | lmata | 80 | |
| 606670e… | lmata | 81 | exit 0 |