CI & Build / Plugin hooks (push) Successful in 18s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m23s
CI & Build / Python tests (push) Successful in 2m0s
CI & Build / Build & push image (push) Successful in 36s
moment_actions.resolve(tool, input) names every moment a call reaches and the action that reached it. One call can reach several: kubectl apply is a run, a deliver and a reach outside the workspace. Command tools match by how each segment of the line starts, with a word boundary; other tools by field=value arguments. The MCP server prefix and case are ignored. 56 shipped defaults cover the harness tools, Scribe tools and common command shapes. moment_mappings (migration 0116) holds what an install adds and the defaults it switches off. A removal is a stored row, so an upgrade does not switch the default back on. Per the operator ruling, corrections happen in the session: map_action and unmap_action (write tools) return now_reaches so the fix can be confirmed in the same reply. list_moments now shows each moment's actions on this install. REST mirrors both doors, recorded as human. Backup v21 carries the mappings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
112 lines
5.0 KiB
Python
112 lines
5.0 KiB
Python
"""Moments as MCP tools: the catalog, and the in-session corrections to it (milestone 458).
|
|
|
|
A session needs the vocabulary in hand to mount a rule, to read a line that
|
|
names a moment, and to fix a misfire — and the operator's ruling is that the
|
|
fix happens in the session, not on a settings page:
|
|
|
|
"making the user leave the session to fix a misfire is not desirable and
|
|
the llm session should be able to offer corrections"
|
|
|
|
So the mapping writes are tools, built to be offered mid-work and made on the
|
|
operator's yes.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from scribe.mcp._context import current_user_id
|
|
from scribe.services import moment_actions as actions_svc
|
|
from scribe.services import moments as moments_svc
|
|
|
|
|
|
async def list_moments() -> dict:
|
|
"""The moments of work that rules mount on, and which actions reach each one here.
|
|
|
|
A rule mounted on a moment arrives whenever that moment happens, whatever
|
|
the words of the work look like. That is how a rule reaches you when it is
|
|
about WHEN something is done rather than WHAT it is about: a rule on
|
|
finishing work belongs at `work.finish` and `reply.report`, where nothing
|
|
said needs to resemble it.
|
|
|
|
Read this when you are about to mount a rule, when a line you were shown
|
|
names a moment and you want its meaning, or when an action reached the
|
|
wrong moment — or none — and you are about to correct it with
|
|
`map_action` / `unmap_action`.
|
|
|
|
Names are `<area>.<verb>`; a named procedure (a skill or a stored process)
|
|
is its own moment, `skill.<name>`, listed under `families`. Each moment
|
|
says what is happening at it (`means`) and the kinds of action that
|
|
typically reach it (`reached_by`).
|
|
|
|
`actions` maps each moment to the actions that reach it on this install:
|
|
`via: "default"` ships with the product, `via: "install"` is this
|
|
install's own mapping. `removed_defaults` are shipped defaults this install
|
|
switched off.
|
|
"""
|
|
out = moments_svc.catalog()
|
|
out.update(await actions_svc.actions_by_moment(current_user_id()))
|
|
return out
|
|
|
|
|
|
async def map_action(tool: str, moment: str, match: str = "", reason: str = "") -> dict:
|
|
"""Make an action reach a moment on this install — the in-session fix for a missed moment.
|
|
|
|
Use it when an action plainly happened at a moment and the moment did not
|
|
fire: the operator ships with `make ship`, and nothing mounted on
|
|
`work.deliver` arrived. Offer the mapping when you notice, in one line
|
|
("that `make ship` was a deliver and nothing fired — map it?"), and make it
|
|
on their yes. The correction lasts: every later session on this install
|
|
gets it.
|
|
|
|
Args:
|
|
tool: the tool as the harness names it — `Bash`, `Edit`,
|
|
`update_task`. An MCP server prefix is ignored.
|
|
moment: a moment from `list_moments`, or `skill.<name>`.
|
|
match: which calls of the tool. Empty = every call. For a tool that
|
|
runs a command, how the command starts (`make ship`, `./deploy.sh`)
|
|
— it is checked against each part of a compound command line. For
|
|
any other tool, its arguments as `field=value` pairs, comma-separated
|
|
(`status=done`).
|
|
reason: what misfired, or what this action is for here. Optional; it
|
|
is shown beside the mapping when someone reviews it later.
|
|
|
|
Returns the change made and `now_reaches`: every moment that action
|
|
reaches after the change, so you can confirm it in the same reply.
|
|
Mapping a shipped default this install had switched off switches it back
|
|
on.
|
|
"""
|
|
return await actions_svc.map_action(
|
|
current_user_id(), tool, match, moment, reason=reason, actor="model",
|
|
)
|
|
|
|
|
|
async def unmap_action(tool: str, moment: str, match: str = "", reason: str = "") -> dict:
|
|
"""Stop an action reaching a moment on this install — the fix for a moment that fires wrongly.
|
|
|
|
Use it when a moment fires on an action that is not that moment here — a
|
|
`curl` to a local test server reaching `env.reach`, say — and the rules it
|
|
brings are noise every time. Offer it when you notice, and make it on the
|
|
operator's yes.
|
|
|
|
The arguments name the mapping exactly as `list_moments` shows it. This
|
|
install's own mapping is removed; a shipped default is switched off for
|
|
this install only, and stays off across upgrades. `map_action` with the
|
|
same arguments switches a default back on.
|
|
|
|
Args:
|
|
tool: the tool as `list_moments` names it.
|
|
moment: the moment it should stop reaching.
|
|
match: the mapping's match, as listed (empty for a whole-tool mapping).
|
|
reason: why it misfires here — worth giving for a default, since it is
|
|
the record of why this install differs from the product.
|
|
|
|
Returns the change made and `now_reaches` for that action.
|
|
"""
|
|
return await actions_svc.unmap_action(
|
|
current_user_id(), tool, match, moment, reason=reason, actor="model",
|
|
)
|
|
|
|
|
|
def register(mcp) -> None:
|
|
mcp.tool(name="list_moments")(list_moments)
|
|
mcp.tool(name="map_action")(map_action)
|
|
mcp.tool(name="unmap_action")(unmap_action)
|