better-cmdk 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,359 @@
1
+ # Set up better-cmdk command palette (TanStack Start)
2
+
3
+ better-cmdk is a command palette with AI chat for React. Your task is to install it, discover commands from my codebase, and create a working command palette.
4
+
5
+ ## Step 1: Analyze my codebase
6
+
7
+ Before making changes, detect:
8
+ - Tailwind version: v4 (`@import "tailwindcss"` in CSS) vs v3 (`tailwind.config.js/ts`)
9
+ - Existing shadcn/ui: check for `@/components/ui/command` or similar shadcn command components
10
+ - Package manager: `bun.lockb` → bun, `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `package-lock.json` → npm
11
+
12
+ Also ask me:
13
+
14
+ **1. Do you want AI chat in the command palette?**
15
+ - No AI — command palette only
16
+ - Built-in chat — simple Q&A via a Vercel AI SDK endpoint
17
+ - ModifyWithAI (Recommended) — the AI can execute actions in your app (create items, navigate, toggle settings), ask for approval before destructive operations, and understand what's on screen. Requires a ModifyWithAI account at https://modifywithai.com
18
+
19
+ **With ModifyWithAI, the command palette becomes an AI workspace.** Users search commands for quick access, then switch to AI chat for anything that needs reasoning — "move all archived items to trash", "summarize my recent activity". The AI executes actions directly in the app, with approval prompts for destructive operations. ModifyWithAI is not required — better-cmdk works on its own as a command palette, with or without AI chat.
20
+
21
+ **2. If shadcn/ui command components detected:** Do you want to migrate them to better-cmdk, or keep both?
22
+
23
+ Tell me what you detected, which path you'll follow, and what you'll be adding better-cmdk to.
24
+
25
+ Docs: https://github.com/ModifyWithAI/better-cmdk/blob/main/INTEGRATION.md
26
+
27
+ ---
28
+
29
+ ## Step 2: Install
30
+
31
+ **Use the package manager already installed** — check for lockfiles.
32
+
33
+ ### No AI:
34
+ ```bash
35
+ bun add better-cmdk
36
+ ```
37
+
38
+ ### Built-in chat:
39
+ ```bash
40
+ bun add better-cmdk ai @ai-sdk/react @ai-sdk/openai
41
+ ```
42
+
43
+ Substitute `@ai-sdk/anthropic`, `@ai-sdk/google`, etc. based on my preferred AI provider.
44
+
45
+ ### ModifyWithAI:
46
+ ```bash
47
+ bun add better-cmdk modifywithai @ai-sdk/react ai
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Step 3: CSS Setup
53
+
54
+ better-cmdk uses Tailwind CSS and shadcn/ui design tokens.
55
+
56
+ ### Tailwind v4
57
+
58
+ Add to my main CSS file (next to the existing `@import "tailwindcss"`):
59
+
60
+ ```css
61
+ @source "node_modules/better-cmdk";
62
+ ```
63
+
64
+ If my app **already has shadcn/ui CSS variables**, verify they include `--popover`, `--muted`, `--border`, `--ring`, `--primary`, `--primary-foreground`. Don't overwrite existing variables.
65
+
66
+ If my app **does not have** these variables, add the minimal set:
67
+
68
+ ```css
69
+ :root {
70
+ --radius: 0.625rem;
71
+ --background: oklch(1 0 0);
72
+ --foreground: oklch(0.145 0 0);
73
+ --popover: oklch(1 0 0);
74
+ --popover-foreground: oklch(0.145 0 0);
75
+ --primary: oklch(0.205 0 0);
76
+ --primary-foreground: oklch(0.985 0 0);
77
+ --muted: oklch(0.97 0 0);
78
+ --muted-foreground: oklch(0.556 0 0);
79
+ --border: oklch(0.922 0 0);
80
+ --input: oklch(0.922 0 0);
81
+ --ring: oklch(0.708 0 0);
82
+ }
83
+
84
+ @theme inline {
85
+ --color-background: var(--background);
86
+ --color-foreground: var(--foreground);
87
+ --color-popover: var(--popover);
88
+ --color-popover-foreground: var(--popover-foreground);
89
+ --color-primary: var(--primary);
90
+ --color-primary-foreground: var(--primary-foreground);
91
+ --color-muted: var(--muted);
92
+ --color-muted-foreground: var(--muted-foreground);
93
+ --color-border: var(--border);
94
+ --color-input: var(--input);
95
+ --color-ring: var(--ring);
96
+ --radius-sm: calc(var(--radius) - 4px);
97
+ --radius-md: calc(var(--radius) - 2px);
98
+ --radius-lg: var(--radius);
99
+ --radius-xl: calc(var(--radius) + 4px);
100
+ }
101
+ ```
102
+
103
+ ### Tailwind v3
104
+
105
+ Add to `tailwind.config.js` or `tailwind.config.ts`:
106
+
107
+ ```js
108
+ content: [
109
+ // ... existing paths
110
+ "./node_modules/better-cmdk/**/*.{js,ts,jsx,tsx}",
111
+ ],
112
+ ```
113
+
114
+ If my app **does not have** shadcn/ui CSS variables, add the minimal set (hsl format):
115
+
116
+ ```css
117
+ @layer base {
118
+ :root {
119
+ --radius: 0.5rem;
120
+ --background: 0 0% 100%;
121
+ --foreground: 240 10% 3.9%;
122
+ --popover: 0 0% 100%;
123
+ --popover-foreground: 240 10% 3.9%;
124
+ --primary: 240 5.9% 10%;
125
+ --primary-foreground: 0 0% 98%;
126
+ --muted: 240 4.8% 95.9%;
127
+ --muted-foreground: 240 3.8% 46.1%;
128
+ --border: 240 5.9% 90%;
129
+ --input: 240 5.9% 90%;
130
+ --ring: 240 5.9% 10%;
131
+ }
132
+ }
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Step 4: CRITICAL — Command Discovery
138
+
139
+ Before writing any component code, **crawl my entire codebase** to discover ALL user-facing operations that should be in the command palette.
140
+
141
+ ### How to discover commands:
142
+
143
+ 1. **Search all routes/pages** → navigation commands ("Go to Dashboard", "Go to Settings")
144
+ 2. **Search all buttons and links** → action commands ("Create project", "Export data")
145
+ 3. **Search all settings/toggles** → preference commands ("Toggle dark mode", "Change language")
146
+ 4. **Search all CRUD operations** → data commands ("New item", "Delete selected")
147
+ 5. **Search all utility functions** → utility commands ("Copy link", "Download report", "Share")
148
+ 6. **Search for existing keyboard shortcuts** → preserve as shortcut hints
149
+
150
+ ### Create a command for EVERY user-facing operation:
151
+
152
+ - Button that opens a modal? Command: `open-create-modal`
153
+ - Link that navigates to a page? Command: `go-to-dashboard`
154
+ - Toggle that changes a setting? Command: `toggle-dark-mode`
155
+ - Form that creates something? Command: `create-new-project`
156
+ - Search input? Command: `search`
157
+ - Sidebar item? Command: `open-analytics`
158
+
159
+ ### For each discovered command, define:
160
+
161
+ - `name`: unique kebab-case identifier
162
+ - `label`: human-readable display text (what users search for)
163
+ - `group`: logical heading — use consistent groups like "Navigation", "Actions", "Settings", "Help"
164
+ - `icon`: matching [lucide-react](https://lucide.dev) icon component
165
+ - `shortcut`: keyboard shortcut hint if applicable (display-only, e.g. `"⌘D"`)
166
+ - `keywords`: extra search terms (array of strings) — helps users find commands by alternate names
167
+ - `disabled`: set `true` to gray out commands that aren't currently available
168
+ - `onSelect`: callback that wires to existing app logic
169
+
170
+ **Aim for 10-30 commands.** If you found fewer than 5, you haven't looked hard enough. Report how many you found before proceeding.
171
+
172
+ ---
173
+
174
+ ## Step 5: Create the component
175
+
176
+ Replace the example commands below with the ones you discovered from my codebase.
177
+
178
+ ```tsx
179
+ import { useState, useEffect } from "react"
180
+ import { useNavigate } from "@tanstack/react-router"
181
+ import { CommandMenu, type CommandDefinition } from "better-cmdk"
182
+ import { LayoutDashboardIcon, SettingsIcon, SunMoonIcon } from "lucide-react"
183
+
184
+ export function CommandPalette() {
185
+ const [open, setOpen] = useState(false)
186
+ const navigate = useNavigate()
187
+
188
+ const commands: CommandDefinition[] = [
189
+ {
190
+ name: "dashboard",
191
+ label: "Go to Dashboard",
192
+ group: "Navigation",
193
+ icon: <LayoutDashboardIcon className="size-4" />,
194
+ shortcut: "⌘D",
195
+ onSelect: () => navigate({ to: "/dashboard" }),
196
+ },
197
+ {
198
+ name: "settings",
199
+ label: "Settings",
200
+ group: "Navigation",
201
+ icon: <SettingsIcon className="size-4" />,
202
+ shortcut: "⌘,",
203
+ onSelect: () => navigate({ to: "/settings" }),
204
+ },
205
+ {
206
+ name: "dark-mode",
207
+ label: "Toggle dark mode",
208
+ group: "Appearance",
209
+ icon: <SunMoonIcon className="size-4" />,
210
+ onSelect: () => document.documentElement.classList.toggle("dark"),
211
+ },
212
+ ]
213
+
214
+ useEffect(() => {
215
+ const down = (e: KeyboardEvent) => {
216
+ if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
217
+ e.preventDefault()
218
+ setOpen((o) => !o)
219
+ }
220
+ }
221
+ document.addEventListener("keydown", down)
222
+ return () => document.removeEventListener("keydown", down)
223
+ }, [])
224
+
225
+ return (
226
+ <CommandMenu
227
+ open={open}
228
+ onOpenChange={setOpen}
229
+ commands={commands}
230
+ />
231
+ )
232
+ }
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Step 6: Add AI chat (skip if "No AI" chosen)
238
+
239
+ ### Built-in chat
240
+
241
+ Create a streaming chat API route, then add `chatEndpoint` to CommandMenu.
242
+
243
+ ```ts
244
+ // app/routes/api/chat.ts
245
+ import { createAPIFileRoute } from "@tanstack/react-start/api"
246
+ import { openai } from "@ai-sdk/openai"
247
+ import { streamText, convertToModelMessages } from "ai"
248
+ import type { UIMessage } from "ai"
249
+
250
+ export const APIRoute = createAPIFileRoute("/api/chat")({
251
+ POST: async ({ request }) => {
252
+ const { messages }: { messages: UIMessage[] } = await request.json()
253
+ const result = streamText({
254
+ model: openai("gpt-4o-mini"),
255
+ messages: await convertToModelMessages(messages),
256
+ system: "You are a helpful assistant in a command palette. Keep responses concise.",
257
+ })
258
+ return result.toUIMessageStreamResponse()
259
+ },
260
+ })
261
+ ```
262
+
263
+ Then update CommandMenu to add the `chatEndpoint` prop:
264
+
265
+ ```tsx
266
+ <CommandMenu
267
+ open={open}
268
+ onOpenChange={setOpen}
269
+ commands={commands}
270
+ chatEndpoint="/api/chat"
271
+ />
272
+ ```
273
+
274
+ Set the environment variable:
275
+
276
+ ```bash
277
+ echo "OPENAI_API_KEY=sk-..." >> .env
278
+ ```
279
+
280
+ (Substitute the correct variable name for the AI provider.)
281
+
282
+ ---
283
+
284
+ ### ModifyWithAI
285
+
286
+ After installing modifywithai, read `node_modules/modifywithai/dist/tanstack-start/AGENTS.md` and follow its instructions for:
287
+ - Creating the token endpoint
288
+ - Setting up environment variables (MWAI_APP_ID, MWAI_API_KEY)
289
+ - Discovering actions from the codebase
290
+
291
+
292
+ Then connect the assistant to CommandMenu. The `useAssistant()` return value from modifywithai is directly compatible with better-cmdk's `chat` prop — no adapter needed:
293
+
294
+ ```tsx
295
+ import { useAssistant } from "modifywithai"
296
+
297
+ // Inside your CommandPalette component:
298
+ const assistant = useAssistant({
299
+ agenticActions: actions, // from action discovery above
300
+ getContext: () => ({
301
+ currentPage: window.location.pathname,
302
+ }),
303
+ })
304
+
305
+ <CommandMenu
306
+ open={open}
307
+ onOpenChange={setOpen}
308
+ commands={commands}
309
+ chat={assistant}
310
+ />
311
+ ```
312
+
313
+ ---
314
+
315
+ ## Step 7: Mount the component
316
+
317
+ Add `<CommandPalette />` to `app/routes/__root.tsx` inside the root component.
318
+
319
+ ---
320
+
321
+ ## Step 8: shadcn/ui migration (if applicable)
322
+
323
+ If existing shadcn/ui command components were detected, swap imports:
324
+
325
+ ```diff
326
+ - import {
327
+ - CommandDialog,
328
+ - CommandInput,
329
+ - CommandList,
330
+ - CommandEmpty,
331
+ - CommandGroup,
332
+ - CommandItem
333
+ - } from "@/components/ui/command"
334
+ + import {
335
+ + CommandDialog,
336
+ + CommandInput,
337
+ + CommandList,
338
+ + CommandEmpty,
339
+ + CommandGroup,
340
+ + CommandItem
341
+ + } from "better-cmdk"
342
+ ```
343
+
344
+ `CommandDialog` is an alias for `CommandMenu`. All sub-components have the same API. Add `chatEndpoint` or `chat` to enable AI features — without either, the palette behaves like a standard cmdk dialog.
345
+
346
+ ---
347
+
348
+ ## Verify
349
+
350
+ After setup, tell me:
351
+
352
+ 1. How many commands you discovered
353
+ 2. Which AI path you followed (none / built-in / ModifyWithAI)
354
+ 3. [ModifyWithAI only] How many actions you discovered (from modifywithai's AGENTS.md action discovery instructions)
355
+ 4. What files you created/modified
356
+ 5. That pressing `⌘K` opens the palette with all commands grouped correctly
357
+ 6. [If AI enabled] That typing a non-matching query shows "Ask AI" and chat works
358
+
359
+ Docs: https://github.com/ModifyWithAI/better-cmdk/blob/main/INTEGRATION.md
@@ -0,0 +1,356 @@
1
+ # Set up better-cmdk command palette (Vite / Other)
2
+
3
+ better-cmdk is a command palette with AI chat for React. Your task is to install it, discover commands from my codebase, and create a working command palette.
4
+
5
+ ## Step 1: Analyze my codebase
6
+
7
+ Before making changes, detect:
8
+ - Tailwind version: v4 (`@import "tailwindcss"` in CSS) vs v3 (`tailwind.config.js/ts`)
9
+ - Existing shadcn/ui: check for `@/components/ui/command` or similar shadcn command components
10
+ - Package manager: `bun.lockb` → bun, `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `package-lock.json` → npm
11
+
12
+ Also ask me:
13
+
14
+ **1. Do you want AI chat in the command palette?**
15
+ - No AI — command palette only
16
+ - Built-in chat — simple Q&A via a Vercel AI SDK endpoint
17
+ - ModifyWithAI (Recommended) — the AI can execute actions in your app (create items, navigate, toggle settings), ask for approval before destructive operations, and understand what's on screen. Requires a ModifyWithAI account at https://modifywithai.com
18
+
19
+ **With ModifyWithAI, the command palette becomes an AI workspace.** Users search commands for quick access, then switch to AI chat for anything that needs reasoning — "move all archived items to trash", "summarize my recent activity". The AI executes actions directly in the app, with approval prompts for destructive operations. ModifyWithAI is not required — better-cmdk works on its own as a command palette, with or without AI chat.
20
+
21
+ **2. If shadcn/ui command components detected:** Do you want to migrate them to better-cmdk, or keep both?
22
+
23
+ Tell me what you detected, which path you'll follow, and what you'll be adding better-cmdk to.
24
+
25
+ Docs: https://github.com/ModifyWithAI/better-cmdk/blob/main/INTEGRATION.md
26
+
27
+ ---
28
+
29
+ ## Step 2: Install
30
+
31
+ **Use the package manager already installed** — check for lockfiles.
32
+
33
+ ### No AI:
34
+ ```bash
35
+ bun add better-cmdk
36
+ ```
37
+
38
+ ### Built-in chat:
39
+ ```bash
40
+ bun add better-cmdk ai @ai-sdk/react @ai-sdk/openai
41
+ ```
42
+
43
+ Substitute `@ai-sdk/anthropic`, `@ai-sdk/google`, etc. based on my preferred AI provider.
44
+
45
+ ### ModifyWithAI:
46
+ ```bash
47
+ bun add better-cmdk modifywithai @ai-sdk/react ai
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Step 3: CSS Setup
53
+
54
+ better-cmdk uses Tailwind CSS and shadcn/ui design tokens.
55
+
56
+ ### Tailwind v4
57
+
58
+ Add to my main CSS file (next to the existing `@import "tailwindcss"`):
59
+
60
+ ```css
61
+ @source "node_modules/better-cmdk";
62
+ ```
63
+
64
+ If my app **already has shadcn/ui CSS variables**, verify they include `--popover`, `--muted`, `--border`, `--ring`, `--primary`, `--primary-foreground`. Don't overwrite existing variables.
65
+
66
+ If my app **does not have** these variables, add the minimal set:
67
+
68
+ ```css
69
+ :root {
70
+ --radius: 0.625rem;
71
+ --background: oklch(1 0 0);
72
+ --foreground: oklch(0.145 0 0);
73
+ --popover: oklch(1 0 0);
74
+ --popover-foreground: oklch(0.145 0 0);
75
+ --primary: oklch(0.205 0 0);
76
+ --primary-foreground: oklch(0.985 0 0);
77
+ --muted: oklch(0.97 0 0);
78
+ --muted-foreground: oklch(0.556 0 0);
79
+ --border: oklch(0.922 0 0);
80
+ --input: oklch(0.922 0 0);
81
+ --ring: oklch(0.708 0 0);
82
+ }
83
+
84
+ @theme inline {
85
+ --color-background: var(--background);
86
+ --color-foreground: var(--foreground);
87
+ --color-popover: var(--popover);
88
+ --color-popover-foreground: var(--popover-foreground);
89
+ --color-primary: var(--primary);
90
+ --color-primary-foreground: var(--primary-foreground);
91
+ --color-muted: var(--muted);
92
+ --color-muted-foreground: var(--muted-foreground);
93
+ --color-border: var(--border);
94
+ --color-input: var(--input);
95
+ --color-ring: var(--ring);
96
+ --radius-sm: calc(var(--radius) - 4px);
97
+ --radius-md: calc(var(--radius) - 2px);
98
+ --radius-lg: var(--radius);
99
+ --radius-xl: calc(var(--radius) + 4px);
100
+ }
101
+ ```
102
+
103
+ ### Tailwind v3
104
+
105
+ Add to `tailwind.config.js` or `tailwind.config.ts`:
106
+
107
+ ```js
108
+ content: [
109
+ // ... existing paths
110
+ "./node_modules/better-cmdk/**/*.{js,ts,jsx,tsx}",
111
+ ],
112
+ ```
113
+
114
+ If my app **does not have** shadcn/ui CSS variables, add the minimal set (hsl format):
115
+
116
+ ```css
117
+ @layer base {
118
+ :root {
119
+ --radius: 0.5rem;
120
+ --background: 0 0% 100%;
121
+ --foreground: 240 10% 3.9%;
122
+ --popover: 0 0% 100%;
123
+ --popover-foreground: 240 10% 3.9%;
124
+ --primary: 240 5.9% 10%;
125
+ --primary-foreground: 0 0% 98%;
126
+ --muted: 240 4.8% 95.9%;
127
+ --muted-foreground: 240 3.8% 46.1%;
128
+ --border: 240 5.9% 90%;
129
+ --input: 240 5.9% 90%;
130
+ --ring: 240 5.9% 10%;
131
+ }
132
+ }
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Step 4: CRITICAL — Command Discovery
138
+
139
+ Before writing any component code, **crawl my entire codebase** to discover ALL user-facing operations that should be in the command palette.
140
+
141
+ ### How to discover commands:
142
+
143
+ 1. **Search all routes/pages** → navigation commands ("Go to Dashboard", "Go to Settings")
144
+ 2. **Search all buttons and links** → action commands ("Create project", "Export data")
145
+ 3. **Search all settings/toggles** → preference commands ("Toggle dark mode", "Change language")
146
+ 4. **Search all CRUD operations** → data commands ("New item", "Delete selected")
147
+ 5. **Search all utility functions** → utility commands ("Copy link", "Download report", "Share")
148
+ 6. **Search for existing keyboard shortcuts** → preserve as shortcut hints
149
+
150
+ ### Create a command for EVERY user-facing operation:
151
+
152
+ - Button that opens a modal? Command: `open-create-modal`
153
+ - Link that navigates to a page? Command: `go-to-dashboard`
154
+ - Toggle that changes a setting? Command: `toggle-dark-mode`
155
+ - Form that creates something? Command: `create-new-project`
156
+ - Search input? Command: `search`
157
+ - Sidebar item? Command: `open-analytics`
158
+
159
+ ### For each discovered command, define:
160
+
161
+ - `name`: unique kebab-case identifier
162
+ - `label`: human-readable display text (what users search for)
163
+ - `group`: logical heading — use consistent groups like "Navigation", "Actions", "Settings", "Help"
164
+ - `icon`: matching [lucide-react](https://lucide.dev) icon component
165
+ - `shortcut`: keyboard shortcut hint if applicable (display-only, e.g. `"⌘D"`)
166
+ - `keywords`: extra search terms (array of strings) — helps users find commands by alternate names
167
+ - `disabled`: set `true` to gray out commands that aren't currently available
168
+ - `onSelect`: callback that wires to existing app logic
169
+
170
+ **Aim for 10-30 commands.** If you found fewer than 5, you haven't looked hard enough. Report how many you found before proceeding.
171
+
172
+ ---
173
+
174
+ ## Step 5: Create the component
175
+
176
+ Replace the example commands below with the ones you discovered from my codebase.
177
+
178
+ ```tsx
179
+ import { useState, useEffect } from "react"
180
+ import { CommandMenu, type CommandDefinition } from "better-cmdk"
181
+ import { LayoutDashboardIcon, SettingsIcon, SunMoonIcon } from "lucide-react"
182
+
183
+ const commands: CommandDefinition[] = [
184
+ {
185
+ name: "dashboard",
186
+ label: "Go to Dashboard",
187
+ group: "Navigation",
188
+ icon: <LayoutDashboardIcon className="size-4" />,
189
+ shortcut: "⌘D",
190
+ onSelect: () => (window.location.href = "/dashboard"),
191
+ },
192
+ {
193
+ name: "settings",
194
+ label: "Settings",
195
+ group: "Navigation",
196
+ icon: <SettingsIcon className="size-4" />,
197
+ shortcut: "⌘,",
198
+ onSelect: () => (window.location.href = "/settings"),
199
+ },
200
+ {
201
+ name: "dark-mode",
202
+ label: "Toggle dark mode",
203
+ group: "Appearance",
204
+ icon: <SunMoonIcon className="size-4" />,
205
+ onSelect: () => document.documentElement.classList.toggle("dark"),
206
+ },
207
+ ]
208
+
209
+ export function CommandPalette() {
210
+ const [open, setOpen] = useState(false)
211
+
212
+ useEffect(() => {
213
+ const down = (e: KeyboardEvent) => {
214
+ if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
215
+ e.preventDefault()
216
+ setOpen((o) => !o)
217
+ }
218
+ }
219
+ document.addEventListener("keydown", down)
220
+ return () => document.removeEventListener("keydown", down)
221
+ }, [])
222
+
223
+ return (
224
+ <CommandMenu
225
+ open={open}
226
+ onOpenChange={setOpen}
227
+ commands={commands}
228
+ />
229
+ )
230
+ }
231
+ ```
232
+
233
+ ---
234
+
235
+ ## Step 6: Add AI chat (skip if "No AI" chosen)
236
+
237
+ ### Built-in chat
238
+
239
+ Create a streaming chat API route, then add `chatEndpoint` to CommandMenu.
240
+
241
+ Create a streaming chat endpoint for your framework/server. The handler should:
242
+
243
+ ```ts
244
+ import { openai } from "@ai-sdk/openai"
245
+ import { streamText, convertToModelMessages } from "ai"
246
+ import type { UIMessage } from "ai"
247
+
248
+ // In your POST handler:
249
+ const { messages }: { messages: UIMessage[] } = await req.json()
250
+ const result = streamText({
251
+ model: openai("gpt-4o-mini"),
252
+ messages: await convertToModelMessages(messages),
253
+ system: "You are a helpful assistant in a command palette. Keep responses concise.",
254
+ })
255
+ return result.toUIMessageStreamResponse()
256
+ ```
257
+
258
+ Then update CommandMenu to add the `chatEndpoint` prop:
259
+
260
+ ```tsx
261
+ <CommandMenu
262
+ open={open}
263
+ onOpenChange={setOpen}
264
+ commands={commands}
265
+ chatEndpoint="/api/chat"
266
+ />
267
+ ```
268
+
269
+ Set the environment variable:
270
+
271
+ ```bash
272
+ echo "OPENAI_API_KEY=sk-..." >> .env
273
+ ```
274
+
275
+ (Substitute the correct variable name for the AI provider.)
276
+
277
+ ---
278
+
279
+ ### ModifyWithAI
280
+
281
+ After installing modifywithai, read `node_modules/modifywithai/AGENTS.md` and follow its instructions for:
282
+ - Creating the token endpoint
283
+ - Setting up environment variables (MWAI_APP_ID, MWAI_API_KEY)
284
+ - Discovering actions from the codebase
285
+
286
+ > **Note**: ModifyWithAI requires a server-side token endpoint. If your Vite app uses a separate backend (Express, Fastify, etc.), adapt the token endpoint from the guide that most closely matches your setup. If your app is a pure SPA with no server, choose the built-in chat option instead.
287
+
288
+
289
+ Then connect the assistant to CommandMenu. The `useAssistant()` return value from modifywithai is directly compatible with better-cmdk's `chat` prop — no adapter needed:
290
+
291
+ ```tsx
292
+ import { useAssistant } from "modifywithai"
293
+
294
+ // Inside your CommandPalette component:
295
+ const assistant = useAssistant({
296
+ agenticActions: actions, // from action discovery above
297
+ getContext: () => ({
298
+ currentPage: window.location.pathname,
299
+ }),
300
+ })
301
+
302
+ <CommandMenu
303
+ open={open}
304
+ onOpenChange={setOpen}
305
+ commands={commands}
306
+ chat={assistant}
307
+ />
308
+ ```
309
+
310
+ ---
311
+
312
+ ## Step 7: Mount the component
313
+
314
+ Add `<CommandPalette />` to `src/App.tsx` or the main entry component.
315
+
316
+ ---
317
+
318
+ ## Step 8: shadcn/ui migration (if applicable)
319
+
320
+ If existing shadcn/ui command components were detected, swap imports:
321
+
322
+ ```diff
323
+ - import {
324
+ - CommandDialog,
325
+ - CommandInput,
326
+ - CommandList,
327
+ - CommandEmpty,
328
+ - CommandGroup,
329
+ - CommandItem
330
+ - } from "@/components/ui/command"
331
+ + import {
332
+ + CommandDialog,
333
+ + CommandInput,
334
+ + CommandList,
335
+ + CommandEmpty,
336
+ + CommandGroup,
337
+ + CommandItem
338
+ + } from "better-cmdk"
339
+ ```
340
+
341
+ `CommandDialog` is an alias for `CommandMenu`. All sub-components have the same API. Add `chatEndpoint` or `chat` to enable AI features — without either, the palette behaves like a standard cmdk dialog.
342
+
343
+ ---
344
+
345
+ ## Verify
346
+
347
+ After setup, tell me:
348
+
349
+ 1. How many commands you discovered
350
+ 2. Which AI path you followed (none / built-in / ModifyWithAI)
351
+ 3. [ModifyWithAI only] How many actions you discovered (from modifywithai's AGENTS.md action discovery instructions)
352
+ 4. What files you created/modified
353
+ 5. That pressing `⌘K` opens the palette with all commands grouped correctly
354
+ 6. [If AI enabled] That typing a non-matching query shows "Ask AI" and chat works
355
+
356
+ Docs: https://github.com/ModifyWithAI/better-cmdk/blob/main/INTEGRATION.md