Add the agent Skills tab -- reusable playbooks a Base Agent can
discover and follow. An Agent Skill is a written instruction bundle
(plus optional reference files) for one job, like researching a topic
on the web or reviewing code. The agent reads a skill only when it's
relevant, so an agent can carry many capabilities without bloating
every conversation. The surface has two tabs — Agent Skills (the
agent's own set, with enable/disable toggles) and Available Skills
(the organization catalog, with one-click "Add to Agent") — plus New/Edit
skill dialogs (with a Resources file manager) and the chat /
skill picker that lets users invoke a skill from the composer.
Skills are managed independently of the Claw sandbox — no sandbox
instance is required. (They were previously documented as a section of
/iblai-vibe-agent-sandbox.)








Common setup (brand, conventions, env files, verification): see docs/skill-setup.md.
Prerequisites
- Auth must be set up first (
/iblai-vibe-auth) - MCP server + skills configured (
@iblai/mcpin.mcp.json) - Ask the user for a real
mentorId(agent UUID). Do NOT invent one. - Agent Skills only apply to Base Agent agents (template slug
base-agent, or its legacy aliasesai-mentor/ai-agent). UseisBaseAgentMentor()from@iblai/iblai-js/data-layerif you need to gate the tab per agent type.
Step 1: Check Environment
Before proceeding, check for an iblai.env in the project root. Look for
PLATFORM, DOMAIN, and TOKEN variables. If the file does not exist or
is missing these variables, tell the user:
"You need an iblai.env with your platform configuration. Download the
template and fill in your values:
curl -o iblai.env https://raw.githubusercontent.com/iblai/vibe/refs/heads/main/iblai.env"
Step 2: Mount AgentSkills
AgentSkills is an independent component — it does not read from
AgentSettingsProvider. It takes platformKey and mentorUniqueId
as required props and manages everything else (fetching, dialogs,
pagination, toasts) internally.
// app/(app)/agents/[mentorId]/skills/page.tsx
"use client";
import { useEffect, useState } from "react";
import { useParams } from "next/navigation";
import { AgentSkills } from "@iblai/iblai-js/web-containers";
export default function AgentSkillsPage() {
const { mentorId } = useParams<{ mentorId: string }>();
const [platformKey, setPlatformKey] = useState("");
useEffect(() => {
try {
const resolvedTenant =
localStorage.getItem("app_tenant") ??
(() => {
try {
return JSON.parse(localStorage.getItem("current_tenant") ?? "{}").key;
} catch { return undefined; }
})() ??
localStorage.getItem("tenant") ??
"";
setPlatformKey(resolvedTenant);
} catch {}
}, []);
if (!platformKey) return null;
return (
<div className="flex h-full flex-col bg-white p-6">
<AgentSkills platformKey={platformKey} mentorUniqueId={mentorId} />
</div>
);
}
Skills work without any sandbox connection — org-level skills and the agent's assignments both resolve on their own, so the component always renders its full UI.
Step 3: Enable the chat / skill picker
The chat composer ships a / skill combobox (see the last screenshot):
typing / as the first word opens a popup listing the agent's skills
(name + /slug, arrow keys + Enter/Tab to select, Esc to dismiss).
Selecting inserts /slug into the composer. It is off by default
— opt in with one prop on <Chat> (from /iblai-vibe-agent-chat):
import { Chat } from "@iblai/iblai-js/web-containers/next";
<Chat
// ...existing chat config...
slashSkillsEnabled
/>;
With slashSkillsEnabled, the composer lazily fetches the agent's
skills from the per-agent skills endpoint on the first / keystroke
(20 per page, loading more as the list scrolls; errors degrade to an
inactive picker). To supply the list yourself instead, pass
slashSkills (an EffectiveAgentSkill[]) and slashSkillsLoading
while resolving it — a host-supplied list bypasses fetching entirely.
The same three props exist on ChatInputForm for custom chat
surfaces.
For a fully custom composer, the pieces are exported individually:
useSlashSkills (lazy paged fetch), useSlashSkillPicker (open/close
- keyboard state machine),
SlashSkillPicker(the listbox popup), andisSlashCommandToken(is the composer text a/token).
Step 4: Use MCP Tools for Customization
get_component_info("AgentSkills")
get_component_info("Chat")
Component Props
<AgentSkills> (from @iblai/iblai-js/web-containers)
| Prop | Type | Required | Description |
|---|---|---|---|
platformKey |
string |
Yes | Organization key (org slug) |
mentorUniqueId |
string |
Yes | Agent UUID |
<Chat> / <ChatInputForm> slash-picker props
| Prop | Type | Required | Description |
|---|---|---|---|
slashSkillsEnabled |
boolean |
No | Turns the / picker on (default false). Fetches the agent's skills lazily |
slashSkills |
EffectiveAgentSkill[] |
No | Host-supplied list — overrides the internal fetch |
slashSkillsLoading |
boolean |
No | True while the host is still resolving slashSkills — the popup shows a loading row |
What the component renders
- Header note + New Skill — "Skills added or removed here apply to new chat sessions only. Edits to a skill's instructions apply immediately, including in conversations already in progress." plus the gradient New Skill button.
Agent Skills tab
The agent's effective set: its skill assignments plus its own private skills, deduped by slug and paged 10 per row-page.
- Rows — name, version, badges (category, Native, Featured, Private), description, enable/disable Switch, kebab menu.
- Toggle — for an assigned skill, PATCHes the assignment's
enabled; for a private skill, PATCHes the skill itself. - Kebab menu — Remove from Agent (deletes the assignment;
only for assigned skills), Edit, Delete (edit/delete only
for skills the organization owns — featured skills from the
mainplatform are read-only).
Available Skills tab
The organization's full skill catalog, paged server-side 10 at a time.
- Rows — same name/version/badges/description block.
- Add to Agent — creates a
MentorSkillAssignment(enabled) for the agent. Rows already covered by the agent's set — attached, or shadowed by a same-slug private skill — show a green Added chip instead. Another agent's private skills are not attachable.
New / Edit Skill dialog
- Only This Agent toggle — "Private skills are available to this
agent only and take precedence over platform skills with the same
slug." Sets
mentorto the agent's UUID (null = platform-wide). - Fields — Name, Slug (both required), Version (default
1.0.0), Category (e.g. web, code, data), Description, and Instruction (aRichTextEditor— the playbook the agent follows). - The Edit dialog has General and Resources sub-tabs; files can be attached after the skill is created.
Resources sub-tab (skill files)
Optional files the agent can use with the skill:
- Types —
referenceandscriptare text files (filename + content in a textarea);assetis a binary upload (multipart). - Rows — filename + type chip + kebab (Download for assets, Edit for text files, Delete with confirmation). Paged 20 per page.
Chat / skill picker
- Opens while the composer holds a single
/-prefixed word and at least one enabled skill matches (name or slug, case-insensitive). - ArrowUp/ArrowDown browse, Enter/Tab select (inserts
/slug), Esc dismisses until the token is cleared — plain text starting with/is never blocked. - Skills load lazily on the first
/keystroke and page in as the list scrolls; the popup shows spinner rows while loading.
Related Exports
From @iblai/iblai-js/web-containers:
AgentSkills— the Skills surface.SlashSkillPicker,SlashSkillPickerProps— the/popup listbox.useSlashSkillPicker,isSlashCommandToken— composer keyboard / open-state machine.useSlashSkills— lazy paged fetch of the agent's skills for the picker.
From @iblai/iblai-js/data-layer:
useGetAgentSkillsQuery,useGetAgentSkillQuery,useCreateAgentSkillMutation,useUpdateAgentSkillMutation,useDeleteAgentSkillMutation— skill catalog CRUD.useGetAgentSkillResourcesQuery,useCreateAgentSkillResourceMutation,useUpdateAgentSkillResourceMutation,useUploadAgentSkillResourceAssetMutation,useDeleteAgentSkillResourceMutation— skill file resources.useGetMentorSkillAssignmentsQuery,useGetMentorSkillAssignmentsInfiniteQuery(one growing cache entry per agent — powers the/picker's lazy list),useCreateMentorSkillAssignmentMutation,useUpdateMentorSkillAssignmentMutation,useDeleteMentorSkillAssignmentMutation— per-agent assignments.resolveEffectiveAgentSkills— client-side join of catalog + assignments into the agent's effective set (private > organization > global/featured, deduped by slug).filterSlashSkills— enabled-only name/slug filter used by the picker.isBaseAgentMentor,BASE_AGENT_TEMPLATE_SLUGS— Base Agent gate.MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE— the picker's page size (20).AgentSkill,AgentSkillResource,MentorSkillAssignment,EffectiveAgentSkill— payload types.
Step 5: Verify
Run /iblai-vibe-ops-test before telling the user the work is ready:
pnpm build-- must pass with zero errorspnpm test-- vitest must pass- Start dev server and touch test:
pnpm dev & npx playwright screenshot http://localhost:3000/agents/<id>/skills /tmp/agent-skills.png
Important Notes
- Redux store: Must include
mentorReducerandmentorMiddleware initializeDataLayer(): 5 args (v1.2+)@reduxjs/toolkit: Deduplicated via webpack aliases innext.config.ts- Peer deps:
sonnerand@iblai/iblai-web-mentormust be installed (pnpm add sonner @iblai/iblai-web-mentor) - Base Agent only: Agent Skills apply to Base Agent agents only.
Gate the tab with
isBaseAgentMentor({ mentorSlug, templateMentorSlug })when your app hosts other agent types. - Session behaviour: adding/removing a skill applies to new chat sessions only; editing a skill's instructions applies immediately, including to conversations already in progress. The component surfaces this note in its header — keep it visible in custom UI.
- Skill UUID, not pk:
MentorSkillAssignment.skillis the skill'sunique_id, not the integer id. Custom UI joining skills to assignments must key onskill.unique_id. (Skill resources key on the integerskill.idinstead.) - Private-skill precedence: a skill created with Only This
Agent shadows platform skills with the same slug for that agent
(
resolveEffectiveAgentSkillsranks private > organization > featured). - Featured skills are read-only: featured skills come from the
mainplatform; the API 404s organization writes against them, so the component hides their Edit/Delete actions. Mirror that in custom UI. - Enabled is an AND: an effective skill is enabled only when the
catalog skill AND its assignment are both enabled; the
/picker offers enabled skills only. - Brand guidelines: BRAND.md
Agent Skills REST API
For custom UI beyond the component. All endpoints are prefixed with
${dmUrl}/api/ai-mentor/orgs/{org}/ where dmUrl is
NEXT_PUBLIC_API_BASE_URL. Auth: Authorization: Token <token>.
Skill catalog (platform-level)
| Method | Path | Purpose |
|---|---|---|
| GET | agent-skills/ |
List skills — filters: enabled, search (name/slug), limit, offset |
| POST | agent-skills/ |
Create — { name, slug, version, category, description, instruction, mentor, enabled } |
| GET | agent-skills/{id}/ |
Retrieve |
| PATCH | agent-skills/{id}/ |
Update |
| DELETE | agent-skills/{id}/ |
Delete |
mentor (agent UUID) makes the skill private to that agent; null
makes it platform-wide. Featured skills (is_featured) are served
read-only to organizations.
Skill resources (files)
| Method | Path | Purpose |
|---|---|---|
| GET | agent-skill-resources/ |
List — filters: skill (integer pk), file_type, limit, offset |
| POST | agent-skill-resources/ |
Create — { skill, file_type, filename, content } for text; multipart with file for asset |
| PATCH | agent-skill-resources/{id}/ |
Update filename/content |
| DELETE | agent-skill-resources/{id}/ |
Delete |
file_type is reference, script (text — send content), or
asset (binary — send multipart file; the response carries a
download URL in file).
Per-agent assignments
| Method | Path | Purpose |
|---|---|---|
| GET | agents/{mentor_unique_id}/skills/ |
Skills bound to this agent |
| POST | agents/{mentor_unique_id}/skills/ |
Bind — { "skill": "<skill-uuid>", "enabled": true } |
| PATCH | agents/{mentor_unique_id}/skills/{id}/ |
Toggle enabled |
| DELETE | agents/{mentor_unique_id}/skills/{id}/ |
Unbind |
Uses the canonical agents/ spelling — the mentors/ route is a
deprecated alias slated for removal. The skill field is the UUID
(unique_id), not the integer primary key — keying assignments by
unique_id keeps the binding stable across skill edits.