better-cmdk 0.0.4 → 0.0.6

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,358 @@
1
+ # Set up better-cmdk command palette (Next.js (App Router))
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
+ "use client"
180
+
181
+ import { useState, useEffect } from "react"
182
+ import { useRouter } from "next/navigation"
183
+ import { CommandMenu, type CommandDefinition } from "better-cmdk"
184
+ import { LayoutDashboardIcon, SettingsIcon, SunMoonIcon } from "lucide-react"
185
+
186
+ // REPLACE with discovered commands from my codebase
187
+ const commands: CommandDefinition[] = [
188
+ {
189
+ name: "dashboard",
190
+ label: "Go to Dashboard",
191
+ group: "Navigation",
192
+ icon: <LayoutDashboardIcon className="size-4" />,
193
+ shortcut: "⌘D",
194
+ onSelect: () => router.push("/dashboard"),
195
+ },
196
+ {
197
+ name: "settings",
198
+ label: "Settings",
199
+ group: "Navigation",
200
+ icon: <SettingsIcon className="size-4" />,
201
+ shortcut: "⌘,",
202
+ onSelect: () => router.push("/settings"),
203
+ },
204
+ {
205
+ name: "dark-mode",
206
+ label: "Toggle dark mode",
207
+ group: "Appearance",
208
+ icon: <SunMoonIcon className="size-4" />,
209
+ onSelect: () => document.documentElement.classList.toggle("dark"),
210
+ },
211
+ ]
212
+
213
+ export function CommandPalette() {
214
+ const [open, setOpen] = useState(false)
215
+
216
+ useEffect(() => {
217
+ const down = (e: KeyboardEvent) => {
218
+ if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
219
+ e.preventDefault()
220
+ setOpen((o) => !o)
221
+ }
222
+ }
223
+ document.addEventListener("keydown", down)
224
+ return () => document.removeEventListener("keydown", down)
225
+ }, [])
226
+
227
+ return (
228
+ <CommandMenu
229
+ open={open}
230
+ onOpenChange={setOpen}
231
+ commands={commands}
232
+ />
233
+ )
234
+ }
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Step 6: Add AI chat (skip if "No AI" chosen)
240
+
241
+ ### Built-in chat
242
+
243
+ Create a streaming chat API route, then add `chatEndpoint` to CommandMenu.
244
+
245
+ ```ts
246
+ // app/api/chat/route.ts
247
+ import { openai } from "@ai-sdk/openai"
248
+ import { streamText, convertToModelMessages } from "ai"
249
+ import type { UIMessage } from "ai"
250
+
251
+ export async function POST(req: Request) {
252
+ const { messages }: { messages: UIMessage[] } = await req.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
+ Then update CommandMenu to add the `chatEndpoint` prop:
263
+
264
+ ```tsx
265
+ <CommandMenu
266
+ open={open}
267
+ onOpenChange={setOpen}
268
+ commands={commands}
269
+ chatEndpoint="/api/chat"
270
+ />
271
+ ```
272
+
273
+ Set the environment variable:
274
+
275
+ ```bash
276
+ echo "OPENAI_API_KEY=sk-..." >> .env.local
277
+ ```
278
+
279
+ (Substitute the correct variable name for the AI provider.)
280
+
281
+ ---
282
+
283
+ ### ModifyWithAI
284
+
285
+ After installing modifywithai, read `node_modules/modifywithai/dist/nextjs/AGENTS.md` and follow its instructions for:
286
+ - Creating the token endpoint
287
+ - Setting up environment variables (MWAI_APP_ID, MWAI_API_KEY)
288
+ - Discovering actions from the codebase
289
+
290
+
291
+ Then connect the assistant to CommandMenu. The `useAssistant()` return value from modifywithai is directly compatible with better-cmdk's `chat` prop — no adapter needed:
292
+
293
+ ```tsx
294
+ import { useAssistant } from "modifywithai"
295
+
296
+ // Inside your CommandPalette component:
297
+ const assistant = useAssistant({
298
+ agenticActions: actions, // from action discovery above
299
+ getContext: () => ({
300
+ currentPage: window.location.pathname,
301
+ }),
302
+ })
303
+
304
+ <CommandMenu
305
+ open={open}
306
+ onOpenChange={setOpen}
307
+ commands={commands}
308
+ chat={assistant}
309
+ />
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Step 7: Mount the component
315
+
316
+ Add `<CommandPalette />` to `app/layout.tsx` inside the `<body>` tag.
317
+
318
+ ---
319
+
320
+ ## Step 8: shadcn/ui migration (if applicable)
321
+
322
+ If existing shadcn/ui command components were detected, swap imports:
323
+
324
+ ```diff
325
+ - import {
326
+ - CommandDialog,
327
+ - CommandInput,
328
+ - CommandList,
329
+ - CommandEmpty,
330
+ - CommandGroup,
331
+ - CommandItem
332
+ - } from "@/components/ui/command"
333
+ + import {
334
+ + CommandDialog,
335
+ + CommandInput,
336
+ + CommandList,
337
+ + CommandEmpty,
338
+ + CommandGroup,
339
+ + CommandItem
340
+ + } from "better-cmdk"
341
+ ```
342
+
343
+ `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.
344
+
345
+ ---
346
+
347
+ ## Verify
348
+
349
+ After setup, tell me:
350
+
351
+ 1. How many commands you discovered
352
+ 2. Which AI path you followed (none / built-in / ModifyWithAI)
353
+ 3. [ModifyWithAI only] How many actions you discovered (from modifywithai's AGENTS.md action discovery instructions)
354
+ 4. What files you created/modified
355
+ 5. That pressing `⌘K` opens the palette with all commands grouped correctly
356
+ 6. [If AI enabled] That typing a non-matching query shows "Ask AI" and chat works
357
+
358
+ Docs: https://github.com/ModifyWithAI/better-cmdk/blob/main/INTEGRATION.md
@@ -0,0 +1,356 @@
1
+ # Set up better-cmdk command palette (Remix / React Router v7)
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 "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("/dashboard"),
196
+ },
197
+ {
198
+ name: "settings",
199
+ label: "Settings",
200
+ group: "Navigation",
201
+ icon: <SettingsIcon className="size-4" />,
202
+ shortcut: "⌘,",
203
+ onSelect: () => navigate("/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 { openai } from "@ai-sdk/openai"
246
+ import { streamText, convertToModelMessages } from "ai"
247
+ import type { UIMessage, ActionFunctionArgs } from "ai"
248
+
249
+ export async function action({ request }: ActionFunctionArgs) {
250
+ const { messages }: { messages: UIMessage[] } = await request.json()
251
+ const result = streamText({
252
+ model: openai("gpt-4o-mini"),
253
+ messages: await convertToModelMessages(messages),
254
+ system: "You are a helpful assistant in a command palette. Keep responses concise.",
255
+ })
256
+ return result.toUIMessageStreamResponse()
257
+ }
258
+ ```
259
+
260
+ Then update CommandMenu to add the `chatEndpoint` prop:
261
+
262
+ ```tsx
263
+ <CommandMenu
264
+ open={open}
265
+ onOpenChange={setOpen}
266
+ commands={commands}
267
+ chatEndpoint="/api/chat"
268
+ />
269
+ ```
270
+
271
+ Set the environment variable:
272
+
273
+ ```bash
274
+ echo "OPENAI_API_KEY=sk-..." >> .env
275
+ ```
276
+
277
+ (Substitute the correct variable name for the AI provider.)
278
+
279
+ ---
280
+
281
+ ### ModifyWithAI
282
+
283
+ After installing modifywithai, read `node_modules/modifywithai/dist/remix/AGENTS.md` and follow its instructions for:
284
+ - Creating the token endpoint
285
+ - Setting up environment variables (MWAI_APP_ID, MWAI_API_KEY)
286
+ - Discovering actions from the codebase
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 `app/root.tsx` inside the root 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