# Skills & entity pills

An assistant whose capabilities are exposed as / skill pills and skill badges — typing / opens a menu that inserts an atomic pill and drives a tailored reply, powered by kai-chat and kai-skills.

An assistant that surfaces its capabilities as <code>/</code> skill pills. Type <code>/</code> in the composer to open the menu, pick a skill like <strong>Summarize</strong> or <strong>Code Review</strong>, and send it to get a reply tailored to that capability. The badges above the thread show the same skills as <code>&lt;kai-skills&gt;</code>.

## How it works

`<kai-chat>` owns the thread and the composer. Pass a `triggers` array as a property — typing `/` opens a menu, and choosing a skill inserts an atomic **pill** into the composer. On `kai-submit` the pill is flattened into the message text (using its `promptText`), so you can route to the matching capability. `<kai-skills>` renders the same capabilities as badges from a separate `skills` property.

```html
<kai-skills id="skills"></kai-skills>
<kai-chat id="chat" chat-title="Workspace assistant"></kai-chat>

<script type="module">

  await customElements.whenDefined('kai-chat');

  const chat = document.getElementById('chat');
  const skills = document.getElementById('skills');

  // `/` skill triggers — each item becomes an atomic pill when chosen.
  chat.triggers = [
    { char: '/', kind: 'skill', items: [
      { id: 'summarize',   label: 'Summarize',   description: 'Condense a thread into key points', promptText: 'Summarize this thread.' },
      { id: 'translate',   label: 'Translate',   description: 'Translate text into another language', promptText: 'Translate this text.' },
      { id: 'code-review', label: 'Code Review', description: 'Review a diff for bugs and style', promptText: 'Review this diff.' },
      { id: 'search-docs', label: 'Search Docs', description: 'Search the knowledge base', promptText: 'Search the docs.' },
    ] },
  ];

  // Skill badges: { id, name }
  skills.skills = [
    { id: 'summarize',   name: 'Summarize' },
    { id: 'translate',   name: 'Translate' },
    { id: 'code-review', name: 'Code Review' },
    { id: 'search-docs', name: 'Search Docs' },
  ];

  // The submitted value carries the pill's prompt text — route on it.
  chat.addEventListener('kai-submit', async (e) => {
    const prompt = e.detail.value;
    const aId = crypto.randomUUID();

    chat.messages = [
      ...chat.messages,
      { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: prompt }] },
      { id: aId, role: 'assistant', parts: [] },
    ];
    chat.loading = true;

    // Dispatch to the matching capability on your backend.
    const reply = await runCapability(prompt);
    let answer = '';
    for await (const token of reply) {
      answer += token;
      chat.messages = chat.messages.map((m) =>
        m.id === aId ? { ...m, parts: [{ type: 'text', text: answer }] } : m,
      );
    }
    chat.loading = false;
  });
</script>
```

**`triggers` shape** — each entry is `{ char, kind, items }`; each item:

| field | type | notes |
|---|---|---|
| `id` | `string` | Stable identifier; carried on the emitted `entities` |
| `label` | `string` | Shown in the menu and on the pill (the sigil is rendered for you) |
| `description` | `string?` | Secondary text shown beside the label |
| `promptText` | `string?` | What the pill flattens to in the submitted text (defaults to `label`) |

Use `kind: 'agent'` (or per-item `kind: 'plugin'`) and `char: '@'` for an agent/plugin menu. Set `triggers` on `<kai-prompt-input>` directly when you compose the input yourself instead of using the full `<kai-chat>` — there, `kai-submit` and `kai-value-change` also carry the structured `doc` + `entities`.

## Next steps

- **[`kai-chat` reference](/components/chat/)** — the `triggers` and `kindIcons` props plus the `kai-submit` event.
- **[`kai-composer` reference](/components/composer/)** — the full entity-pill model behind the input.
- **[`kai-skills` reference](/components/skills/)** — the `skills` property and the declarative `<kai-skill>` children path.
- **[Streaming recipe](/guides/recipes/streaming/)** — parsing SSE from your backend and driving the token loop.
