#!/usr/bin/env bash
# Scribe plugin — UserPromptSubmit push channel (knowledge auto-inject, Path A).
#
# On each user prompt, asks the operator's Scribe instance for a TITLE-FIRST
# awareness hint: the few notes that clear the per-user auto-inject gates
# (high-confidence threshold, margin gate, session dedup, top-k). Titles + ids
# only — never bodies; the agent calls get_note(id) to pull anything it judges
# relevant. Most turns inject nothing.
#
# Best-effort enrichment ONLY: unlike the SessionStart channel there is no
# static floor here. If the instance is unconfigured/unreachable, or anything
# fails, the hook stays SILENT and exits 0 — it must never block a prompt.
#
# Config (same as scribe_session_context.sh), exported to the hook by Claude Code
# with the userConfig key UPPERCASED (see #2198 — reading the lowercase spelling
# silently disables this hook, and silence is indistinguishable from "nothing
# cleared the threshold"):
#   CLAUDE_PLUGIN_OPTION_API_ENDPOINT  base URL, no trailing slash
#   CLAUDE_PLUGIN_OPTION_API_TOKEN     fmcp_ API key (sensitive)
#   SCRIBE_URL / SCRIBE_TOKEN override for the settings.json dogfooding path.
#
# Session dedup: each surfaced note id is remembered in a per-session file so a
# note is injected at most once per session. Passed back as exclude_ids.
set -uo pipefail

command -v jq   >/dev/null 2>&1 || exit 0
command -v curl >/dev/null 2>&1 || exit 0

# UserPromptSubmit delivers a JSON event on stdin: { prompt, session_id, cwd, ... }
event=$(cat 2>/dev/null || true)
prompt=$(printf '%s' "$event" | jq -r '.prompt // empty' 2>/dev/null) || prompt=""
session_id=$(printf '%s' "$event" | jq -r '.session_id // empty' 2>/dev/null) || session_id=""
event_cwd=$(printf '%s' "$event" | jq -r '.cwd // empty' 2>/dev/null) || event_cwd=""

# Nothing to retrieve against.
[ -n "$prompt" ] || exit 0

url=${SCRIBE_URL:-${CLAUDE_PLUGIN_OPTION_API_ENDPOINT:-}}
token=${SCRIBE_TOKEN:-${CLAUDE_PLUGIN_OPTION_API_TOKEN:-}}
# Guard against an unexpanded ${...} placeholder arriving as a literal.
case "$url"   in *'${'*) url="" ;;  esac
case "$token" in *'${'*) token="" ;; esac
# Unconfigured install → silent (auto-inject is pure enrichment).
[ -n "$url" ] && [ -n "$token" ] || exit 0

# Cap the query length — a giant prompt makes a giant URL for no extra signal.
# `head -c`, not `cut -c1-2000`: cut is line-oriented and caps EACH LINE, so a
# long multi-line prompt sailed past the budget entirely. Same defect as the
# prior-art hook's code cap; this copy was missed when that one was fixed, and
# scripts/check_plugin.py caught it.
q=$(printf '%s' "$prompt" | head -c 2000)
# `-sRr`, not `-rR`: jq -R reads LINE BY LINE, so a multi-line prompt encoded as
# several lines joined by raw newlines and the request died. Single-line prompts
# worked, which is why this looked healthy — the long, substantial prompts most
# worth retrieving against were exactly the ones silently dropped. -s slurps.
q_enc=$(printf '%s' "$q" | jq -sRr '@uri' 2>/dev/null) || exit 0

# Resolve the working repo's remote so the server can scope to the bound project.
repo_dir=${event_cwd:-${CLAUDE_PROJECT_DIR:-$PWD}}
repo=$(git -C "$repo_dir" remote get-url origin 2>/dev/null || true)
repo_q=""
if [ -n "$repo" ]; then
  enc=$(printf '%s' "$repo" | jq -sRr '@uri' 2>/dev/null) || enc=""
  [ -n "$enc" ] && repo_q="&repo=${enc}"
fi

# Per-session dedup: ids already injected this session are skipped.
state_dir="${TMPDIR:-/tmp}/scribe-autoinject"
mkdir -p "$state_dir" 2>/dev/null || true
idfile=""
exclude_q=""
if [ -n "$session_id" ]; then
  # session_id is an opaque token from Claude Code; keep only filename-safe chars.
  safe_sid=$(printf '%s' "$session_id" | tr -c 'A-Za-z0-9._-' '_')
  idfile="$state_dir/${safe_sid}.ids"
  if [ -f "$idfile" ]; then
    seen=$(tr '\n' ',' < "$idfile" 2>/dev/null | sed 's/,$//')
    [ -n "$seen" ] && exclude_q="&exclude_ids=${seen}"
  fi
fi

body=$(curl -fsS --max-time 5 \
  -H "Authorization: Bearer ${token}" \
  "${url%/}/api/plugin/retrieve?q=${q_enc}${repo_q}${exclude_q}" 2>/dev/null) || exit 0
[ -n "$body" ] || exit 0

context=$(printf '%s' "$body" | jq -r '.context // empty' 2>/dev/null) || exit 0
[ -n "$context" ] || exit 0

# Remember the surfaced ids so they aren't injected again this session.
if [ -n "$idfile" ]; then
  printf '%s' "$body" | jq -r '.note_ids[]? // empty' 2>/dev/null >> "$idfile" || true
fi

jq -n --arg c "$context" \
  '{hookSpecificOutput: {hookEventName: "UserPromptSubmit", additionalContext: $c}}'
exit 0
