feat(plugin): ship the Scribe Claude Code plugin in-repo (marketplace + plugin)

Per operator: the plugin lives in the app repo so it ships and versions in
lockstep with the app and the /api/plugin/context contract it targets (same
co-location rationale as the former in-repo MCP). A git-cloned marketplace
supports relative plugin sources, so the FabledScribe repo IS the marketplace.

- .claude-plugin/marketplace.json — source ./plugin
- plugin/.claude-plugin/plugin.json — userConfig (base URL, api key, project id)
- plugin/.mcp.json — http scribe server, ${user_config.*} substitution
- plugin/hooks/ — SessionStart push-channel hook (fail-open)
- plugin/skills/using-scribe — bootstrap skill
- plugin/README.md — install via the FabledScribe repo marketplace

Phase 2 of plan #755. Install/userConfig-substitution test pending.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 23:04:09 -04:00
parent 3ab16fcbdb
commit 9924f873b9
6 changed files with 176 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"name": "scribe-plugin",
"owner": { "name": "Bryan Van Deusen" },
"description": "Scribe ships its own Claude Code plugin from this repo, versioned in lockstep with the app + the /api/plugin/context contract.",
"plugins": [
{
"name": "scribe",
"source": "./plugin",
"description": "Scribe second brain: MCP tools + session-start push channel + universal process-skills."
}
]
}
+25
View File
@@ -0,0 +1,25 @@
{
"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.0",
"author": { "name": "Bryan Van Deusen" },
"mcpServers": "./.mcp.json",
"userConfig": {
"api_endpoint": {
"type": "string",
"title": "Scribe base URL",
"description": "Base URL of your Scribe instance, no trailing slash (e.g. https://scribe.example.com)"
},
"api_token": {
"type": "string",
"title": "Scribe API key",
"description": "An fmcp_ API key from Settings → API Keys (read scope is enough for the session-start hook; write scope to use the tools)",
"sensitive": true
},
"project_id": {
"type": "string",
"title": "Active project id (optional)",
"description": "Numeric Scribe project id to scope the session-start context. Leave blank to inject standing rules only."
}
}
}
+47
View File
@@ -0,0 +1,47 @@
# Scribe plugin for Claude Code
Turns a self-hosted [Scribe](https://git.fabledsword.com/bvandeusen/FabledScribe)
instance into a first-class Claude Code extension:
- **MCP tools** over your notes, tasks, projects, milestones, events, typed
entities, and rulebook (the `scribe` server).
- **Session-start push channel** — a `SessionStart` hook injects your always-on
rules + active-project context so Scribe surfaces *without being asked*.
- **Universal process-skills** — brainstorm, systematic-debugging, TDD,
writing-plans, verification, receiving-code-review (replaces superpowers).
It is designed so you can uninstall `superpowers` and disable auto-memory and
depend on neither.
## Install
The plugin ships inside the Scribe app repo, so the marketplace *is* that repo —
you always get the plugin version that matches your Scribe instance.
```
/plugin marketplace add https://git.fabledsword.com/bvandeusen/FabledScribe.git
/plugin install scribe@scribe-plugin
```
On install you'll be asked for:
| Setting | What |
|---|---|
| **Scribe base URL** | e.g. `https://scribe.example.com` (no trailing slash) |
| **Scribe API key** | an `fmcp_` key from **Settings → API Keys** (stored in your OS keychain) |
| **Active project id** | optional — numeric project id to scope the session-start context |
## What gets wired
- `.mcp.json` → the `scribe` MCP server at `<base URL>/mcp` (Bearer auth).
- `hooks/hooks.json` → SessionStart hook (`hooks/scribe_session_context.sh`),
**fail-open**: if Scribe is unreachable it injects nothing and never blocks
the session.
- `skills/` → the universal process-skills, surfaced by description match.
## Notes
- Set a `version` bump in `.claude-plugin/plugin.json` per release so clients
pick up changes.
- The session-start hook needs only a **read**-scoped key; the MCP tools need
**write** scope to create/update.
+14
View File
@@ -0,0 +1,14 @@
{
"hooks": {
"SessionStart": [
{
"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\""
}
]
}
]
}
}
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Scribe plugin — SessionStart push channel.
#
# 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)
#
# FAIL-OPEN: any missing tool/config, network error, or unreachable instance
# injects nothing and exits 0. A session must never be blocked by Scribe.
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:-}
[ -n "$url" ] && [ -n "$token" ] || exit 0
q=""
[ -n "$project_id" ] && q="?project_id=${project_id}"
body=$(curl -fsS --max-time 8 \
-H "Authorization: Bearer ${token}" \
"${url%/}/api/plugin/context${q}" 2>/dev/null) || exit 0
text=$(printf '%s' "$body" | jq -r '.context // empty' 2>/dev/null) || exit 0
[ -n "$text" ] || exit 0
jq -n --arg c "$text" \
'{hookSpecificOutput: {hookEventName: "SessionStart", additionalContext: $c}}'
exit 0
+43
View File
@@ -0,0 +1,43 @@
---
name: using-scribe
description: Use at the start of any session and before answering questions about the operator's work or starting a task — establishes the Scribe-first reflex (recall before acting, load standing rules, update over duplicate, plan in Scribe not in files).
---
# Using Scribe
Scribe is the operator's self-hosted second brain (notes, tasks, projects,
milestones, events, typed entities) and rulebook, reachable through the bundled
`scribe` MCP server. Its value is mostly in what it **already holds** — so make
reading it a reflex, not something you wait to be asked for.
## The reflex
1. **Recall before acting.** Before answering a question about the operator's
work, or starting a task, `search` Scribe (and `list_tasks` / `list_notes`)
for prior art — an existing ticket, decision, or dev-log — instead of
re-deriving it or opening a duplicate. When a project is in scope, pass its
`project_id` so results stay scoped.
2. **Standing rules are binding.** The SessionStart context lists the operator's
always-on rule titles. Treat them as binding. Pull full text with
`list_always_on_rules()` or `get_rule(id)` when a rule is about to bite.
When a project is in scope, `enter_project(id)` also returns its applicable
rules.
3. **Update over duplicate.** When recording, prefer updating an existing
note/rule/task over creating a new one. Search first; revise what's there.
4. **Plans live in Scribe.** For non-trivial work call `start_planning(project_id,
title)` FIRST — the plan body + step checklist live in the `kind=plan` task,
progress goes in work-logs (`add_task_log`). Do not write plans/specs to local
`.md` files.
5. **Keep state honest.** Set a task `in_progress` when you start it, `done` the
moment it's complete; log progress as you go.
## Other Scribe process-skills
This plugin also ships focused process-skills — brainstorming, systematic
debugging, test-driven development, writing-plans, verification, receiving code
review. Reach for the matching one when its situation arises, the same way you
reach for this skill.