feat(plugin): surface dynamic-tier failures in SessionStart hook (A2)
Fail-open but no longer silent. When the dynamic context fetch yields nothing,
append a short status line to the injected context so a session can tell
'couldn't load live context' apart from 'Scribe had nothing to say':
- endpoint+token present but fetch empty/failed -> 'instance unreachable / request failed'
- endpoint present but token absent -> fingerprints the known Claude Code
userConfig export gap ('API token did not reach this hook')
A fully unconfigured install (no url AND no token) stays quiet — static-only is
the intended mode there. Static Tier 1 still always carries the mandate.
Refs plan 812 item A2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,16 +23,22 @@
|
||||
# vars above are the supported channel; SCRIBE_URL / SCRIBE_TOKEN override for
|
||||
# the settings.json dogfooding path.
|
||||
#
|
||||
# FAIL-OPEN: the dynamic tier injects nothing on any error; the static tier
|
||||
# still fires. A session is never blocked by Scribe.
|
||||
# FAIL-OPEN, BUT NOT SILENT: the dynamic tier never blocks a session. A *failed*
|
||||
# dynamic fetch is surfaced as a short status line (not swallowed), so a session
|
||||
# can tell "couldn't load live context" apart from "Scribe had nothing to say".
|
||||
# A fully unconfigured install (no url AND no token) is the intended static-only
|
||||
# mode and stays quiet.
|
||||
set -uo pipefail
|
||||
|
||||
command -v jq >/dev/null 2>&1 || exit 0 # needed to emit the JSON envelope safely
|
||||
|
||||
here=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) || exit 0
|
||||
|
||||
# --- Tier 1: static behavioral mandate (always, keyless, networkless) ---
|
||||
out=""
|
||||
# Append $1 to $out, separated by a horizontal rule when $out already has content.
|
||||
append() { if [ -n "$out" ]; then out="${out}"$'\n\n---\n\n'"$1"; else out="$1"; fi; }
|
||||
|
||||
# --- Tier 1: static behavioral mandate (always, keyless, networkless) ---
|
||||
[ -f "$here/scribe_static_context.md" ] && out=$(cat "$here/scribe_static_context.md")
|
||||
|
||||
# --- Tier 2: dynamic rules + active-project context (best-effort) ---
|
||||
@@ -44,7 +50,9 @@ token=${SCRIBE_TOKEN:-${CLAUDE_PLUGIN_OPTION_api_token:-}}
|
||||
case "$url" in *'${'*) url="" ;; esac
|
||||
case "$token" in *'${'*) token="" ;; esac
|
||||
|
||||
if command -v curl >/dev/null 2>&1 && [ -n "$url" ] && [ -n "$token" ]; then
|
||||
dyn=""
|
||||
status=""
|
||||
if [ -n "$url" ] && [ -n "$token" ] && command -v curl >/dev/null 2>&1; then
|
||||
# Resolve the working repo's remote so the server can map it to a project.
|
||||
repo_dir=${CLAUDE_PROJECT_DIR:-$PWD}
|
||||
repo=$(git -C "$repo_dir" remote get-url origin 2>/dev/null || true)
|
||||
@@ -56,19 +64,18 @@ if command -v curl >/dev/null 2>&1 && [ -n "$url" ] && [ -n "$token" ]; then
|
||||
body=$(curl -fsS --max-time 8 \
|
||||
-H "Authorization: Bearer ${token}" \
|
||||
"${url%/}/api/plugin/context${q}" 2>/dev/null) || body=""
|
||||
if [ -n "$body" ]; then
|
||||
dyn=$(printf '%s' "$body" | jq -r '.context // empty' 2>/dev/null) || dyn=""
|
||||
if [ -n "$dyn" ]; then
|
||||
if [ -n "$out" ]; then
|
||||
out="${out}"$'\n\n---\n\n'"${dyn}"
|
||||
else
|
||||
out="$dyn"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
[ -n "$body" ] && dyn=$(printf '%s' "$body" | jq -r '.context // empty' 2>/dev/null)
|
||||
[ -z "$dyn" ] && status="> ⚠️ Scribe: live rules/project context could not be loaded this session (instance unreachable or request failed). The standing guidance above still applies — pull rules with \`list_always_on_rules()\` and project context with \`enter_project()\` as needed."
|
||||
elif [ -n "$url" ] && [ -z "$token" ]; then
|
||||
# Endpoint configured but token absent: the signature of the known Claude Code
|
||||
# userConfig export gap (sensitive values not always reaching the hook).
|
||||
status="> ⚠️ Scribe: live context disabled this session — the API token did not reach this hook (a known Claude Code plugin-config gap). Tools still work; pull rules with \`list_always_on_rules()\` and project context with \`enter_project()\`."
|
||||
fi
|
||||
|
||||
# Nothing to inject (no static file AND no dynamic context) → stay silent.
|
||||
[ -n "$dyn" ] && append "$dyn"
|
||||
[ -n "$status" ] && append "$status"
|
||||
|
||||
# Nothing at all to inject (no static file, no dynamic context, no status) → stay silent.
|
||||
[ -n "$out" ] || exit 0
|
||||
|
||||
jq -n --arg c "$out" \
|
||||
|
||||
Reference in New Issue
Block a user