Last updated

/iblai-vibe-agent-skills

Mirrored from iblai/vibe Β· skills/agents/iblai-vibe-agent-skills/SKILL.md. This page is generated β€” edit it in the repository, not here.

Add the agent Skills tab (AgentSkillsTab) -- 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 summarizing a meeting or searching the web. The agent reads a skill only when it is relevant, so it can carry many without bloating every conversation. Scope: skills are defined per organization (or private to one agent); the tab manages which of them are attached to this agent. It has two sub-tabs, Agent Skills and Available Skills, a New Skill button, and Edit dialogs with a Resources file manager. Users invoke a skill from the chat composer with / (Step 4). Skills need no sandbox.

Agent Skills -- the skills attached to this agent, each with its /slug, an on/off switch (the assignment's enabled), and Remove.

Skills -- Agent Skills
Skills -- Agent Skills

Available Skills -- the organization's catalog, paged 10 at a time: name, version, category and Only This Agent badges, description, and Add (or a green Added chip once attached).

Skills -- Available Skills
Skills -- Available Skills

Row actions -- each catalog row has a switch (the skill's own enabled) and a menu with Edit and Delete, shown only for skills the organization owns.

Skills -- row actions
Skills -- row actions

New Skill -- Only This Agent, Name, Slug, Version, Category, Description, and Instruction (a rich-text playbook).

Skills -- New Skill dialog
Skills -- New Skill dialog

Edit Skill -> Resources -- files the agent can use with the skill: reference and script (text) or asset (upload), each with a menu.

Skills -- Edit Skill, Resources
Skills -- Edit Skill, Resources

Common setup (brand, conventions, env files, verification): see docs/skill-setup.md.

Prerequisites

  • Auth set up (/iblai-vibe-auth, or vibe-starter).
  • AgentSettingsProvider wraps the route (/iblai-vibe-agent Β§1).
  • @iblai/iblai-js@^2.62.1 (resolves @iblai/web-containers 1.62.1).
  • A real agent UUID. Ask the user; never invent one.
  • The signed-in user is an organization admin (or holds the agent's skill-assignment roles). The tab renders nothing for userIsStudent.
  • Agent Skills apply to Base Agent agents only (template slug base-agent, or the legacy ai-mentor / ai-agent). For another agent type the tab shows "Skills are only available to Base Agents."

Step 1: Check Environment

Look for iblai.env in the project root with PLATFORM, DOMAIN, and TOKEN. If it is missing, 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 AgentSkillsTab and load its permissions

AgentSkillsTab resolves the agent from AgentSettingsProvider, then gates every read and write on the agent's RBAC grants in the Redux store (state.rbac, key /mentors/{mentorDbId}/): view_skill_assignments (the list), create_skill_assignment (Available Skills, Add, New Skill), write_skill_assignment (the switch), delete_skill_assignment (Remove). Nothing fills that store for you -- without the grants the tab shows only "No skills enabled for this agent yet." The chat / picker (Step 4) reads the same grant, so put the loader in a hook:

// lib/iblai/use-agent-grants.ts
"use client";

import { useEffect } from "react";
import { useDispatch } from "react-redux";
import {
  useGetMentorSettingsQuery,
  useGetRbacPermissionsMutation,
} from "@iblai/iblai-js/data-layer";
import { updateRbacPermissions } from "@iblai/iblai-js/web-utils";

/** Loads the agent's `/mentors/{dbId}/` grants into `state.rbac`. */
export function useAgentGrants(tenantKey: string, mentorId: string) {
  const dispatch = useDispatch();
  const [getRbacPermissions] = useGetRbacPermissionsMutation();
  const { data: settings } = useGetMentorSettingsQuery(
    { mentor: mentorId, org: tenantKey },
    { skip: !tenantKey || !mentorId },
  );
  const mentorDbId = settings?.mentor_id;

  useEffect(() => {
    if (!mentorDbId) return;
    getRbacPermissions({
      requestBody: { platform_key: tenantKey, resources: [`/mentors/${mentorDbId}/`] },
    })
      .unwrap()
      .then((permissions) => dispatch(updateRbacPermissions(permissions)))
      .catch((error) => console.error("Could not load the agent's permissions", error));
  }, [mentorDbId, tenantKey, getRbacPermissions, dispatch]);
}
// app/(app)/agents/[mentorId]/skills/page.tsx
"use client";

import { AgentSkillsTab, useAgentSettings } from "@iblai/iblai-js/web-containers/next";
import { useAgentGrants } from "@/lib/iblai/use-agent-grants";

export default function AgentSkillsPage() {
  const { tenantKey, mentorId } = useAgentSettings();
  useAgentGrants(tenantKey, mentorId);

  return (
    <div className="flex h-full flex-col bg-white">
      <AgentSkillsTab />
    </div>
  );
}

The settings query has the same arguments as the tab's, so it is a cache read. The store must include the rbac reducer (vibe-starter's store/iblai-store.ts does).

Lower level: AgentSkills

AgentSkillsTab wraps AgentSkills (still exported from @iblai/iblai-js/web-containers) with the header, the Base Agent check, and the userIsStudent guard. Mount AgentSkills directly only for a custom shell. It takes platformKey, mentorUniqueId, and optional mentorDbId; with mentorDbId it applies the same RBAC gating, without it nothing is gated.

import { AgentSkills } from "@iblai/iblai-js/web-containers";

<AgentSkills platformKey="acme-demo" mentorUniqueId="<agent-uuid>" />;

Step 3: Customize Labels (Optional)

import { AgentSkillsTab } from "@iblai/iblai-js/web-containers/next";

<AgentSkillsTab labels={{ header: { title: "Playbooks" } }} />;

Label groups (SkillsTabLabels): header (title, description), notBaseAgent, and loading. The rows, dialogs, and toasts inside come from the SDK's i18n and are not part of the bundle.

Step 4: Enable the chat / skill picker

The chat composer ships a / skill combobox: 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 slashSkillsEnabled on <Chat> (from /iblai-vibe-agent-chat) and load the agent's grants on the chat page: the composer fetches the agent's skills only when state.rbac holds /mentors/{dbId}/#view_skill_assignments for that agent, and with no grant data it skips the fetch, so the picker never opens. vibe-starter loads only organization-level grants, so call the Step 2 hook in app/(app)/page.tsx:

import { Chat } from "@iblai/iblai-js/web-containers/next";
import { useAgentGrants } from "@/lib/iblai/use-agent-grants";

// inside AgentChat, which already has mentorId:
useAgentGrants(resolveAppTenant(), mentorId);

<Chat
  // ...existing chat config...
  slashSkillsEnabled
/>;

With the grant loaded, the composer lazily fetches the agent's skills on the first / keystroke (20 per page, loading more as the list scrolls; errors degrade to an inactive picker). Users without the grant (usually non-admin members) get no picker. To supply the list yourself, pass slashSkills (an EffectiveAgentSkill[]) and slashSkillsLoading while resolving it; that skips the internal fetch and its grant check. The same three props exist on ChatInputForm. For a fully custom composer use useSlashSkills, useSlashSkillPicker, SlashSkillPicker, and isSlashCommandToken.

Chat composer -- `/` skill picker
Chat composer -- `/` skill picker
Chat page -- `/` picker open above the composer
Chat page -- `/` picker open above the composer

Step 5: Use MCP Tools for Customization

get_component_info("AgentSkillsTab")
get_component_info("AgentSkills")
get_component_info("Chat")

Component Props

<AgentSkillsTab> (from @iblai/iblai-js/web-containers/next)

PropTypeRequiredDescription
labelsDeepPartial<SkillsTabLabels>NoOverride the header, Base Agent note, and loading copy
tenantKeystringNoIdentity override. Falls back to AgentSettingsProvider
mentorIdstringNoIdentity override (agent UUID). Falls back to AgentSettingsProvider
usernamestringNoIdentity override. Falls back to AgentSettingsProvider
userIsStudentbooleanNotrue renders nothing (skills are admin-only). Falls back to AgentSettingsProvider

<AgentSkills> (from @iblai/iblai-js/web-containers)

PropTypeRequiredDescription
platformKeystringYesOrg key
mentorUniqueIdstringYesAgent UUID
mentorDbIdnumber | stringNoAgent DB id; turns on RBAC gating against /mentors/{id}/

<Chat> / <ChatInputForm> slash-picker props

PropTypeRequiredDescription
slashSkillsEnabledbooleanNoTurns the / picker on (default false)
slashSkillsEffectiveAgentSkill[]NoHost-supplied list; skips the internal fetch
slashSkillsLoadingbooleanNoShows a loading row while the host resolves slashSkills

What the tab does

  • 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."
  • Agent Skills lists the agent's skill assignments, paged 10 per page. The switch PATCHes the assignment's enabled; Remove deletes the assignment (the skill itself stays in the catalog).
  • Available Skills lists the whole catalog, paged 10, including disabled skills so they can be re-enabled. Add creates an enabled assignment; rows already assigned show Added. The switch PATCHes the skill. Edit / Delete appear only for skills the organization owns; featured skills from the main organization are read-only.
  • New / Edit Skill -- Only This Agent sets mentor to this agent's UUID (private; takes precedence over a featured main skill with the same slug). The tab then tries to attach the private skill to the agent, which the backend rejects (see Important Notes). Edit adds the General / Resources sub-tabs; a skill private to another agent keeps its owner (the toggle is locked).
  • Resources -- reference and script are text (filename + content); asset is a multipart upload. Row menu: Download (assets), Edit (text), Delete (with confirmation). Paged 20 per page.

From @iblai/iblai-js/web-containers/next:

  • AgentSkillsTab β€” the tab (header, Base Agent check, userIsStudent guard).
  • AGENT_SKILLS_TAB_LABELS, SkillsTabLabels, AgentSkillsTabProps.

From @iblai/iblai-js/web-containers:

  • AgentSkills β€” the manager inside the tab.
  • 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.
  • useGetRbacPermissionsMutation β€” loads the grants the tab gates on (dispatch the result with updateRbacPermissions from @iblai/iblai-js/web-utils).
  • MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE β€” the picker's page size (20).
  • AgentSkill, AgentSkillResource, MentorSkillAssignment, EffectiveAgentSkill β€” payload types.

Platform data

HookPurpose
useGetMentorSkillAssignmentsQuery + create/update/delete mutationsThe agent's assignments (Agent Skills sub-tab)
useGetAgentSkillsQuery + create/update/delete mutationsThe organization catalog (Available Skills, New/Edit)
useGetAgentSkillResourcesQuery + resource mutationsSkill files (Resources)
useGetRbacPermissionsMutationThe agent's skill-assignment grants

REST twin: /iblai-api-agent-skill; the endpoint summary is also at the end of this page.

Step 6: Verify

Run /iblai-vibe-ops-test before telling the user the work is ready, and give every page, control and state this skill added its own checkpoint β€” one Playwright test titled <ID> Β· … in e2e/journeys/, listed in e2e/coverage.json; pnpm e2e:coverage must pass:

  1. pnpm build -- must pass with zero errors.
  2. pnpm test -- vitest must pass.
  3. Touch test /agents/<uuid>/skills in the browser as /iblai-vibe-ops-test says, signed in as an organization admin: Agent Skills and Available Skills both show and New Skill is visible. If only "No skills enabled for this agent yet" renders with no Available Skills sub-tab, the RBAC grants were not loaded (Step 2).
  4. Add a skill from Available Skills; it appears under Agent Skills with its /slug.
  5. If you enabled the picker, open /, type / in the composer: the popup lists that skill. No popup means the chat page did not load the agent's grants (Step 4).
  6. Read the console and check the phone width.

Important Notes

  • Redux store: Must include mentorReducer and mentorMiddleware
  • initializeDataLayer(): 5 args (v1.2+)
  • @reduxjs/toolkit: Deduplicated via webpack aliases in next.config.ts
  • Peer deps: sonner and @iblai/iblai-web-mentor must be installed (pnpm add sonner @iblai/iblai-web-mentor)
  • Base Agent only: AgentSkillsTab shows a note for other agent types; hide the tab yourself with isBaseAgentMentor({ mentorSlug, templateMentorSlug }) when you can.
  • Private skills are not assigned: the backend applies a skill created with Only This Agent to its agent directly and, by design, rejects assigning it (400 "Object with unique_id=… does not exist"). The tab still tries to attach it after creating it, so it toasts "Failed to create skill" although the skill was created and is live. What is lost is visibility: the skill is not listed under Agent Skills, and the / picker (which lists assignments) does not offer it.
  • RBAC grants in the store: see Step 2. A missing grant hides the matching control rather than showing an error.
  • 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.skill is the skill's unique_id, not the integer id. Custom UI joining skills to assignments must key on skill.unique_id. (Skill resources key on the integer skill.id instead.)
  • Private-skill precedence: slugs are unique per organization, so only a featured skill from main can share a private skill's slug; the private one shadows it for that agent (resolveEffectiveAgentSkills ranks private > organization > featured).
  • Featured skills are read-only: featured skills come from the main organization; 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}/dm (vibe-starter's config.dmUrl()). Auth: Authorization: Token <token>.

Skill catalog (platform-level)

MethodPathPurpose
GETagent-skills/List skills β€” filters: enabled, search (name/slug), limit, offset
POSTagent-skills/Create β€” { name, slug, version, category, description, instruction, mentor, enabled }
GETagent-skills/{id}/Retrieve
PATCHagent-skills/{id}/Update
DELETEagent-skills/{id}/Delete

mentor (agent UUID) makes the skill private to that agent; null makes it organization-wide. Featured skills (is_featured) are served read-only to organizations.

Skill resources (files)

MethodPathPurpose
GETagent-skill-resources/List β€” filters: skill (integer pk), file_type, limit, offset
POSTagent-skill-resources/Create β€” { skill, file_type, filename, content } for text; multipart with file for asset
PATCHagent-skill-resources/{id}/Update filename/content
DELETEagent-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

MethodPathPurpose
GETagents/{mentor_unique_id}/skills/Skills bound to this agent
POSTagents/{mentor_unique_id}/skills/Bind β€” { "skill": "<skill-uuid>", "enabled": true }
PATCHagents/{mentor_unique_id}/skills/{id}/Toggle enabled
DELETEagents/{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.

Copyright Β© ibl.ai | support@iblai.zendesk.com