Add the in-process Projects surface (project landing page — chat input + project files + instructions + assigned agents) to a Next.js app
Scanned 9/2/2026
Install to Claude Code
npx -y skills add iblai/vibe --skill iblai-vibe-project --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Iblai Vibe Project?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/iblai-iblai-vibe-project)More formats (shields.io, HTML) on the badges page.
---
name: iblai-vibe-project
description: Add the in-process Projects surface (project landing page — chat input + project files + instructions + assigned agents) to a Next.js app
globs:
alwaysApply: false
---
# /iblai-vibe-project
Add the ibl.ai **project** surface to your Next.js app: project files,
project instructions, list of agents assigned to a project, and the
project-scoped chat input. Reuses the SDK's `Chat` component — when you
pass a `projectId`, the SDK automatically swaps its default
welcome/explore surface for `ProjectLandingPage`. The same `<Chat>` you
already mounted for `/iblai-vibe-agent-chat` does both modes; this skill
just adds the routing + URL plumbing.


> **What you get:** a route at `/agents/[mentorId]/projects/[projectId]`
> that reads both IDs from the URL, hands them to `<Chat>`, and the SDK
> renders the project landing page (chat input + Project files / Add
> project instructions cards + Project Agents grid). Clicking "Project
> files" opens the SDK's `ProjectFilesModal`. Clicking "Add project
> instructions" opens `ProjectInstructionsModal`. "Add Agent" opens
> `AddMentorToProjectModal`.
Do NOT add custom styles, colors, or CSS overrides to the SDK
components. They ship with their own styling. Keep them as-is.
Do NOT implement dark mode unless the user explicitly asks for it.
> **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
This skill is a **thin delta** on top of `/iblai-vibe-agent-chat`. It does
not duplicate the provider/store/peer-dep wiring — most of the
integration cost lives over there. Run `/iblai-vibe-agent-chat` first.
- `/iblai-vibe-agent-chat` complete (providers wrapped, store reducers
registered, service worker shipped, peer deps installed). The project
surface uses the **same** `<Chat>` component, so the same wiring
applies.
- An agent/mentor ID — same as `/iblai-vibe-agent-chat`.
- A **real `projectId`** to test against. Either:
- Create one in the admin UI, or
- List the user's projects:
`GET https://api.$DOMAIN/dm/api/ai-mentor/orgs/<tenant>/users/<username>/projects/?limit=5`
(use `Authorization: Token <axd_token>`; `$DOMAIN` is `DOMAIN` from
`iblai.env`, default `iblai.app`).
- **Minimum SDK versions.** The `projectId` prop on `<Chat>` and the
`ProjectLandingPage` slot were added in:
| package | min version |
|---|---|
| `@iblai/iblai-js` | `^2.5.10` |
| `@iblai/agent-ai` | `^2.8.1` |
- **Pre-flight prop check (do this before writing the route):**
```bash
grep -q "projectId?: string" \
node_modules/@iblai/web-containers/dist/next/index.d.ts \
&& echo "projectId prop present" \
|| echo "MISSING — upgrade @iblai/web-containers (Step 2)"
```
## What Gets Wired
| File | Change |
|------|--------|
| `package.json` | (No new packages — already covered by `/iblai-vibe-agent-chat` if both are on the same SDK release. If the host is on an older SDK, bump to the versions above.) |
| `app/agents/[mentorId]/projects/[projectId]/page.tsx` | New route rendering `<Chat>` with `projectId={projectId}` (Step 3) |
## Step 1: Confirm `/iblai-vibe-agent-chat` is Wired
The project surface inherits **all** of `/iblai-vibe-agent-chat`'s setup.
Don't proceed until you confirm:
| Check | Command / file |
|---|---|
| Auth providers tree wrapped in `<ServiceWorkerProvider>` | `providers/index.tsx` |
| `skip={isSsoLoginRoute}` on Auth + Tenant providers | `providers/index.tsx` |
| `chat`, `chatInput`, `chatSliceShared`, `files`, `rbac`, `subscription`, `topBanner` reducers registered | `store/index.ts` |
| `public/sw.js` present | `ls public/sw.js` |
| `@iblai/agent-ai` installed | `cat node_modules/@iblai/agent-ai/package.json \| grep version` |
| `/agents/[mentorId]/chat-new` route renders for an authed user | open in browser |
If any of these fail, run `/iblai-vibe-agent-chat` first.
## Step 2: Bump the SDK if Needed
If `/iblai-vibe-agent-chat` was set up against an older SDK release, bump
the packages so `projectId` is recognized:
```bash
pnpm add @iblai/iblai-js@^2.5.10 @iblai/agent-ai@^2.8.1
```
(v2 bundles `web-containers`, `web-utils`, and `data-layer` as
`@iblai/iblai-js/*` subpaths — the separate `@iblai/web-*`/`@iblai/data-layer`
packages are v1-era and must not be installed alongside v2.)
Run the **Pre-flight prop check** in *Prerequisites* to confirm
`projectId?: string` is on the `Chat` Props type.
## Step 3: Create the Route
Create `app/agents/[mentorId]/projects/[projectId]/page.tsx`:
```tsx
"use client";
export const dynamic = "force-dynamic";
import { Suspense, useEffect, useState } from "react";
import { useParams, useRouter } from "next/navigation";
import { Chat, type ChatConfig } from "@iblai/iblai-js/web-containers/next";
import {
useUsername,
useAxdToken,
useUserTenants,
useVisitingTenant,
useIsAdmin,
} from "@iblai/iblai-js/web-utils";
import { redirectToAuthSpa } from "@/lib/iblai/auth-utils";
import config from "@/lib/iblai/config";
export default function AgentProjectPageWrapper() {
return (
<Suspense fallback={null}>
<AgentProjectPage />
</Suspense>
);
}
function AgentProjectPage() {
const { mentorId, projectId } = useParams<{
mentorId: string;
projectId: string;
}>();
const router = useRouter();
const [tenantKey, setTenantKey] = useState("");
useEffect(() => {
const appTenant = localStorage.getItem("app_tenant");
const tenant = localStorage.getItem("tenant");
let currentTenant = "";
try {
currentTenant =
JSON.parse(localStorage.getItem("current_tenant") ?? "{}")?.key ?? "";
} catch {}
setTenantKey(
appTenant || currentTenant || tenant || config.mainTenantKey(),
);
}, []);
const username = useUsername();
const axdToken = useAxdToken();
const { userTenants } = useUserTenants();
const { visitingTenant } = useVisitingTenant();
const isAdmin = useIsAdmin();
const chatConfig: ChatConfig = {
baseWsUrl: () => config.wsUrl(),
supportEmail: () =>
process.env.NEXT_PUBLIC_SUPPORT_EMAIL ?? "support@ibl.ai",
authUrl: () => config.authUrl(),
mainTenantKey: config.mainTenantKey(),
navigateToAdminBilling: () =>
router.push(`/agents/${mentorId}/settings?tab=billing`),
navigateToExplore: () => router.push("/agents"),
navigateToMentor: (id) => router.push(`/agents/${id}`),
};
// Gate render until all 3 IDs are available — projectId from URL,
// mentorId from URL, tenantKey from localStorage.
if (!tenantKey || !mentorId || !projectId) return null;
return (
<div className="flex h-screen w-full flex-col">
<Chat
isPreviewMode={false}
mentorId={mentorId}
tenantKey={tenantKey}
projectId={projectId} /* ← only change vs /iblai-vibe-agent-chat */
config={chatConfig}
redirectToAuthSpa={redirectToAuthSpa}
username={username ?? null}
userTenants={userTenants ?? []}
visitingTenant={visitingTenant}
axdToken={axdToken ?? ""}
userIsStudent={!isAdmin}
/>
</div>
);
}
```
**Why each piece:**
| Item | Why |
|------|-----|
| Same wrapper / Suspense / `dynamic` / `chatConfig` / hooks as `/iblai-vibe-agent-chat` | The SDK component is the same; only the `projectId` prop differs. |
| `mentorId` from `useParams<{ mentorId; projectId }>()` | Project chats are agent-scoped — the WebSocket session still needs a agent. |
| `projectId` from `useParams<...>()` | Triggers the SDK to render `ProjectLandingPage` instead of the default welcome surface. |
| `if (!tenantKey \|\| !mentorId \|\| !projectId) return null` | All three are required; render only when all are resolved. |
| `tenantKey` from `localStorage` (`appTenant` / `current_tenant` / `tenant` / `config.mainTenantKey()`) | Matches `/iblai-vibe-agent-chat`'s pattern. |
## What the SDK Does With `projectId`
`<Chat>` itself doesn't change shape. Internally, when `projectId` is
truthy, the bundled `WelcomeChatNew` slot switches its render branch:
- Calls `useGetUserProjectDetailsQuery({ id: parseInt(projectId) })`
- If the project resolves, renders `<ProjectLandingPage>` with the
project name, chat input, **Project files** card (opens
`ProjectFilesModal`), **Add project instructions** card (opens
`ProjectInstructionsModal`), and **Project Agents** grid (opens
`AddMentorToProjectModal` from the "Add Agent" button)
- If the project 404s, the surface renders empty — you'll see a clean
body and a `[data-layer] API error` in the console. Don't treat that
as a bug in this skill; verify the `projectId` exists for the
authenticated user/tenant.
The chat input you see in the project surface is the same chat input
as the regular `<Chat>` flow — sending a message creates a session
**scoped to this project + agent combo** (the WebSocket URL still
uses `mentorId`).
## Props
The full `Chat` props table is in `/iblai-vibe-agent-chat`. The
project-relevant delta:
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `projectId` | `string` | no | Stringified integer matching a project the user has access to. When set, the welcome surface switches to `ProjectLandingPage`. |
| `mentorId` | `string` | yes | Still required even with `projectId` set — the chat session is agent-scoped. |
`ChatConfig` is identical to `/iblai-vibe-agent-chat` — no project-specific
fields.
## Step 4: Verify
1. `pnpm build` — must pass with zero errors.
2. `pnpm dev`, log in, navigate to
`http://localhost:3000/agents/<mentorId>/projects/<projectId>`.
3. You should see:
- The project name as the page heading (folder icon next to it)
- "AI is capable of making mistakes…" disclaimer
- "Ask anything" chat input with the standard action buttons (Canvas,
Prompts, voice, screen-share, send) + Web Search toggle
- "Project files" card showing "N files added"
- "Add project instructions" card
- "Project Agents" section with the assigned agent cards and an
"Add Agent" button
4. Click "Project files" → SDK's `ProjectFilesModal` opens with the
datasets table.
5. Open `/agents/<mentorId>/projects/<bad-id>` to confirm graceful 404
handling (empty body + data-layer console error, no app crash).
If the project surface renders but the chat WebSocket can't connect,
that's a backend issue (LLM config on the agent) — see
`/iblai-vibe-agent-chat`'s **Known issues**.
**Brand guidelines**: [BRAND.md](https://raw.githubusercontent.com/iblai/vibe/refs/heads/main/BRAND.md)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!