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 server was updated. Reloading the page to pick up the latest version.