Skills¶
Skills are reusable prompt packages loaded from SKILL.md files.
mevedel-skills-core.el owns the skill model, discovery, persisted enablement,
session installation, path-scoped activation state, and hot reload.
mevedel-tool-skills.el registers the model-visible Skill tool schemas.
mevedel-skills-preparation.el owns argument substitution and body injection
execution. mevedel-skills-syntax.el owns shared authored Markdown and
required-skill syntax. mevedel-skills-invoke.el owns request-scoped context, preparation
policy, invocation, fork dispatch, and model-tool behavior.
mevedel-skills-input.el owns user token binding, raw gptel-send dispatch,
inline attachment projection, and direct fork response insertion.
mevedel-skills-plan.el owns deterministic composer invocation plans.
mevedel-mention-bindings.el owns the atomic text-property lifecycle shared
with direct reference, file, and MCP mentions; skill resolution remains in the
skill modules.
mevedel-turn.el owns the canonical terminal transaction shared by
ordinary responses and direct fork completion.
mevedel-skills-prompt.el owns prompt roster rendering, event-shaped reminders,
and path-activation notices. mevedel-skills-ui.el owns local commands, the
skills cockpit, command/skill completion, font-lock, and send-dispatch
composition.
Roster-render telemetry (skills-roster-advertised) is emitted only during
detailed profiling. Rendering can also be requested for prompt sizing or an
unchanged retained-context observation, so it does not prove a new delivery.
Plugin ownership follows its existing trust boundaries:
mevedel-plugin-registry.el owns manifests, discovery, activation state, and
hook consent; mevedel-plugin-lifecycle.el owns managed Git installation,
update, and removal; mevedel-plugin-ui.el owns the cockpit and /plugin
parser. mevedel-plugins.el retains only the session/workspace projection used
by skill discovery and cockpit refresh.
Skill flow¶
Reading SKILL.md through Read returns source text. It does not perform
argument substitution, dependency preparation, configured fork dispatch or
invocation policy handling. A user can ask the model to consult textual guidance
without invoking a workflow; that is distinct from prepared Skill execution.
A retained instruction or marker in the transcript proves neither invocation
nor current hook/permission state.
flowchart TD
A[Scan skill directories] --> B[Parse SKILL.md frontmatter]
B --> C[Register visible skills]
C --> D{Invocation source}
D -- Composer $skill --> E[Bind and build command/instruction plan]
E --> F[Prepare unique skills sequentially]
F --> G[Run UserPromptExpansion per skill]
G --> H[Run UserPromptSubmit on complete inert prompt]
H --> I{Leading fork command?}
I -- No --> J[Dispatch one model request]
I -- Yes --> K[Dispatch one sub-agent]
D -- Model Skill tool --> L[Prepare one command]
L --> M{Context}
M -- Inline --> N[Return expanded body]
M -- Fork --> O[Dispatch one sub-agent]
Browser skill selections use the same planner with explicit skill names and
literal message arguments. Only the selected prefix is resolved; a $skill
inside the message does not acquire invocation authority. The collaboration
host checks each selected name at receipt and queued delivery. See
browser collaboration.
Discovery¶
Skills are scanned from configured user/project/managed/plugin dirs plus
bundled skills under skills/ when mevedel-skills-include-bundled is
non-nil. The bundled frontend skill preserves product UI guidance outside
the shared baseline. Its description is discoverable with the other skills;
the body loads through Skill(name="frontend") or an explicit $frontend
invocation, without automatically activating for every source-file read.
The default search order is .mevedel/skills/,
.agents/skills/, skills/ under mevedel-user-dir (default
~/.mevedel/), then ~/.agents/skills/.
Unique names stay unqualified. When non-plugin skills from different
sources share a name, all colliding entries are exposed with the shortest
deterministic prefix that disambiguates them, such as mevedel:review,
agents:review, local:review, global:review, or
bundled:review. If all four ordinary roots contain the same name, the
visible names are local-mevedel:review, local-agents:review,
global-mevedel:review, and global-agents:review. Same-source
duplicates keep the first entry.
Recursive discovery does not follow directory symlinks. A linked skill tree must be configured as its own explicit root; repository links cannot escape a configured root or create an ancestor cycle during scanning.
Parsed frontmatter is cached by source file identity, modification time, and size. Changed files are parsed again on refresh. Content supplied directly by a caller, including hook snapshots, bypasses the file cache because the file's fingerprint does not identify those bytes.
Plugin skills are discovered from enabled .codex-plugin/plugin.json
manifests. A manifest skills path is resolved relative to the plugin
root and scanned with source plugin. User-facing plugin skill names
are prefixed with the plugin name from the manifest, so
skills/brainstorming/SKILL.md in the superpowers plugin appears as
superpowers:brainstorming in $ completion, the Skill tool listing,
and direct Skill(name=...) calls. The on-disk SKILL.md name remains
unchanged. New installs live under ~/.agents/plugins/, including
GitHub installs below github.com/OWNER/REPO. Plugin roots are
discovered from workspace .mevedel/plugins/, workspace
.agents/plugins/, global ~/.mevedel/plugins/, global
~/.agents/plugins/, then mevedel-plugin-extra-roots. Plugin
activation is workspace-scoped: plugins are inactive by default in each
project until enabled, and the state is stored in that workspace's
.mevedel/plugins.el. Activating a plugin enables all implemented plugin
components for that workspace; plugins with executable hooks require a
concise consent summary before those hooks are enabled. The summary shows
the plugin identity/source, exposed skills, each executable hook handler's
event and matcher, and the workspace plugin data directory rather than dumping
the full manifest.
Every plugin-state write replaces the file through a rename, so an interrupted write leaves the previous activations and hook consent intact rather than a truncated file, which the reader would treat as no state at all. Two Emacs instances writing the same workspace still resolve last-writer-wins on the whole file, not per entry.
For a remote workspace, plugin-state mutations additionally require the live session's lease and use the same disclosed, serialized publication path as other durability-critical state. They never write through TRAMP before storage disclosure or from a read-only inspector.
When multiple plugin roots contain the same manifest name, mevedel keeps
the highest-precedence plugin and reports shadowed duplicates in
/plugin list. Precedence is workspace .mevedel/plugins/, workspace
.agents/plugins/, global ~/.mevedel/plugins/, global
~/.agents/plugins/, then mevedel-plugin-extra-roots. Activation is
bound to the plugin name and source root, so a higher-precedence plugin
with the same name does not silently inherit enabled state from a
different source. /plugin list reports the conflict so the user can
consciously enable the winning source.
Activation survives updates when the plugin name and source root stay the
same. Executable hook consent is tied to a fingerprint of the hook surface;
when an update changes hook files, events, matchers, commands, or functions,
skills remain enabled but hooks require consent again before running.
Plugin runtime data stays workspace-scoped and keyed by plugin name, so
switching an activation between roots with the same manifest name reuses
the same <workspace>/.mevedel/state/plugin-data/<plugin-name> directory.
Bundled skills currently include:
review— bundled forked code-review skill. The first-class/reviewcommand uses the same reviewer contract but dispatches its task directly so user/project skills namedreviewcannot override the review workflow. A skill namedreviewremains selectable through its generated visible name, for examplelocal:reviewormevedel:review.git-worktree— model-only guidance for checking Git worktree isolation and mirroring the/worktreedefaults when explicit model-driven fallback is needed.analyze-log— user-invocable gptel HTTP log analysis helper.frontend— guidance for complete frontend implementations across mobile and desktop.clean-work— user-only housekeeping for confirmed obsolete shared working material, using normal ApplyPatch permissions and review.learn— user-invocable durable write-back helper. Repository-derived contributor facts go to the nearest applicableAGENTS.md; reusable user, feedback, enduring project, and reference knowledge follows the memory save policy. Tasks, deadlines, blockers, progress, and decisions stay in trackers or maintained documentation; no qualifying findings is a successful no-change result. Relevant files discovered underwork://sharedandmemory://journal/digests supply additional evidence; the skill retains attribution, verifies cheap claims, and acknowledges relevant missing captures without treating repeated summaries as independent evidence.artifact— user-invocable base skill for project artifacts. Owns the artifact store lookup, the new-artifact convention (one id directory per artifact), and the self-contained/size rules every artifact must obey; the other artifact skills attach it rather than restating them.artifact-design— design fundamentals for artifact HTML: palette, typography from system stacks, both themes, layout, and copy. Attached by the artifact leaf skills rather than invoked directly.artifact-diagramming— when a diagram earns its place, and the hand-authored inline SVG mechanics that keep it legible in both themes. Attached by the artifact skills that carry visuals.artifact-doc— user-invocable document artifact: a typeset memo, proposal, plan, spec, or meeting-notes page.artifact-dashboard— user-invocable dashboard artifact: KPI tiles, a chart drawn by a bundled spec-driven renderer, and a breakdown table.artifact-data-table— user-invocable data-table artifact: a sortable, filterable table over a JSON dataset embedded in the page.artifact-explainer— user-invocable explainer artifact: a numbered-step or sectioned walkthrough pairing prose with a visual at each stage.shared-whiteboard— user- and model-invocable layout and styling guidance for native shared whiteboards, including substantial edits. Uses Shared tools, preserves established board styles, and offers an Excalidraw-style sketch treatment or a polished warm-neutral/blue treatment with shared composition and checking guidance; does not attach the HTML-specific artifact skills.
The four artifact leaf skills each build from a template.html beside their
SKILL.md, reached through ${MEVEDEL_SKILL_DIR}, and attach artifact plus
artifact-design; artifact-explainer additionally attaches
artifact-diagramming. Together they are the worked example of the
required-attachment contract in bundled skills: the bases carry the rules once
and every leaf inherits them. Because a model-origin root requires every
descendant to be model-invocable, none of the bases may take
disable-model-invocation.
The dashboard and data-table templates carry inline renderers whose degenerate
cases are covered by test/artifact-templates-test.js (node
test/artifact-templates-test.js); like the viewer's JavaScript tests, it is
not part of the ERT run.
/remember [focus] is a local command that starts a bounded sessionless memory
review and opens the proposals cockpit. M-x mevedel-remember does the same;
a prefix argument asks for focus. In manual mode, proposals wait for explicit
decisions through the captured-state application checks. The table provides
full body/diff/evidence inspection, accept/reject, and checked recovery or
reversal. See memory.
Raw skill names come from frontmatter name when valid, otherwise the
containing directory name. Raw names must match [a-z0-9-]+; visible
names may include a generated source: or plugin prefix.
Hot reload marks consuming chat buffers dirty when watched skill
directories change. A configured directory that does not exist yet is
watched through its nearest existing ancestor, such as .mevedel/ or the
workspace root, where a change counts only on the path to that directory;
the files a turn writes there do not mark buffers dirty. Completion,
reminders and the skills section of the system prompt rescan on demand when
a buffer is dirty.
skill://NAME@SOURCE-KEY[/RELATIVE-PATH] is the read-only resource address
for an exact discovered skill source. SOURCE-KEY is the full lowercase
SHA-256 digest of the canonical source key used by atomic bindings; NAME is
only a label. Resolution checks that source again and never falls back to a
different same-named skill. Hot reload may change content at the same source,
and skill addresses retain their client-local origin rather than becoming
execution-target paths. See address-to-resource.md.
The model-facing roster and ListSkills include the exact registered resource
address for each file-backed entry. Package guidance directs source inspection
to Read on that address and resource discovery to Read("skill://"), avoiding
host-directory rediscovery for known skills. This does not invoke the skill.
The ToolCall expression (Skill :name "...") still prepares and invokes it.
Disabled or stale resources retain the provider's normal validation, and raw
skill development uses ordinary
filesystem grants.
Prepared deliveries also disclose that exact address as Skill source: before
each file-backed skill body. This covers model-side inline Skill results,
required and user-inline attachments, every stacked user command, and fork
prompts. Dependencies still precede their parent, and each contribution names
its own source rather than borrowing the root's origin. The address identifies
source for inspection with Read; it does not identify a prepared-body snapshot
or grant invocation authority. Synthetic skills without a source file receive
no invented address. Preparation, expansion hooks, and invocation records keep
the raw prepared body; the collapsed Skill row and user command Prompt drawer
also use that raw body, so delivery headers do not inflate their line counts.
Bare skill:// listings also expose readable origin aliases alongside exact
addresses: skill://local-mevedel/SKILL, skill://local-agents/SKILL,
skill://global-mevedel/SKILL, skill://global-agents/SKILL,
skill://bundled/SKILL, skill://managed/SKILL, and
skill://plugin/PLUGIN/SKILL, each with optional descendants. An alias resolves
to the exact current full-hash locator; the exact locator remains the authority,
while transcripts, errors, and results preserve the alias the model authored.
There is no unqualified skill://NAME alias.
Remote skill directories use TRAMP file notifications when the execution
target provides either inotifywait or gio. Without either program,
mevedel reports the limitation once per target capability state and leaves
save-triggered checks plus M-x mevedel-skills-rescan available. After
installing a notifier, refresh target readiness and rescan to install the
watcher. stat-when-checking remains an explicit opt-in because it adds one
remote stat per known skill at each pull check. Notification filtering uses
resolved roots cached at installation, including enabled plugin roots; it does
not recheck filesystem containment on each event. A watched ancestor of a
missing root ignores unrelated entries, while every change within a discovery
root still invalidates its consumers.
Local Slash Commands¶
Local slash commands are separate from $skill lookup. Built-ins include
/tokens, /usage, /model, /compact, /remember, /btw, /init, /prompt, /review, /verify,
/worktree, /mode, /skills, /tools, /edits, /clear, /plugin,
and /help. /init sends the repository bootstrap prompt that helps create
or improve AGENTS.md, AGENTS.local.md, .agents skills and memory,
and mevedel hooks. /edits toggles the current session between ask and
edits; it never grants full-auto execution. /mode accepts only the
canonical ask, edits, and full-auto values. Entering and leaving
full-auto installs the corresponding boundary
reminders.
/usage calls mevedel-subscription-usage-show, also available as cockpit U.
It inspects the selected backend's account-wide subscription quotas in a separate
read-only report, including while a model request is active. It does not enter
conversation admission, history, token accounting, or Goal continuation.
/tokens continues to estimate the current conversation context.
No-argument or list-style commands open the same session cockpit surfaces
as their matching cockpit rows when a live view/data pair exists:
/plugin and /plugin list, /skills and /skills list, /mode,
/model, /tools and /tools list, /worktree and /worktree status,
and /help. Outside a cockpit-capable session, commands fall back to the
older message-style behavior where that still exists. Explicit mutation
forms remain direct and scriptable. Permission-mode state commits before its
optional view-prompt repaint; an unavailable or incomplete view cannot undo
the mode change.
Worktree isolation has split surfaces:
/worktreeand/worktree statusopen the worktree cockpit surface with read-only branch/worktree status and refresh/create keys./worktree listopens the tabulated worktree list for selection-oriented actions on linked worktrees./worktree create [NAME] [--for "purpose"] [--clean]creates a new branch and linked checkout under.worktrees/, opens a fresh session there, and does not auto-send a model prompt.- Standalone Plan approval exposes independent Location, Context, and Mode axes. Here supports Current, Fresh, and Summary; Worktree supports Fresh and Summary. Worktree validates the branch before acceptance and reuses the same worktree-session creation path; dirty source changes remain only in the source checkout.
git-worktreeis model-visible but not user-invocable. It embeds best-effort read-only Git detection and tells the model how to mirror/worktreedefaults when the user explicitly requested isolation.- Model-driven worktree creation uses ordinary permission-gated Bash.
Plugin management:
/plugin install OWNER/REPOclones a GitHub plugin into~/.agents/plugins/github.com/OWNER/REPO; existing installs are left untouched and should be updated with/plugin update NAME. Install is always global and does not enable the plugin in the current workspace. The clone is staged beside its destination and published only once its manifest validates, so a failed clone or an unusable manifest leaves nothing at the destination and can simply be retried./plugin update NAMErunsgit pull --ff-onlyfor global managed installs under~/.mevedel/plugins/or~/.agents/plugins/. If the update changes an enabled plugin's executable hook surface, the update result reports that hook consent is pending./plugin remove NAMEand/plugin uninstall NAMEdelete a global managed installed plugin. If the current workspace activation points at that source, mevedel clears that activation. Workspace plugin data is not deleted by default because it is shared by plugin name across source switches. Plugins discovered from workspace.mevedel/plugins/,.agents/plugins/, ormevedel-plugin-extra-rootsmust be removed manually. Use/plugin disable NAMEfor project-only deactivation./pluginand/plugin listopen the session-owned plugin cockpit for the current workspace in a dedicated*mevedel plugins*buffer. The cockpit is atabulated-list-modetable with one selected plugin at a time; it is not added to the chat transcript. The buffer supports refresh, adaptive enable/disable, hook override, install, update, reload, uninstall, source-opening, detail, help, and back-to-cockpit keybindings backed by the same command functions as the slash commands. Shadowed duplicate plugin sources and pending hook consent are marked in the table state column and explained inRETdetails. Malformed plugin manifests are rendered as warning rows instead of being silently hidden. If an old activation binding points at a shadowed source, the enable action still routes through an explicit switch confirmation. Plugin details are available from the management buffer./plugin enable NAMEactivates all implemented plugin components for the current workspace. If the plugin contributes executable hooks, mevedel shows a concise consent summary of the risky/executable surface before enabling them./plugin disable NAMEdisables plugin skills and hooks in the current workspace./plugin hooks NAME offdisables executable plugin hooks while leaving plugin skills enabled./plugin hooks NAME onre-enables hooks for an already-enabled plugin after the same consent summary. The/plugin hooks enable NAMEand/plugin hooks disable NAMEforms are accepted aliases. These are advanced/scriptable overrides;/plugin enable NAMEis the normal activation path./plugin reloadrefreshes plugin-visible skills in the current chat session when possible.
Plugin mutations refresh the current session's visible plugin skills and hook state immediately when possible; users should not need to start a new session after enabling, disabling, installing, updating, or removing a plugin. Cockpit mutations perform that refresh in the cockpit's owning chat buffer, never in the sessionless table buffer. Starting or resuming an interactive session reports enabled plugins whose hooks still need new consent; hooks remain withheld until consent is reviewed per plugin.
Codex plugin manifest fields apps and mcpServers are not loaded
today; mevedel does not start plugin apps or bundled MCP servers yet.
/skills manages visible skills:
/skillsor/skills listopens the skills cockpit surface, listing skills visible in the current workspace and whether each is enabled there./skills help NAMEshows description, source, and file metadata./skills enable NAMEremoves NAME's source from the workspace disabled set./skills disable NAMEpersists NAME's source as disabled in the workspace.
Disabled skills stay on the session for inspection, but they are omitted
from $ completion, rejected by explicit $skill invocation and
Skill(name=...), and omitted from model-facing skill discovery and
skill:// resources. The denylist is stored as
(:disabled-keys (...)) in <workspace>/.mevedel/skills-state.el; a missing
file leaves every skill enabled. Project-contained skill sources use portable
workspace-relative identities, while external user, bundled, managed, and
plugin skill sources use canonical absolute identities. Visible names and
collision prefixes therefore do not change persisted policy.
Local state is replaced atomically, so an interrupted write leaves the
previous denylist intact rather than a partial file the strict reader would
reject. Remote state is published through the owning live session and its
normal storage disclosure and lease checks; a sessionless inspector cannot
mutate it. A scan reads state once and caches effective enablement on the
session's skill objects. A command or cockpit toggle updates every live session
in that workspace immediately. The former global
~/.mevedel/skills-state.el is no longer read and is left untouched for manual
reference.
Completion offers local command names at leading / and user-invocable
skill names at leading $ in the composer, with annotations for every
included slash command and every visible skill. Inline $ completion
offers only user-invocable context: inline skills; leading $
completion may offer user-invocable inline and fork skills. Completing a
root name inserts a real argument separator so display-only skill hints
cannot make $skill [arg] look typed when the buffer only contains
$skill.
Commands can also expose argument candidates based on the current
argument position; /mode completes ask, edits, and full-auto, while
/model completes model names from the current gptel backend. /plugin
completes subcommands, then installed plugin names for enable,
disable, update, remove, uninstall, and supported hooks forms;
/plugin install remains freeform. Skill names with prefixes, such as
superpowers:brainstorming, are valid $ candidates.
Resource-address completion likewise offers scheme prefixes and bounded
descendants as plain canonical text. It does not bind a mention, invoke a
skill, read content, refresh a source, materialize a session, or change
durable state.
/review and /verify complete shared explicit target forms such as
current, HEAD, branch:<name>, and commit:<rev>. With no arguments
they open the target picker; unknown free-form arguments remain custom
instructions.
Frontmatter¶
Current fields include:
name,display-name,descriptionargument-hint,argumentsuser-invocable,disable-model-invocationallowed-tools,ptc-primitivesmodel,effortcontext,agentpathshooks(skill-scoped hooks active during invocation)
user-invocable defaults to true; disable-model-invocation defaults to
false.
Command hooks retain their skill resource origin. Project skill hooks are
omitted until mevedel-hooks-trust-project records the exact workspace, path,
and SKILL.md content hash; editing the manifest invalidates that consent for
both user- and model-origin invocation. Trusted project skill commands and
same-target remote project-plugin commands run on the session target. User,
bundled, managed, and client-local plugin skill commands stay local at the
directory containing their SKILL.md. Elisp hooks remain local.
description follows the Agent Skills convention: it should describe both
what the skill does and when the model should use it.
paths gates model-listing visibility only. Explicit $skill or
model-side invocation by name can still run the skill subject to the
user/model invocation gates.
The host distinguishes explicit user invocation from optional discovery.
Matching a description or a paths pattern does not compel a workflow.
Prepared bodies already in context remain usable for their applicable task;
there is no automatic one-message lifetime. User direction and authored
applicability determine when guidance ends or is replaced. Missing or changed
guidance must be retrieved before relying on its omitted details. This policy
does not rewrite skill authors' instructions or grant additional authority.
The retained current catalog includes enabled model-invocable skills without paths
restrictions. Path-scoped skills remain outside it even after matching file
activity: optional path notices and ListSkills deliver their discovery facts
later, without changing the system prefix ahead of conversation history.
ListSkills without a query includes active path-scoped skills; a query also
searches dormant ones. Explicit invocation is independent of path activity.
Authored configuration changes deliver a fresh retained catalog update.
The model-aware roster budget (mevedel-skills-listing-budget, default 0.02)
uses the effective request model's context window and estimates four characters
per token. It shortens descriptions before omitting entries.
Its stable usage contract always advertises ListSkills(query), including when
no names fit. There is no separate budget reminder to repeat that instruction
or consume a notice before dispatch.
Path activity may expand the shared catalogue, but a notice belongs to the conversation that observed the path. Each Skill-capable conversation gets its own optional notice, even if a sibling already discovered the same skill. Per-skill keys preserve matches across multi-file calls. Notices acknowledge delivery only after the request payload exists; cancellation allows a later matching observation to retry. Path notices never change the shared non-path catalog. Agent delivery cannot acknowledge for the root or another agent. Retained catalog observations are acknowledged only when their trusted messages are present in the outgoing payload.
The optional notice throttle lasts for a live conversation; changed name, source, description, or path metadata can rearm it. Compaction alone does not repeat these hints; fresh resumed buffers start a new throttle. ListSkills remains available for rediscovery. A path notice does not load the skill body or enforce required instructions before editing: project-wide/scoped guidance and explicitly invoked skills retain their separate delivery boundaries.
Invocation¶
Invocation role and origin are independent. Roles are command and
instruction; origins are user, model, and internal. A leading user
mention and a model-side Skill call are commands. A user mention embedded in
ordinary prose is an instruction, regardless of the skill's context field.
The view composer builds one invocation plan from the original, atomically
bound input before running prompt-submit hooks. Consecutive distinct leading
context: inline commands form a stack of at most six commands and share the
remaining text as their raw argument string. Duplicate commands collapse to
their first canonical source. A standalone -- ends the stack. A leading
context: fork command runs alone; a fork skill after another command or in
ordinary prose is an instruction and does not create a child.
Commands prepare left to right, followed by instructions in first-occurrence
order. Every occurrence remains represented, but preparation is deduplicated
by exact canonical source across the complete submission. A command occurrence
wins over instruction occurrences of the same source. Instruction occurrences
become inert model-visible placeholders such as
[skill:to-prd -- attached], and each unique instruction body is supplied once
as hidden turn context. The attachment reminder tells the model that the host
already attached the skill and that it must follow the skill without calling
Skill. A redundant model-side call for the same canonical skill in that
request succeeds with already attached and does not inject the body again.
After a request ends, a model re-read of a user-only skill can reuse its latest
user-prepared body from the same conversation. This uses the durable resolved
invocation record, including after Plan handoff, rather than preparing the
skill again: no injections, hooks, model/permission policy, fork, or duplicate
record. The body remains scoped by the user's task and current applicability.
Disabled skills, user-only skills never prepared there, and calls with different
nonempty arguments still refuse. Other agent conversations cannot reuse the
record. This does not make the skill model-invocable.
Instruction preparation always receives empty
arguments and ignores context, model, effort, agent, and declared skill
hooks. Its allowed-tool rules exist only while preparing body injections.
Required skill attachments¶
A SKILL.md author can require other skills as instruction context with a
literal authored declaration. Inline !$NAME is argument-free. A full line of
the form !$NAME -- RAW ARGUMENTS passes everything after -- as the child's
argument template. That template goes through the normal parent argument
substitution, but the substituted text retains non-author provenance. Each
authored declaration occurrence becomes [skill:NAME -- attached] in its
parent body. !$skill declares an attachment; it does not execute a skill, call
a tool, or spawn an agent.
Only declarations originating literally in the source SKILL.md are active.
Escaped forms, Markdown code spans and fences, and markers introduced by
argument substitution, injections, hooks, prepared bodies, child or model
output remain inert. This provenance rule is the narrow exception to the rule
that skill bodies and generated text are not recursively interpreted as skill
syntax.
Qualified dependency names resolve exactly by visible name. An unqualified name prefers the parent skill's plugin or discovery root; otherwise it must have one unique global raw-name match. A dormant path-scoped child attaches for that invocation without becoming active. Missing, disabled, unauthorized, ambiguous, or cyclic dependencies reject the graph.
Dependencies recurse depth first and dependency first, preserving authored sibling order; the root contribution comes last. Preparation, attachment, and invocation records use that order and deduplicate by canonical source plus exact arguments. Reaching one canonical source with different arguments rejects the graph instead of executing the same skill twice with conflicting inputs. Every authored declaration still leaves its placeholder in its immediate parent body, including a duplicate whose contribution was already prepared.
The root invocation origin propagates unchanged through the graph. A
model-origin root requires the root and every descendant to be model-invocable,
so a parent cannot launder model access to a user-only child. A user-origin root
must be user-invocable, but its descendants need not be: user-invocable: false
keeps an attachment-only skill out of direct $ completion without blocking a
user-invoked parent. Conversely, disable-model-invocation: true on any node
rejects the complete model-origin graph but does not affect user-origin
invocation. Each child's injections run with only that child's preparation
permissions; parent and sibling permissions do not flow into it.
Required children are instructions only. They ignore their own command or fork
policy, agent, model, effort, hooks, and request permissions. User instruction
attachments flatten dependencies and parent into the existing pending
skill-attachment collection. User commands and fork prompts, and model-side
inline results, receive the complete dependency contribution. A model-side fork
still returns the eventual fork result.
Graph validation and request or child dispatch are atomic: a failed dependency does not send a partial model request or child. Preparation itself can have irreversible effects, however; completed Bash or live Elisp injections cannot be rolled back if a later dependency fails.
Accepted Plan implementation uses this same planner. The approval's s
selector stores exact canonical sources and emits argument-free instruction
mentions in the generated implementation prompt. Live $skill mentions in
the approval's i instructions editor are bound and prepared identically, so
they deduplicate with s selections by canonical source. Unlike an ordinary
soft-unavailable prose mention, an explicitly bound Plan implementation skill
blocks the retryable handoff before request startup when its source is missing,
disabled, malformed, or no longer user-invocable.
Quoted "$foo" / '$foo', escaped \$foo, Markdown code spans and fences,
and unknown $foo names remain literal. A bound or known skill that is
missing, disabled, malformed on disk, or no longer user-invocable emits a
warning and becomes a temporary [skill:foo -- unavailable] annotation for
that request; the stored prompt remains unchanged and the turn continues.
A structurally malformed live binding still blocks submission because its
intended target cannot be trusted. Mevedel prepares every unique available
skill before dispatching anything; the first injection, permission,
validation, or hook failure sends neither a partial request nor a child and
leaves the direct draft or queued entry editable.
Injection outcomes are classified from the pipeline's canonical status, not from the wording of the result: a failure whose text this side does not recognise would otherwise become legitimate-looking body content that the model cannot distinguish from an expression returning that string.
Preparation is asynchronous whenever a body injects, so the raw gptel-send
path tracks the command it dispatched with markers rather than the positions
it started from, and abandons a late outcome when that text no longer matches
what was dispatched. A draft the user edits, shortens, or replaces during
preparation is therefore left as the user left it, and nothing is sent for
arguments that no longer exist.
Skill completion binds the exact visible token to the selected canonical
SKILL.md source immediately. Manually typed known skills bind at send before
preparation, queueing, and history insertion. Dispatch reloads the selected
source's current contents and metadata; the source pathname is identity, not a
body snapshot or durable permission grant. A bound skill never falls back to a
later same-visible-name discovery result. Copy, edit invalidation, retry,
history persistence, corrupt-binding handling, and soft-failure behavior follow
the shared lifecycle documented in
mentions.md.
This $skill binding and invocation lifecycle is distinct from reading a
skill:// resource address: an address exposes source content through the
filesystem-shaped read tools and never invokes or attaches the skill.
UserPromptExpansion runs once per prepared canonical skill. After all bodies,
placeholders, and instruction reminders exist, UserPromptSubmit runs once on
the complete inert prompt. Hook output is never rescanned for skill syntax; a
hook may add a prefix or suffix but cannot rewrite away any prepared
contribution, and it cancels planned work only by blocking the whole
submission. Skill bodies are not generally rescanned as user mentions; only
literal author-origin !$skill declarations use the required-attachment scan
described above. Prepared output, model output, child prompts and results, and
model-supplied arguments never introduce new dependencies or user $skill
mentions.
A plan with exactly one leading inline command lets that command select the
model and effort of the request that the composer has not yet realized. This
overrides the root consumer's workload, such as planning during Plan mode.
A stack of two or more distinct inline commands retains that consumer
workload; every stacked command's frontmatter and preset-side skill policy is
ignored without choosing a winner. Instruction mentions also retain the
consumer workload, including when the mentioned skill normally has
context: fork.
Ordinarily, queued prompts retain their original bound text. Queueing performs
no body preparation and runs no prompt hook. When an entry becomes the next
turn, it is planned, prepared, and submitted independently; the entry leaves
the queue only at the request or fork dispatch boundary. If a prepared
WaitAgent steering attempt loses its waiter race, its already-approved outcome
is queued instead, owns its accepted hook context while queued, and drains
without repeating preparation or UserPromptSubmit. Editing or clearing the
queue restores that context to the session.
The transcript and input history keep the exact original user text. An ignored
render-data block connects that text to the prepared model prompt without
exposing the control metadata to the model. It also records the names of the
required attachments whose <system-reminder> blocks lead that prompt, so the
view rows them as a collapsed attached-skills disclosure and keeps the prompt
drawer to the prepared body. Both disclosures appear immediately on prepared
composer submissions and generated implementation turns, before the provider
starts, and use the same source-backed presentation after reconstruction.
The attached-skills row expands independently of Prompt and ordinary reminder
batches. $skill preparation may block chat
input while asynchronous injections run, and an awaited fork keeps its owning
workflow open until its child settles. Cancellation or buffer death invalidates
the preparation token so late callbacks cannot dispatch.
The model-side Skill tool and internal callers use the same normalized
preparation outcomes. Model failures remain ordinary tool errors, and fork
dispatch uses the shared agent runtime rather than a skill-specific recursion
or scheduling limit. Model-side inline Skill invocation does not change the
root request model policy; forked skills and retained agents resolve their own
workloads. ListSkills mirrors the active, enabled,
model-invocable roster and can query dormant path-scoped skills.
Review and Verify Commands¶
/review / M-x mevedel-review and /verify / M-x mevedel-verify are
local commands, not ordinary skill resolution. They share a target picker
for uncommitted changes, a base branch, a specific commit, the last
commit, or custom instructions. Inline /review and /verify arguments
can name explicit target forms such as current, HEAD, branch:<name>, and
commit:<rev>; other non-empty arguments remain custom instructions.
/review starts a context-isolated retained reviewer leaf and awaits its
ordinary asynchronous RESULT. The command ignores user/project skills named
review; the generic bundled review skill remains separately invocable. The
reviewer prompt asks for strict JSON with prioritized findings. The parent turn
stores a synthetic <user_action> block
containing the rendered review results before the assistant summary, so
follow-up prompts like "fix finding 2" have the findings in model context.
Schema-invalid JSON remains visible as raw reviewer output instead of blocking
terminal settlement.
The normal view hides that synthetic block and shows only the readable
review summary.
/verify starts and awaits a context-isolated retained verifier leaf. It
uses the same target text but asks for adversarial
validation, relevant checks when allowed, and the verifier prompt's final
VERDICT: PASS, VERDICT: FAIL, or VERDICT: PARTIAL line. Verifier
output is not parsed as review JSON, but its exact final verdict is validated
and stored on the invocation. Missing, duplicated, or non-final verdict lines
leave the original report visible with a rejection warning.
The parent view displays a live inline Review or Verify handle while
the agent runs. This handle is sourced from a hidden canonical collaboration
event, updates as the agent calls tools, and remains separate from the final
summary.
At dispatch time, mevedel-review.el keeps target/result semantics local,
creates a unique path such as /root/review or /root/verify_2, and attaches
the workflow result consumer before provider dispatch. It spawns the bundled
agent it resolved rather than naming a role, so the request-local role
roster, which belongs to the preset's request setup, is neither read nor
written. A user command still runs the reviewer whatever that roster holds;
what it can no longer do is widen the roster the model sees, or leave a
narrowed one behind that refuses a later spawn. A forked skill's synthetic
agent is dispatched the same way, for the same reason. After the parent
submission passes its mutation-authority checks, concrete Git targets receive
a collision-free package under
.mevedel/state/review-packages/; the command tells the reviewer or verifier to
read that file before rerunning broad repository inspection. Package collection
uses one fixed Git runner that disables pagers, replacement objects,
filesystem monitors, external diffs, and text conversion. It supplies
skill-scoped allow rules for read-only git Bash commands
used to inspect diffs (git diff, git status, git log, git show,
git merge-base, git rev-parse, git ls-files, and git cat-file),
plus exact no-argument head as a stdin-only pipe filter for bounded object
inspection. Review adds a local deny rule for other Bash commands; verify does
not, so normal permission policy decides whether validation commands may run.
Read tools come from the selected agent's tool list.
Allowed Tools¶
allowed-tools is permission augmentation, not tool selection. It never
removes tools from the model and never denies unspecified tools.
For command invocations, parsed entries become skill-scoped permission rules
on the owned request or agent invocation. For instructions they apply only
while the skill prepares and do not grant tools to the consuming request. These buckets outrank session and persistent rules for allow/ask
resolution, while deny remains absolute across all buckets. Plan mode applies its capability restriction before skill allow grants: only
ApplyPatch to explicit session-owned work:// descendants has a bounded write
exception, and directive planning has no patch exception. Goal execution uses
ordinary request permissions. See Plan mode.
Preparation's ! commands run through the tool pipeline under the buffer's
request. Between turns a temporary preparation request holds that slot with
the skill's rules and hooks and the caller's Plan limits, and is removed when
preparation settles. A skill prepared during a turn, by steering or a Skill
call, never takes the turn's slot: its rules and hooks join the turn's
innermost scope, the agent invocation if any, until preparation settles. The
turn keeps its own rules, Plan limits and cancellation, can end meanwhile, and
an abort stops the skill's commands with the turn. A slot that changed hands
mid-turn once let a turn end against a stand-in and be put back afterwards,
reported as running forever, and made ACP turns stop as interrupted.
ToolCall Primitives¶
ptc-primitives narrows the nested tools visible to ToolCall for a command
skill. Omitting the field leaves the request roster unchanged; an explicit
empty list allows no nested tools. Stacked commands intersect their lists, so
adding a skill can only remove authority. The result is intersected again with
the request's native and discoverable capabilities; it
never grants or activates a tool. Instruction occurrences ignore this field
because they do not own the consuming request. Retained agents carry
ToolCall through their own role tools; prepared fork metadata neither grants
nor narrows it, so ptc-primitives applies only to the request that owns the
skill, and a child invocation's callable roster comes from its own native and
discoverable tools. Composition admission is a separate restriction.
Hooks¶
Skill frontmatter hooks uses the same event -> matcher -> handler shape
as .mevedel/hooks.el and .mevedel/hooks.json. Command skills install
those rules on their request or child invocation. Instruction invocations
ignore their declared hooks; non-skill hook layers may still observe and
rewrite or block instruction body preparation. For fork commands, a
frontmatter Stop declaration is scoped to that child invocation and is
normalized to SubagentStop; top-level Stop remains a main-turn event.
Successful awaited fork user skills also complete the parent turn and
use the same successful-turn transaction as an ordinary model response. The
top-level Stop hook runs before request-scoped layers are cleared; queued
follow-ups and other terminal cleanup run through that shared boundary.
Model And Effort¶
model can name a preset-local tier or concrete provider selector. effort
uses gptel's public gptel-reasoning-effort values. Both fields are policy of
the request that a skill owns, not ambient changes to the session.
Root composer, generated turns and raw gptel-send submissions resolve this
policy before selecting gptel or Claude Code. Raw sends retain slash/skill
preparation and reject busy or read-only sessions before launch; prefix-zero
steering reports that it is unavailable for an external engine. /init uses
the same engine dispatcher even without a paired view.
Plan's planning workload and a leading skill's override
therefore apply to native subscription turns too. Admission captures the
effective policy before consuming pending skill state, so later skill-roster
context sizing uses the actual request model. The session's selected provider
and effort stay unchanged. Switching engines after root history exists starts
a visibly labelled excerpt continuation from the effective canonical history;
it does not replay tools or reuse another engine's hidden conversation.
The existing mevedel-model-workloads map can tune an external skill without
editing its SKILL.md. The key must be an Emacs symbol consisting of $
followed by the skill's final visible name; string keys and unprefixed symbols
do not address skills. Qualified names keep their qualification:
(:model-workloads
(($grill-with-docs :tier strong)
($code-review :provider "OpenAI:gpt-5.6-sol" :effort high)
($plugin:review :effort low)))
The values use the workload map's existing :tier, :provider, and :effort
fields. A preset :tier or :provider replaces the skill's frontmatter
model; a preset :effort replaces frontmatter effort. An omitted field
retains frontmatter, and an omitted frontmatter field inherits the normal
session or child workload policy. This is field-by-field replacement, so a
preset may replace effort while retaining the skill's model, or vice versa.
Policy is applied only at a request-owner boundary:
| Invocation | Model and effort policy |
|---|---|
| One leading user inline command | Applied before the parent request is realized |
| User- or model-origin fork command | Applied to the dedicated child request |
| Two or more leading inline commands | Ignored; the request retains session policy |
| Instruction mention in user prose | Ignored; the request retains session policy |
| Plan implementation skill or instruction mention | Ignored; the accepted session snapshot applies |
Model-side inline Skill call |
Ignored; the parent request is already realized |
Mevedel resolves and validates provider-dependent policy only for the first two
owner cases. Effort is checked against the selected model's
:reasoning-effort metadata before dispatch; unsupported explicit effort
fails the owning command instead of being dropped or downgraded. Ignored
provider-dependent metadata is not resolved or validated, so invalid model or
effort values cannot make an instruction, command stack, or model-side inline
call fail.
A model-side inline call still returns the prepared skill body unchanged to
the model, with the required-attachment <system-reminder> blocks leading it
so dependencies are read first. The same render-data side channel carries the
bare body and the attachment names, so the Skill tool row shows the skill's own
body and names its dependencies in the header
(Skill: artifact-doc (75 lines; attached artifact, artifact-design)) instead
of rendering the reminder markup as prose. When frontmatter or the
$skill-name workload declared a model and/or effort field, that row receives
warning status and names only the ignored fields; its expanded detail
recommends context: fork. That warning is render data and is never prepended
to the tool result. No warning is shown when neither field exists.
An agent field selects the child type only for context: fork. Combining
agent with context: inline records one configuration warning shown by skill
inspection; invocation does not repeat it.
Body Preparation¶
Argument substitution runs before execution. Supported argument
placeholders are $ARGUMENTS for the raw argument string,
$ARGUMENTS[N] for a zero-based parsed argument, $N as shorthand for
$ARGUMENTS[N], and $name for names declared in frontmatter
arguments.
Claude-compatible literal substitutions are supported for imported skills:
${CLAUDE_SESSION_ID}, ${CLAUDE_SKILL_DIR}, and ${CLAUDE_EFFORT}.
Mevedel-native aliases are also available and preferred for new
mevedel-specific skills: ${MEVEDEL_SESSION_ID}, ${MEVEDEL_SKILL_DIR},
and ${MEVEDEL_EFFORT}. Effort substitutions reflect the parsed skill
effort. Literal ${...} substitutions do not suppress the
automatic ARGUMENTS: ... fallback when invocation arguments are supplied
but no argument placeholder is present.
In a remote session, project skills and same-target remote project-plugin skills substitute both skill-directory aliases with the target-native path. Their shell body injections use the ordinary session Bash pipeline. Shell body injections from user, bundled, managed, or client-local plugin skills are refused before Bash dispatch so one session never executes them on a different target. Elisp body injections continue to run in the local Emacs. A skill directory naming another remote target is refused rather than dispatched.
The view composer uses argument-hint and arguments as display-only
typing guidance. argument-hint appears before the user starts typing
arguments. Named arguments appear as remaining positional slots, e.g.
[service] [environment], and disappear as tokens are supplied. These
hints do not validate arguments and are never inserted into the
model-visible prompt.
Body injections support shell inline !`cmd`, shell fenced ```!
blocks, elisp inline !el`(emacs-version)`, and elisp fenced
```!el blocks. Inline elisp is intended for short one-line
expressions; fenced elisp is the supported form for longer expressions.
Shell injections substitute trimmed stdout. Elisp injections substitute
the printed return value, with captured stdout appended in a compact
STDOUT: section when present.
Bash and Eval body injections are treated as author-written skill code
and use trusted-literal permission handling. Bash injections require a
covering Bash allow rule; elisp injections require allowed-tools:
[Eval]. Explicit deny rules still win.
Only markers written literally in SKILL.md are executable body
injections. Markers introduced by argument substitution or produced by
another injection's output remain literal text in the prepared body.
The marker syntax and delimiters must be author-written, but the
command/expression body may contain substituted arguments. For example,
!el`(length "$ARGUMENTS")` is valid when the skill author controls
the surrounding elisp expression. Substitution is textual, so skill
authors must quote or escape interpolated values correctly before using
them inside shell scripts or elisp strings.
Each invocation records a mevedel-skill-invocation-record on the
live session. Compaction includes the recorded prepared body and source
identity as historical evidence even if SKILL.md changes later. The record
does not assert that the skill is still applicable: task completion, authored
scope, replacement, and explicit user deactivation govern its lifetime.
The record list is not serialized in the session sidecar; cold resume relies
on the restored transcript and retained summary. See compaction
for evidence selection and input-size limits.
Restored Skill results, including direct ToolCall invocations, retain their complete instruction text in summary
evidence; the ordinary tool-output cap does not discard trailing obligations.
Path discovery runs at the shared post-tool pipeline hook, so native and ToolCall child operations use the same recipient and delivery acknowledgement. Hook metadata retains the tool category; equal names on different servers do not select another tool's path extractor. Failed path attempts can also suggest optional guidance. A notice requires an available native Skill or an effective Skill capability through ToolCall.
Catalog delivery¶
Canonical names and short purposes arrive as retained current-context updates. Purposes use the first sentence/line capped at 160 characters; detailed authored descriptions remain searchable through ListSkills, and Skill retrieves bodies. A stable system component owns invocation syntax, optionality and guidance lifetime, including when the catalog is empty. Catalog changes no longer rewrite the system prefix or require a separate persisted skills-delta acknowledgement. See retained instruction context.
The bundled $clean-work [focus] workflow reviews work://shared for confirmed
duplicates, superseded drafts and completed handoffs. It preserves unresolved
work and files that may still be in active use, prefers deletion when only
future usefulness is doubtful, checks surviving sources, and uses normal ApplyPatch
permissions and review. It does not expire files by age or reorganize folders.
It is an explicit, conservative cleanup within a conversation. Automatic
cleanup belongs to memory consolidation, which extracts lasting knowledge from
quiet shared files and prefers deleting them afterwards; see
Shared working-note lifecycle.