fix: feat: Matrix notifications — contextual, linked, conversational (#76)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7cc9ce1b39
commit
814706bf90
3 changed files with 137 additions and 17 deletions
42
lib/env.sh
42
lib/env.sh
|
|
@ -74,12 +74,13 @@ wpdb() {
|
|||
-t "$@" 2>/dev/null
|
||||
}
|
||||
|
||||
# Matrix messaging helper — usage: matrix_send <prefix> <message> [thread_event_id]
|
||||
# Matrix messaging helper — usage: matrix_send <prefix> <message> [thread_event_id] [context_tag]
|
||||
# Returns event_id on stdout. Registers threads for listener dispatch.
|
||||
# context_tag is stored in the thread map (e.g. issue number) for routing replies.
|
||||
MATRIX_THREAD_MAP="${MATRIX_THREAD_MAP:-/tmp/matrix-thread-map}"
|
||||
matrix_send() {
|
||||
[ -z "${MATRIX_TOKEN:-}" ] && return 0
|
||||
local prefix="$1" msg="$2" thread_id="${3:-}"
|
||||
local prefix="$1" msg="$2" thread_id="${3:-}" ctx_tag="${4:-}"
|
||||
local room_encoded="${MATRIX_ROOM_ID//!/%21}"
|
||||
local txn="$(date +%s%N)$$"
|
||||
local body
|
||||
|
|
@ -101,7 +102,42 @@ matrix_send() {
|
|||
printf '%s' "$event_id"
|
||||
# Register thread root for listener dispatch (escalations only)
|
||||
if [ -z "$thread_id" ]; then
|
||||
printf '%s\t%s\t%s\n' "$event_id" "$prefix" "$(date +%s)" >> "$MATRIX_THREAD_MAP" 2>/dev/null || true
|
||||
printf '%s\t%s\t%s\t%s\n' "$event_id" "$prefix" "$(date +%s)" "${ctx_tag}" >> "$MATRIX_THREAD_MAP" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# matrix_send_ctx — Send rich Matrix message with HTML formatting
|
||||
# Usage: matrix_send_ctx <prefix> <plain_text> <html_body> [thread_event_id]
|
||||
# Use for notifications that benefit from links, code blocks, or structured content.
|
||||
matrix_send_ctx() {
|
||||
[ -z "${MATRIX_TOKEN:-}" ] && return 0
|
||||
local prefix="$1" plain="$2" html="$3" thread_id="${4:-}"
|
||||
local room_encoded="${MATRIX_ROOM_ID//!/%21}"
|
||||
local txn
|
||||
txn="$(date +%s%N)$$"
|
||||
local body
|
||||
if [ -n "$thread_id" ]; then
|
||||
body=$(jq -nc \
|
||||
--arg m "[${prefix}] ${plain}" \
|
||||
--arg h "<b>[${prefix}]</b> ${html}" \
|
||||
--arg t "$thread_id" \
|
||||
'{msgtype:"m.text",body:$m,format:"org.matrix.custom.html",formatted_body:$h,"m.relates_to":{rel_type:"m.thread",event_id:$t}}')
|
||||
else
|
||||
body=$(jq -nc \
|
||||
--arg m "[${prefix}] ${plain}" \
|
||||
--arg h "<b>[${prefix}]</b> ${html}" \
|
||||
'{msgtype:"m.text",body:$m,format:"org.matrix.custom.html",formatted_body:$h}')
|
||||
fi
|
||||
local response
|
||||
response=$(curl -s -X PUT \
|
||||
-H "Authorization: Bearer ${MATRIX_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${MATRIX_HOMESERVER}/_matrix/client/v3/rooms/${room_encoded}/send/m.room.message/${txn}" \
|
||||
-d "$body" 2>/dev/null) || return 0
|
||||
local event_id
|
||||
event_id=$(printf '%s' "$response" | jq -r '.event_id // empty' 2>/dev/null)
|
||||
if [ -n "$event_id" ]; then
|
||||
printf '%s' "$event_id"
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,8 +142,44 @@ while true; do
|
|||
matrix_send "gardener" "✓ received, will act on next poll" "$THREAD_ROOT" >/dev/null 2>&1 || true
|
||||
;;
|
||||
dev)
|
||||
# Write to flat file for backward compat
|
||||
printf '%s\t%s\t%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$SENDER" "$BODY" >> /tmp/dev-escalation-reply
|
||||
matrix_send "dev" "✓ received, will inject on next poll" "$THREAD_ROOT" >/dev/null 2>&1 || true
|
||||
|
||||
# Route reply into the dev tmux session using context_tag (issue number)
|
||||
DEV_ISSUE=$(awk -F'\t' -v id="$THREAD_ROOT" '$1 == id {print $4}' "$THREAD_MAP" 2>/dev/null || true)
|
||||
if [ -n "$DEV_ISSUE" ]; then
|
||||
DEV_SESSION="dev-${PROJECT_NAME}-${DEV_ISSUE}"
|
||||
DEV_PHASE_FILE="/tmp/dev-session-${PROJECT_NAME}-${DEV_ISSUE}.phase"
|
||||
if tmux has-session -t "$DEV_SESSION" 2>/dev/null; then
|
||||
DEV_CUR_PHASE=$(head -1 "$DEV_PHASE_FILE" 2>/dev/null | tr -d '[:space:]' || true)
|
||||
if [ "$DEV_CUR_PHASE" = "PHASE:needs_human" ] || [ "$DEV_CUR_PHASE" = "PHASE:awaiting_review" ]; then
|
||||
DEV_INJECT_MSG="Human guidance from ${SENDER} in Matrix:
|
||||
|
||||
${BODY}
|
||||
|
||||
Consider this guidance for your current work."
|
||||
DEV_INJECT_TMP=$(mktemp /tmp/dev-q-inject-XXXXXX)
|
||||
printf '%s' "$DEV_INJECT_MSG" > "$DEV_INJECT_TMP"
|
||||
tmux load-buffer -b "dev-q-${DEV_ISSUE}" "$DEV_INJECT_TMP" || true
|
||||
tmux paste-buffer -t "$DEV_SESSION" -b "dev-q-${DEV_ISSUE}" || true
|
||||
sleep 0.5
|
||||
tmux send-keys -t "$DEV_SESSION" "" Enter || true
|
||||
tmux delete-buffer -b "dev-q-${DEV_ISSUE}" 2>/dev/null || true
|
||||
rm -f "$DEV_INJECT_TMP"
|
||||
log "human guidance from ${SENDER} injected into ${DEV_SESSION}"
|
||||
matrix_send "dev" "✓ guidance forwarded to dev session for issue #${DEV_ISSUE}" "$THREAD_ROOT" >/dev/null 2>&1 || true
|
||||
else
|
||||
log "dev session ${DEV_SESSION} is busy (phase: ${DEV_CUR_PHASE:-active}), queuing"
|
||||
matrix_send "dev" "✓ received — session is busy, will be available when dev pauses" "$THREAD_ROOT" >/dev/null 2>&1 || true
|
||||
fi
|
||||
else
|
||||
log "dev session ${DEV_SESSION} not found for issue #${DEV_ISSUE}"
|
||||
matrix_send "dev" "dev session not active for issue #${DEV_ISSUE}" "$THREAD_ROOT" >/dev/null 2>&1 || true
|
||||
fi
|
||||
else
|
||||
log "dev thread ${THREAD_ROOT:0:20} has no issue mapping"
|
||||
matrix_send "dev" "✓ received, will act on next poll" "$THREAD_ROOT" >/dev/null 2>&1 || true
|
||||
fi
|
||||
;;
|
||||
review)
|
||||
# Route human questions to persistent review tmux session
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue