better-cmdk 0.0.20 → 0.0.22

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.
@@ -1,6 +1,6 @@
1
- # Set up better-cmdk command palette (Remix / React Router v7)
1
+ # Set up better-cmdk action palette (Remix / React Router v7)
2
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.
3
+ better-cmdk is a command palette with AI chat for React. Your task is to install it, discover actions from my codebase, and create a working palette.
4
4
 
5
5
  ## Step 1: Analyze my codebase
6
6
 
@@ -11,14 +11,12 @@ Before making changes, detect:
11
11
 
12
12
  Also ask me:
13
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
14
+ **1. Do you want AI chat in the palette?**
15
+ - No AI — action search only
16
+ - Built-in chat — free developer-trial Q&A via https://better-cmdk.com/api/chat (no signup, 10 requests per 10 minutes; use your own chat URL for production)
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. Get your App ID and API key from https://modifywithai.com/dashboard
18
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?
19
+ **2. If shadcn/ui command components are detected:** Do you want to migrate them to better-cmdk, or keep both?
22
20
 
23
21
  Tell me what you detected, which path you'll follow, and what you'll be adding better-cmdk to.
24
22
 
@@ -46,16 +44,16 @@ yarn add better-cmdk
46
44
  ### Built-in chat:
47
45
  ```bash
48
46
  # bun
49
- bun add better-cmdk ai @ai-sdk/react @ai-sdk/openai
47
+ bun add better-cmdk
50
48
  # pnpm
51
- pnpm add better-cmdk ai @ai-sdk/react @ai-sdk/openai
49
+ pnpm add better-cmdk
52
50
  # npm
53
- npm install better-cmdk ai @ai-sdk/react @ai-sdk/openai
51
+ npm install better-cmdk
54
52
  # yarn
55
- yarn add better-cmdk ai @ai-sdk/react @ai-sdk/openai
53
+ yarn add better-cmdk
56
54
  ```
57
55
 
58
- Substitute `@ai-sdk/anthropic`, `@ai-sdk/google`, etc. based on my preferred AI provider.
56
+ If you later add a custom/self-hosted endpoint, install AI SDK/provider packages for that server route.
59
57
 
60
58
  ### ModifyWithAI:
61
59
  ```bash
@@ -84,9 +82,7 @@ Add to my main CSS file:
84
82
  @import "better-cmdk";
85
83
  ```
86
84
 
87
- If my app wants custom theming, override better-cmdk's namespaced variables.
88
-
89
- Add this minimal override block only when needed:
85
+ If my app needs custom theming, override better-cmdk's namespaced variables:
90
86
 
91
87
  ```css
92
88
  .bcmdk-root {
@@ -113,80 +109,126 @@ Use the same CSS import approach:
113
109
 
114
110
  ---
115
111
 
116
- ## Step 4: CRITICAL — Command Discovery
112
+ ## Step 4: CRITICAL — Command Action Discovery (better-cmdk scope)
113
+
114
+ Before writing component code, **crawl my codebase** to discover all meaningful actions that can run immediately from the command palette.
115
+
116
+ ### Scope rules (required)
117
117
 
118
- Before writing any component code, **crawl my entire codebase** to discover ALL user-facing operations that should be in the command palette.
118
+ - better-cmdk is concerned with **no-argument command actions** (no `inputSchema`, no required arguments)
119
+ - Define actions as underlying app/domain operations (API calls, mutations, workflow transitions), not raw UI gestures
120
+ - Model what the app does, not how a user physically triggers it
121
+ - Good: `goToBilling`, `toggleSidebar`, `archiveCurrentProject`
122
+ - Bad: `clickCreateButton`, `openDropdown`, `typeIntoInput`, `focusSearchField`
123
+ - If an operation needs runtime arguments (for example refund amount, assignee, date range), treat it as **modifywithai-owned** and do not add it as a new better-cmdk command action in this step
124
+ - If a standard shared `actions` array already exists, **extend it in place**; do not create a second array
125
+ - If no standard shared array exists, create one
119
126
 
120
- ### How to discover commands:
127
+ ### Discovery checklist
121
128
 
122
- 1. **Search all routes/pages** navigation commands ("Go to Dashboard", "Go to Settings")
123
- 2. **Search all buttons and links** action commands ("Create project", "Export data")
124
- 3. **Search all settings/toggles**preference commands ("Toggle dark mode", "Change language")
125
- 4. **Search all CRUD operations**data commands ("New item", "Delete selected")
126
- 5. **Search all utility functions** utility commands ("Copy link", "Download report", "Share")
127
- 6. **Search for existing keyboard shortcuts** preserve as shortcut hints
129
+ 1. Locate existing action arrays/types first (`actions`, `defineActions(...)`, exported action modules) and reuse them
130
+ 2. Search routes/pagesnavigation command actions
131
+ 3. Search buttons/links/forms/menusidentify outcomes that can run with no extra user input
132
+ 4. Trace handlers to services/API/domain functions map each to one operation-level command action
133
+ 5. Search toggles/settings/feature flagspreference command actions
134
+ 6. Search CRUD/services/utilities/API calls include only operations that can run safely without additional arguments
135
+ 7. Search existing keyboard shortcuts → keep as shortcut hints
136
+ 8. Mark argument-requiring operations for modifywithai ownership (same shared array, with `inputSchema`)
128
137
 
129
- ### Create a command for EVERY user-facing operation:
138
+ ### Canonical single-array rules
130
139
 
131
- - Button that opens a modal? Command: `open-create-modal`
132
- - Link that navigates to a page? Command: `go-to-dashboard`
133
- - Toggle that changes a setting? Command: `toggle-dark-mode`
134
- - Form that creates something? Command: `create-new-project`
135
- - Search input? Command: `search`
136
- - Sidebar item? Command: `open-analytics`
140
+ Use **one deduped `actions` array** as the source of truth:
137
141
 
138
- ### For each discovered command, define:
142
+ - better-cmdk-owned entries: no `inputSchema` (direct command execution)
143
+ - modifywithai-owned entries (if present): has `inputSchema` (agentic argument collection)
144
+ - Keep exactly one canonical action per underlying operation
145
+ - Do not create separate “command” and “AI action” entries for the same operation
146
+ - Put synonyms in `keywords`, not duplicate actions
147
+ - Reuse the same `name` everywhere (CommandMenu + assistant provider)
148
+
149
+ Use consistent `group` values:
150
+ - `Navigation`
151
+ - `UI / Preferences`
152
+ - `Data`
153
+ - `Help / Utilities`
154
+
155
+ ### For each command action discovered, define
139
156
 
140
157
  - `name`: unique kebab-case identifier
141
- - `label`: human-readable display text (what users search for)
142
- - `group`: logical heading use consistent groups like "Navigation", "Actions", "Settings", "Help"
143
- - `icon`: matching [lucide-react](https://lucide.dev) icon component
144
- - `shortcut`: keyboard shortcut hint if applicable (display-only, e.g. `"⌘D"`)
145
- - `keywords`: extra search terms (array of strings) — helps users find commands by alternate names
146
- - `disabled`: set `true` to gray out commands that aren't currently available
147
- - `onSelect`: callback that wires to existing app logic
158
+ - `label`: human-readable label
159
+ - `group`: taxonomy group from above
160
+ - `icon`: lucide-react icon when useful
161
+ - `shortcut`: optional display hint
162
+ - `keywords`: optional search aliases
163
+ - `semanticKey`: optional operation identity (required when names differ but operation is the same)
164
+ - `disabled`: optional availability guard
165
+ - `execute`: required implementation used when the command is selected
166
+ - `onSelect`: optional UI-only override (modifywithai ignores it)
167
+
168
+ `inputSchema` is optional and reserved for modifywithai-owned argument actions in the same shared array.
169
+ Unused fields are safe: each library should ignore values it doesn't need.
170
+
171
+ **Coverage first, count second.** Many non-trivial apps should land in the 10-30+ no-argument command-action range. If you have fewer than 8, you likely missed meaningful command flows.
172
+
173
+ ### Label distinctiveness for mixed arrays
174
+
175
+ When navigation and data actions share the same domain concept, make labels clearly distinct:
176
+
177
+ - Navigation: "Out of Office" (navigates to the page)
178
+ - Data: "Create Out of Office Entry" (creates via API)
148
179
 
149
- **Aim for 10-30 commands.** If you found fewer than 5, you haven't looked hard enough. Report how many you found before proceeding.
180
+ Prefix data-action labels with their verb ("Create …", "Update …", "Delete …") to disambiguate from navigation labels.
150
181
 
151
182
  ---
152
183
 
153
184
  ## Step 5: Create the component
154
185
 
155
- Replace the example commands below with the ones you discovered from my codebase.
186
+ Use one shared `actions` array (from Step 4). If one already exists, extend it. Do not create a second array.
187
+ Replace examples with discovered actions.
156
188
 
157
189
  ```tsx
158
190
  import { useState, useEffect } from "react"
159
191
  import { useNavigate } from "react-router"
160
- import { CommandMenu, type CommandDefinition } from "better-cmdk"
192
+ import { CommandMenu, type CommandAction } from "better-cmdk"
161
193
  import { LayoutDashboardIcon, SettingsIcon, SunMoonIcon } from "lucide-react"
162
194
 
163
195
  export function CommandPalette() {
164
196
  const [open, setOpen] = useState(false)
165
197
  const navigate = useNavigate()
166
198
 
167
- const commands: CommandDefinition[] = [
199
+ const actions: CommandAction[] = [
168
200
  {
169
- name: "dashboard",
201
+ name: "go-dashboard",
170
202
  label: "Go to Dashboard",
203
+ description: "Navigate to the dashboard page",
171
204
  group: "Navigation",
172
205
  icon: <LayoutDashboardIcon className="size-4" />,
173
206
  shortcut: "⌘D",
174
- onSelect: () => navigate("/dashboard"),
207
+ execute: () => navigate("/dashboard"),
175
208
  },
176
209
  {
177
- name: "settings",
178
- label: "Settings",
210
+ name: "open-settings",
211
+ label: "Open Settings",
212
+ description: "Navigate to the settings page",
179
213
  group: "Navigation",
180
214
  icon: <SettingsIcon className="size-4" />,
181
215
  shortcut: "⌘,",
182
- onSelect: () => navigate("/settings"),
216
+ execute: () => navigate("/settings"),
183
217
  },
184
218
  {
185
- name: "dark-mode",
219
+ name: "toggle-dark-mode",
186
220
  label: "Toggle dark mode",
221
+ description: "Toggle the application theme",
187
222
  group: "Appearance",
188
223
  icon: <SunMoonIcon className="size-4" />,
189
- onSelect: () => document.documentElement.classList.toggle("dark"),
224
+ execute: () => document.documentElement.classList.toggle("dark"),
225
+ },
226
+ {
227
+ name: "go-home",
228
+ label: "Go Home",
229
+ description: "Navigate to the home page",
230
+ group: "Navigation",
231
+ execute: () => navigate("/"),
190
232
  },
191
233
  ]
192
234
 
@@ -205,7 +247,7 @@ export function CommandPalette() {
205
247
  <CommandMenu
206
248
  open={open}
207
249
  onOpenChange={setOpen}
208
- commands={commands}
250
+ actions={actions}
209
251
  />
210
252
  )
211
253
  }
@@ -213,11 +255,28 @@ export function CommandPalette() {
213
255
 
214
256
  ---
215
257
 
216
- ## Step 6: Add AI chat (skip if "No AI" chosen)
258
+ ## Step 6: Add AI chat (skip if No AI chosen)
217
259
 
218
260
  ### Built-in chat
219
261
 
220
- Create a streaming chat API route, then add `chatEndpoint` to CommandMenu.
262
+ Use the default hosted endpoint: `https://better-cmdk.com/api/chat`.
263
+ This service is provided by better-cmdk as a free developer trial with no signup.
264
+ Rate limit: **10 requests per 10 minutes**.
265
+
266
+ Use CommandMenu without `chatEndpoint`:
267
+
268
+ ```tsx
269
+ <CommandMenu
270
+ open={open}
271
+ onOpenChange={setOpen}
272
+ actions={actions}
273
+ />
274
+ ```
275
+
276
+ ### Optional: custom/self-hosted endpoint
277
+
278
+ For production, use your own chat URL (or choose ModifyWithAI for agentic capabilities).
279
+ If you need your own model/provider route, create a streaming chat API route:
221
280
 
222
281
  ```ts
223
282
  // app/routes/api.chat.ts
@@ -231,24 +290,24 @@ export async function action({ request }: ActionFunctionArgs) {
231
290
  const result = streamText({
232
291
  model: openai("gpt-4o-mini"),
233
292
  messages: await convertToModelMessages(messages),
234
- system: "You are a helpful assistant in a command palette. Keep responses concise.",
293
+ system: "You are a helpful assistant in an action palette. Keep responses concise.",
235
294
  })
236
295
  return result.toUIMessageStreamResponse()
237
296
  }
238
297
  ```
239
298
 
240
- Then update CommandMenu to add the `chatEndpoint` prop:
299
+ Then update CommandMenu:
241
300
 
242
301
  ```tsx
243
302
  <CommandMenu
244
303
  open={open}
245
304
  onOpenChange={setOpen}
246
- commands={commands}
305
+ actions={actions}
247
306
  chatEndpoint="/api/chat"
248
307
  />
249
308
  ```
250
309
 
251
- Set the environment variable:
310
+ Set environment variable:
252
311
 
253
312
  ```bash
254
313
  echo "OPENAI_API_KEY=sk-..." >> .env
@@ -262,27 +321,34 @@ echo "OPENAI_API_KEY=sk-..." >> .env
262
321
 
263
322
  After installing modifywithai, read `node_modules/modifywithai/dist/remix/AGENTS.md` and follow its instructions for:
264
323
  - Creating the token endpoint
265
- - Setting up environment variables (MWAI_APP_ID, MWAI_API_KEY)
324
+ - Setting up environment variables (MWAI_APP_ID, MWAI_API_KEY) from https://modifywithai.com/dashboard
266
325
  - Discovering actions from the codebase
267
326
 
268
327
 
269
- Then connect the assistant to CommandMenu. The `useAssistant()` return value from modifywithai is directly compatible with better-cmdk's `chat` prop — no adapter needed:
328
+ Use the **same discovered `actions` list** for both the assistant provider and CommandMenu.
329
+ UI-only fields (like `icon`, `group`, `shortcut`) are ignored by modifywithai:
330
+
331
+ Ownership split:
332
+ - better-cmdk focuses on no-argument command actions
333
+ - modifywithai owns both no-argument and argument-requiring actions (`inputSchema`)
334
+ - Keep both in one shared array and extend the existing array in place if present
270
335
 
271
336
  ```tsx
272
337
  import { useAssistant } from "modifywithai"
273
338
 
274
339
  // Inside your CommandPalette component:
275
340
  const assistant = useAssistant({
276
- agenticActions: actions, // from action discovery above
341
+ actions,
277
342
  getContext: () => ({
278
343
  currentPage: window.location.pathname,
344
+ currentDateTime: new Date().toISOString(),
279
345
  }),
280
346
  })
281
347
 
282
348
  <CommandMenu
283
349
  open={open}
284
350
  onOpenChange={setOpen}
285
- commands={commands}
351
+ actions={actions}
286
352
  chat={assistant}
287
353
  />
288
354
  ```
@@ -297,7 +363,7 @@ Add `<CommandPalette />` to `app/root.tsx` inside the root component.
297
363
 
298
364
  ## Step 8: shadcn/ui migration (if applicable)
299
365
 
300
- If existing shadcn/ui command components were detected, swap imports:
366
+ If existing shadcn/ui command components are detected, swap imports:
301
367
 
302
368
  ```diff
303
369
  - import {
@@ -318,7 +384,7 @@ If existing shadcn/ui command components were detected, swap imports:
318
384
  + } from "better-cmdk"
319
385
  ```
320
386
 
321
- `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.
387
+ `CommandDialog` is an alias for `CommandMenu`. AI chat works by default via the hosted trial endpoint (10 requests per 10 minutes). For production, use `chatEndpoint` with your own URL or pass `chat` (for example with ModifyWithAI for agentic capabilities). Set `chatEndpoint={null}` to disable.
322
388
 
323
389
  ---
324
390
 
@@ -326,11 +392,14 @@ If existing shadcn/ui command components were detected, swap imports:
326
392
 
327
393
  After setup, tell me:
328
394
 
329
- 1. How many commands you discovered
330
- 2. Which AI path you followed (none / built-in / ModifyWithAI)
331
- 3. [ModifyWithAI only] How many actions you discovered (from modifywithai's AGENTS.md action discovery instructions)
395
+ 1. How many no-argument command actions you discovered/added
396
+ 2. Whether you reused an existing shared `actions` array or created one, and where it lives
397
+ 3. Which AI path you followed (none / built-in / ModifyWithAI)
332
398
  4. What files you created/modified
333
- 5. That pressing `⌘K` opens the palette with all commands grouped correctly
334
- 6. [If AI enabled] That typing a non-matching query shows "Ask AI" and chat works
399
+ 5. That pressing `⌘K` opens the palette with grouped actions and direct execution works for command actions
400
+ 6. [If ModifyWithAI path] That argument-requiring actions (`inputSchema`) are kept in the same shared array (extended, not duplicated)
401
+ 7. [If built-in/default chat enabled] That you explicitly told me this is a developer-trial endpoint (`https://better-cmdk.com/api/chat`, 10 requests per 10 minutes) and that for production I should use my own `chatEndpoint` or use ModifyWithAI for agentic capabilities.
402
+
403
+ End your final response with a dedicated **Hosted Endpoint Note** section so this warning is unmissable.
335
404
 
336
405
  Docs: https://github.com/ModifyWithAI/better-cmdk/blob/main/INTEGRATION.md