Skip to content

Commit 90743d0

Browse files
antfuclaude
andauthored
docs: add commands and when clauses agent skills (#252)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent ddd7708 commit 90743d0

File tree

4 files changed

+430
-2
lines changed

4 files changed

+430
-2
lines changed

skills/vite-devtools-kit/SKILL.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ A DevTools plugin extends a Vite plugin with a `devtools.setup(ctx)` hook. The c
2525
| `ctx.logs` | Emit structured log entries and toast notifications |
2626
| `ctx.terminals` | Spawn and manage child processes with streaming terminal output |
2727
| `ctx.createJsonRenderer` | Create server-side JSON render specs for zero-client-code UIs |
28+
| `ctx.commands` | Register executable commands with keyboard shortcuts and palette visibility |
2829
| `ctx.viteConfig` | Resolved Vite configuration |
2930
| `ctx.viteServer` | Dev server instance (dev mode only) |
3031
| `ctx.mode` | `'dev'` or `'build'` |
@@ -107,12 +108,13 @@ export default function myAnalyzer(): Plugin {
107108

108109
## Namespacing Convention
109110

110-
**CRITICAL**: Always prefix RPC functions, shared state keys, and dock IDs with your plugin name:
111+
**CRITICAL**: Always prefix RPC functions, shared state keys, dock IDs, and command IDs with your plugin name:
111112

112113
```ts
113114
// Good - namespaced
114115
'my-plugin:get-modules'
115116
'my-plugin:state'
117+
'my-plugin:clear-cache' // command ID
116118

117119
// Bad - may conflict
118120
'get-modules'
@@ -254,6 +256,27 @@ await session.restart()
254256

255257
A common pattern is combining with launcher docks — see [Terminals Patterns](./references/terminals-patterns.md).
256258

259+
## Commands & Command Palette
260+
261+
Register executable commands discoverable via `Mod+K` palette:
262+
263+
```ts
264+
import { defineCommand } from '@vitejs/devtools-kit'
265+
266+
ctx.commands.register(defineCommand({
267+
id: 'my-plugin:clear-cache',
268+
title: 'Clear Build Cache',
269+
icon: 'ph:trash-duotone',
270+
keybindings: [{ key: 'Mod+Shift+C' }],
271+
when: 'clientType == embedded',
272+
handler: async () => { /* ... */ },
273+
}))
274+
```
275+
276+
Commands support sub-commands (two-level hierarchy), conditional visibility via `when` clauses, and user-customizable keyboard shortcuts.
277+
278+
See [Commands Patterns](./references/commands-patterns.md) and [When Clauses](./references/when-clauses.md) for full details.
279+
257280
## Logs & Notifications
258281

259282
Plugins can emit structured log entries from both server and client contexts. Logs appear in the built-in **Logs** panel and can optionally show as toast notifications.
@@ -498,6 +521,8 @@ export default defineConfig({
498521
6. **Use Iconify icons** - Prefer `ph:*` (Phosphor) icons: `icon: 'ph:chart-bar-duotone'`
499522
7. **Deduplicate logs** - Use explicit `id` for logs representing ongoing operations
500523
8. **Use Self-Inspect** - Add `@vitejs/devtools-self-inspect` during development to debug your plugin
524+
9. **Namespace command IDs** - Use `my-plugin:action` pattern for command IDs, same as RPC and state
525+
10. **Use `when` clauses** - Conditionally show commands/docks with `when` expressions instead of programmatic show/hide
501526

502527
## Example Plugins
503528

@@ -516,3 +541,5 @@ Real-world example plugins in the repo — reference their code structure and pa
516541
- [JSON Render Patterns](./references/json-render-patterns.md) - Server-side JSON specs, components, state binding
517542
- [Terminals Patterns](./references/terminals-patterns.md) - Child processes, custom streams, session lifecycle
518543
- [Logs Patterns](./references/logs-patterns.md) - Log entries, toast notifications, and handle patterns
544+
- [Commands Patterns](./references/commands-patterns.md) - Command registration, sub-commands, keybindings, palette integration
545+
- [When Clauses](./references/when-clauses.md) - Conditional expression syntax, context variables, API reference
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
# Commands & Command Palette
2+
3+
Register executable commands discoverable through a built-in palette with keyboard shortcuts.
4+
5+
## Defining Commands
6+
7+
```ts
8+
import { defineCommand } from '@vitejs/devtools-kit'
9+
10+
const clearCache = defineCommand({
11+
id: 'my-plugin:clear-cache',
12+
title: 'Clear Build Cache',
13+
description: 'Remove all cached build artifacts',
14+
icon: 'ph:trash-duotone',
15+
category: 'tools',
16+
handler: async () => {
17+
await fs.rm('.cache', { recursive: true })
18+
},
19+
})
20+
21+
// Register in plugin setup
22+
ctx.commands.register(clearCache)
23+
```
24+
25+
## Command Options
26+
27+
| Field | Type | Description |
28+
|-------|------|-------------|
29+
| `id` | `string` | **Required.** Unique namespaced ID (e.g. `my-plugin:action`) |
30+
| `title` | `string` | **Required.** Human-readable title shown in the palette |
31+
| `description` | `string` | Optional description text |
32+
| `icon` | `string` | Iconify icon string (e.g. `ph:trash-duotone`) |
33+
| `category` | `string` | Category for grouping |
34+
| `showInPalette` | `boolean \| 'without-children'` | Whether to show in command palette (default: `true`). `'without-children'` shows the command but doesn't flatten children into search — only accessible via drill-down. |
35+
| `when` | `string` | Conditional visibility expression — see [When Clauses](./when-clauses.md) |
36+
| `keybindings` | `DevToolsCommandKeybinding[]` | Default keyboard shortcuts |
37+
| `handler` | `Function` | Server-side handler. Optional if the command is a group for children. |
38+
| `children` | `DevToolsServerCommandInput[]` | Static sub-commands (two levels max) |
39+
40+
## Command Handle
41+
42+
`register()` returns a handle for live updates:
43+
44+
```ts
45+
const handle = ctx.commands.register({
46+
id: 'my-plugin:status',
47+
title: 'Show Status',
48+
handler: () => { /* ... */ },
49+
})
50+
51+
// Update later
52+
handle.update({ title: 'Show Status (3 items)' })
53+
54+
// Remove
55+
handle.unregister()
56+
```
57+
58+
## Sub-Commands
59+
60+
Two-level hierarchy. Selecting a parent in the palette drills down into its children.
61+
62+
```ts
63+
ctx.commands.register({
64+
id: 'git',
65+
title: 'Git',
66+
icon: 'ph:git-branch-duotone',
67+
category: 'tools',
68+
// No handler — group-only parent
69+
children: [
70+
{
71+
id: 'git:commit',
72+
title: 'Commit',
73+
icon: 'ph:check-duotone',
74+
keybindings: [{ key: 'Mod+Shift+G' }],
75+
handler: async () => { /* ... */ },
76+
},
77+
{
78+
id: 'git:push',
79+
title: 'Push',
80+
handler: async () => { /* ... */ },
81+
},
82+
],
83+
})
84+
```
85+
86+
Each child must have a globally unique `id`. Use the pattern `parentId:childAction` (e.g. `git:commit`).
87+
88+
Sub-commands with keybindings can be executed directly via the shortcut without opening the palette.
89+
90+
## Keyboard Shortcuts
91+
92+
### Key Format
93+
94+
Use `Mod` as a platform-aware modifier — maps to `Cmd` on macOS and `Ctrl` on other platforms.
95+
96+
| Key string | macOS | Windows/Linux |
97+
|------------|-------|---------------|
98+
| `Mod+K` | `Cmd+K` | `Ctrl+K` |
99+
| `Mod+Shift+P` | `Cmd+Shift+P` | `Ctrl+Shift+P` |
100+
| `Alt+N` | `Option+N` | `Alt+N` |
101+
102+
```ts
103+
ctx.commands.register(defineCommand({
104+
id: 'my-plugin:toggle-overlay',
105+
title: 'Toggle Overlay',
106+
keybindings: [{ key: 'Mod+Shift+O' }],
107+
handler: () => { /* ... */ },
108+
}))
109+
```
110+
111+
### User Overrides
112+
113+
Users can customize shortcuts in Settings > **Keyboard Shortcuts**. Overrides persist across sessions. Setting an empty array disables a shortcut.
114+
115+
The shortcut editor includes:
116+
- **Key capture** — click the input and press any key combination
117+
- **Modifier toggles** — toggle Cmd/Ctrl, Alt, Shift individually
118+
- **Conflict detection** — warns on browser shortcut conflicts, duplicate bindings, and weak shortcuts (single key without modifiers)
119+
120+
`KNOWN_BROWSER_SHORTCUTS` is exported from `@vitejs/devtools-kit` and maps key combinations to human-readable descriptions.
121+
122+
## Conditional Visibility
123+
124+
Commands support a `when` expression for conditional visibility:
125+
126+
```ts
127+
ctx.commands.register(defineCommand({
128+
id: 'my-plugin:embedded-only',
129+
title: 'Embedded-Only Action',
130+
when: 'clientType == embedded',
131+
handler: async () => { /* ... */ },
132+
}))
133+
```
134+
135+
When set, the command only appears in the palette and is only triggerable via shortcuts when the expression evaluates to `true`.
136+
137+
See [When Clauses](./when-clauses.md) for full syntax reference and context variables.
138+
139+
## Client-Side Commands
140+
141+
Client commands register in the webcomponent context and execute directly in the browser:
142+
143+
```ts
144+
context.commands.register({
145+
id: 'devtools:theme',
146+
source: 'client',
147+
title: 'Theme',
148+
icon: 'ph:palette-duotone',
149+
children: [
150+
{
151+
id: 'devtools:theme:light',
152+
source: 'client',
153+
title: 'Light',
154+
action: () => setTheme('light'),
155+
},
156+
{
157+
id: 'devtools:theme:dark',
158+
source: 'client',
159+
title: 'Dark',
160+
action: () => setTheme('dark'),
161+
},
162+
],
163+
})
164+
```
165+
166+
Client commands can return dynamic sub-items:
167+
168+
```ts
169+
context.commands.register({
170+
id: 'devtools:docs',
171+
source: 'client',
172+
title: 'Documentation',
173+
action: async () => {
174+
const docs = await fetchDocs()
175+
return docs.map(doc => ({
176+
id: `docs:${doc.slug}`,
177+
source: 'client' as const,
178+
title: doc.title,
179+
action: () => window.open(doc.url, '_blank'),
180+
}))
181+
},
182+
})
183+
```
184+
185+
## Command Palette
186+
187+
Built-in palette toggled with `Mod+K`. Features:
188+
189+
- **Fuzzy search** across all registered commands (including sub-commands)
190+
- **Keyboard navigation** — Arrow keys, Enter to select, Escape to close
191+
- **Drill-down** — Commands with children show breadcrumb navigation
192+
- **Server command execution** — via RPC with loading indicator
193+
- **Dynamic sub-menus** — Client commands can return sub-items at runtime
194+
195+
## Complete Example
196+
197+
```ts
198+
/// <reference types="@vitejs/devtools-kit" />
199+
import type { Plugin } from 'vite'
200+
import { defineCommand } from '@vitejs/devtools-kit'
201+
202+
export default function myPlugin(): Plugin {
203+
return {
204+
name: 'my-plugin',
205+
206+
devtools: {
207+
setup(ctx) {
208+
// Simple command with keybinding
209+
ctx.commands.register(defineCommand({
210+
id: 'my-plugin:restart',
211+
title: 'Restart Dev Server',
212+
icon: 'ph:arrow-clockwise-duotone',
213+
keybindings: [{ key: 'Mod+Shift+R' }],
214+
handler: async () => {
215+
await ctx.viteServer?.restart()
216+
},
217+
}))
218+
219+
// Command group with sub-commands
220+
ctx.commands.register(defineCommand({
221+
id: 'my-plugin:cache',
222+
title: 'Cache',
223+
icon: 'ph:database-duotone',
224+
children: [
225+
{
226+
id: 'my-plugin:cache:clear',
227+
title: 'Clear Cache',
228+
handler: async () => { /* ... */ },
229+
},
230+
{
231+
id: 'my-plugin:cache:inspect',
232+
title: 'Inspect Cache',
233+
handler: async () => { /* ... */ },
234+
},
235+
],
236+
}))
237+
},
238+
},
239+
}
240+
}
241+
```

skills/vite-devtools-kit/references/dock-entry-types.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ interface DockEntryBase {
1313
icon: string // URL, data URI, or Iconify name
1414
category?: string // Grouping category
1515
defaultOrder?: number // Sort order (higher = earlier)
16-
when?: string // Conditional visibility expression (same syntax as command when clauses)
16+
when?: string // Conditional visibility expression — see [When Clauses](./when-clauses.md)
1717
badge?: string // Badge text on dock icon (e.g., count)
1818
}
1919
```

0 commit comments

Comments
 (0)