Stream Builder — scaffold + enhance
Read first (every session): the
skill's
— non-negotiable rules apply, especially
Secrets,
No auto-seeding,
Login Screen first,
Strict mode protection,
Package manager,
Theme,
Reference authority,
Base UI, and
Moderation is Dashboard-only.
This skill covers two flows:
- Track A — Scaffold a new app: Steps 0–7 below. Use when the cwd is empty / new and the user said "build me a … app".
- Track E — Enhance an existing app: see . Skips scaffold + theme; reuses the same SDK wiring and component blueprints.
Preflight: hand off to the
skill before any Steps 0–7 work — it owns CLI install, credential resolution, and auth. Wait for its
readout, then continue from "Start" below. Do not inline-read
/
; loading the CLI skill primes its endpoint cache + cookbook for any ad-hoc query later in the build (RULES.md › CLI safety). The same hand-off applies to any
query the builder needs mid-flow — never improvise an endpoint name.
Start
Once preflight has reported
, announce the network plan once, then
immediately start executing Steps 0–7 — no interactive prompts at the start (the user has authorized the build by asking for it).
Trust readout (announce, then continue on the same turn — do not wait)
Before the first network command, print this verbatim to the user, then proceed straight into Step 0 without stopping for a reply:
Scaffolding now. Network calls you'll see:
- (Vercel) — scaffold + UI components from npm.
npm install <stream-packages> --legacy-peer-deps
— Stream SDKs from npm (, @stream-io/video-react-sdk
, etc.).
- — local CLI, no network; writes (gitignored by the Next.js scaffold's default; Task B verifies).
Interrupt me at any point if something looks wrong. The only step that pauses for explicit consent is the optional third-party skill packs in Task A.2.
Full per-command audit (publisher, why unpinned, what each writes): § Install trust & integrity below. The user's continued silence after the readout is implicit consent for this scaffold; an objection or stop instruction aborts the run.
Shadcn/ui is always installed during Step 3. Third-party
frontend skills (
vercel-react-best-practices
,
,
) are installed
only with explicit user consent — see Task A.2 for the disclosure script. If the user declines, Step 4 proceeds using Stream references only.
Precedence (when the skills are present): Stream references win for SDK wiring; frontend skills guide generic React / UI polish.
Install trust & integrity
The builder runs three classes of network-touching commands. Each is listed here so a reviewer can audit before approving. The full CLI installer audit (SHA-256 verification, TTY confirmation, scoped platform) lives in the
skill's
.
| Command | Publisher | Why unpinned | What it writes |
|---|
| (Task A) | Vercel — | Scaffolder; is the maintainer's documented usage. Pinning ships outdated scaffolds. | Project files in cwd. Next.js scaffold's ignores by default. |
| (Task A.1) | Vercel — same source as above | Same scaffolder; component sync depends on registry parity. | Component files under . |
npm install <stream-packages> --legacy-peer-deps
(Task C) | GetStream (npm) for and ; transitive deps via standard npm trust | Latest published versions of GetStream's own SDKs — same trust model as the CLI itself. | Modules under . Runtime SDKs + transitive deps. |
| (Task A.2) | and | Optional. Markdown-only skill packs; is the published install path. | Markdown files in the user's skills directory. Gated by explicit user consent in Task A.2 — never runs without an affirmative answer. |
| (Task B) | GetStream — installed via the skill's (SHA-256 verified) | n/a (local CLI, no network at this step) | in the project root with + . Task B verifies covers before writing (Next.js scaffold's default already does). The agent never reads (RULES.md › Secrets). |
Reviewer checklist:
- All invocations resolve to the publishers listed above; substitute a different publisher and the install fails.
- runs only after the disclosure prompt in Task A.2 and an explicit user "yes."
- is written by the Stream CLI directly, not by the agent, and is not transmitted into the conversation.
- If the user wants to pin a specific shadcn version, replace with in Tasks A and A.1.
Builder Steps
Execute phases
in order (later steps depend on earlier ones). Do
not run independent phases in parallel. Shell discipline (one
per phase, no
,
standalone) lives in the
skill's
› Shell discipline.
Two-call exception: If you must Read JSON (e.g.
) and then choose IDs, use one call for the read, one batched call for all creates +
.
Step 0: Package manager
Always use
. Never use bun.
Step 1: Auth
Test auth first, then act — don't skip this and don't wait until Step 2 surfaces an error. Run
stream api OrganizationRead
as a probe:
- Exit 0 → already authenticated, continue to Step 1b.
- Exit 2 / "not authenticated" → immediately run as its own Bash invocation. This is a hard constraint:
- Browser PKCE requires an unwrapped call — never chain with , embed in a heredoc, or bundle with other commands.
- Do not ask the user first; just run it. Give it up to ~3 minutes for the browser flow.
- Login hangs past ~60s, or the user reports the browser is stuck → run to clear any stale session state, then retry once. If the second attempt also hangs, stop and ask the user to run themselves (the prefix runs it in-session so you see the result).
Step 1b: Theme pick
Ask the user which Shadcn theme they'd like before doing anything else:
Quick theme pick: I can use a random shadcn theme, or you can design your own at
ui.shadcn.com/create and share the
value (e.g.
). Want a random one or do you have a preset?
STOP here and wait for the user's answer. Do not continue with org/app creation or any other steps until the user responds. Asking a question and continuing to work in parallel is confusing — the user misses the question as output scrolls past.
- User provides a preset → store it for Task A scaffold command.
- User says random / doesn't care / wants to move on → pick a random preset from , , , , , .
Step 2: Create org + app
First, check existing orgs with
stream api OrganizationRead
. If there are already 10 orgs, do NOT create a new one — pick an existing
org and create a new app inside it.
App names are globally unique. Always use
where hash =
.
bash
# Check existing orgs first:
stream api OrganizationRead
# If under 10 orgs, create new:
HASH=$(openssl rand -hex 4)
stream api OrganizationCreate name=builder-$HASH slug=builder-$HASH
# Create app with Feeds v3 + US East (region_id=1):
stream api AppCreate name=app-$HASH org_id=<org_id> is_development=true region_id=1 feeds_version=v3
# Set defaults:
stream config set org <org_id> && stream config set app <app_id>
Never use the auto-created app from OrganizationCreate — it uses Feeds v2 and US Ohio.
Fallback (org limit / 429): Use
to list existing builder orgs, pick one, create a new app in it.
Step 3: Scaffold + .env + SDKs + Configure — SEQUENTIALLY
Scaffold order
Order:
- Steps 1–1b: Auth + theme pick (wait for answer).
- Step 2: Create org/app.
- Task A: Scaffold with Shadcn + Next.js using the chosen preset.
- Task A.1: Add base Shadcn components.
- Task A.2: Disclose + ask about third-party frontend skill installs; install only with user consent.
- Continue with Task B (.env), Task C (SDKs), Task D (CLI config).
Task A: Scaffold — scaffolds Next.js + Tailwind + Shadcn/ui (Base UI) into the current directory. Use the theme preset chosen in Step 1b.
The scaffold command creates a new directory, so we scaffold into a temporary
subdirectory and move everything up:
bash
npx shadcn@latest init -t next -b base -n .scaffold --no-monorepo -p <random-preset> && mv .scaffold/* .scaffold/.* . 2>/dev/null; rm -rf .scaffold
Task A.1: Add base Shadcn components:
bash
npx shadcn@latest add button input textarea card avatar badge separator
Add more components as the use case requires (e.g.
,
,
,
).
Task A.2: Frontend skills — third-party skill packs. You must disclose and ask before installing. Do NOT construct your own command variant.
Print this disclosure verbatim, then stop and wait for the user's answer:
I'd like to install three third-party skill packs that improve generic UI quality:
vercel-react-best-practices
— from
- — from
- — from
The packs are markdown only — no scripts execute. If you say yes, I'll run
once per pack from those GitHub repos at their current
branch (
skips the installer's own confirmation since you've consented here). These aren't required — Stream reference files cover SDK wiring either way. Install them?
- User agrees → run:
bash
npx skills add https://github.com/vercel-labs/agent-skills --skill vercel-react-best-practices -y && npx skills add https://github.com/vercel-labs/agent-skills --skill web-design-guidelines -y && npx skills add https://github.com/anthropics/skills --skill frontend-design -y
- User declines → skip silently and continue to Task B. Do not retry, do not bring it up again this session.
- Install fails → continue with Stream reference files only; mention the failure briefly.
Do
not modify
or
after scaffold — use Shadcn's defaults as-is (RULES.md › Theme).
Task B: .env — run AFTER scaffold so the
lands inside the project directory.
First, verify is gitignored (the
skill's
› Secrets). The Next.js scaffold's default already includes it; this is a safety net for projects whose
was hand-edited or doesn't yet exist:
bash
bash -c 'test -f .gitignore && grep -qE "^\.env" .gitignore || echo ".env*" >> .gitignore'
Then write secrets:
writes
and
— both server-side. The client never reads env vars directly; it gets
,
, and its token from the
response and holds them in React state. No
duplication, no
gymnastics.
Task C: Install Stream SDKs + verify icons — Only what the use case needs:
bash
# Chat: stream-chat stream-chat-react
# Video: @stream-io/video-react-sdk
# Feeds: @stream-io/feeds-react-sdk
# Server: @stream-io/node-sdk
npm install <packages> --legacy-peer-deps
After installing SDKs, verify an icon package is available. Some Shadcn presets bundle one, others don't:
bash
node -e "try{require.resolve('lucide-react');console.log('ICONS_OK')}catch{try{require.resolve('@phosphor-icons/react');console.log('ICONS_OK')}catch{console.log('NO_ICONS')}}"
If
, install
:
npm install lucide-react --legacy-peer-deps
. If an icon package is already present, use that one throughout the app — do not install a second.
Task D: Configure Stream — run the CLI commands from the relevant
(App Integration → Setup) for each product the use case needs.
Step 4: Generate code and UI
Load and
only the relevant
header +
references/<Product>-blueprints.md
for the sections you are implementing — not every reference file.
For multi-product apps (Chat + Video, Chat + Feeds, Video + Feeds, etc.), also load references/CROSS-PRODUCT.md
before writing AppShell — it has the canonical multi-client provider hierarchy and an error → cause → fix table.
Step 5: Verify
Type-check first (reports ALL errors at once, ~3s):
Fix all type errors. Then run the full build:
Fix any remaining errors. Do NOT skip
— it catches every type error in one pass, while
stops at the first error per file and requires multiple rebuild cycles.
Step 6: Start dev server
Pick a random 5-digit port (10000–65535). Run the server using
:
bash
PORT=$((RANDOM % 55536 + 10000))
npx next dev -p $PORT
Important: The dev server is a long-running process. When run in the background it will eventually emit a "completed" notification — this does not mean the server stopped. The server is still running and serving requests. Do not respond to the background-task completion notification by telling the user the server has stopped. If you receive that notification after Step 7, ignore it silently — do not output anything.
Step 7: Summary
Show what was created: org, app, resources, files. Include the local URL. Do NOT say "you can now start the dev server" — it's already running.
End with:
Open
, enter a username, and start testing. Open a second tab with a different username to test multi-user interactions.
Use Case Matching
Only build with the products the user explicitly mentions. If unclear, ask.
| User says | Use case | Products |
|---|
| "Twitch", "YouTube Live", "Kick", "livestream" | Livestreaming | Video + Chat + Feeds |
| "Zoom", "Google Meet", "video call", "meeting" | Video Conferencing | Video [+ Chat] |
| "Slack", "Discord", "team chat", "channels" | Team Messaging | Chat |
| "WhatsApp", "iMessage", "DM", "messaging" | Direct Messaging | Chat [+ Video] |
| "Instagram", "Twitter", "social feed", "Reddit" | Social Feed | Feeds + Chat |
Moderation is configured via CLI during setup only.
Never build moderation review UI in the app (RULES.md › Moderation is Dashboard-only) — review happens in the
Stream Dashboard.
Page Flow
Every app needs a clear navigation structure. Users should always understand where they are and what they can do. Never drop a user into a camera/mic prompt, an empty state, or a feature-heavy screen without context.
Principle: Hub-first
After login, land on a hub — a home screen that shows what's happening and lets the user choose their path. The hub is the anchor; everything else is a destination the user navigates to intentionally.
Flow by use case
Livestreaming (Twitch, YouTube Live, Kick):
Login → Feed hub (live streams + posts) → Watch a stream (viewer: video + chat, no camera)
→ Go Live (explicit action → then camera/mic setup → streaming)
- The feed hub shows live streams (if any) as prominent cards, plus regular posts below
- Clicking a live card opens the watch view — video player + chat as a viewer. No camera permissions.
- "Go Live" is a deliberate action (button in header or dedicated screen). Only THEN prompt for camera/mic. The streamer sees a setup/preview before going live.
- Viewers and streamers are the same user type — the difference is the action they take, not the page they land on.
Video Conferencing (Zoom, Google Meet):
Login → Lobby (list of calls or "start a call") → Join call (camera/mic preview → join)
- Land on a lobby or call list — not directly in a call.
- Joining a call shows a preview screen (camera/mic toggles) before connecting. The user opts in.
Team Messaging (Slack, Discord):
Login → Channel list + active channel → Browse/search channels
- Land on the channel list with the most recent channel open (or a welcome state if no channels).
Direct Messaging (WhatsApp, iMessage):
Login → Conversation list → Open a conversation → Start new conversation
Social Feed (Instagram, Twitter):
Login → Feed hub (follow users + composer + tabs: Timeline | My Posts) → Comments → User profiles
- The user posts to their own feed and reads from (aggregates followed users' posts)
- Feed hub tabs: Use a component with two views:
- Timeline (default) — shows (posts from followed users)
- My Posts — shows (the current user's own posts)
- Refresh button: Place a refresh/reload button next to the tabs. On click, re-call
feed.getOrCreate({ watch: true })
on the active feed to re-fetch the latest activities. This gives users an explicit way to refresh after follows or if real-time events are missed.
- A Follow User input (username + follow button) must be visible so users can populate their timeline
- Without following, the timeline is permanently empty — this component is not optional
- Follow wiring: The Follow component must receive the timeline feed instance and call
timelineFeed.follow('user:targetId')
— not . Using the feed instance keeps in sync so the timeline updates immediately after following.
Key rules
- Camera/mic: opt-in only. Never request permissions on page load. Only when the user takes an explicit action (Go Live, Join Call).
- No empty ambiguity. If there's no content yet, show a clear empty state that tells the user what to do ("No live streams yet — be the first to Go Live").
- Navigation is visible. The user should always be able to get back to the hub. Use the App Header or a sidebar for navigation.
- One primary action per screen. The hub's primary action is browsing/discovering. The watch screen's primary action is viewing. The Go Live screen's primary action is streaming. Don't mix them.
Cross-Product Integration
When building apps that combine multiple products, read each relevant
App Integration section. Key patterns:
- Combined token route: returns tokens for each product (
{ chatToken, videoToken, feedToken, apiKey }
). Upsert only the requesting user — never seed demo users.
- Video + Feeds (Livestreaming): Feed hub separates activities as prominent live cards. "Go Live" posts a live activity via . "End Stream" removes it.
- Video + Chat (Livestreaming): Chat alongside video on the watch screen. Use channel type — one channel per stream, keyed by call ID. Create the chat channel in the route.
- Moderation (all use cases): Run Moderation CLI setup commands from (App Integration → Setup), adjusting channel type name. Never build moderation review UI (RULES.md › Moderation is Dashboard-only) — review happens in the Stream Dashboard.
Authentication
If not authenticated:
- Has account →
- No account → Open
https://getstream.io/try-for-free/
, then after signup
Reference file paths
Blueprint files live under
agent-skills/skills/stream-builder/references/
inside the Stream skill pack. Reference them as
agent-skills/skills/stream-builder/references/FEEDS.md
from the
root of this repository. Do not use machine-specific absolute paths.