Files
FabledScribe/src/scribe/routes/plugin.py
T
bvandeusen 3ab16fcbdb
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 35s
CI & Build / Python tests (push) Successful in 49s
CI & Build / Build & push image (push) Successful in 1m6s
feat(plugin): add /api/plugin/context push-channel endpoint + dogfood hook
Phase 1 of plan #755 (Scribe-as-plugin). Gives Scribe its own session-start
push channel so always-on rules + active-project context surface without being
asked — the gap behind 'I have to prompt for everything'.

- services/plugin_context.build_session_context: renders always-on rule titles
  grouped by topic (under the 10k additionalContext cap; full text stays one
  list_always_on_rules/get_rule call away) + optional project goal/open-task
  count + a recall/update-over-create reflex line. Capped at 9000 chars.
- routes/plugin GET /api/plugin/context (login_required already accepts Bearer
  fmcp_ keys; read scope suffices).
- tests: titles-not-statements, project scoping, length cap (pure mocks).
- scripts/scribe_session_context.sh: dogfood SessionStart hook, fail-open,
  reads url+token from .mcp.json. Superseded in Phase 2 by the plugin-bundled
  hook using userConfig.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 22:43:08 -04:00

33 lines
1.2 KiB
Python

"""Scribe-plugin support endpoints.
Consumed by the Scribe Claude Code plugin (not the web UI). `GET /api/plugin/
context` is curled by the plugin's SessionStart hook to push the operator's
always-on rules + active-project context into the session — Scribe's own push
channel. Auth is the standard `@login_required` path, which accepts a `Bearer
fmcp_<key>` API key (a read-scoped key suffices for this GET).
"""
from __future__ import annotations
from quart import Blueprint, g, jsonify, request
from scribe.auth import login_required
from scribe.services import plugin_context as plugin_ctx_svc
plugin_bp = Blueprint("plugin", __name__, url_prefix="/api/plugin")
@plugin_bp.get("/context")
@login_required
async def session_context():
"""Return SessionStart context (always-on rules + optional project).
Query: project_id (optional int) — when set, includes that project's goal
and open-task count. The hook passes the bound project's id here.
"""
try:
project_id = int(request.args.get("project_id", 0) or 0)
except (TypeError, ValueError):
project_id = 0
result = await plugin_ctx_svc.build_session_context(g.user.id, project_id)
return jsonify(result)