Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Iblai Vibe Application

BSecurity

Add the platform application gate (admissions) — the admin Applications tab (forms editor, submissions pipeline, reviewer workspace, blocked users) and the public applicant apply wizard route. Use when the user mentions "applications", "admissions", "apply form", "application form", "enrollment application", "review applicants", "accept/decline applicants", or "apply wizard".

15 stars
0 votes
0 copies
0 views
Added 9/20/2026
developmentgobashreactnodegitapibackend

Works with

terminalcliapimcp

Security Analysis

B75/100
criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study

Scanned 9/20/2026

Install to Claude Code

$npx -y skills add iblai/vibe --skill iblai-vibe-application --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Iblai Vibe Application?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Iblai Vibe Application
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/iblai-iblai-vibe-application/badge)](https://www.skillsdirectory.com/skills/iblai-iblai-vibe-application)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: iblai-vibe-application
description: Add the platform application gate (admissions) — the admin Applications tab (forms editor, submissions pipeline, reviewer workspace, blocked users) and the public applicant apply wizard route. Use when the user mentions "applications", "admissions", "apply form", "application form", "enrollment application", "review applicants", "accept/decline applicants", or "apply wizard".
globs:
alwaysApply: false
metadata:
  kind: ui
---

# /iblai-vibe-application

Add the **platform application gate** (admissions) — the surface a platform
uses to make membership application-only: staff author application forms and
review submissions, applicants fill the form on a public link and track their
outcome.

It ships as two independent halves:

| Half | Component | Where it lives |
|------|-----------|----------------|
| **Admin** | `Admin` → Applications tab | Inside the account/settings page (`Account` → Management tab), or mounted directly |
| **Applicant** | `ApplyWizard` | Two public routes: `/applications/<platform-key>` and `/applications/<platform-key>/<form-id>` |

The two connect through a copyable link: an admin copies a form's applicant
link from the Applications tab and shares it; the link opens the wizard for
that form.

> **Common setup (brand, conventions, env files, verification):** see [docs/skill-setup.md](https://raw.githubusercontent.com/iblai/vibe/refs/heads/main/docs/skill-setup.md).

## Prerequisites

- Auth must be set up first (`/iblai-vibe-auth`)
- MCP server + skills configured (`@iblai/mcp` in `.mcp.json`)
- `@iblai/iblai-js` recent enough to export both surfaces. Verify:
  ```bash
  grep -rl "ApplyWizard" node_modules/@iblai/iblai-js/dist/web-containers | head -1
  ```
  Nothing printed → upgrade (`/iblai-vibe-ops-upgrade`).
- The admin half needs a reviewer account on the platform. The manage
  endpoints are RBAC-gated — see `/iblai-vibe-rbac`.
- The platform must be in **application mode** for the wizard to render a
  form. `open` / `invite_only` platforms render an informational notice
  instead (the gate `mode`, set platform-side, not from this UI).

## 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 the Admin Applications Tab

The Applications tab lives inside the `Admin` component, which the `Account`
component renders under its **Management** tab. If the app already has an
account page (`/iblai-vibe-account`), the tab is one prop away — pass
`authURL` so the copyable applicant links resolve:

```tsx
// app/(app)/account/page.tsx — existing Account page
<Account
  tenant={tenantKey}
  tenants={tenants}
  username={username}
  isAdmin={isAdmin}
  authURL={config.authUrl()}   // ← required for the copyable applicant link
  onInviteClick={() => {}}
  onClose={() => router.push("/")}
  targetTab="management"       // opens straight on Management
/>
```

To give applications their own page instead, mount `Admin` directly:

```tsx
// app/(app)/applications/page.tsx
"use client";

import { Admin } from "@iblai/iblai-js/web-containers";
import config from "@/lib/iblai/config";
import { resolveAppTenant } from "@/lib/iblai/tenant";

export default function ApplicationsAdminPage() {
  const tenantKey = resolveAppTenant();

  return (
    <div className="mx-auto w-full flex-1 overflow-auto px-4 py-8 md:w-[85vw] md:px-0">
      <Admin
        tenant={tenantKey}
        authSpaUrl={config.authUrl()}
        onInviteClick={() => {}}
      />
    </div>
  );
}
```

`Admin` renders the Applications tab **unconditionally** — every other tab
(Users, Groups, Roles, Policies, Teams, Alerts) is behind a
`has*TabPermission` prop, so with none of them passed the page shows
Applications only. The tab reads `platformKey` from `tenant` and fetches
everything itself; no other props are required.

## Step 3: Add the Public Applicant Routes

The wizard is what a form's copied link opens. Create two routes so both the
default-form link and per-form links work:

```
app/applications/[platformKey]/page.tsx            → the platform's DEFAULT form
app/applications/[platformKey]/[formId]/page.tsx   → one specific form
```

Both render the same component:

```tsx
// app/applications/[platformKey]/page.tsx        (and .../[formId]/page.tsx)
"use client";

import ApplicationPage from "@/components/applications/application-page";

export const dynamic = "force-dynamic";

export default ApplicationPage;
```

```tsx
// components/applications/application-page.tsx
"use client";

import { useEffect, useRef, useState } from "react";
import { useParams, useRouter } from "next/navigation";
import { ApplyWizard } from "@iblai/iblai-js/web-containers";
import { useGetAppTokensMutation } from "@iblai/iblai-js/data-layer";
import config from "@/lib/iblai/config";

/** Origin to send the applicant back to after submitting (?redirect-to=). */
function redirectTo(): string | null {
  if (typeof window === "undefined") return null;
  return new URLSearchParams(window.location.search).get("redirect-to");
}

export default function ApplicationPage() {
  const router = useRouter();
  const { platformKey, formId } = useParams<{
    platformKey: string;
    formId?: string;
  }>();
  const [getAppTokens] = useGetAppTokensMutation();
  const [dmToken, setDmToken] = useState<string | null>(null);
  const requested = useRef(false);

  // The gate calls run with the MAIN tenant's DM token — applicants are
  // usually not members of the platform they are applying to. Held in memory
  // only, so the selected tenant's stored token is left untouched.
  useEffect(() => {
    if (requested.current) return;
    requested.current = true;
    (async () => {
      const body = new FormData();
      body.append("platform_key", config.mainTenantKey() || "main");
      const { data } = await getAppTokens(body).unwrap();
      setDmToken(data?.dm_token?.token ?? null);
    })();
  }, [getAppTokens]);

  if (!platformKey || !dmToken) return <div className="p-8">Loading…</div>;

  return (
    <ApplyWizard
      platformKey={platformKey}
      {...(formId ? { formId } : {})}
      token={dmToken}
      dmUrl={config.dmUrl()}
      onComplete={() => router.push(redirectTo() || "/")}
    />
  );
}
```

### Provider requirements for the applicant routes

- The wizard needs the **data-layer store + `initializeDataLayer`** only.
- Do **not** wrap these routes in `TenantProvider` — it blocks on organization
  membership, and applicants are usually not members. In a vibe-starter app
  the provider chain lives in `providers/iblai-providers.tsx`; skip the
  organization layer on these routes the same way it already skips SSO routes:

  ```tsx
  const isApplyRoute = pathname?.startsWith("/applications") ?? false;
  // …
  <TenantProvider skip={isSsoRoute || isApplyRoute} /* … */>
  ```

  Leave `AuthProvider` in place — applicants must still be **authenticated**.
  It sends unauthenticated visitors through SSO with the current path and
  query preserved, so the link's `?redirect-to=` survives the round trip.
- Import the SDK stylesheet once: `@iblai/iblai-js/web-containers/styles`.
- Keep the routes at `app/applications/…`, outside the `(app)` group, so the
  navbar/chrome layout does not wrap the applicant experience.

### The applicant link

The Applications tab copies links in this shape — build them the same way if
the app needs to render one itself (the SDK's internal helper is not exported):

```
<AUTH_URL>/applications/<platform-key>/<form-id>?redirect-to=<origin>
<AUTH_URL>/applications/<platform-key>?redirect-to=<origin>        # default form
```

`AUTH_URL` comes from the `authSpaUrl` / `authURL` prop, else
`window.__ENV__.NEXT_PUBLIC_AUTH_URL`, else `process.env.NEXT_PUBLIC_AUTH_URL`.
With none of them resolvable the copy button hides itself. Hosting the routes
in this app instead of the auth SPA? Pass that origin as `authSpaUrl`.

## Surfaces

### Admin (inside the Applications tab)

| Surface | What it does |
|---------|--------------|
| **Forms list** (entry) | Every application form of the platform: title, version, enabled/disabled, default badge. Per-row: copy applicant link, view submissions, edit. Header: **Blocked users**, **All submissions** (count from pipeline stats), **New form** |
| **Submissions pipeline** | Paginated rows (20/page) with status filter (all + the 9 statuses), debounced search, a structured student filter (`key=value` → `candidate.<key>=<value>`), and sort. Filtering by form is client-side per page — the endpoint has no form filter |
| **Submission workspace** | One application: applicant header, per-student (candidate) cards with status / identity / enrollment-phase badges, decide dialogs (per student or family-wide), answers rendered against the form-version snapshot, attachment downloads, the submit-blocking checklist with waivers + override submit, placements and course assignments for accepted students, internal notes, audit trail |
| **Questions editor** | Create or edit a form: title, description, enabled toggle, "make default", sections (reorder, repeatable, one candidate section) and fields via the question dialog. Save errors come back per `section.field` |
| **Blocked users** | The ban list (entries are created by decline-with-block) with removal |

### Applicant (the wizard)

| State | What renders |
|-------|--------------|
| **Steps** | One step per section, a repeatable candidate section ("Add another student"), explicit **Save draft**, a review step, then submit |
| **Resume** | An editable submission (`draft`, `needs_more_info`) seeds the steps with saved answers |
| **Status page** | A decided/locked submission: family status, per-student outcomes with decision messages, withdraw while undecided, **Renew** once approved (pre-filled from the approved application), the caller's other applications |
| **Mode notice** | Platform is `open` or `invite_only` — nothing to fill in |
| **Gate block** | `already_member`, `open_submission_exists`, `reapply_cooldown`, `renewal_priority_window`, `cannot_apply` render as terminal states |
| **404** | Bare link with no default form, an unresolvable form id, or no renderable schema |

## Form schema contract

A form is `sections` → `fields`, saved as the record's `schema`. The backend
re-validates on save and on submit and stays authoritative.

```jsonc
{
  "title": "Fall Admission",
  "description": "Apply for the fall term.",
  "sections": [
    {
      "key": "students",
      "title": "Students",
      "repeatable": true,      // applicant may add several entries
      "candidate": true,       // ← entries become per-student candidates (max one such section)
      "item_label": "student", // "Add another student"
      "min_items": 1,
      "fields": [
        { "key": "name", "type": "text", "label": "Full name", "required": true },
        { "key": "grade", "type": "select", "label": "Grade",
          "options": [{ "value": "k", "label": "Kindergarten" }],
          "ui": { "widget": "radio" } },
        { "key": "transcript", "type": "file", "label": "Transcript",
          "file_config": { "allowed_extensions": ["pdf"], "max_size_mb": 5, "max_files": 2 } },
        { "key": "consent", "type": "boolean", "label": "I agree", "must_be_true": true },
        { "key": "sibling", "type": "text", "label": "Sibling name",
          "visible_if": { "field": "has_sibling", "operator": "equals", "value": true } }
      ]
    }
  ],
  "resources": [{ "label": "Handbook", "url": "https://…" }]
}
```

- **Field types**: `text`, `textarea`, `email`, `phone`, `number`, `date`,
  `boolean`, `select`, `multi_select`, `file`, `signature`, `info`
  (`info` displays content and collects nothing).
- **Validators**: `min_length`, `max_length`, `pattern`, `min`, `max`,
  `min_items`, `max_items` — each `{ check, value, error? }`.
- **`visible_if`** operators: `equals`, `not_equals`, `in`, `gte`, `lte`.
  The field is `"section.field"` (absolute) or a bare `"field"` (same
  repeatable entry).
- **Uploads** are capped at 5 MB client-side; a field's `file_config.max_size_mb`
  may tighten that, never widen it.
- **`sensitive`** on a section or field masks the answer in the reviewer
  workspace (the detail payload lists masked paths in `sensitive_masked`).
- The question dialog edits label, type, required, placeholder, help text,
  options, `must_be_true`, and `info` content — it preserves `visible_if`,
  `validators`, and `file_config` untouched, so author those in the schema
  and they survive edits. A field's `key` is slugified from its label on
  creation and never changes afterward.

## Statuses and transitions

| Family (application) | Candidate (per student) |
|---|---|
| `draft`, `submitted`, `under_review`, `needs_more_info`, `interview_required`, `waitlisted`, `approved`, `denied`, `withdrawn` | `pending`, `under_review`, `needs_more_info`, `interview_required`, `waitlisted`, `accepted`, `declined` |

Reviewer actions (`transitionApplication`, family-wide or with a
`candidate_id`): `start_review`, `request_info`, `require_interview`,
`waitlist`, `accept`, `decline`. `decline` with `block: true` adds the
applicant to the ban list. `message` reaches the applicant (shown on the
status page); `note` is internal.

`draft` and `needs_more_info` are the **editable** statuses — the applicant
resumes the wizard; everything else routes to the status page.

## `<ApplyWizard>` Props

Import from `@iblai/iblai-js/web-containers` (framework-agnostic bundle).

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `platformKey` | `string` | Yes | Platform whose gate/form is fetched |
| `formId` | `number \| string` | No | Specific form; omit to resolve the platform default |
| `token` | `string` | No | Explicit DM token overriding the stored one on every gate call — use the main organization's token when the wizard runs outside the target organization's app |
| `dmUrl` | `string` | No | DM base URL; the top bar then shows `<dmUrl>/api/core/orgs/<platformKey>/logo/` |
| `logoUrl` | `string` | No | Explicit logo URL; wins over `dmUrl` |
| `brandName` | `string` | No | Top-bar name. Defaults to the form title |
| `showTopBar` | `boolean` | No | Default `true` |
| `showLogo` | `boolean` | No | Default `true` (renders only when a URL resolves) |
| `className` | `string` | No | Wrapper class |
| `onComplete` | `() => void` | No | Fires from the Continue CTA on the submitted / gate-blocked screens. Omitted → no CTA |

## `<Admin>` Props (applications-relevant subset)

Import from `@iblai/iblai-js/web-containers`.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `tenant` | `string` | Yes | Platform key — becomes the tab's `platformKey` |
| `onInviteClick` | `() => void` | Yes | Users-tab callback; pass a no-op when mounting for applications only |
| `authSpaUrl` | `string` | No | Base URL for the copyable applicant link. Falls back to runtime env |
| `has*TabPermission` | `boolean` | No | Gate the other tabs (Users, Groups, Roles, Policies, Teams, Alerts). Applications is always shown |

## Data-layer hooks

From `@iblai/iblai-js/data-layer` — all under
`/api/catalog/applications/platform/…` on the DM service. Every
applicant-facing hook takes an optional `token`.

**Applicant:** `useGetApplicationStatusQuery` (the gate state that drives
routing), `useGetApplicationFormQuery`, `useCreateApplicationDraftMutation`,
`useUpdateApplicationMutation` (replaces the whole responses object, not a
delta), `useSubmitApplicationMutation`,
`useUploadApplicationAttachmentMutation`,
`useDeleteApplicationAttachmentMutation`, `useListOwnApplicationsQuery`,
`useRenewApplicationMutation`, `useWithdrawApplicationMutation`.

**Forms management:** `useGetApplicationFormsQuery`,
`useGetApplicationFormDetailQuery`, `useCreateApplicationFormMutation`,
`useUpdateApplicationFormMutation`.

**Reviewer:** `useGetApplicationsPipelineQuery`,
`useGetApplicationsPipelineStatsQuery`, `useGetApplicationAdminDetailQuery`,
`useTransitionApplicationMutation`, `useCreateApplicationWaiverMutation`,
`useDeleteApplicationWaiverMutation`, `useSubmitApplicationOverrideMutation`,
`useGetApplicationNotesQuery` + create/update/delete,
`useLazyGetApplicationAttachmentFileQuery`, `useGetApplicationBlocksQuery`,
`useDeleteApplicationBlockMutation`, `useGetApplicationPlacementsQuery` +
create/update, `useGetApplicationCourseAssignmentsQuery` + create/delete,
`useInviteApplicationCandidateMutation`,
`useLinkApplicationCandidateMutation`.

Build custom application UI on these hooks rather than calling the endpoints
directly — they carry the cache tags (`platform_applications`,
`platform_application_forms`) the SDK components invalidate.

## Step 4: Use MCP Tools for Customization

```
get_component_info("ApplyWizard")
get_component_info("Admin")
get_component_info("Account")
get_api_query_info("useGetApplicationStatusQuery")
```

## Step 5: Verify

Run `/iblai-vibe-ops-test` before telling the user the work is ready:

1. `pnpm build` — must pass with zero errors
2. `pnpm test` — vitest must pass
3. Start dev server and touch test both halves:
   ```bash
   pnpm dev &
   npx playwright screenshot http://localhost:3000/account /tmp/applications-admin.png
   npx playwright screenshot http://localhost:3000/applications/<platform-key> /tmp/apply.png
   ```

End-to-end check: create a form in the editor, enable it, mark it default,
copy its link, open it in a private window as a non-member account, submit,
then decide it from the pipeline.

## Important Notes

- **Redux store**: must include `mentorReducer` and `mentorMiddleware`; the
  platform API slice ships inside those bundles.
- **`initializeDataLayer()`**: 5 args (v1.2+).
- **`@reduxjs/toolkit`**: deduplicated via webpack aliases in `next.config.ts`.
- **Peer dep**: `sonner` — the admin surfaces toast on save/decide.
- **Token nuance**: the wizard's `token` is held in memory and never written
  to storage, so the selected organization's `dm_token` stays intact. Skip it and
  the wizard uses the current organization's stored token — correct only when the
  applicant is already a member of that organization.
- **Application rules are per form.** A family may hold applications on two
  forms of one platform; a submission on a sibling form does not block a
  fresh apply on this one.
- **No default form set** and several forms active → a bare
  `/applications/<key>` link 404s. Either mark one form default or hand out
  per-form links.
- **Blocking checklist**: a reviewer can waive individual required items
  (excused items stop blocking) or force-submit a stuck draft with
  `submitApplicationOverride`, which waives every missing required item.
- **Placements and course assignments** only apply to `accepted` candidates.
- **The fee panel is deliberately not implemented** in the reviewer
  workspace — fee state comes back on the gate payload but no UI drives it.
- **Brand guidelines**: [BRAND.md](https://raw.githubusercontent.com/iblai/vibe/refs/heads/main/BRAND.md)

Attribution

iblaiiblai
View sourceMore from iblai →
SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

281612 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2132 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →