Chat Slash-Skills

The chat input supports slash-skills (/agent, /model, /harness, …). A skill is "a thing that does something" when the user invokes it — and this is the one unified concept: what we used to call slash-commands are skills. A skill's "doing" can be:

Skills are not agents. Agents == system prompts (a persona the model runs as — including the utility agents: naming, summary, NodeInitializer, DescriptionWriter). Skills == capabilities loaded as you go. An agent is never a skill, and agents are not mounted to disk.

A skill is a declarative mesh node (nodeType:Skill) — there is no C# handler class and no GUI code per skill. A module, Space, NodeType or user ships a Skill node and it just works in the chat, discovered through namespace inheritance.


The skill node

A skill's content is a SkillDefinition. The slash word is the node's id, the display name + help text are the node's name + description:

public record SkillDefinition
{
    public string? Instructions { get; init; }   // SKILL.md body — CLI harnesses + agent load-on-demand
    public SkillAction? Action { get; init; }     // what it DOES in the chat (null for a pure instruction skill)
    public bool AutoMount { get; init; } = true;  // advertise the skill to the agent up-front (vs load-on-demand only)
    public bool LaunchesSubThread { get; init; } = false; // run in a sub-thread vs inline
}

public record SkillAction
{
    public SkillActionKind Kind { get; init; } = SkillActionKind.Pick;  // Pick | OpenContent | Navigate | Connect | Disconnect | NewThread
    public string? Query { get; init; }        // Pick: the combobox query (+ `sort:order`)
    public string? Field { get; init; }        // Pick: camelCase ThreadComposer field (harness|agentName|modelName)
    public string? Title { get; init; }        // Pick: combobox title
    public string? ContentPath { get; init; }  // OpenContent / Navigate: node/path (Navigate: optional fixed target)
    public string? Provider { get; init; }     // Connect/Disconnect: ClaudeCode | Copilot
}

A skill is a behaviour (Action) and/or an instruction (Instructions). A pure-instruction skill has no Action (it does nothing in the chat — it is mounted to the CLIs / advertised to the agent); a pure-behaviour skill has no Instructions.

Selections persist on ThreadComposer

The composer (a data-bound [MeshNode]) is the single source of truth for the thread's harness/agent/model. A Pick skill writes the selected node's path onto a named composer field; the read-only status row above the input and the next submission read it back. The skill carries no state of its own — its Action names a query and a field.


The common case: pick a mesh node (Pick)

Most skills "pick a node by a query and drop it into the composer". That is a Pick action — no C# at all:

{ "$type": "SkillDefinition",
  "action": {
    "kind": "Pick",
    "query": "namespace:Agent nodeType:Agent sort:order",  // what the picker lists + how it's ordered
    "field": "agentName",                                  // camelCase ThreadComposer field to write
    "title": "Choose an agent" } }

On execution the chat host builds a NodePickerRequest from the action and pops the generic node selector; selecting a node writes its path to the composer field. This is "open a combobox and select an agent".

Ordering + eligibility live in the query, never in the GUI. sort:order makes the picker's default-to-first land on the catalog head (e.g. Assistant's order: -1). The picker renders the query result as-is — to change which nodes appear or their order, change the query, not the view.

The standard skills are authored as .md files

/agent, /model, /harness ship as Pick skill nodes, authored the same way as agents: a markdown file with a YAML frontmatter header (content/ai/Skill/*.md — e.g. agent.md/agent), loaded by BuiltInSkillProvider. A behaviour skill puts its action: block in the frontmatter; an instruction skill puts its how-to in the markdown body:

---
nodeType: Skill
name: /agent
description: Switch the agent for subsequent messages
action: { kind: Pick, query: "namespace:Agent nodeType:Agent sort:order", field: agentName, title: Choose an agent }
---

They are imported into Postgres on boot via SkillStaticRepoSource (mirroring agents/models/Doc) — the distributed/Orleans routing never consults the in-memory static adapter, so without the import namespace:Skill queries return nothing. See StaticRepoImport.

Discovery is the unified registry pattern — same as agents and models

Skills, agents, and models are discovered the identical way: a per-partition registry query unioning the platform namespace, the current space's, and the user's own — namespace:{user}/Skill|{space}/Skill|Skill nodeType:Skill (AgentPickerProjection.BuildSkillQueries, the sibling of BuildAgentQueries / BuildModelQueries). All are public-read top-level domains. So a Space's own skills (under {space}/Skill) or the user's ({user}/Skill) surface alongside the platform catalog, with per-user RLS hiding another user's private skills. Drop a nodeType:Skill node under {space}/Skill and /yourskill works in that Space's chat, with zero code.

Agents proactively offer to create skills

A conversational agent watches for repetition — the user asking for the same multi-step task more than once — and proactively offers to capture it as a /<name> skill (it does not wait to be asked). "Create a skill" means create a nodeType:Skill node with a SkillDefinition content (an Instructions how-to and/or an Action), placed under the user's own {user}/Skill (private to them) or the Space's {space}/Skill (shared with that Space) — the platform-wide catalog is Skill. This guidance lives in the shared agent base prompt (AgentChatClient); the agent reads the SkillDefinition shape from this page.


Other behaviours

Using an instruction skill: /code <task>

An instruction skill is invoked with the task typed after the slash word — everything after the skill name is the work order:

/code build a Todo NodeType with a Kanban board layout area

The chat digests the trailing text into a normal round (SkillInfo.ToSubmissionText): the typed task is submitted prefixed with a load_skill directive, so the agent loads the skill's instructions (the SKILL.md body) first and then applies them to exactly what you typed. /code alone (no task) just shows the skill's help text — there is nothing to run. Under a CLI harness (Claude Code / Copilot) the raw /code … text is instead forwarded 1:1 to the harness, which resolves the skill itself through the meshweaver MCP server.


How dispatch works

When the user runs /name, the chat view (ThreadChatView.HandleSlashCommandAsync):

  1. harness-owned command? (/login, /logout under a non-MeshWeaver harness) → route to the harness.
  2. otherwise → resolve a nodeType:Skill mesh node by slash word (with namespace inheritance, ResolveSkillNodeAndRun) and run its ActionPick pops the combobox, OpenContent loads the content window, Navigate resolves the target and opens it pane-aware, NewThread (/clear) replaces the side panel with a fresh new-chat composer. A pure-instruction skill with trailing text submits the task as a round with a load_skill directive (see above); without trailing text it shows the skill's help.

The view contains no per-skill code — only the generic picker + content-window callbacks. All pick skills write to the same ThreadComposer.


Tests

Reconnecting…
The connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.