# Skill Auto Select

> Design a Skill (or Plugin) that an LLM agent can reliably discover, select, and invoke based on its description, with explicit selection syntax, name-collision handling, and three-layer matching. USE WHEN: authoring a new skill for a Plugin, designing skill frontmatter, deciding between structured `UserInput::Skill` vs implicit `$skill-name` mention, handling duplicate skill names, picking between path-precise and name-based matching, or any task involving "make my skill actually get picked up by the agent". TRIGGER PHRASES: "skill selection", "skill auto-pick", "$skill-name mention", "skill description", "skill metadata", "SkillMetadata", "ExplicitSkillLookup", "three-layer matching", "name collision", "ambiguous skill name". SKIP WHEN: writing a one-shot script (use `error-recovery-strategy` or similar task skill), skill is human-only (no agent invocation), skill is bundled and not selectable.

- Skill: `minimax-ai/skill-auto-select` (Agent Skill)
- Install (CLI): `npx skillmds@latest add minimax-ai/skill-auto-select`
- Raw SKILL.md: https://api.skillmd.com/api/skills/minimax-ai/skill-auto-select/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: Apache-2.0
- Author: MiniMax AI (https://skillmd.com/u/minimax-ai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/minimax-ai/skill-auto-select

---


# Skill Auto-Select

Design a Skill (or a whole Plugin) so an LLM agent can reliably discover it, decide
it is the right one, and invoke it. Mirrors the design of Codex's `codex-rs/skills/`
runtime, which is what this very Plugin is mimicking.

## When to use

Activate when designing:

- A new skill's frontmatter (`name`, `description`, `short_description`, `interface`, `dependencies`, `policy`).
- A skill marketplace or registry where multiple skills may collide on name.
- A path-based discovery surface (logical discovery path vs canonical path).
- An explicit-vs-implicit invocation model (structured input vs `$name` mention vs shell command invocation).

## When NOT to use

- Skills that are bundled, not selectable (e.g. always-on system skills). Use a different distribution model.
- One-shot scripts that should never be auto-selected. Use task skills (`error-recovery-strategy`, `plan-stream-emit`).

## Process

### 1. Write the 11-field SkillMetadata

Every skill should expose at minimum these fields:

| Field | Type | Purpose |
|---|---|---|
| `name` | `String` (≤ 64 chars) | Canonical name, used in mentions and uniqueness checks. |
| `description` | `String` (≤ 128 chars) | One-line purpose, used by LLM to decide "is this for me?". |
| `short_description` | `Option<String>` | UI label, used in lists. |
| `interface` | `Option<SkillInterface>` | UI metadata (`display_name`, `icon`, `brand_color`, `default_prompt`). |
| `dependencies` | `Option<SkillDependencies>` | Declared external tools (MCP / function / etc). |
| `policy` | `Option<SkillPolicy>` | `allow_implicit_invocation` (default `true`), `products`. |
| `path_to_skills_md` | `AbsolutePathBuf` | Host-side canonical path. |
| `scope` | `SkillScope` | Source: `User` / `System` / `Plugin` / etc. |
| `plugin_id` | `Option<String>` | If from a marketplace plugin. |
| `remote_plugin_id` | `Option<String>` | If remote. |
| (system) | `enabled` | Computed from `disabled_paths`. |

In your frontmatter, the **only fields that matter for LLM matching** are `name` and
`description`. The other fields matter for the runtime.

### 2. Write a keyword-greppable description (v0.6.1 format)

```yaml
description: |
  <one-sentence purpose>.
  USE WHEN: <comma-separated concrete signals and keywords>.
  TRIGGER PHRASES: <user-original-language phrases the user might say>.
  SKIP WHEN: <anti-patterns where this skill does not apply>.
```

Why:

- The LLM matches on real signals (`ECONNREFUSED`, `permission denied`, `retries exceeded`, "上下文满了" / "出错了" / "重试"), not abstract prose.
- `USE WHEN` and `TRIGGER PHRASES` are greppable substrings; `SKIP WHEN` reduces false positives.
- Bilingual (English + Chinese) descriptions match user language directly.

### 3. Adopt three-layer matching

When a user types `$skill-name` or `[$skill-name](path)`:

```text
Layer 1 — canonical path:    /path/to/skills/SKILL.md
Layer 2 — discovery path:    skill://skill-name/SKILL.md  (logical)
Layer 3 — plain name:         skill-name                   (only if unambiguous)
```

Rules:

- Layer 1 wins if path matches canonical.
- Layer 2 wins if path matches discovery path AND Layer 1 missed.
- Layer 3 wins ONLY if `skill_count == 1 && connector_count == 0` (uniqueness check via `name_counts`).
- If a structured `UserInput::Skill` already matched some name, **block** that name from Layer 3 (`blocked_plain_names`).

Complexity target: `O(T + (N_s + N_t) * S)` time, `O(S + M)` space (T = text length, S = skill count, M = mentions per input). With ~20 skills and 1KB text, this is sub-millisecond.

### 4. Provide explicit invocation syntax

Two syntaxes, both supported:

```text
$skill-name                                  # plain
[$skill-name](skill://path/SKILL.md)         # linked
```

Exclude environment variables from being mistaken for skills (`is_common_env_var($HOME)` → true, skip). Support the 5 tool mention kinds with 4 path prefixes:

```text
app://app-id/...
mcp://server/tool
plugin://plugin-id/...
skill://skill-name/...
SKILL.md (literal filename)
```

### 5. Detect implicit invocation in shell commands

Before doing the explicit three-layer match, also detect when a shell command references a skill script or document:

```rust
detect_implicit_skill_invocation_for_command(outcome, command, workdir)
```

- Tokenize (Windows: PowerShell; Unix: shlex).
- Look for `python` / `node` / `bash` / `sh` / `pwsh` invocations.
- Look for `Read` operations on `scripts/` or `references/`.
- Match by path (scripts dir → skill) and by doc (read path → skill).

### 6. Cache the loaded snapshot

Use a `SkillRootSnapshotCache<Root>` trait so the loader can re-use a parsed snapshot:

```rust
pub trait SkillRootSnapshotCache<Root>: Send + Sync {
    fn get(&self, root: &Root) -> Option<LoadedSkillRoot>;
    fn insert(&self, root: Root, snapshot: LoadedSkillRoot);
}
```

`SkillRootSnapshots` is `Arc<dyn SkillRootSnapshotCache<Root>>` with identity-based
`Hash` / `Eq` (uses `Arc::ptr_eq`). Cache key safety: clones share the same `Arc`,
so identity equality holds.

### 7. Load with errors-as-data

`LoadedSkillRoot { skills, errors: Vec<SkillError>, ... }` — never let one bad skill
kill the whole root. Collect errors and surface them at the top.

## Output contract

A skill that follows this design:

- Has a 64-char-max `name` and a greppable 128-char-max `description`.
- Supports both `$name` plain and `[$name](path)` linked mention.
- Three-layer matching with uniqueness check on plain name.
- Implicit invocation detection in shell commands.
- Cached snapshot with identity-based hashing.
- Errors collected per-skill, never aborting the whole root.

## Common pitfalls

- **Plain name on a duplicate** → ambiguous; ignored. Always provide a path or qualify with the structured form.
- **Description too abstract** → LLM cannot match. Use the 4-line `USE WHEN / TRIGGER PHRASES / SKIP WHEN` format with concrete keywords.
- **Bypassing the uniqueness check** → two skills fire from one mention. Always require `skill_count == 1`.
- **Forgetting `is_common_env_var`** → `$HOME` / `$PATH` become "skill mentions". Filter them.
- **Loading all skills on every mention** → slow. Use `SkillRootSnapshotCache`.
- **Frontmatter name > 64 chars** → rejected by parser. Count your characters.
- **Skills with `description: ""` → MissingField error**. Description is mandatory.

## Example — minimal frontmatter

```yaml
---
name: my-skill
description: |
  Detect a specific failure mode in the running session and recover.
  USE WHEN: ECONNREFUSED, permission denied, retries exceeded, "can't connect" / "出错了" / "重试" / "权限".
  TRIGGER PHRASES: "recover", "retry failed", "switch tool", "ask me", "出错了", "重试".
  SKIP WHEN: short task, in middle of dictating.
license: Apache-2.0
compatibility: Requires MiniMax Code with Agent Plugins 1.0 support.
metadata:
  author: you
  version: "0.1.0"
---

# My Skill

... the actual instructions ...
```

## Verification checklist

- [ ] Frontmatter has `name` (≤ 64) and `description` (≤ 128, ≥ 1, non-empty after `sanitize_single_line`).
- [ ] Description uses the 4-line `USE WHEN / TRIGGER PHRASES / SKIP WHEN` format.
- [ ] Description is bilingual if your users write in multiple languages.
- [ ] Three-layer matching is implemented: canonical path → discovery path → unique plain name.
- [ ] `name_counts` is built once per selection and consulted for uniqueness.
- [ ] `is_common_env_var` filters out `$HOME` / `$PATH` etc.
- [ ] Implicit invocation detection tokenizes per-platform (PowerShell vs shlex).
- [ ] Snapshot cache is identity-based (`Arc::ptr_eq`).
- [ ] Load errors are collected per-skill, never abort the root.

