Skip to content

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 /review command uses the same reviewer contract but dispatches its task directly so user/project skills named review cannot override the review workflow. A skill named review remains selectable through its generated visible name, for example local:review or mevedel:review.
  • git-worktree — model-only guidance for checking Git worktree isolation and mirroring the /worktree defaults 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 applicable AGENTS.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 under work://shared and memory://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:

  • /worktree and /worktree status open the worktree cockpit surface with read-only branch/worktree status and refresh/create keys.
  • /worktree list opens 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-worktree is model-visible but not user-invocable. It embeds best-effort read-only Git detection and tells the model how to mirror /worktree defaults when the user explicitly requested isolation.
  • Model-driven worktree creation uses ordinary permission-gated Bash.

Plugin management:

  • /plugin install OWNER/REPO clones 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 NAME runs git pull --ff-only for 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 NAME and /plugin uninstall NAME delete 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/, or mevedel-plugin-extra-roots must be removed manually. Use /plugin disable NAME for project-only deactivation.
  • /plugin and /plugin list open the session-owned plugin cockpit for the current workspace in a dedicated *mevedel plugins* buffer. The cockpit is a tabulated-list-mode table 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 in RET details. 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 NAME activates 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 NAME disables plugin skills and hooks in the current workspace.
  • /plugin hooks NAME off disables executable plugin hooks while leaving plugin skills enabled. /plugin hooks NAME on re-enables hooks for an already-enabled plugin after the same consent summary. The /plugin hooks enable NAME and /plugin hooks disable NAME forms are accepted aliases. These are advanced/scriptable overrides; /plugin enable NAME is the normal activation path.
  • /plugin reload refreshes 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:

  • /skills or /skills list opens the skills cockpit surface, listing skills visible in the current workspace and whether each is enabled there.
  • /skills help NAME shows description, source, and file metadata.
  • /skills enable NAME removes NAME's source from the workspace disabled set.
  • /skills disable NAME persists 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, description
  • argument-hint, arguments
  • user-invocable, disable-model-invocation
  • allowed-tools, ptc-primitives
  • model, effort
  • context, agent
  • paths
  • hooks (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.