Files
vercel__chat/apps/docs/components/geistdocs/adapter-slug-list.tsx
Ben Sabic 8f3af76565 feat: add create-chat-sdk CLI (#603)
Adds `create-chat-sdk`, a CLI that scaffolds a Next.js Chat SDK bot
project:

```bash
npm create chat-sdk@latest my-bot

# non-interactive
npm create chat-sdk@latest -- my-bot --adapter slack redis -y
```

The user picks platform and state adapters interactively or via
`--adapter`, and the CLI generates a webhook-only project with
`src/lib/bot.ts`, `.env.example`, `next.config.ts`, `package.json`, and
a README, then optionally runs `git init` and installs dependencies.
There are no pages or client UI in the template.

Adapter choices come straight from the `chat/adapters` catalog, so the
CLI has no adapter registry of its own. When a coding agent such as
Cursor or Claude Code runs the CLI, it uses non-interactive defaults and
requires an explicit platform adapter. `--interactive` forces prompts.

## also in this pr

- `google-chat` is renamed to `gchat` everywhere, including docs pages,
the OG image, and adapter catalog. Old URLs redirect permanently,
including language-prefixed and `/og` paths
- a new docs page is available at `chat-sdk.dev/docs/create-chat-sdk`,
and the CLI is promoted on the homepage, package READMEs, and agent
skill
- `create-chat-sdk` releases independently with a minor changeset for
its initial `0.1.0` release

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-06-16 08:48:38 +01:00

77 lines
2.2 KiB
TypeScript

import DynamicLink from "fumadocs-core/dynamic-link";
import {
listPlatformAdapters,
listStateAdapters,
type CatalogAdapter,
} from "chat/adapters";
const adapterHref = (adapter: CatalogAdapter): string => {
const group =
adapter.group === "vendor-official" ? "vendor-official" : "official";
return `/[lang]/adapters/${group}/${adapter.slug}`;
};
const byName = (first: CatalogAdapter, second: CatalogAdapter): number =>
first.name.localeCompare(second.name);
const getGroups = () => [
{
label: "Official platform adapters",
adapters: listPlatformAdapters()
.filter((adapter) => adapter.group === "official")
.sort(byName),
},
{
label: "Vendor-official platform adapters",
adapters: listPlatformAdapters()
.filter((adapter) => adapter.group === "vendor-official")
.sort(byName),
},
{
label: "State adapters",
adapters: [...listStateAdapters()].sort(byName),
},
];
/**
* Collapsible adapter slug list sourced from the `chat/adapters` catalog.
*/
export const AdapterSlugList = () => {
const groups = getGroups();
const adapterCount = groups.reduce(
(count, group) => count + group.adapters.length,
0
);
return (
<details className="rounded-sm border bg-background p-3">
<summary className="cursor-pointer font-medium text-sm">
View available adapter slugs
</summary>
<div className="mt-4 grid gap-4 md:grid-cols-3">
{groups.map((group) => (
<section key={group.label}>
<h3 className="font-medium text-sm">{group.label}</h3>
<ul className="mt-2 space-y-1 text-sm">
{group.adapters.map((adapter) => (
<li key={adapter.slug}>
<DynamicLink
className="font-mono text-primary text-xs no-underline"
href={adapterHref(adapter)}
>
{adapter.slug}
</DynamicLink>
<span className="text-muted-foreground">
{" "}
- {adapter.name}
</span>
</li>
))}
</ul>
</section>
))}
</div>
</details>
);
};