1feef179d2fcfc125c79716dcfc5b30431a32b4d
33 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
51e5c22818 |
ci(plugin): gate plugin/** — the path that shipped two live defects
CI & Build / Plugin hooks (push) Failing after 2s
CI & Build / Python lint (push) Successful in 8s
CI & Build / integration (push) Successful in 31s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 55s
CI & Build / Build & push image (push) Successful in 20s
`plugin/` is not built into the image; installs fetch it from this repo via .claude-plugin/marketplace.json, so a push IS the release. It was absent from the workflow's `paths:` filter entirely, meaning plugin changes ran no CI at all. Two separate defects reached a live install through that gap: #2198 — all four hook scripts inert (lowercase userConfig env vars, line-oriented `jq -rR`, line-oriented `cut -c`) #2209 — the fix for #2198 couldn't reach an install because the manifest version wasn't bumped, so the installer never refreshed its cache Adds `plugin/**` + `.claude-plugin/**` to `paths:` and a `plugin` job running scripts/check_plugin.py: 1. `bash -n` on every hook. 2. The three known-bad patterns from #2198. Verified by replay against c569cdd^ — all three are caught. Narrow by design; see below. 3. Shipped plugin content differs from origin/main => the manifest version must differ too. Stated against the base branch, not per-commit, so a batch needs one bump rather than one per commit. Replayed against |
||
|
|
21cb9ee537 |
fix(plugin): bump to 0.1.19 so the hook fixes actually reach installs
|
||
|
|
c569cdd0eb |
fix(plugin): every hook's credential + URL encoding was broken
Three defects, each of which independently made a hook a no-op, and all three failing silently — which is why the whole dynamic side of the plugin looked "shipped" while doing nothing. 1. Wrong env var case. Claude Code exports userConfig to hooks as CLAUDE_PLUGIN_OPTION_<KEY> with the key UPPERCASED. All four hooks read CLAUDE_PLUGIN_OPTION_api_endpoint / _api_token, so both values were always empty. That killed the SessionStart dynamic tier, process sync, prompt auto-inject, and the write-path prior-art trigger at once. 2. jq -rR is line-oriented. `@uri` under -R encodes input LINE BY LINE, so a multi-line payload came back as several encoded lines joined by raw newlines — an invalid URL, curl fails, hook exits 0 in silence. Now -sRr. This one hid behind (1): auto-inject only ever worked for single-line prompts, and prior-art (which posts code, always multi-line) could never have worked at all. 3. cut -c1-1200 caps each LINE, not the payload, so the prior-art code budget wasn't a budget. Now head -c 1200. Also widens the SessionStart warning: "neither URL nor token arrived" used to be treated as a benign unconfigured install and stayed quiet. That is exactly the state defect (1) produced, so the one install state that most needed a signal was the only one that emitted none. It now says so, and names the two other features it silently disables. Verified against the live instance: dynamic rules + project context load, auto-inject surfaces #2192 on a multi-line prompt, and the write-path trigger returns the [here] place-arm hit on a multi-line edit with session dedup suppressing the repeat. Refs #2198, #2082 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs |
||
|
|
e0328f2b1c |
feat(plugin): write-path trigger — offer prior art before code is rewritten
CI & Build / Python lint (push) Successful in 4s
CI & Build / integration (push) Successful in 27s
CI & Build / TypeScript typecheck (push) Successful in 35s
CI & Build / Python tests (push) Successful in 47s
CI & Build / Build & push image (push) Successful in 1m2s
The milestone headline. Auto-inject fires on the operator's prompt; the moment reuse is actually lost is later, when the agent decides mid-task to write a helper. A PreToolUse hook on Write|Edit now fires there. Channel: `additionalContext` with NO permissionDecision, so the note reaches Claude beside the tool result and the write is never blocked — a recall aid must not be able to stop the operator's work. Plain stdout would have been invisible to the model, and deny/ask would have made a nudge into a gate. Two arms, different in kind: - BY PLACE — a snippet recorded at this path (or its directory) is prior art by definition, not resemblance, so it is neither scored nor thresholded. This is what #2083's reverse lookup was built to answer. - BY MEANING — semantic search restricted to snippets (new `note_type` filter on semantic_search_notes) over the code about to be written. Place ranks first; the top-k cap spans both arms. Gates carried over from milestone 93 verbatim: threshold, margin, session dedup, titles-never-bodies. Own `source='write_path'` in retrieval_logs so precision is tunable separately — the docstring records that the place arm is unlogged and hands that to #2085. Its own on/off in Settings but the SAME threshold/top-k: one "how loud may Scribe be" knob is easier to reason about than two that drift, and splitting them later is then a data-backed change rather than a guess. Details worth keeping: the hook sends a REPO-RELATIVE path because that is how locations are recorded; the git remote resolves to a project and is never used as the location `repo` filter (different namespaces, would silently match nothing); the endpoint stays a GET because a read-scoped API key cannot POST and every other hook depends on that. plugin.json 0.1.17 -> 0.1.18. Refs #2082, milestone #232. |
||
|
|
dd1b5e5ddb |
feat(snippets): reverse lookup — find snippets by repo/path/symbol
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 12s
CI & Build / integration (push) Failing after 27s
CI & Build / Python tests (push) Successful in 50s
CI & Build / Build & push image (push) Successful in 1m42s
"What canonical helpers already live in this file?" was unanswerable: location lived only in the body markdown. It is now a jsonpath containment query over the `notes.data` mirror added by migration 0070. - One predicate in two dialects in services/knowledge.py: SQL (`data @?`, applied in the browse arm and the keyword arm before count/pagination, so totals stay honest) and Python (`location_matches`, for the semantic arm which post-filters candidates it already holds). Both must change together. - Parts are ANDed within a SINGLE locations entry — repo A in one entry and path B in another is not "recorded at A/B". `path` also matches as a directory prefix, via jsonpath `starts with` rather than `@>`, which the same GIN index serves. - `repo`/`path`/`symbol` reach the service, the REST list and the MCP tool under one name with one default (rule #33); the MCP docstring teaches the place form, and so does the reusing-code skill (plugin.json bumped). - UI: a Location disclosure beside the snippet search, with its own empty state — "nothing kept there, so what you're about to write is new." Settles #2083's open question (pre-0070 NULL `data`) by backfilling after all: `backfill_snippet_data` runs at startup, deriving the mirror from the body with the same parser the read path trusts. 0070's caution was about mangling a hand-edited body; this never touches the body. The alternative was a permanent second body-regex arm, or a query that silently answers "nothing here" for an old snippet and gets the helper written twice. Refs #2083, milestone #232. |
||
|
|
04b58ce01e |
feat(acl): shared records are search-only and always labelled as someone else's
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 28s
CI & Build / integration (push) Successful in 35s
CI & Build / Python tests (push) Failing after 41s
CI & Build / Build & push image (push) Has been skipped
Narrows the |
||
|
|
b33e2a79c6 |
fix(snippets): close the recall-surface gaps found reviewing the Drafter
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 11s
CI & Build / integration (push) Successful in 19s
CI & Build / Python tests (push) Successful in 43s
CI & Build / Build & push image (push) Successful in 1m7s
Four defects from the 2026-07-25 review of the recall (#227) and merge (#231) milestones. The theme: a snippet could be recorded but not fully corrected, and the agent and web surfaces had drifted apart. - #2076 language was mis-derived from the first caller tag, so a snippet created with tags and no language read that tag back as its language — corrupting the tag set and the code fence on the next update. Only the FIRST tag can carry the language, since compose_tags emits [language, "snippet", *caller]. - #2077 MCP update_snippet mapped "" to "unchanged", so no field could ever be cleared and no snippet detached from its project. Now an omitted field is left alone, an empty string clears, and project_id follows the -1 = detach convention. A service-level UNSET sentinel keeps None available as the clear. - #2078 surface parity: adds delete_snippet (MCP had none, so a wrong snippet could not be retired by the agent that recorded it), locations on MCP create and update, system_ids through the REST routes and the editor, and the near-duplicate gate on REST create with a "record it anyway" escape. - #2079 project scoping: list_snippets takes project_id through the service, the MCP tool and the REST route, defaulting to every project — reaching across projects is the point when the helper you need was written elsewhere. Sharing the list across owners is deliberately NOT in here: query_knowledge is shared with the Knowledge browse surface, so widening it changes behaviour well beyond snippets. Left open on #2079 for a scope decision. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLwAaV4DQEmVyn496HnEvt |
||
|
|
85625de394 |
feat(scribe): merge_snippets — unify found one-off snippets into one canonical
CI & Build / Python lint (push) Successful in 4s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / integration (push) Successful in 38s
CI & Build / Python tests (push) Successful in 59s
CI & Build / Build & push image (push) Successful in 1m10s
Step 2 of the snippet-merge milestone (#231). The dedup gate only PREVENTS new near-duplicates; merge is the CURE for the ones already scattered. - services/snippets.py: merge_snippets(user_id, target_id, source_ids) — keep the target's scalar fields (name/when_to_use/signature/language/ code), union the sources' locations + extra tags onto it (so the survivor carries every call site as a location), trash the sources (recoverable), re-embed the survivor. Pure merge_snippet_fields() factored out for unit testing. Returns (survivor_note, merged_ids). - mcp/tools/snippets.py: merge_snippets(target_id, source_ids) tool (5th), and a create_snippet dedup-path nudge toward merge over a forced copy. - routes/snippets.py: POST /api/snippets/<id>/merge {source_ids} — share- aware (can_write target + every source, rule #78) with a same-owner guard (cross-owner merge is out of scope). - plugin reusing-code skill + MCP _INSTRUCTIONS: point found-duplicates at merge as the cure (rule #119 surfaces, not a Scribe rule). plugin.json 0.1.13 -> 0.1.14 in the same change (the #1040 marketplace-ship lesson). - Tests: pure merge-helper union/dedup; MCP tool (requires a source, survivor+merged_ids, not-found); route handler + 5-tool registration. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pa2EsuB54BuWQ8GfJq9c7t |
||
|
|
0ea3bff797 |
feat(scribe): snippet recording nudge — reusing-code skill + MCP/SessionStart guidance
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Successful in 31s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 55s
CI & Build / Build & push image (push) Successful in 1m10s
Step 5 of the Drafter recall milestone (#227): teach agents the two snippet reflexes — search recorded snippets before writing a new helper/util/component, and record something reusable the moment it's built — via the app's own instruction surfaces, not a Scribe rule (project rule #119). All instance-agnostic (rule #115). - plugin/skills/reusing-code/SKILL.md: new auto-surfacing process-skill covering both reflexes (recall-before-rebuild + record-when-reusable). - src/scribe/mcp/server.py: a Snippets paragraph in the MCP _INSTRUCTIONS. - plugin/hooks/scribe_static_context.md: a "reuse before rebuilding" bullet in the SessionStart static context. - plugin/.claude-plugin/plugin.json: version 0.1.12 -> 0.1.13 in the same change so the autoUpdate marketplace ships it (the #1040 lesson); description skill list updated. - plugin/README.md: trued the process-skill list to what actually ships. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pa2EsuB54BuWQ8GfJq9c7t |
||
|
|
ab9b5b647c |
docs(scribe): true up plugin + MCP docs after calendar/entity removal
Milestone #194 plugin/docs slice. - plugin.json: "second brain" framing -> "system-of-record"; bump 0.1.11 -> 0.1.12 (any plugin/ change must bump the manifest, issue #1040) - plugin/README.md + using-scribe SKILL + scribe_static_context: drop events/typed-entities from the surface lists; "second brain" -> "system of record" in the SessionStart context Claude reads each session - docs/api-keys-and-mcp.md: drop the Typed-entities + Events MCP tool rows, add the Systems row - README.md: rewrite the stale front-matter (chat/RAG/calendar/weather/push described a pre-pivot product) to the current Claude-driven work store Deeper pre-pivot doc-rot in docs/features.md + docs/api-reference.md (chat/journal/weather/web-research) is tracked separately as an issue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BPtbSzA4JLMAKgFZ8VTg7Q |
||
|
|
f6629d4bcf |
fix(plugin): keep always-on rules alive across compaction (0.1.10 → 0.1.11)
Always-on rules were on-demand, not always-present: Tier-1 static context only
tells the agent to call list_always_on_rules(), and Tier-2 dynamic fetch is dark
(token doesn't reach the hook subprocess). On compaction the fetched rules get
summarized away while the harness's own built-in git instruction ("branch first")
survives in the base prompt — so post-compact the generic git instinct wins and
rule #1 ("dev is home") is missed.
- scribe_static_context.md: new "Operator rules govern consequential actions"
bullet — before any git branch/commit/push or hard-to-reverse action, loaded
rules beat generic harness/default habits; re-pull rules if not loaded or
summarized by a compaction. Tier 1 = always fires, keyless, re-fires on compact.
- scribe_session_context.sh: compaction banner now re-pulls list_always_on_rules(),
not just enter_project().
- plugin.json: 0.1.10 → 0.1.11 so autoUpdate ships the plugin/ change (#1040).
Generic and instance-agnostic per rules #115/#119 — no operator-specific rule
text hardcoded. Refs issue #1197.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E4bNefPFAz7esmMZMZmkzL
|
||
|
|
2bc054d7ef |
fix(plugin): bump version 0.1.9 → 0.1.10 to ship auto-inject hook
Path A's UserPromptSubmit hook (scribe_autoinject.sh) + hooks.json were merged to main in PR #74 but the plugin version was never bumped, so the autoUpdate marketplace (keyed by version string) never re-pulled the snapshot — the hook was stranded, uninstallable, and not running in any session. Bumping the version is what makes installs detect and pull it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz4j1H7pjYSjKsEpgcNH5E |
||
|
|
eec241d3c0 |
feat(plugin): sharpen the recall-before-acting reflex in static context
Turn the SessionStart static guidance into a concrete recall trigger — search Scribe before answering about the operator projects/people/places/decisions or starting a task, and pass the active project id to scope results — instead of a vague "search for related work". Step 4 (pull-path sharpening); the cross-encoder rerank half is deferred until auto_inject telemetry shows precision is the bottleneck. Scribe: project 2, milestone 93, task 1034. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz4j1H7pjYSjKsEpgcNH5E |
||
|
|
8126db3203 |
feat(plugin): knowledge auto-inject (Path A) — title-first per-turn awareness
CI & Build / Python lint (push) Successful in 2s
CI & Build / integration (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 21s
CI & Build / Python tests (push) Successful in 43s
CI & Build / Build & push image (push) Successful in 55s
New UserPromptSubmit hook (scribe_autoinject.sh) + GET /api/plugin/retrieve that surface the TITLES (never bodies) of the few notes clearing four anti-bloat gates: a per-user confidence threshold (stricter than pull search), a margin gate, per-session dedup (exclude_ids), and a top-k ceiling. Each retrieval is logged to retrieval_logs as source=auto_inject so the threshold can be tuned from data. Per-user config (enable / threshold / top-k) is DB-backed via /api/settings with a Settings UI card; defaults enabled, threshold 0.55, top-k 3 (conservative — tune once auto_inject telemetry accrues). Scribe: project 2, milestone 93, task 1033. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz4j1H7pjYSjKsEpgcNH5E |
||
|
|
5fbee18a94 |
feat(mcp): S5 — issue-kind guidance across all instruction surfaces
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 46s
CI & Build / Build & push image (push) Successful in 1m3s
Plan #825 (T2 — Issues task_kind) shipped S1–S4 but its S5 docs slice never landed, so every behavioral surface the plugin pushes to the agent still described the pre-kind convention ("tag `issue`" on a create_note). Result: agents fixed bugs without reaching for kind=issue and dumped the work as logs on unrelated open tasks. - _INSTRUCTIONS: rewrite the "record a problem" bullet to create_task(kind="issue") with symptom→cause→fix + arose_from_id / system_ids, and an explicit "not a work-log on an unrelated task"; add Issue + System to the hierarchy section. - skills/systematic-debugging, verification: drop "tag `issue`" / create_note-issue, point at create_task(kind="issue"). - skills/using-scribe: add issues/systems to the entity list + reflex #6. - hooks/scribe_static_context: fix → its own issue on the keyless floor. Instance-agnostic, prose-only; no schema or tool-behavior change. Pairs with always-on rule #118. Issue: #855. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ee02ed37c1 |
feat(plugin): compaction-hygiene guidance — recommend safe compaction at seams
#834. The pre-compaction complement to the shipped post-compaction re-grounding banner. Because Scribe records progress as you go (task status, work-logs, decision notes), a compaction at a clean work-seam is lossless — so guide the model to recommend it proactively rather than letting auto-compact fire mid-task. Placed in the ALWAYS-loaded channels (operator wants it consistently in context, not relevance-gated like a skill): MCP _INSTRUCTIONS (every handshake) + the static SessionStart floor (every session, MCP-independent). Behavior: at the end of a block of work in a long session, ensure in-flight state is logged, then tell the operator it's a safe moment to /compact (naming what was logged); recommend at seams, not every turn; the model can't run /compact itself. plugin.json 0.1.8 → 0.1.9 so clients re-pull the static-context change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
33f9a0a4d4 |
feat(plugin): Phase 4 — Scribe Processes auto-surface as local skills
#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> |
||
|
|
f7742173aa |
chore(plans): make kind=plan retirement consistent across MCP, REST, UI, skills
Audit of the plugin + MCP surface after milestone-as-plan (T3): every path that could still create a kind=plan task or describe the old plan-task model is now aligned with the hard-retire decision. - create_task (MCP + REST POST /api/tasks): reject kind=plan with a message pointing to start_planning. The 'plan' enum value stays valid so legacy plan-tasks remain readable; update paths never touch kind, so they round-trip. - create_task / get_task docstrings: 'plan' dropped from creatable kinds; get_task's rules-augmentation noted as legacy-only (get_milestone for new plans). - skills/writing-plans: rewritten for milestone-as-plan (body = design, steps = child tasks, get_milestone to read back). - skills/using-scribe: "plans live in milestones via start_planning", not kind=plan. - TaskEditorView Kind selector: offers Work/Issue; "Plan (legacy)" shown only when the loaded task is already kind=plan (display round-trip). - test: create_task rejects kind=plan. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
88106309f4 |
feat(plugin): add 4 Scribe-native process-skills (restore superpowers gap)
Superpowers was uninstalled but its replacements were never built (only using-scribe shipped) — a live functional hole. Author the 4 the operator wants back, each integrated with Scribe's toolset rather than generic copies: - writing-plans -> start_planning / kind=plan task, not local .md - systematic-debugging -> capture issue (symptom->cause->fix, tag issue) on resolve - verification -> log results to the task work-log; honest done - brainstorming -> recall prior thinking first; capture the decision note Skipped TDD + receiving-code-review per operator (well-covered by Claude/them). Manifest + using-scribe list now advertise only the 4 that ship. Remove the stale docs/superpowers/*.md reference in _INSTRUCTIONS (superpowers is gone). Plugin 0.1.6 -> 0.1.7. Refs plan 821 (Phase 3 of 755). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d99c4e3c15 |
feat(plugin): compaction re-grounding in SessionStart hook (A7)
Deliver 'don't silently lose work at compaction' via the mechanism that actually works. Verified contract: a PreCompact hook CANNOT make the model flush to Scribe (host hooks can't trigger model tool calls, and can't know the in-flight task ids), and its additionalContext only shapes the one-shot summary. The correct tool is SessionStart scoped to source=compact, which fires AFTER compaction and injects context the model reads. Our SessionStart hook is matcher-less, so it already fires on compact — it just said nothing compaction-specific. Now it reads the stdin event and, when source==compact, leads with a banner telling the model to reload the active project + in-flight tasks from Scribe and reconcile half-remembered state. Durable path = record-as-you-go (A4/B8) + this post-compaction reload. Refs plan 812 (A7); supersedes the literal 'PreCompact hook' idea. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
700cfc664b |
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>
|
||
|
|
95e1d47ceb |
ref(mcp): neutralize dev-shaped vocabulary in _INSTRUCTIONS + add write-mandate (B9/A4)
The MCP instructions are domain-neutral except a thin layer of dev vocabulary and one project-specific paragraph (B10 audit, task 812). Make the data store's own instructions serve any domain, and add the missing positive write-mandate. B9 (neutralize): - 'before writing code' -> 'before you dive in' - Note examples 'dev-logs' -> 'logs of what happened' - record trigger 'a merge, a shipped feature, a finished plan' + 'dev-log note' -> 'finishing a task, or hitting/discovering a problem that changes direction' (folds in B8: log pivots, not just wins; mirrors the static-tier wording) - recall examples 'ticket/dev-log' -> 'task/prior note' (server + SKILL.md) - 'Engineering and workflow rules' -> 'Workflow and standards rules' - slim the 'developing Scribe itself' ACL paragraph to a neutral one-liner (project-specific specifics already live in rules #47/#78) A4 (write-mandate): state up front that Scribe is the system of record — record work here, recall before acting, don't keep project work in local files. Refs plan 812 (B9, A4, B8-trigger); B10 audit work-log. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f2ab02ba2b |
ref, plugin: neutral, concrete triggers in static SessionStart mandate
Plain-language 'related prior work' instead of 'prior art'; replace the dev-shaped 'meaningful landing (a merge, a shipped feature, a finished plan)' with concrete neutral triggers — log on task completion and when a problem is found, so direction pivots are captured, not just successes. Keeps the static mandate domain-neutral (pre-empts B9 drift in plan 812). Refs task 809 / plan 812 item A1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d11eb9145b |
feat(plugin): two-tier SessionStart hook — static keyless floor + dynamic enrichment
The SessionStart push channel was single-tier: it curled /api/plugin/context with a Bearer token and, on any failure (missing/unexported token, network error, missing curl), injected nothing and exited 0 — silently. A known upstream Claude Code gap (sensitive userConfig not reliably exported to hook subprocesses) trips this routinely, so a fresh session gets no signal to reach for Scribe and falls back to local file-memory (root cause of unlogged work on remote/rc sessions). Split into two tiers: - Tier 1 (static, keyless, networkless, always fires): inject bundled scribe_static_context.md — the load-bearing behavioral mandate. Cannot be suppressed by the upstream key bug. - Tier 2 (dynamic, best-effort, fails open): existing curl for live rules + active-project context, appended below the static block. Lights up as enrichment once the key reaches the hook. Only jq is now required (JSON envelope); curl/token gate the dynamic tier only. Bump plugin 0.1.5 -> 0.1.6 so clients pick up the change. Refs milestone 55; task 809; decision note 810. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9eddb8497c |
feat(plugin): Scribe replaces native memory by instruction; tighten project-scope discipline
Direction change (operator, see plan task #755 work-log): the plugin must NOT depend on disabling a native Claude function to work. It earns its place by steering behavior, not by toggling autoMemoryEnabled. Memory doctrine (no dual-write): - using-scribe SKILL.md gains "Scribe holds these functions — don't keep a second copy": route rules/recall/planning to Scribe, don't also write them to native auto-memory, never instruct disabling a native function, and accept a "Scribe-shaped hole" if the plugin is removed (recover over time). - mcp/server.py _INSTRUCTIONS: drop the paragraph that told the model to create/refresh a "rules live in Scribe" pointer in CLAUDE.md / ~/.claude memory. That was an active dual-write instruction; the SessionStart hook is the bridge now. Replaced with the no-dual-write / no-settings-dependency doctrine. Supersedes plan #755 Phase 6 ("set autoMemoryEnabled:false"). Project-scope discipline (stop cross-project bleed): - using-scribe SKILL.md gains "Stay inside the active project's scope": pass project_id to every read, only reference/offer work on the in-scope project, ask before switching. - _INSTRUCTIONS scope bullet extended from reads to referencing/offering, and flags get_recent as cross-project. - get_recent docstring gains a scope note steering to scoped list_* when a project is active. plugin.json 0.1.4 -> 0.1.5 so clients' caches actually refresh (re-shipping under the same version does not bust the cache). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2f9c9b0e0b |
chore(plugin): bump to 0.1.4 so clients pick up the using-scribe rule-scope update
SKILL.md gained the 'Where a new rule goes' section (rule-scope model) in
|
||
|
|
50b6902fe2 |
docs(mcp): encode rule-scope model in rulebook tool descriptions
Make the always-on / subscribed / project-rule distinction explicit at the authoring surface so it can't silently regress (for this operator or other users). Previously the tools said only 'cross-project rulebook rule' and a bare 'subscribe a project' — nothing steered project-specific detail away from shared rulebooks, which is how a Scribe-pinned rule ends up binding every family project. Principle encoded in 5 places: a rule's home is chosen by WHO it should bind, and both rulebook tiers are SHARED so their rules stay general — they differ in reach (all projects vs opt-in by theme), not generality. Project-specific detail goes in create_project_rule. - server.py MCP instructions: add the 3-tier authoring principle - create_rule / create_rulebook / create_project_rule / subscribe_* docstrings - using-scribe SKILL.md: a 'Where a new rule goes' note for the pull path Refs #755 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1983e8f4b1 |
feat(plugin): make using-scribe skill actively pull standing rules
The SessionStart push channel cannot reliably deliver a sensitive API token to the hook subprocess (upstream Claude Code bug anthropics/ claude-code#62442 — sensitive plugin userConfig is not persisted and is absent on a normal session). Stop depending on that push for standing rules: make the using-scribe bootstrap skill own the load instead. - description: name the FIRST ACTION (list_always_on_rules + enter_project when a repo/project is in scope) so it auto-surfaces at session start - add a 'Do this first' block instructing an active pull; demote the SessionStart hook to a bonus, not a precondition (it fail-opens and may be absent) - reflex step 2: rules come from list_always_on_rules(), not from an assumed SessionStart injection The hook + hooks.json are left in place: they fail-open and resume adding value automatically if #62442 is fixed or the token is made non-sensitive. Refs #755 (Phase 1: push channel descoped to optional; pull is load-bearing) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8fe571e175 |
feat(plugin): resolve session project from git remote, not a pinned project_id
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> |
||
|
|
651119cfb0 |
fix(plugin): read API token from CLAUDE_PLUGIN_OPTION env, not user_config placeholder
The SessionStart push-channel hook passed the key via
SCRIBE_TOKEN="${user_config.api_token}" in hooks.json, but api_token is
sensitive:true. Claude Code keeps sensitive userConfig in the keychain and
does not interpolate it into hook command strings (only into mcpServers
headers), so the hook received the literal placeholder, sent it as the Bearer
token, and the context endpoint 401'd -> fail-open -> no context injected.
Read the harness-exported CLAUDE_PLUGIN_OPTION_<key> env vars instead (SCRIBE_*
still override for the settings.json dogfooding path), and treat any unexpanded
${...} literal as unset so the hook fails open cleanly instead of 401-ing.
Bump 0.1.1 -> 0.1.2 so installs refresh the cache.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
ff91948fa3 |
chore(plugin): bump to 0.1.1 so clients pick up the mcpServers fix
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
559f70eef0 |
fix(plugin): inline mcpServers in plugin.json so the MCP server actually loads
/reload-plugins reported '0 plugin MCP servers'. Root cause: plugin.json had
"mcpServers": "./.mcp.json" — a string path, which is neither a valid inline
object nor a recognized reference (per docs, plugin MCP servers are a root
.mcp.json OR an inline object in plugin.json), so it parsed to zero servers.
Inline the mcpServers object directly in plugin.json and remove the separate
.mcp.json. The user_config substitution syntax was already correct
(plugins-reference: values substitute as ${user_config.KEY} in MCP configs).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
9924f873b9 |
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>
|