
Stream
- 1.2k installs
- 17 repo stars
- Updated August 4, 2026
- getstream/agent-skills
Stream is a meta agent skill that automatically discovers, installs, and routes coding-agent requests to related sibling skills defined in a peers.yaml manifest within the GetStream agent-skills pack.
About
Stream is a router skill from getstream/agent-skills that sits at the center of the Stream skill pack. It consults a peers.yaml manifest validated by peers.schema.json to detect which sibling skills are installed, install missing peers, and intelligently forward user requests to the right specialized skill. Developers reach for Stream when they want one entry-point skill instead of manually installing and choosing among multiple related agent capabilities in the same repository. The JSON Schema manifest requires a peers array with at least one peer entry, each specifying name, glob, and install instructions for automated discovery. Stream reduces friction for teams standardizing on the GetStream agent-skills collection and keeps routing logic declarative rather than hard-coded in prompts.
- Dynamically detects installed peer skills via glob patterns on SKILL.md files
- Routes user requests to the best-matching specialized skill in the pack
- Central manifest (peers.yaml) defines name, install command, purpose and detection rules for every sibling skill
- Enforces consistent kebab-case naming with stream- prefix across the entire skill family
- Supports one-command installation policy for every peer skill
Stream by the numbers
- 1,247 all-time installs (skills.sh)
- +56 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #916 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/getstream/agent-skills --skill streamAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.2k |
|---|---|
| repo stars | ★ 17 |
| Security audit | 1 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | getstream/agent-skills ↗ |
How do you auto-route agent requests across a skill pack?
Automatically discover, install, and intelligently route requests to a growing collection of related agent skills from a single pack.
Who is it for?
Developers maintaining a multi-skill agent pack who want automatic peer discovery and request routing from a single entry skill.
Skip if: Teams with a single standalone skill and no sibling peers.yaml manifest to coordinate installs or routing.
When should I use this skill?
The user asks to install Stream, route between agent skills, or manage peers in the GetStream agent-skills pack.
What you get
Installed peer skills, populated peers.yaml manifest, and routed agent invocations to the matching sibling skill.
- Installed peer skills
- Configured peers.yaml routing
- Delegated agent requests
Files
Stream - skill router + CLI
This skill picks the track from the user's input. Building (web + platform) and docs go to dedicated sub-skills; CLI tasks - querying data, configuring an app, onboarding, installing skills - are handled here (see Stream CLI below), since the getstream CLI underlies every track.
Read first: `RULES.md`. Non-negotiable rules apply, including the Peer skills procedure. `peers.yaml` (schema: `peers.schema.json`) is the single source of truth for peer names, Glob paths, install commands, and routing signals.
>
Install a missing peer on demand per its policy inpeers.yaml(Glob its path, run its install command -getstream skills <name>), then invoke via theSkilltool or Read it inline. Don't callSkillbefore the Glob; it surfaces a confusing "Unknown skill" error. Do not stop after naming the track.
---
By task
Build or integrate Stream in a platform-specific app -> peer pack from `peers.yaml` (check peer signals first)
- Match user input or cwd against each peer's
signals(e.g.swift/swiftui/.xcodeproj->stream-swift;react native/expo/stream video react native->stream-react-native) - All peers install on demand - install if missing, then route, no prompt
- Peer signals take precedence over the web rows below. A request like "add a video call to my Expo app" or "scaffold a React Native app with Stream Video" matches
stream-react-native, not the web packs - the platform token wins.
Build / enhance / audit / migrate a web app with Stream (React / Next.js) -> use the stream-react skill (the default web pack when no other platform signal is present)
- "build me a Chat/Video/Feeds app", "scaffold", "create a new ...", "add Chat to this app", "integrate Video", "drop Feeds into ...", "upgrade/migrate ... to vN" - and no platform signal (no
react native,expo,swift,ios,android, etc.) - React / Next.js tokens (
stream-chat-react,@stream-io/video-react-sdk,useCreateChatClient,MessageList, ...) with a build/integrate verb also route here - Covers Track A (scaffold, Steps 0-7), Track E (enhance an existing project), Track F (read-only best-practices audit), Track M (migrate/upgrade an SDK version)
Build with the framework-agnostic builder -> use the stream-builder skill only when the user names it explicitly ("use stream-builder", "/stream-builder")
stream-builderis the generic builder being extended to other app kinds; web React/Next.js defaults tostream-reactabove
Audit/review an existing Stream Video integration against best practices (read-only - no scaffolding, no CLI, no build steps)
- Peer signal (
react native/expo) ->stream-react-native; web / React / Next.js or no platform signal ->stream-react(Track F) - Video only. The dedicated best-practices audit covers Stream Video. Chat/Feeds have no dedicated audit checklist yet -
stream-reacthandles those requests with a general docs-based review and says so up front. - Triggers: "audit/review my video integration", "is my video app production-ready?", "what am I missing before launch?"
- Routes here even when the request contains "check" - the audit intent takes precedence over the CLI "check {anything}" route below
Query Stream data or run a CLI command -> handled here (see Stream CLI below)
- "list calls", "show channels", "any flagged", "find users", a literal
getstreamcommand, or "install the CLI" / "set up stream"
Search Stream SDK documentation -> use the stream-docs skill
- "docs", "documentation", explicit SDK token (
Chat React,Video iOS,Feeds Node,Moderation) - "how do I ... in <framework>", "how does <hook/component/method> work?", "what does <SDK thing> do?"
- No CLI needed - answers come from getstream.io with citations
---
Pick a track
Scan the user's input for the signals below in order. The classifier is deterministic - no probes, no fetches, no CLI checks at this stage.
| Signal in user input | Route |
|---|---|
Upgrade / migrate an installed SDK (build/integrate intent - matched before the docs rows): "upgrade/migrate/bump/update stream-chat-react to vN", "migrate to the new SDK version", "bump my Stream version" - an upgrade verb (upgrade/migrate/bump/update) + a Stream package token, no peer signal. Peer signal (react native / expo / an @stream-io/*-react-native-* token) -> stream-react-native instead (its own migration flow). | stream-react (Track M) |
Audit/review an existing integration (read-only - matched before the SDK-token / docs rows below): "audit/review my video integration", "audit my Chat React integration", "review my Video React app", "is my video app production-ready?", "what am I missing before launch?" - audit/review intent even when an SDK token like `Chat React` / `Video React` is present. Peer signal (react native / expo) -> stream-react-native; web / React / Next.js or no platform signal -> stream-react (Track F - Video has a dedicated checklist; Chat/Feeds get a general docs-based review, stated up front). Also wins over the CLI "check {anything}" row below whenever the request frames a best-practices / production-readiness review rather than a data query. | matching platform pack (read-only audit) |
Explicit SDK/framework token: Chat React, Video iOS, Feeds Node, Moderation, etc. (with or without version), and no build/integrate verb and no audit/review intent (upgrade/migrate/bump/update count as build/integrate verbs -> migration row above; "audit/review" -> audit row above) | stream-docs |
| Words "docs" or "documentation" (and no build/integrate verb) | stream-docs |
"How do I {X} in {framework}?", "How does {hook/component/method} work?", "What does {SDK thing} do?" - and no build/integrate verb. If the request is "how do I add/build/integrate/scaffold {X} in {framework}" and {framework} matches a peer signal, the peer row below wins instead. | stream-docs |
Operational verbs + Stream noun: "list calls", "show channels", "any flagged", "find users", "check {anything}", or a literal getstream command (getstream api, getstream init, getstream login, ...), or "install the CLI" / "set up stream" | Stream CLI - handle here (below) |
Build/integration intent + a token matching a peer's `signals` in [`peers.yaml`](peers.yaml) (e.g. swift / .xcodeproj -> stream-swift; react native / expo / stream video react native / stream video rn -> stream-react-native). This row takes precedence over the web `stream-react` rows below whenever a peer signal is present, and also wins over the docs how-to rows above whenever the request contains a build/integrate verb (`add`, `build`, `integrate`, `scaffold`, `wire`, `set up`, `create`, `upgrade`, `migrate`, `bump`, `update`) alongside the peer signal. Note: react native / react-native (and @stream-io/*-react-native-* tokens) are stream-react-native signals and win over the web react default - including for upgrade/migrate/update requests, which the RN pack handles itself. | matching peer (installed on demand if missing) |
| Literal mention of `stream-builder` / `/stream-builder` (the framework-agnostic builder) | stream-builder |
"Build me a ... app", "scaffold", "create a new ..." + Stream product, OR a React/Next.js token (stream-chat-react, @stream-io/video-react-sdk, useCreateChatClient, ...) + build/integrate verb, and no peer signal present | stream-react (web/Next.js, the default when no platform signal is given) |
| "Add Chat/Video/Feeds to this app", "integrate Stream into", "upgrade/migrate ... to vN" - existing project, and no peer signal present | stream-react (web/Next.js, the default when no platform signal is given) |
| Operational verb wrapped in how-to phrasing (e.g. "how do I list my calls?" - docs or CLI) | Ask one disambiguator |
Onboarding carve-outs. stream-docs answers from documentation only - no shell, no project inspection. Read-only / local-only tracks also skip onboarding: a platform pack's audit track (e.g. stream-react Track F) and migrate track (e.g. stream-react Track M) only inspect/edit local files and the live docs - they do not provision orgs/apps or call getstream api, so they need no CLI onboarding. Only build/integrate work (scaffold a new app, add a product to an existing one) runs getstream init before doing real work.
Docs vs platform packs. A pure how-to or method-lookup question about an iOS/Android/etc. SDK symbol stays in stream-docs - don't pull in a platform pack for a documentation answer. Platform packs (e.g. stream-swift) are for building or integrating - scaffolding projects, wiring packages, generating views.
React framework scope. stream-react scaffolds (Track A) a Next.js app. For enhance / audit / migrate on a non-Next.js React project (Vite, CRA, Remix, TanStack Start, etc.) stream-react still owns it - the Stream SDK wiring is identical - but the agent must adapt the Next.js-specific bits: the server-side token route lives in the project's own backend (not a Next.js /api route), and verification uses the project's build command (npm run build), not next build. Never assume Next.js APIs on a non-Next project.
Disambiguator. If the input fits more than one row (typically operational verb + how-to phrasing), ask one short question and wait. Don't probe before the answer:
Want me to look up the SDK method (docs) or run it now via CLI?
After the answer, route as if the user had given that signal directly.
Bare `/stream` with no args. Render the menu under "Quick navigation" verbatim, then wait for input. No shell execution, no probing, no install.
---
Stream CLI
CLI tasks - querying data, configuring an app, onboarding, installing skills - are handled here; the getstream CLI is the substrate every track uses. Run getstream -h for the command list and getstream <command> -h for usage, and follow what the CLI prints. Safety and posture (no guessing, confirm before writing): `RULES.md` > CLI safety. If getstream isn't installed, ask the user to install it from https://getstream.io and wait - never fetch or run an install script.
| Command | When to reach for it |
|---|---|
getstream init | Onboard a project - authenticate, pick or create an org + app, write credentials. Start here. |
getstream api <Endpoint> | Query data or run a one-off API operation. |
getstream env | Write the app's API key (and secret, for server targets) into the platform's env file. |
getstream token <user> | Mint a token for a user (e.g. demo/dev auth, seeding). |
getstream login | Authenticate (--guest for a throwaway account). |
getstream skills <name> | Install a Stream agent skill on demand (e.g. getstream skills stream-swift). |
---
Sendbird data migration (shared, language-agnostic)
A platform pack's Sendbird migration covers the code/SDK swap (e.g. stream-swift sendbird-migration.md). Moving the data (users, channels, message history, reactions) is server-side and SDK-independent, so it lives once, here, in `sendbird-data-migration.md` - any platform pack hands off to it. It picks a strategy (hard switch / uni-directional / bi-directional sync), exports from Sendbird, builds the JSONL import file, validates, and imports via the getstream CLI (CreateImportURL -> upload -> CreateImport -> GetImport). Read it when a code migration finishes and the user wants their history brought over.
---
Quick navigation
For a bare /stream (and whenever the user wants to pick a skill directly), output the block below verbatim - keep the Core / Platform SDKs split, the examples, and the closing line - then wait:
Stream - Chat - Video - Feeds - Moderation. Tell me what you want, or pick a skill directly:
>
Core
- /stream-react - scaffold, enhance, audit, or migrate a React / Next.js web app with Stream (the default for web) - e.g. "build me a chat app"- /stream-docs - search live SDK docs, with citations - e.g. "how does useChannel work?"-/stream-builder- the framework-agnostic builder (web defaults to/stream-react; pick this only if you name it explicitly)
>
Platform SDKs
- /stream-swift - Swift - SwiftUI - UIKit - iOS- /stream-android - Android - Jetpack Compose - Kotlin- /stream-react-native - React Native - Expo- /stream-flutter - Flutter - Dart>
Or just ask - query data or run getstream commands directly: "list my channels". New to a skill? Just describe the task and I'll install the right one automatically.The closing line is load-bearing: typing an uninstalled slash command errors with "Unknown skill" before this router runs, so natural-language description is the only dead-end-proof path - it routes here and the missing peer is installed per `peers.yaml`. Keep this menu in sync with peers.yaml: every peer there appears here under Core or Platform SDKs, and new platforms get a bullet under Platform SDKs when their entry is added.
---
Hand-off
See preamble: install if missing, invoke via Skill tool, don't stop. Cross-cutting rules in `RULES.md` apply to every sub-skill, including Cross-track follow-ups (offer, don't auto-execute, the natural next action across track boundaries).
---
Support
If the user asks for support or how to contact someone, direct them to getstream.io/contact.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/GetStream/agent-skills/skills/stream/peers.schema.json",
"title": "Stream Router Peer Manifest",
"description": "Schema for skills/stream/peers.yaml - the manifest the Stream router consults to detect, install, and route to sibling skills in the Stream pack.",
"type": "object",
"additionalProperties": false,
"required": ["peers"],
"properties": {
"peers": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/peer" }
}
},
"$defs": {
"peer": {
"type": "object",
"additionalProperties": false,
"required": ["name", "glob", "install", "install_policy", "purpose"],
"properties": {
"name": {
"type": "string",
"pattern": "^stream(-[a-z0-9]+)+$",
"description": "Skill name as used by the Skill tool and the install CLI. Must be kebab-case and prefixed with 'stream-'. The router itself ('stream') is not listed here."
},
"glob": {
"type": "string",
"pattern": "^\\{\\.agents,\\.claude\\}/skills/stream-[a-z0-9-]+/SKILL\\.md$",
"description": "Project-relative Glob path used to test whether the peer is installed. Points at the peer's SKILL.md under either link directory the Stream CLI creates: .agents/skills (Cursor, Codex) or .claude/skills (Claude Code)."
},
"install": {
"type": "string",
"pattern": "^getstream skills stream-[a-z0-9-]+$",
"description": "Exact shell command the router runs to install the peer via the Stream CLI; the router enforces install_policy on top."
},
"install_policy": {
"type": "string",
"enum": ["silent", "ask"],
"description": "silent: install without prompting (default for first-party peers the user already opted into). ask: require explicit user confirmation before installing - reserved for large platform-specific packs the user may not want."
},
"purpose": {
"type": "string",
"minLength": 10,
"description": "One-sentence summary of what this peer does. Surfaced in router prose and in the install-confirm prompt for ask peers."
},
"signals": {
"type": "array",
"items": { "type": "string", "minLength": 1 },
"uniqueItems": true,
"description": "Tokens that, when found in user input or in the cwd (filename, extension), indicate this peer is the right route. Platform packs only - the cli/docs/builder peers are routed via SKILL.md prose, not signal matching."
},
"fallback_on_decline": {
"type": "string",
"pattern": "^stream(-[a-z0-9]+)+$",
"description": "Peer to route to if the user declines an 'ask' install. Typically 'stream-docs' for read-only follow-ups. Only meaningful when install_policy is 'ask'."
}
},
"allOf": [
{
"if": {
"properties": { "install_policy": { "const": "silent" } }
},
"then": {
"not": { "required": ["fallback_on_decline"] }
}
}
]
}
}
}
# Stream router peer manifest.
#
# One entry per sibling skill in the Stream pack. The router (skills/stream/SKILL.md)
# and RULES.md reference this file as the single source of truth for:
# - Glob path used to detect whether a peer is installed. Project-relative:
# `getstream skills` links skills into the project's .agents/skills and/or
# .claude/skills, so the pattern covers both.
# - Install command (`getstream skills <name>`) used when a peer is missing.
# - Install policy: "silent" peers install on demand with no prompt; "ask" peers
# require one confirmation first.
# - Routing signals (platform packs only): tokens that, when seen in user input
# or detected in the cwd, indicate the user wants this pack.
#
# Defaults: `stream` (the router, which includes the CLI), `stream-builder`, and
# `stream-docs` ship by default. The platform packs below install on demand via
# `getstream skills <name>`.
#
# Schema: peers.schema.json (sibling file). Validate with any JSON Schema validator
# after converting YAML -> JSON.
#
# Adding a new peer pack:
# 1. Append an entry below.
# 2. If the pack is platform-specific, add tokens to `signals`. Keep
# `install_policy: silent` so the router auto-installs it on demand.
# 3. Add it to the Quick-navigation menu in skills/stream/SKILL.md. The
# Pick-a-track classifier reads this file at runtime - no code change needed.
peers:
- name: stream-docs
glob: "{.agents,.claude}/skills/stream-docs/SKILL.md"
install: getstream skills stream-docs
install_policy: silent
purpose: Search live Stream SDK documentation (Chat/Video/Feeds/Moderation across React, iOS, Android, Node, Flutter, Unity).
- name: stream-react
glob: "{.agents,.claude}/skills/stream-react/SKILL.md"
install: getstream skills stream-react
install_policy: silent
purpose: "Default web pack: scaffold, enhance, audit, or migrate a React / Next.js app with Stream Chat/Video/Feeds/Moderation. Picked for all web React work unless stream-builder is named explicitly. Routed via SKILL.md prose (the no-platform-signal default), not signal matching - so a bare 'react' token never collides with stream-react-native."
- name: stream-builder
glob: "{.agents,.claude}/skills/stream-builder/SKILL.md"
install: getstream skills stream-builder
install_policy: silent
purpose: "Explicit-invocation only: the framework-agnostic builder (being extended to other app kinds). Web React/Next.js defaults to stream-react; route here only when the user names stream-builder / the generic builder."
- name: stream-swift
glob: "{.agents,.claude}/skills/stream-swift/SKILL.md"
install: getstream skills stream-swift
install_policy: silent
purpose: Build, integrate, or migrate Stream Chat/Video/Feeds in Swift/SwiftUI/UIKit/iOS apps. Includes package setup, auth wiring, view blueprints, and a Sendbird -> Stream Chat migration runbook.
signals:
- swift
- swiftui
- uikit
- ios
- xcode
- .xcodeproj
- Package.swift
- name: stream-android
glob: "{.agents,.claude}/skills/stream-android/SKILL.md"
install: getstream skills stream-android
install_policy: silent
purpose: Build or integrate Stream Chat/Video/Feeds in Android/Jetpack Compose apps. Includes package setup, auth wiring, and screen blueprints.
signals:
- android
- jetpack compose
- jetpack
- compose
- kotlin
- android studio
- gradle
- .gradle
- build.gradle
- name: stream-flutter
glob: "{.agents,.claude}/skills/stream-flutter/SKILL.md"
install: getstream skills stream-flutter
install_policy: ask
purpose: Build or integrate Stream Chat in Flutter apps. Includes pubspec setup, auth wiring, and widget blueprints for stream_chat_flutter and stream_chat_flutter_core.
signals:
- flutter
- dart
- pubspec.yaml
- stream_chat_flutter
- stream_chat_flutter_core
- chat flutter
fallback_on_decline: stream-docs
- name: stream-react-native
glob: "{.agents,.claude}/skills/stream-react-native/SKILL.md"
install: getstream skills stream-react-native
install_policy: silent
purpose: Create, build, or integrate Stream Chat and Stream Video React Native in React Native CLI and Expo apps. Includes package setup, auth wiring, calling lifecycle, and Chat/Video UI blueprints.
signals:
- react native
- react-native
- rn cli
- expo
- expo router
- expo-router
- stream-chat-react-native
- stream-chat-expo
- chat react native
- stream chat rn
- "@stream-io/video-react-native-sdk"
- stream video react native
- stream video rn
- video rn
- video react native
- video call react native
- livestream react native
- audio room react native
Stream - non-negotiable rules
Every rule below is stated once. Other files reference this file - do not duplicate these rules inline.
---
Peer skills
The Stream pack ships a router (this skill) plus a set of peer skills declared in `peers.yaml` (schema: `peers.schema.json`). To check whether a peer is installed, Glob the entry's glob path. To install a missing peer per its install_policy, run its install command. To use it after install, Skill tool if listed in the system reminder's available-skills, otherwise Read the file inline (available-skills doesn't refresh mid-session).
When adding a new peer pack, edit peers.yaml only.
---
Secrets
Never Read/Edit `.env` / `.env.local` in chat - the secret leaks into the conversation. Let the CLI own it: getstream env writes the platform's API key var (and the secret, for server targets) into the right file, and that's all you need. Don't grep, don't cat, don't echo >> a file holding the secret. Never hardcode the secret in code.
The secret is server-side only - never in the client bundle, never NEXT_PUBLIC. The public API key may be client-exposed: getstream env writes it with the framework's client prefix (e.g. NEXT_PUBLIC_STREAM_API_KEY, EXPO_PUBLIC_STREAM_API_KEY). The client may read that var directly or receive apiKey from the /api/token response; either way the token is minted server-side (with the secret) and returned by /api/token.
`.gitignore` before any `.env` write. Before any tool writes secrets to .env (notably getstream env in builder Task B), confirm a line covering .env* exists in .gitignore and add one if missing. The Next.js scaffold's default already does - this rule covers the edge case where the project's .gitignore was hand-edited or doesn't exist yet.
No auto-seeding
Never auto-create demo users (alex, maya, jake, sarah) or sample posts/channels/content. The /api/token route upserts only the requesting user and returns their token(s). Seed functions are opt-in only when the user explicitly asks for sample data.
Login Screen first
Every app opens with a Login Screen as its root page (app/page.tsx). The app never auto-connects or hardcodes a user. Credentials (token, apiKey, userId) live in React state - not localStorage - so each browser tab can operate as an independent user. Layout and behavior details: the active builder pack's builder-ui.md > Login Screen (`stream-react` for web React; `stream-builder` when explicitly invoked).
Strict mode protection
React packs only (stream-react, stream-react-native, and stream-builder's web flow) - not applicable to the Swift/Android/Flutter packs. Protection is per-SDK, not one blanket pattern:
- Chat (client-side): use the official
useCreateChatClient()hook - it handles strict mode internally. NevergetInstance()on the client (singletons break strict mode). - Feeds (client-side):
useCreateFeedsClient()handles the connection internally. Onlyfeed.getOrCreate()needs the manualsetTimeout(50ms)+mountedguard + cleanup inuseEffect. - Video (client-side): the
StreamVideoClientconstructor is synchronous - plainuseState+useEffectwithdisconnectUser()cleanup; no timer, no mounted flag, neveruseMemo. - Server-side:
StreamChat.getInstance(apiKey, apiSecret)is fine (singleton OK). - Never use `useRef` as a "run once" guard in setup effects with cleanup - the ref survives strict mode's unmount->remount cycle, so the second mount skips initialization entirely.
Authoritative detail and canonical snippets live in the React pack's own rules: `stream-react/RULES.md` > Strict mode protection (web; includes the carve-outs and the canonical video snippet). stream-react-native carries its own RULES.md.
Base UI (not Radix)
Shadcn components use @base-ui/react, NOT @radix-ui. Key differences:
- Never use `asChild` - it does not exist in Base UI. Trigger components render children directly.
- Style triggers by passing
classNamedirectly to<DropdownMenuTrigger>,<PopoverTrigger>, etc. - Do NOT wrap triggers with
<Button>- style the trigger element itself.
CLI safety
The getstream CLI owns onboarding, auth, and credentials. Drive it from the Stream CLI section of `SKILL.md` and read the CLI's output to understand what happened - it explains failures and next steps the same way for an agent as for a person. There are no exit-code conventions to memorize.
- No guessing. Endpoint names, parameters, and body shapes come from
getstream api -h(orstream -h), never from training-data recall. If you're about to type agetstream api <Endpoint>you haven't confirmed this turn, stop and look it up first. This applies to every sub-skill, includingstream-builderfollow-ups and one-off "let me just check" queries. - Confirm before writing. Default to read-only. Before any operation that creates, changes, or deletes - or any outward-facing action - describe it and get the user's confirmation. When the CLI refuses an operation and asks for an explicit flag or confirmation, it is flagging a dangerous action: surface it, confirm, then re-run with the flag the CLI named.
- Sign-in opens a browser.
getstream init/getstream loginlaunch a browser flow - run them as their own invocation (never chained with&&or wrapped in a heredoc). If sign-in hangs, ask the user to run it themselves with! <command>.
Onboarding & phase order
Onboarding is owned by the CLI: getstream init authenticates, selects or creates the org + app, and writes project credentials; getstream env provisions the app's server-side secret without exposing it. If getstream isn't installed, ask the user to install it from https://getstream.io and wait - never fetch or run an install script. The `stream-docs` skill skips onboarding entirely and never runs the CLI except an on-demand read-only probe when the SDK can't be resolved from user input.
- Do not load
references/*.md(in thestream-builderskill) until the user names the product(s). - Do not load
builder-ui.md(in thestream-builderskill) before Step 4. - Shadcn/ui is always installed during Step 3 - never skip. Third-party frontend skills (
vercel-labs/*,anthropics/*) require one explicit user confirmation per session before install - see thestream-builderskill'sSKILL.mdTask A.2.
Shell discipline
- Never `bash -ce` or `set -e` in probes or batched phases.
grep(and friends) return exit 1 on "no match," which under-eaborts the whole script and leaves you with partial output. Tolerate specific failures explicitly (|| echo NOT_FOUND,|| true) instead. - One `bash -c` per phase where possible. Chain with
&&on a single line to minimize sandbox approval prompts. If you need to read JSON and then act on it, use one call to read and one batched call for the writes. - Browser sign-in stays its own invocation.
getstream init/getstream loginopen a browser - never chain with&&, embed in a heredoc, or bundle with other commands. Hang recovery is in CLI safety above.
Cross-track follow-ups
A result from one sub-skill can naturally enable an action in another. Surface a follow-up offer when it genuinely helps the user - not as boilerplate on every turn.
- `stream-docs` -> CLI: a docs answer that names a runnable operation can offer "want me to run that now via CLI?" (only if read-safe or clearly operational intent).
- CLI -> `stream-docs`: a CLI result that has a relevant docs page can offer "want the page that explains this?" (link only - don't fetch unprompted).
- `stream-builder` -> `stream-docs`: after scaffold or integration completes, mention that the SDK + version is preloaded and ask-anything is available.
- `stream-docs` -> `stream-builder`: a docs answer that describes a setup-heavy flow can mention scaffold / integrate is available - without running it.
Do not auto-execute a cross-skill action. Offer, then wait for the user to confirm. The skill switch happens through the user's reply, which re-enters the stream router or jumps straight to the named sub-skill.
Theme
Use whatever theme Shadcn generates. Do not modify globals.css after init - no dark mode overrides, no custom variable blocks. The scaffold includes next-themes with a ThemeProvider (system default, class-based toggle) - use it as-is.
Reference authority
Reference files are the only source of truth for HTML structure, SDK wiring, and property paths. Do not generate Stream SDK code from training data. Before writing each component, map it to the relevant references sections (Blueprint -> JSX structure, Wiring table -> data fetching/mutations, Requirements -> setup, App Integration -> routes and patterns).
Package manager
Always use `npm`. Never use bun. Always `--legacy-peer-deps` for Stream packages.
Moderation is Dashboard-only
Never build a moderation review queue, review panel, or flagged-item UI in the app. Moderation review always happens in the Stream Dashboard. The app's role is limited to:
- CLI setup during scaffold (blocklists, automod config via
references/MODERATION.mdSetup) - End-user actions (report, block, mute) if the product needs them
- Do not load Review Queue, Flagged Item, or Auto-Mod Status blueprints from
MODERATION-blueprints.md
---
Sandboxed / blocked shell fallback
If terminal is denied or offline: print commands for the user to run locally; continue with Read/file work only.
Sendbird -> Stream Chat data migration (language-agnostic)
Migrates the data (users, channels, message history, reactions, members, devices) from Sendbird into Stream Chat. It is server-side and SDK-independent - the same procedure applies whether the client is Swift, Kotlin, Flutter, React, or React Native. A platform pack's code migration (e.g. stream-swift sendbird-migration.md) swaps the SDK; this file moves the history behind it.
The docs are the source of truth - fetch them, don't reproduce them. This file is an orchestrator: it picks the strategy with the user, points at the exact doc pages, and adds only the three things the docs don't give you - the interactive decision flow, the verified `getstream` CLI import sequence (the docs show the older standalone stream-cli), and the Sendbird -> Stream transform gotchas. Everything else - strategy pros/cons, field-mapping tables, the JSONL schema, validation errors, the Sendbird event list, the webhook URL - lives in the docs. Fetch the .md twins at run time and follow them:
- Migration guide (strategies, Sendbird->Stream field mappings, real-time sync + webhook +
event list): https://getstream.io/chat/docs/node/migrating-from-sendbird.md
- Import (JSONL format, object schema, ordering, validation, limits):
https://getstream.io/chat/docs/node/import.md
When to run: after the code migration builds and connects. A migrated app talks to an
empty Stream app until you import. Ask the user first - never start a data migration
unsolicited (it touches production data and may incur attachment-transfer cost).
---
Step 1 - Pick a strategy (ASK the user; this is the branch point)
The guide describes three approaches. Summarize them, recommend one, and let the user choose - fetch the guide for the full pros/cons:
- A. No Sync (hard switch) - simplest; needs a maintenance window; users update the app.
- B. Uni-directional sync - zero downtime, the most common choice. Lead with this.
- C. Bi-directional sync - zero downtime, no forced app update; most complex (Enterprise).
All three share the same bulk import (steps 2-4). B and C add real-time sync on top (step 5). So run 2-4 regardless, then add step 5 for B/C.
Ask, e.g.:
The SDK migration is done. Do you also want to migrate your Sendbird data (users,
channels, message history, reactions)? If so: A hard switch (simplest, needs a window),
B uni-directional sync (zero downtime, most common), or C bi-directional sync (zero
downtime, no forced app update, Enterprise)?
---
Step 2 - Export from Sendbird
Pull users, channels, members, messages (reactions ship inline on messages) via the Sendbird Platform API (server-side; needs your Sendbird app id + API token), or the Data Export API for very large datasets. This is the only language-specific step - any HTTP client in any language works; paginate every endpoint and handle rate limits. Keep the raw export on disk for step 3.
---
Step 3 - Build + validate the import file
Read `import.md` for the exact format and follow it - JSONL, one { "type", "data" } object per line, the per-type schema, the required object ordering, the 300 MB / 5 KB / RFC3339 / 64-char limits, and the JSON-Schema validation. Map Sendbird fields to Stream fields per the table in `migrating-from-sendbird.md`. Don't hand-roll either from memory - re-read them.
What those docs don't spell out - the Sendbird -> Stream transform gotchas (this is the real value of doing it as a migration rather than a generic import):
- Channel id length: Sendbird
channel_urloften exceeds Stream's 64-char id limit ->
hash/truncate to a stable id and keep a url->id map so members and messages line up.
- Timestamps: Sendbird emits epoch milliseconds; Stream needs RFC3339. Convert every one.
- Reactions are aggregated in Sendbird (
{ key, user_ids: [...] }) -> emit **one Stream
reaction row per user**, not per key.
- Channel type: map Sendbird group channels ->
messaging, open channels ->
livestream (or your own choice); distinct/1:1 channels use member_ids and omit `id`.
- Attachments need publicly reachable URLs; set
migrate_resources: trueto copy them onto
Stream's CDN instead of hot-linking Sendbird.
Validate against the JSON Schema before uploading - it catches almost everything offline.
---
Step 4 - Import via the getstream CLI
The import docs show the older standalone stream-cli. This pack uses `getstream`, which drives the same import API. Run inside an initialized project (getstream init) so the API key/secret are picked up:
# 1. Get a signed upload URL + the import path
getstream api CreateImportURL --request '{"filename":"migration.jsonl"}'
# -> returns an upload_url (signed S3) and a path
# 2. Upload the file to that signed URL (PUT, raw body)
curl -X PUT --upload-file migration.jsonl \
-H 'Content-Type: application/octet-stream' "<upload_url>"
# 3. Start the import. mode: "upsert" (overwrite) or "insert" (skip existing).
# Add "merge_custom": true to merge custom fields instead of replacing on upsert.
getstream api CreateImport --request '{"path":"<path>","mode":"upsert"}'
# -> returns the created import; note its id
# 4. Poll until completed/failed; list past imports
getstream api GetImport --id <import-id>
getstream api ListImportsStart tiny - import a few users + one channel + a few messages, verify in the dashboard (or getstream api QueryChannels), then run the full export. A full historical import is asynchronous and can take hours-to-days at scale; split exports larger than 300 MB into multiple ordered files.
For strategy A, that's it: schedule the window, run the full import, verify, deploy. Stop here.
---
Step 5 - Real-time sync (strategies B and C only)
Bulk import is a snapshot; B/C also mirror new Sendbird activity into Stream so nothing is lost between the snapshot and cutover. Stream supports Sendbird sync out of the box - read the "real-time sync" section of migrating-from-sendbird.md for the exact webhook URL and the supported-event list. In short: give Stream your Sendbird app id + token, point Sendbird's webhook at the Stream endpoint, and enable the events you need.
- B (uni-directional): Sendbird -> Stream only; flip clients to Stream when ready, then
disable the webhook.
- C (bi-directional): also Stream -> Sendbird so un-upgraded clients keep working during a
gradual rollout - the Enterprise path; involve Stream support to configure both directions.
---
Step 6 - Cut over and verify
Confirm the import finished (GetImport state) and spot-check counts (users, channels, messages) against Sendbird; open a migrated channel and check history, reactions, threads, and attachments in-app; then switch client traffic to Stream. For B/C keep the sync running through the rollout, then disable the Sendbird webhook and decommission Sendbird.
---
Source
https://getstream.io/chat/docs/node/migrating-from-sendbird.mdhttps://getstream.io/chat/docs/node/import.md
Cite these when you apply the runbook, and if anything here disagrees with the live .md, the docs win - re-fetch and follow them.
Related skills
How it compares
Pick Stream when coordinating a declarative multi-skill pack; use individual skills directly when only one capability is needed.
FAQ
What manifest does Stream read for routing?
Stream consults skills/stream/peers.yaml, described by the Stream Router Peer Manifest JSON Schema in peers.schema.json. The schema requires a peers array with at least one peer object containing name, glob, and install fields for detection and installation.
When should developers install the Stream skill?
Developers install Stream when using the GetStream agent-skills pack and wanting one router to discover missing sibling skills, install them automatically, and forward each user request to the most relevant specialized peer skill.
Is Stream safe to install?
skills.sh reports 1 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.