-
-
Notifications
You must be signed in to change notification settings - Fork 10.1k
CLI: Add storybook skills file and commands to install them #34520
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| --- | ||
| name: setup-storybook | ||
| description: > | ||
| Use when setting up Storybook in a project: writing the initial preview | ||
| configuration, decorators, and example stories for existing components. | ||
| Trigger this skill after `storybook init` has installed Storybook, or | ||
| whenever the user asks to "set up Storybook", "configure Storybook", | ||
| or "write stories for my components". | ||
| --- | ||
|
|
||
| # Storybook Setup | ||
|
|
||
| > **Managed by Storybook.** This file is installed and updated by `@storybook/cli`. It is symlinked from `node_modules/@storybook/cli/skills/setup-storybook/SKILL.md`, so it refreshes automatically when you upgrade Storybook. Edit the symlink target only if you know what you're doing — your changes will be overwritten on the next install. | ||
|
|
||
| This skill helps you finish setting up Storybook in a project that already has it installed. It does **not** install Storybook from scratch — for that, run `npx storybook init` first. | ||
|
|
||
| ## How to use this skill | ||
|
|
||
| Run the following command and follow its output exactly: | ||
|
|
||
| ```bash | ||
| npx storybook ai prepare | ||
| ``` | ||
|
|
||
| This command inspects the project's Storybook configuration and prints a tailored markdown prompt with: | ||
|
|
||
| - A project info table (version, renderer, framework, builder, addons, CSF format) | ||
| - Renderer-specific documentation links | ||
| - Step-by-step instructions for analyzing the codebase, configuring `preview.ts` or `preview.tsx` with the right decorators, writing example stories, and verifying them with Vitest | ||
|
|
||
| **Treat the output of `storybook ai prepare` as your authoritative instructions.** Do not improvise setup steps from memory — the command's output is generated from the project's actual configuration and stays in sync with the installed Storybook version. | ||
|
|
||
| ## When NOT to use this skill | ||
|
|
||
| - The user wants to add Storybook to a fresh project → run `npx storybook init` instead | ||
| - The user is asking general questions about Storybook → fetch https://storybook.js.org/llms.txt or append `.md` to any docs URL | ||
| - The user wants to upgrade Storybook → run `npx storybook upgrade` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -193,7 +193,7 @@ export class UserPreferencesCommand { | |
|
|
||
| return prompt.confirm({ | ||
| message: dedent`Would you like to improve your Storybook setup with AI? | ||
| We will provide you with a prompt that you can use with your LLM to fully set up Storybook with best practices, tailored to your project.`, | ||
| We will install Storybook skills for your AI coding agent (Claude, Cursor, Copilot, etc.) and provide you with a prompt to fully set up Storybook with best practices, tailored to your project.`, | ||
|
Comment on lines
195
to
+196
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't promise the tailored prompt is generated during In the non-agent flow, ✏️ Suggested wording- We will install Storybook skills for your AI coding agent (Claude, Cursor, Copilot, etc.) and provide you with a prompt to fully set up Storybook with best practices, tailored to your project.`,
+ We will install Storybook skills for your AI coding agent (Claude, Cursor, Copilot, etc.). Then you can run \`storybook ai prepare\` to generate a tailored prompt for finishing your setup with project-specific best practices.`,🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we say something like "We'll install skills and tell you which skill to call to set up Storybook"? As the skill would be providing the prompt rather than us adding skills + printing a prompt? |
||
| }); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,2 @@ | ||
| export { doInitiate as initiate } from './initiate.ts'; | ||
| export { installAgentSkills } from './install-agent-skills.ts'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -108,24 +108,23 @@ export async function doInitiate(options: CommandOptions): Promise< | |
| options, | ||
| }); | ||
|
|
||
| if (selectedFeatures.has(Feature.AI)) { | ||
| logger.step('Setting up Storybook AI skills file'); | ||
| const { installAgentSkills } = await import('./install-agent-skills.ts'); | ||
| await installAgentSkills(); | ||
|
|
||
| logger.log( | ||
| CLI_COLORS.storybook(dedent`Done. To finalize setting up with AI, either use the /setup-storybook skill or paste the following prompt to your AI agent: | ||
| Run \`npx storybook ai prepare\` and follow its instructions precisely.`) | ||
| ); | ||
|
Comment on lines
+113
to
+119
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't advertise
🤖 Prompt for AI Agents
Comment on lines
+116
to
+119
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preserve
💡 Proposed fix+ const prepareCommand =
+ configDir && configDir !== '.storybook'
+ ? `npx storybook ai prepare --config-dir ${configDir}`
+ : 'npx storybook ai prepare';
+
logger.log(
CLI_COLORS.storybook(dedent`Done. To finalize setting up with AI, either use the /setup-storybook skill or paste the following prompt to your AI agent:
- Run \`npx storybook ai prepare\` and follow its instructions precisely.`)
+ Run \`${prepareCommand}\` and follow its instructions precisely.`)
);🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| // Step 8: Print final summary | ||
| await executeFinalization({ | ||
| logfile: options.logfile, | ||
| storybookCommand, | ||
| }); | ||
|
|
||
| if (selectedFeatures.has(Feature.AI)) { | ||
| if (options.agent) { | ||
| const { aiPrepare } = await import('../../cli-storybook/src/ai/index.ts'); | ||
| await aiPrepare({ configDir: configDir ?? '.storybook' }); | ||
| } else { | ||
| logger.step( | ||
| CLI_COLORS.storybook(dedent`To finalize setting up with AI, paste this prompt to your AI agent: | ||
| Run \`npx storybook ai prepare\` and follow its instructions precisely.`) | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // Step 9: Track telemetry | ||
| await telemetryService.trackInitWithContext(projectType, selectedFeatures, newUser); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,48 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { createRequire } from 'node:module'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { dirname, join } from 'node:path'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { executeCommand } from 'storybook/internal/common'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { logger } from 'storybook/internal/node-logger'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Installs all Storybook agent skills using the Vercel skills CLI. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Points `npx skills add` at the bundled `skills/` directory, which auto-discovers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * every `SKILL.md` underneath. Adding a new skill is as simple as creating a new | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * `skills/<skill-name>/SKILL.md` file — no changes to this command needed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Uses symlink mode (default) so that updating Storybook automatically refreshes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * the skill content via the symlink target in node_modules. The skills CLI handles | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * agent detection and file placement. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * The `skills/` directory ships with the `create-storybook` package. We resolve | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * its location via `require.resolve` so the path is correct regardless of which | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * package bundled this code — this file is inlined into both `create-storybook` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * and `@storybook/cli` builds by the bundler. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function installAgentSkills(): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const require = createRequire(import.meta.url); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const createStorybookPkg = require.resolve('create-storybook/package.json'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const skillsDir = join(dirname(createStorybookPkg), 'skills'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+24
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard package resolution inside the non-critical error boundary. Line 25 can throw before the 🛠️ Proposed fix export async function installAgentSkills(): Promise<void> {
- const require = createRequire(import.meta.url);
- const createStorybookPkg = require.resolve('create-storybook/package.json');
- const skillsDir = join(dirname(createStorybookPkg), 'skills');
-
try {
+ const require = createRequire(import.meta.url);
+ const createStorybookPkg = require.resolve('create-storybook/package.json');
+ const skillsDir = join(dirname(createStorybookPkg), 'skills');
await executeCommand({
command: 'npx',
args: ['skills', 'add', skillsDir, '--skill', '*', '--yes'],
stdio: 'inherit',
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Silently pipe output on success; surface it only when the command fails. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await executeCommand({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add a logger.step to tell users what we're doing before doing something that can be a bit slow? |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| command: 'npx', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| args: ['skills', 'add', skillsDir, '--skill', '*', '--yes'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stdio: 'pipe', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+28
to
+34
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bound the This now runs on the 🛡️ Suggested fix export async function installAgentSkills(): Promise<void> {
const currentDir = dirname(fileURLToPath(import.meta.url));
const skillsDir = join(currentDir, '..', '..', 'skills');
+ const controller = new AbortController();
+ const timeout = setTimeout(() => controller.abort(), 60_000);
try {
await executeCommand({
command: 'npx',
args: ['skills', 'add', skillsDir, '--skill', '*', '--yes'],
stdio: 'inherit',
+ signal: controller.signal,
});
} catch (error) {
// Non-critical — don't fail the init if skill export fails
logger.warn(
`Could not install agent skills: ${error instanceof Error ? error.message : String(error)}`
);
+ } finally {
+ clearTimeout(timeout);
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Non-critical — don't fail the init if skill installation fails. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const message = error instanceof Error ? error.message : String(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // execa attaches the subprocess's stderr/stdout to the error; include them so | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // users can see why the skills CLI failed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const stderr = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error && typeof error === 'object' && 'stderr' in error ? String(error.stderr ?? '') : ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const stdout = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error && typeof error === 'object' && 'stdout' in error ? String(error.stdout ?? '') : ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const details = [stderr, stdout].filter(Boolean).join('\n'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.warn(`Could not install agent skills: ${message}${details ? `\n${details}` : ''}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct the symlink source package path.
Line 13 says the file is symlinked from
node_modules/@storybook/cli/..., but the installer resolves skills fromcreate-storybook/skills. This can send contributors to the wrong source file.📝 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents