diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 9b1c34d..4240356 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "scribe", "description": "Scribe second brain for Claude Code: MCP tools over your notes/tasks/projects/rules, a session-start push channel that surfaces your always-on rules + active-project context, and a set of universal process-skills (brainstorm, debug, TDD, plan, verify). Replaces superpowers + file-memory with one app-backed plugin.", - "version": "0.1.1", + "version": "0.1.2", "author": { "name": "Bryan Van Deusen" }, "mcpServers": { "scribe": { diff --git a/plugin/hooks/hooks.json b/plugin/hooks/hooks.json index 8e4a6d7..76ce66e 100644 --- a/plugin/hooks/hooks.json +++ b/plugin/hooks/hooks.json @@ -5,7 +5,7 @@ "hooks": [ { "type": "command", - "command": "SCRIBE_URL=\"${user_config.api_endpoint}\" SCRIBE_TOKEN=\"${user_config.api_token}\" SCRIBE_PROJECT_ID=\"${user_config.project_id}\" bash \"${CLAUDE_PLUGIN_ROOT}/hooks/scribe_session_context.sh\"" + "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/scribe_session_context.sh\"" } ] } diff --git a/plugin/hooks/scribe_session_context.sh b/plugin/hooks/scribe_session_context.sh index ca7b2c5..4ed77bb 100755 --- a/plugin/hooks/scribe_session_context.sh +++ b/plugin/hooks/scribe_session_context.sh @@ -3,10 +3,18 @@ # # Curls the operator's Scribe instance for always-on rules + active-project # context and emits it as SessionStart `additionalContext`. Config comes from -# plugin userConfig, passed in as env by hooks/hooks.json: -# SCRIBE_URL base URL, no trailing slash (user_config.api_endpoint) -# SCRIBE_TOKEN fmcp_ API key (user_config.api_token) -# SCRIBE_PROJECT_ID optional numeric project id (user_config.project_id) +# the plugin's userConfig, which Claude Code exports to hook subprocesses as +# CLAUDE_PLUGIN_OPTION_ env vars: +# CLAUDE_PLUGIN_OPTION_api_endpoint base URL, no trailing slash +# CLAUDE_PLUGIN_OPTION_api_token fmcp_ API key (sensitive) +# CLAUDE_PLUGIN_OPTION_project_id optional numeric project id +# +# IMPORTANT: do NOT pass these via `${user_config.*}` substitution in +# hooks.json — sensitive userConfig values (api_token) are kept in the keychain +# and are never spliced into a hook command line, so the placeholder arrives +# unexpanded. The harness env vars above are the supported channel. SCRIBE_URL +# / SCRIBE_TOKEN / SCRIBE_PROJECT_ID still override, for the settings.json +# dogfooding path where the hook is wired up by hand. # # FAIL-OPEN: any missing tool/config, network error, or unreachable instance # injects nothing and exits 0. A session must never be blocked by Scribe. @@ -15,9 +23,16 @@ set -uo pipefail command -v jq >/dev/null 2>&1 || exit 0 command -v curl >/dev/null 2>&1 || exit 0 -url=${SCRIBE_URL:-} -token=${SCRIBE_TOKEN:-} -project_id=${SCRIBE_PROJECT_ID:-} +url=${SCRIBE_URL:-${CLAUDE_PLUGIN_OPTION_api_endpoint:-}} +token=${SCRIBE_TOKEN:-${CLAUDE_PLUGIN_OPTION_api_token:-}} +project_id=${SCRIBE_PROJECT_ID:-${CLAUDE_PLUGIN_OPTION_project_id:-}} + +# Guard against an unexpanded `${...}` placeholder reaching us as a literal — it +# would otherwise be sent as a garbage Bearer token and 401. Treat as unset. +case "$url" in *'${'*) url="" ;; esac +case "$token" in *'${'*) token="" ;; esac +case "$project_id" in *'${'*) project_id="" ;; esac + [ -n "$url" ] && [ -n "$token" ] || exit 0 q=""