#755 Phase 4. Saved Scribe Processes (DRY pass, Drift Audit, …) now surface as
auto-triggered Claude Code skills instead of pull-only get_process calls.
Design correction vs the plan: stubs live in the USER's ~/.claude/skills/, NOT
plugin/skills/_instance/. The plugin is git-cloned and identical per install, so
instance-specific generated files can't ride in it; personal skills are
live-detected within the session (verified via claude-code-guide). MCP prompts
were the alternative but are pull-only (no relevance auto-surface), so skills are
the right primitive.
- backend: GET /api/plugin/processes manifest (services/plugin_context.
build_process_manifest) — {name, slug, description} per Process; description is
the auto-surface trigger (title + preview); slugs deduped, blanks skipped.
- plugin: scribe_sync_processes.sh writes ~/.claude/skills/scribe-proc-<slug>/
SKILL.md (body = "call get_process(name), follow verbatim") and PRUNES stale
scribe-proc-* stubs. Fail-open + silent; a transient fetch failure never wipes
existing stubs. Runs as a 2nd SessionStart hook + via the /scribe:sync command.
- plugin.json 0.1.7 → 0.1.8; README updated.
- tests: build_process_manifest (render, slug dedupe, blank-title skip, preview
truncation). Sync script's write+prune validated in isolation (plugin/** is not
CI-covered): correct stubs created, stale pruned, unrelated skills untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dashboard:
- 'Done recently' chip-cloud -> compact uniform list (Active-now row style),
showing 5 with inline expand to the rest (backend already returns up to 8).
- New 'Projects' rail card: each active project with 'N open · M done'.
Backend already computed done_count (dashboard.py) — now surfaced in the
/api/dashboard payload per active project.
MCP Access (Connect Claude / Claude Code):
- Progressive disclosure: lead with the pre-filled plugin-install snippet;
fold server name, scope, marketplace URL, and the MCP-only path into a
single 'Customize' expander. Desktop tab keeps its own server-name field.
- Marketplace URL now defaults to this instance's own repo via
config.PLUGIN_MARKETPLACE_URL (env-overridable); /api/plugin/marketplace-url
falls back to it, so the field + install snippet are pre-filled out of the
box instead of showing a generic placeholder.
Refs #761
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The SessionStart hook asked for a project_id via plugin userConfig, which pins
one install to a single project — wrong for an operator working across many
repos/projects. Resolve the active project server-side from the working repo's
git remote instead (a stable identifier, not a dir-name guess).
- repo_bindings table (migration 0064) + RepoBinding model: (user, repo_key) ->
project, FKs CASCADE.
- services/repo_bindings: normalize_repo_key collapses ssh/https/scp/creds/port/
.git to host/owner/repo; resolve/set/list/delete.
- GET /api/plugin/context takes ?repo=<remote>; unbound repo -> a "bind this
repo" hint with a ready bind_repo() call. project_id kept as manual override.
- MCP tools: bind_repo / list_repo_bindings / unbind_repo.
- Hook sends ?repo=$(git remote get-url origin) URL-encoded; all project_id
handling removed. plugin.json drops the project_id userConfig (0.1.2 -> 0.1.3).
- Tests: normalize equivalence classes + unbound-hint rendering.
Refs task 755 (Scribe-as-plugin push channel).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Settings install command had a <your-scribe-repo> placeholder — not
copyable. Add an instance-global 'plugin_marketplace_url' setting (admin sets
it to the app's own repo) that every user's MCP Access reads, so the
/plugin marketplace add command is copyable out of the box. Keeps it universal
(each deployment configures its own repo) rather than hardcoding one.
- services/settings.get_admin_setting(key): admin-scoped global read.
- routes/plugin: GET /api/plugin/marketplace-url (any user) + PUT (admin).
- SettingsView: Admin → 'Plugin marketplace' field to set it; MCP Access
marketplace field falls back to the configured value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>