Skip to content

fix(checklist): respect disableTelemetry config in initializeChecklist#34435

Closed
mixelburg wants to merge 82 commits into
storybookjs:nextfrom
mixelburg:fix/telemetry-respect-disableTelemetry-in-checklist
Closed

fix(checklist): respect disableTelemetry config in initializeChecklist#34435
mixelburg wants to merge 82 commits into
storybookjs:nextfrom
mixelburg:fix/telemetry-respect-disableTelemetry-in-checklist

Conversation

@mixelburg
Copy link
Copy Markdown

@mixelburg mixelburg commented Apr 1, 2026

Closes #34431

What I did

The onboarding checklist telemetry calls (onboarding-checklist-muted and onboarding-checklist-status) were not respecting the disableTelemetry: true config option set in .storybook/main.ts.

initializeChecklist() was called unconditionally in common-preset.ts, and the onStateChange handler inside it called telemetry() without any guard.

Fix: Added a disableTelemetry parameter to initializeChecklist(). The caller in common-preset.ts now passes coreOptions?.disableTelemetry. When set, the onStateChange handler returns early before any telemetry calls.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

  1. Add disableTelemetry: true to .storybook/main.ts core config
  2. Start Storybook dev server
  3. Use the onboarding checklist (complete items, mute items)
  4. Verify no onboarding-checklist-muted or onboarding-checklist-status telemetry events are sent

Summary by CodeRabbit

Release Notes

  • New Features

    • Added sharing functionality for stories (copy link, isolate mode)
    • Improved Vitest addon with better monorepo configuration detection
    • Added Next.js Link component mocking support
    • Support for updated log levels in CLI (trace, debug)
  • Bug Fixes

    • Fixed viewport scaling display behavior
    • Improved Vitest addon setup file handling
    • Enhanced configuration detection for Vitest in multi-project setups
  • Documentation

    • Updated CLI logging options documentation
    • Added guidance for CSF factories automation in monorepos

storybook-bot and others added 30 commits January 21, 2026 14:19
…test-mocker-resolution

Builder-Webpack5: Fix @vitest/mocker resolution issue
(cherry picked from commit abd4b01)
…-init-telemetry

CLI: Add init telemetry for CLI integrations
(cherry picked from commit 94f79bf)
…ent-agent-detection-telemetry

Telemetry: Add agent detection
(cherry picked from commit 945a876)
…-factories-monorepo

Docs: Add FAQ about sharing config in monorepos for CSF Next
(cherry picked from commit 90b7abb)
…-loginurl

Composition: Handle 401 responses with loginUrl from Chromatic
(cherry picked from commit afdf02a)
…vitest-prevent-double-nesting

Addon-Vitest: Append Storybook project to existing test.projects array without double nesting
(cherry picked from commit 5bbb915)
…vitest-support-simple-workspace-config

Addon Vitest: Support simple vite.config without defineConfig helper

(cherry picked from commit 751f18f)
…ckage-benchmarking

Build: Fix package benchmarking
(cherry picked from commit 48cecda)
…st-flake

E2E: Fix flakey test
(cherry picked from commit ac039aa)
…vitest-requireassertions

Addon-Vitest: Update Vitest plugin configuration to disable requireAssertions for expect
(cherry picked from commit b18d12e)
Core: Fix `previewHref` when current path does not end with a slash
(cherry picked from commit 00f506c)
…s-addon-vitest-31768

Addon-Vitest: Normalize Windows paths in addon-vitest automigration
(cherry picked from commit 3802b16)
…mod-windows

Codemod: Fix glob pattern handling on Windows
(cherry picked from commit 4d07f57)
…ories-non-interactive-mode

CSFFactories: Add non-interactive mode and --glob flag
(cherry picked from commit e9c8a87)
valentinpalkovic and others added 24 commits February 5, 2026 12:39
…le-select

Globals: Repair dynamicTitle: false for user-defined tools
(cherry picked from commit d355c8b)
…s-cross-file-imports

CSF: Fix cross-file story imports in csf-factories codemod
(cherry picked from commit 8ef9070)
…lag-works

Logger: honor --loglevel for npmlog output
(cherry picked from commit 02fdf2a)
…o-telemetry-patch-label

Telemetry: Add Expo metaframework
(cherry picked from commit 5175dce)
…hannel-events

Telemetry: Add share events
(cherry picked from commit ce42f59)
…it-telemetry

Telemetry: Add init exit event
(cherry picked from commit 5e1084c)
…ent-view-undefined

Test: Update event creation logic in user-event package
(cherry picked from commit a4a0dff)
…-mocker-relative-path

Builder-Vite: Use relative path for mocker entry in production builds
(cherry picked from commit 3c49657)
…c-issue

Builder-Vite: Update dependencies react-vite framework
(cherry picked from commit dd088b3)
…k-component-override

Next.js: Fix Link component override in appDirectory configuration

(cherry picked from commit 40e556a)
…e-config-file-detection

Addon-Vitest: Improve config file detection in monorepos
(cherry picked from commit d7c87fa)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

Version bump to 10.2.9 across Storybook monorepo with functional enhancements: Vitest addon improvements (config discovery, monorepo support), mocking utilities refactoring (extracting findMockRedirect, replacing runtime bundling), telemetry enhancements (agent detection, share events), CLI automigrations (csf-factories glob/yes support), and framework/builder updates (Next.js link mock, Vite output directory enforcement).

Changes

Cohort / File(s) Summary
Package Version Updates
CHANGELOG.md, code/package.json, code/core/package.json, code/addons/*/package.json, code/builders/*/package.json, code/frameworks/*/package.json, code/lib/*/package.json, code/presets/*/package.json, code/renderers/*/package.json
Version bumped from 10.2.0 to 10.2.9 across all packages. CHANGELOG.md entries added for versions 10.2.9–10.2.1 covering Addon-Vitest, Builder-Vite, and Next.js changes. Docs version files updated with release notes.
Vitest Addon Config Discovery & Postinstall
code/addons/vitest/src/node/vitest-manager.ts, code/addons/vitest/src/postinstall.ts, code/addons/vitest/src/postinstall.test.ts
Vitest config discovery now walks directory tree from package root upward, detecting both vitest.config and vite.config files, checking for storybookTest/addon-vitest markers. Added isConfigAlreadySetup helper to detect existing plugin usage. Postinstall now reuses existing setup files instead of erroring, skips updates when already configured, and supports remote package execution when skipInstall is set.
Vitest Config Merge & Update Logic
code/addons/vitest/src/updateVitestFile.ts, code/addons/vitest/src/updateVitestFile.test.ts
loadTemplate now uses dynamic ?raw imports instead of disk reads. Added getTargetConfigObject helper to resolve config exports (direct objects, defineConfig, or identifier references). Enhanced test.projects array detection: appends Storybook project to existing array instead of nesting, avoiding double-wrapped projects. Tightened validation for function-notation defineConfig.
Vitest Plugin & Typing
code/addons/vitest/src/vitest-plugin/index.ts, code/addons/vitest/src/typings.d.ts
Set expect.requireAssertions to false in base test config. Added typings for ?raw imports as string exports.
Mocking Utilities Refactoring
code/core/src/mocking-utils/index.ts, code/core/src/mocking-utils/redirect.ts, code/core/src/mocking-utils/runtime.ts, code/builders/builder-vite/src/plugins/vite-mock/plugin.ts, code/builders/builder-webpack5/src/plugins/webpack-mock-plugin.ts
Extracted findMockRedirect re-export to core mocking-utils. Runtime now reads from filesystem via import.meta.resolve instead of esbuild bundling. Builders now import findMockRedirect from storybook/internal/mocking-utils instead of @vitest/mocker.
Vite Mocker Plugin
code/builders/builder-vite/src/plugins/vite-inject-mocker/plugin.ts, code/builders/builder-vite/src/plugins/vite-inject-mocker/plugin.test.ts
Replaced file-based runtime injection with emitted chunk mapping. Injected script src now varies by command: relative in build, absolute in dev. Added enforce: 'pre' and tests validating build/serve path handling and head attribute preservation.
Vite Builder Output Directory
code/builders/builder-vite/src/build.ts
Added storybook:enforce-output-dir Vite plugin (enforce: post) with config and configEnvironment hooks to set build.outDir after other plugins, preventing framework redirects (e.g., Nitro).
Telemetry: Agent Detection
code/core/src/telemetry/detect-agent.ts, code/core/src/telemetry/detect-agent.test.ts, code/core/src/telemetry/types.ts, code/core/src/telemetry/telemetry.ts
New detectAgent function checks explicit agent env vars (AGENT=amp, CLAUDEDECODE, GEMINI_CLI, CODEX_SANDBOX, CURSOR_AGENT) and falls back to heuristics (TERM=dumb, GIT_PAGER=cat when !stdoutIsTTY). Added AgentInfo/KnownAgentName types. Integrated into telemetry globalContext.
Telemetry: Channels & Events
code/core/src/core-server/server-channel/telemetry-channel.ts, code/core/src/core-server/server-channel/telemetry-channel.test.ts, code/core/src/core-server/presets/common-preset.ts
Replaced initPreviewInitializedChannel with initTelemetryChannel. Added listeners for SHARE_STORY_LINK, SHARE_ISOLATE_MODE, SHARE_POPOVER_OPENED events emitting share telemetry. Moved from preview-initialized to dedicated telemetry channel. Updated checklist init to receive disableTelemetry flag.
Telemetry: Checklist & Metadata
code/core/src/core-server/utils/checklist.ts, code/core/src/telemetry/storybook-metadata.ts
initializeChecklist accepts disableTelemetry parameter, skips telemetry on initial load. Added expo framework detection in metaFrameworks mapping.
Core Events & Manager Exports
code/core/src/core-events/index.ts, code/core/src/manager/globals/exports.ts
Added SHARE_STORY_LINK, SHARE_ISOLATE_MODE, SHARE_POPOVER_OPENED core event enums and exports.
Share UI & Interactions
code/core/src/manager/components/preview/tools/share.tsx, code/core/src/manager/components/preview/tools/share.stories.tsx
Added useEffect to emit SHARE_POPOVER_OPENED on mount. Copy link button emits SHARE_STORY_LINK. Isolation mode link emits SHARE_ISOLATE_MODE. Updated test mocks to provide api.emit.
Zoom Tool Refactor
code/core/src/manager/components/preview/tools/zoom.tsx, code/core/src/manager/components/preview/tools/zoom.stories.tsx
Changed trigger from Button to ToggleButton, prop from active to pressed. Story test now locates control by role switch instead of button.
Select Component & Toolbar
code/core/src/components/components/Select/Select.tsx, code/core/src/components/components/Select/Select.stories.tsx, code/core/src/toolbar/components/ToolbarMenuSelect.tsx
Added showSelectedOptionTitle prop to Select (defaults true). When true, single-select shows selected option title; when false, shows children placeholder. ToolbarMenuSelect defaults dynamicTitle to true and passes to Select as showSelectedOptionTitle.
Viewport & URL Handling
code/core/src/manager/components/preview/Viewport.tsx, code/core/src/manager-api/modules/url.ts, code/core/src/manager-api/modules/refs.ts
Viewport scale now sets transform to 'none' when scale equals 1. URL getStoryHrefs defaults missing pathname to '/'. Refs handleRequest now returns loginUrl on 401 responses with JSON body.
Logger & CLI Options
code/core/src/node-logger/index.ts, code/core/src/node-logger/index.test.ts, code/core/src/bin/core.ts
Added setLogLevel alias to logger (maps trace→silly, debug→verbose). CLI --loglevel now uses commander Option API with choices constraint and default. Unconditionally calls logger.setLogLevel after --debug handling.
CSF Tools & Meta Detection
code/core/src/csf-tools/CsfFile.ts, code/core/src/csf-tools/CsfFile.test.ts
BadMetaError now only thrown for preview.meta calls, ignoring .meta() on other identifiers (Zod-like patterns). Added tests for complex chained patterns.
Build Config & Runtime Export
code/core/build-config.ts, core/src/common/js-package-manager/JsPackageManager.ts, code/core/src/common/js-package-manager/PNPMProxy.ts
Added ./internal/mocking-utils/mocker-runtime build entry. JsPackageManager.runPackageCommand now accepts useRemotePkg flag; PNPMProxy switches to dlx when true.
Addon A11y & Vitest Postinstall
code/addons/a11y/src/postinstall.ts, code/core/src/cli/AddonVitestService.ts, code/core/src/cli/AddonVitestService.test.ts
a11y postinstall now conditionally pins storybook version and uses remote package when skipInstall is true. AddonVitestService.installPlaywright expanded options to include useRemotePkg. validateConfigFiles now handles mergeConfig patterns and identifier-based config exports.
Cache Path Versioning
code/core/src/common/utils/resolve-path-in-sb-cache.ts, code/core/src/common/utils/resolve-path-in-sb-cache.test.ts
Cache path now includes Storybook version: .../storybook/{version}/{directoryName}. Falls back to 'unknown' when version unavailable.
Common Utilities
code/core/src/common/utils/get-storybook-refs.ts, code/core/src/common/utils/get-storybook-refs.test.ts, code/core/src/common/versions.ts, code/core/src/manager-api/version.ts
checkRef exported from get-storybook-refs (was private). All version strings updated 10.2.0 → 10.2.9.
Manager API Tests
code/core/src/manager-api/tests/refs.test.ts, code/core/src/manager-api/tests/url.test.js
Added test case for 401 auth responses with loginUrl handling. Added test for /index.html pathname handling in getStoryHrefs.
CLI Automigrate Infrastructure
code/lib/cli-storybook/src/automigrate/types.ts, code/lib/cli-storybook/src/automigrate/index.ts, code/lib/cli-storybook/src/automigrate/multi-project.ts
Extended RunOptions with yes and glob flags. AutofixOptionsFromCLI now accepts glob parameter. Automigrate and runFixes now thread yes/glob through fix.run calls.
CSF Factories Codemod
code/lib/cli-storybook/src/codemod/csf-factories.ts
Now accepts yes and glob options. Uses IN_STORYBOOK_SANDBOX to set default glob pattern. Conditionally prompts for glob when neither glob nor yes provided. Forwards glob to migration codemod.
CSF Factory Helper Utilities
code/lib/cli-storybook/src/codemod/helpers/csf-factories-utils.ts, code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts, code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts
Added addImportToTop helper to preserve leading comments when inserting imports. config-to-csf-factory now generates default export when preview is empty, and uses addImportToTop. Tests verify comment preservation.
Story CSF Factory Codemod
code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts, code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
Added cross-file story import detection (.stories files). Transforms BaseStories.Primary.args → BaseStories.Primary.input.args. Handles spread patterns and reuse from imported stories. Uses addImportToTop. Tests verify comment preservation and cross-file transforms.
CLI Codemod Runner
code/lib/codemod/src/index.ts, code/lib/codemod/src/index.test.ts
Normalizes user glob via pathe.normalize before tinyglobby. Passes file paths directly to jscodeshift (not quoted). Added test coverage for CSF2-to-3 transformation with various glob patterns.
CLI Run Command
code/lib/cli-storybook/src/bin/run.ts
Added --glob option to automigrate command. Improved handleCommandFailure to log errors instead of silently failing.
Create Storybook Commands
code/lib/create-storybook/src/commands/AddonConfigurationCommand.ts, code/lib/create-storybook/src/commands/AddonConfigurationCommand.test.ts
Removed dependencyInstallationResult gating. Now routes addons to specific postinstall handlers (vitest, a11y, or generic). Passes useRemotePkg: !!skipInstall to Playwright install. Added extensive test coverage with real addon postinstall paths.
Project Detection & Init Flow
code/lib/create-storybook/src/commands/ProjectDetectionCommand.ts, code/lib/create-storybook/src/initiate.ts, code/lib/create-storybook/src/scaffold-new-project.ts
ProjectDetectionCommand emits exit telemetry on existing-installation detection. scaffold-new-project emits exit telemetry for other strategy. Removed dependencyInstallationResult from executeAddonConfiguration call.
Version Service
code/lib/create-storybook/src/services/VersionService.ts, code/lib/create-storybook/src/services/VersionService.test.ts
Added CLI integration detection for create-rsbuild and @tanstack/start. Tests cover npx, npm create, and pnpm variants.
Sandbox Command
code/lib/cli-storybook/src/sandbox.ts
Changed latestVersion fallback from forced non-null to nullable string. Added debug logging for version/prerelease/outdated detection. Wrapped logBox in try-catch.
Next.js Link Mock
code/frameworks/nextjs/src/export-mocks/link/index.tsx, code/frameworks/nextjs/src/export-mocks/webpack.ts, code/frameworks/nextjs/build-config.ts, code/frameworks/nextjs/package.json
Added MockLink component mock for next/link. Handles href composition, click interception, and emits linkAction telemetry. Added webpack alias and build export for ./link.mock. Updated package exports.
Next.js App Router Context
code/frameworks/nextjs/src/aliases/webpack.ts
Added webpack aliases for next/dist/shared/lib/app-router-context variants (mapping both to shared-runtime).
Vitest/Webpack Dependency Changes
code/builders/builder-vite/package.json, code/builders/builder-webpack5/package.json, code/core/package.json
Removed @vitest/mocker from builder dependencies (moved to core devDependencies). Updated core build config for mocking runtime entry.
Build Scripts & External Handling
scripts/build/utils/entry-utils.ts, scripts/build/utils/generate-bundle.ts, scripts/package.json
Added @vitest/mocker to runtimeExternalExclude. generateBundle now includes rawPlugin from esbuild-raw-plugin. Runtime bundle externals include msw/browser and msw/core/http. Added esbuild-raw-plugin dependency.
Bench Scripts
scripts/bench/bench-packages.ts
Made --base-branch and --pull-request options optional. Changed runtime type checks from truthiness to explicit typeof guards.
ESLint Config & Framework Schemas
code/.eslintrc.js, code/frameworks/*/build-schema.json, code/frameworks/*/start-schema.json
import-x/named rule changed warn → off (TypeScript handles missing imports). Framework loglevel schema descriptions updated from silly/verbose to trace/debug.
Documentation Updates
docs/_snippets/*.md, docs/api/cli-options.mdx, docs/get-started/frameworks/angular.mdx, docs/api/csf/csf-next.mdx, docs/writing-stories/typescript.mdx, docs/versions/*.json
Updated loglevel documentation (trace/debug instead of silly/verbose). Added CSF-factories monorepo guidance and snippet. Updated TypeScript docs for Vue generic component support in CSF Next. Updated version changelogs and release notes.
React Vite Dependency
code/frameworks/react-vite/package.json
Bumped @joshwooding/vite-plugin-react-docgen-typescript from ^0.6.3 to ^0.6.4.
E2E Tests
code/e2e-tests/preview-api.spec.ts
Changed control input from type() to pressSequentially() with delay.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

✨ Finishing Touches
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch fix/telemetry-respect-disableTelemetry-in-checklist

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
code/core/src/common/utils/get-storybook-refs.ts (1)

61-81: ⚠️ Potential issue | 🟠 Major

Handle second-fetch failures in checkRef to avoid promise rejection leakage.

checkRef currently converts first-fetch failures to false, but if the second fetch fails (the JSON probe), the promise rejects instead of returning false. That can break callers that expect a boolean result.

🔧 Proposed fix
-export const checkRef = (url: string) =>
-  fetch(`${url}/iframe.html`).then(
-    async ({ ok, status }) => {
-      if (ok) {
-        if (status !== 200) {
-          return false;
-        }
-
-        // so the status is ok, but if we'd ask for JSON we might get a response saying we need to authenticate.
-        const data = await fetch(`${url}/iframe.html`, {
-          headers: { Accept: 'application/json' },
-        });
-        // we might receive non-JSON as a response, because the service ignored our request for JSON response type.
-        if (data.ok && (await (data as any).json().catch(() => ({}))).loginUrl) {
-          return false;
-        }
-      }
-      return ok;
-    },
-    () => false
-  );
+export const checkRef = async (url: string) => {
+  try {
+    const { ok, status } = await fetch(`${url}/iframe.html`);
+    if (!ok || status !== 200) {
+      return false;
+    }
+
+    // so the status is ok, but if we'd ask for JSON we might get a response saying we need to authenticate.
+    const data = await fetch(`${url}/iframe.html`, {
+      headers: { Accept: 'application/json' },
+    });
+    // we might receive non-JSON as a response, because the service ignored our request for JSON response type.
+    if (data.ok && (await (data as any).json().catch(() => ({}))).loginUrl) {
+      return false;
+    }
+    return true;
+  } catch {
+    return false;
+  }
+};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/common/utils/get-storybook-refs.ts` around lines 61 - 81,
checkRef can reject if the second fetch (the JSON probe to `${url}/iframe.html`)
throws; update checkRef so any errors from the second fetch or from parsing JSON
are caught and result in returning false rather than propagating a rejection.
Specifically, inside the success branch of export const checkRef, wrap the
second fetch and the subsequent (data as any).json() call in a try/catch (or
append a .catch) so that failures during the JSON probe cause the function to
return false; reference symbols: checkRef, the second fetch to
`${url}/iframe.html`, and the (data as any).json() call.
code/frameworks/vue3-vite/package.json (1)

69-69: ⚠️ Potential issue | 🔴 Critical

Fix invalid gitHead across all 39 package.json files in the monorepo.

The gitHead field value a8e7fd8a655c69780bc20b9749d2699e45beae1l is malformed—it ends with lowercase l instead of a valid hexadecimal character. Git commit SHAs must be 40-character hexadecimal strings (0-9, a-f only). This issue affects all package.json files in the monorepo and will break package publishing. The current HEAD commit SHA is e22f6a06600cbc0a99768604c2f94f822ffd16d2; verify the correct commit SHA and update all occurrences accordingly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/frameworks/vue3-vite/package.json` at line 69, The gitHead field in
package.json files contains a malformed SHA
("a8e7fd8a655c69780bc20b9749d2699e45beae1l"); locate every package.json (all 39)
and replace the invalid gitHead value with the correct 40‑char hexadecimal
commit SHA "e22f6a06600cbc0a99768604c2f94f822ffd16d2" (ensure the value is a
plain hex string, no trailing letters or quotes changes), or programmatically
run a safe search-and-replace for the gitHead property key to update the value
consistently across the monorepo and then verify by running a quick JSON
parse/lint of each package.json to confirm validity.
♻️ Duplicate comments (7)
code/lib/cli-sb/package.json (1)

34-34: ⚠️ Potential issue | 🔴 Critical

Duplicate: Invalid gitHead value.

Same malformed gitHead as flagged in code/frameworks/vue3-vite/package.json:69.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/cli-sb/package.json` at line 34, The package.json contains a
malformed gitHead value ("gitHead": "a8e7fd8a655c69780bc20b9749d2699e45beae1l");
update the gitHead field to a valid 40-character hexadecimal commit hash (remove
the trailing non-hex 'l') or remove the gitHead entry entirely; target the
"gitHead" key in package.json to make this change so it matches the valid
gitHead in the other package.json.
code/renderers/svelte/package.json (1)

73-73: ⚠️ Potential issue | 🔴 Critical

Duplicate: Invalid gitHead value.

Same malformed gitHead as flagged in code/frameworks/vue3-vite/package.json:69. This must be corrected to a valid 40-character hexadecimal SHA before publishing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/renderers/svelte/package.json` at line 73, The package.json contains an
invalid gitHead value under the "gitHead" field (currently
"a8e7fd8a655c69780bc20b9749d2699e45beae1l"); replace this malformed value with
the correct 40-character hexadecimal commit SHA for the release (ensure only
[0-9a-f] characters and length 40) so the "gitHead" entry in package.json is a
valid commit SHA before publishing.
code/lib/eslint-plugin/package.json (1)

70-70: ⚠️ Potential issue | 🔴 Critical

Duplicate: Invalid gitHead value.

Same malformed gitHead as flagged in code/frameworks/vue3-vite/package.json:69.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/eslint-plugin/package.json` at line 70, The gitHead field contains
an invalid value ("a8e7fd8a655c69780bc20b9749d2699e45beae1l") — remove or
replace the trailing non-hex character so it is a valid 40-character SHA, or
delete the gitHead property if you don't have the exact commit; update the
"gitHead" key in package.json accordingly (same fix as applied to the other
package.json with the duplicate malformed gitHead).
code/lib/react-dom-shim/package.json (1)

51-51: ⚠️ Potential issue | 🔴 Critical

Duplicate: Invalid gitHead value.

Same malformed gitHead as flagged in code/frameworks/vue3-vite/package.json:69.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/react-dom-shim/package.json` at line 51, The package.json has an
invalid gitHead value "a8e7fd8a655c69780bc20b9749d2699e45beae1l" — the trailing
non-hex character makes the commit hash malformed; update the gitHead field in
code/lib/react-dom-shim/package.json (the "gitHead" property) to a valid commit
SHA (remove the trailing 'l' or replace with the correct hex hash) so it matches
the proper 40-character hex format used elsewhere.
code/renderers/vue3/package.json (1)

68-68: ⚠️ Potential issue | 🔴 Critical

Duplicate: Invalid gitHead value.

Same malformed gitHead as flagged in code/frameworks/vue3-vite/package.json:69.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/renderers/vue3/package.json` at line 68, The package.json contains a
malformed gitHead value ("gitHead" key in the file) — replace the invalid entry
"a8e7fd8a655c69780bc20b9749d2699e45beae1l" with either a valid 40-character git
commit SHA or remove the "gitHead" field entirely; ensure the corrected value
matches the repository commit hash format (hex digits only) and mirrors the fix
applied to the duplicate in the other package.json.
code/frameworks/html-vite/package.json (1)

62-62: ⚠️ Potential issue | 🔴 Critical

Duplicate: Invalid gitHead value.

Same malformed gitHead as flagged in code/frameworks/vue3-vite/package.json:69.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/frameworks/html-vite/package.json` at line 62, The package.json contains
an invalid gitHead value ("gitHead": "a8e7fd8a655c69780bc20b9749d2699e45beae1l")
— replace the malformed entry under the "gitHead" key with the correct
40-character hexadecimal commit SHA for this package (or remove the gitHead
field if it was added accidentally and regenerate the package metadata),
ensuring the value uses only 0-9 and a-f characters; locate the "gitHead" key in
this package.json to make the change.
code/renderers/web-components/package.json (1)

70-70: ⚠️ Potential issue | 🔴 Critical

Duplicate: Invalid gitHead value.

Same malformed gitHead as flagged in code/frameworks/vue3-vite/package.json:69.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/renderers/web-components/package.json` at line 70, The package.json
contains a malformed gitHead value ("gitHead":
"a8e7fd8a655c69780bc20b9749d2699e45beae1l"); locate the gitHead property and fix
it by replacing the trailing non-hex character with the correct commit hash
(ensure it is a valid lowercase hex SHA) or remove the gitHead field if not
needed; use the same corrected value used in the other package.json (or obtain
the proper commit id from your git history) so both package.json files match.
🧹 Nitpick comments (22)
code/core/src/manager/components/preview/Viewport.tsx (1)

14-15: Pre-existing: relative imports missing explicit file extensions.

As per coding guidelines, TypeScript imports should use explicit file extensions (e.g., ./Iframe.tsx, ./NumericInput.tsx). These imports predate this PR's change, so no action required here, but consider addressing in a follow-up.

Suggested fix
-import { IFrame } from './Iframe';
-import { NumericInput } from './NumericInput';
+import { IFrame } from './Iframe.tsx';
+import { NumericInput } from './NumericInput.tsx';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/manager/components/preview/Viewport.tsx` around lines 14 - 15,
Update the two relative imports in Viewport.tsx to include explicit TypeScript
file extensions: change the import specifiers for the IFrame and NumericInput
modules from './Iframe' and './NumericInput' to './Iframe.tsx' and
'./NumericInput.tsx' respectively; ensure the import statements that reference
the IFrame and NumericInput symbols in the Viewport.tsx component are updated so
the module resolver sees the explicit .tsx extensions.
code/core/src/components/components/Select/Select.stories.tsx (1)

1490-1493: Tighten these assertions to avoid false positives.

These checks currently validate substrings only, so they can pass even if selected and default labels are both rendered. Please add explicit negative checks (e.g., selected title not present when showSelectedOptionTitle=false) or exact-match assertions.

Suggested assertion hardening
 export const ShowSelectedOptionTitleFalse = meta.story({
@@
   play: async ({ canvas, step }) => {
     await step('Verify default title is shown instead of selected option', async () => {
       const selectButton = await canvas.findByRole('button');
       expect(selectButton).toHaveTextContent('Animal');
+      expect(selectButton).not.toHaveTextContent('Frog');
     });
   },
 });

Also applies to: 1504-1507, 1519-1522, 1534-1537

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/components/components/Select/Select.stories.tsx` around lines
1490 - 1493, The assertion in the Storybook tests uses toHaveTextContent which
allows substring matches and can give false positives (e.g., both default and
selected labels present); update the assertions in the Select.stories tests that
use the 'selectButton' lookup inside the step blocks (e.g., the steps titled
"Verify selected option title is shown" and the related steps at the other
occurrences) to use exact-match checks or add negative checks: replace substring
assertions with an equality/assertExact match against the full button text, or
add expect(selectButton).not.toHaveTextContent('<default label>') /
expect(selectButton).toHaveTextContent('<selected label>') depending on the
case, and when testing showSelectedOptionTitle=false assert the selected title
is absent; apply the same change to the other indicated steps (the ones around
the later step blocks).
code/core/src/common/utils/get-storybook-refs.test.ts (1)

31-34: Add a test for “second fetch fails after initial 200”.

Current tests cover initial fetch rejection, but not failure of the JSON probe request. Adding that case would prevent regressions in the boolean contract of checkRef.

🧪 Suggested test
   it('returns false when fetch fails', async () => {
     vi.spyOn(global, 'fetch').mockRejectedValue(new Error('Network error'));
     expect(await checkRef('https://chromatic.com')).toBe(false);
   });
+
+  it('returns false when auth probe fetch fails after initial 200', async () => {
+    vi.spyOn(global, 'fetch')
+      .mockResolvedValueOnce({ ok: true, status: 200, json: async () => ({}) } as Response)
+      .mockRejectedValueOnce(new Error('Network error'));
+    expect(await checkRef('https://chromatic.com')).toBe(false);
+  });
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/common/utils/get-storybook-refs.test.ts` around lines 31 - 34,
Add a new unit test for checkRef that simulates the first fetch resolving with a
200 Response and the subsequent JSON probe fetch rejecting; specifically, mock
global.fetch so the first call returns a successful Response-like object and the
second call rejects with an error, then assert checkRef('https://chromatic.com')
resolves to false to ensure the boolean contract is preserved when the probe
fails.
code/lib/codemod/src/index.test.ts (1)

7-7: Missing explicit file extension in relative import.

Per coding guidelines, TypeScript files should use explicit file extensions for relative imports.

Proposed fix
-import { runCodemod } from './index';
+import { runCodemod } from './index.ts';

As per coding guidelines: **/*.{ts,tsx}: Use explicit file extensions for relative code imports and exports in TypeScript

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/codemod/src/index.test.ts` at line 7, Update the relative import to
include an explicit TypeScript extension: change the import of runCodemod in
index.test.ts (currently "import { runCodemod } from './index';") to use
"./index.ts" so the test follows the rule requiring explicit .ts/.tsx extensions
for relative imports.
code/lib/codemod/src/index.ts (1)

12-12: Missing explicit file extension in relative import.

Per coding guidelines, TypeScript files should use explicit file extensions for relative imports.

Proposed fix
-import { jscodeshiftToPrettierParser } from './lib/utils';
+import { jscodeshiftToPrettierParser } from './lib/utils.ts';

As per coding guidelines: **/*.{ts,tsx}: Use explicit file extensions for relative code imports and exports in TypeScript (e.g., ./foo.ts or ./bar.tsx)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/codemod/src/index.ts` at line 12, The import in index.ts uses a
relative path without an explicit TypeScript extension; update the import
statement that references jscodeshiftToPrettierParser to use an explicit .ts
extension (change './lib/utils' to './lib/utils.ts') so the module import
follows the TypeScript explicit-extension guideline and resolves correctly.
docs/get-started/frameworks/angular.mdx (1)

292-292: Tighten wording for readability in the loglevel description.

Small wording tweak to make the sentence read more naturally.

✏️ Suggested docs tweak
-| `"loglevel"`                 | Controls level of logging during build. Can be one of: [trace, debug, info (default), warn, error, silent]. <br /> `"loglevel": "info"`                                                       |
+| `"loglevel"`                 | Controls the logging level during build. It can be one of: [trace, debug, info (default), warn, error, silent]. <br /> `"loglevel": "info"`                                                   |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/get-started/frameworks/angular.mdx` at line 292, Update the table entry
for "loglevel" to a tighter, more natural sentence: replace the current text
with something like: Controls the logging level during the build; one of: trace,
debug, info (default), warn, error, silent. Example: "loglevel": "info". Target
the table cell containing the "loglevel" description so the wording change
applies to that exact entry.
code/core/package.json (1)

129-129: Consider documenting why this export lacks a types field.

Unlike other exports in this file that include both types and default fields, this new mocker-runtime export only specifies a default path. Based on the related build-config.ts change, this appears intentional (dts: false), but a brief comment explaining the rationale would help future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/package.json` at line 129, The package.json export for
"./internal/mocking-utils/mocker-runtime" only provides a "default" path and
omits a "types" entry; update the file to add a short inline comment next to the
export explaining this is intentional because the build config (see
build-config.ts) sets dts: false for the mocker-runtime build, so no generated
.d.ts exists, or alternatively add a corresponding "types" mapping if you want
type declarations—reference the export key "mocker-runtime" and the
build-config.ts setting "dts: false" when making the change.
code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts (1)

10-10: Add explicit .ts extension to the import path.

As per coding guidelines, TypeScript imports should use explicit file extensions.

Proposed fix
 import {
-  addImportToTop,
+  addImportToTop,
   cleanupTypeImports,
   getConfigProperties,
   removeExportDeclarations,
-} from './csf-factories-utils';
+} from './csf-factories-utils.ts';

As per coding guidelines: **/*.{ts,tsx}: Use explicit file extensions for relative code imports and exports in TypeScript.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts` at line
10, Update the import that brings in addImportToTop so the module specifier uses
an explicit TypeScript extension (e.g., change the relative import from
".../someModule" to ".../someModule.ts"); locate the import list that includes
addImportToTop in config-to-csf-factory and append ".ts" to the referenced file
path to comply with the TypeScript explicit-extension rule.
scripts/build/utils/generate-bundle.ts (1)

10-10: Minor formatting inconsistency.

The import lacks spaces around the braces, inconsistent with other imports in this file.

🔧 Suggested fix for consistent formatting
-import {raw as rawPlugin} from 'esbuild-raw-plugin';
+import { raw as rawPlugin } from 'esbuild-raw-plugin';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/build/utils/generate-bundle.ts` at line 10, The import statement
"import {raw as rawPlugin} from 'esbuild-raw-plugin';" is formatted
inconsistently; update it to include spaces around the braces and the "as" alias
to match the file's import style (e.g., "import { raw as rawPlugin } from
'esbuild-raw-plugin';"), locating and changing the line that imports rawPlugin
from esbuild-raw-plugin in generate-bundle.ts.
code/core/src/common/utils/resolve-path-in-sb-cache.test.ts (2)

10-18: Consider using spy: true option for mocks.

As per coding guidelines, vi.mock() should use the spy: true option for package and file mocks. This enables spying on the mocked functions while still allowing custom implementations.

♻️ Suggested refactor
-vi.mock('empathic/package', () => ({
-  cache: vi.fn(),
-}));
+vi.mock('empathic/package', { spy: true });

-vi.mock('../versions', () => ({
-  default: {
-    storybook: '10.3.0-alpha.1',
-  },
-}));
+vi.mock('../versions', { spy: true });

Then set up the default mock implementations in beforeEach:

beforeEach(() => {
  vi.clearAllMocks();
  vi.mocked(versions).storybook = '10.3.0-alpha.1';
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/common/utils/resolve-path-in-sb-cache.test.ts` around lines 10
- 18, Update the test mocks to use the spy option and initialize values in
beforeEach: change the static vi.mock calls for 'empathic/package' and
'../versions' to include { spy: true } so the mocked functions can be spied on,
and move the default implementation of versions.storybook into a beforeEach
where you call vi.clearAllMocks() and set vi.mocked(versions).storybook =
'10.3.0-alpha.1'; ensure you still provide the custom implementation for
empathic/package.cache via the spy-enabled mock.

92-106: Reset mock state in a cleaner way.

Directly mutating vi.mocked(versions).storybook and manually resetting it at the end of the test is fragile. If the test fails before the reset, subsequent tests could be affected. Consider using beforeEach to set up a consistent state, or use a more explicit mock reset pattern.

♻️ Alternative approach using afterEach
+  afterEach(() => {
+    // Ensure versions mock is reset to default
+    vi.mocked(versions).storybook = '10.3.0-alpha.1';
+  });
+
   it('should use "unknown" as version when storybook version is not available', () => {
     const mockCacheDir = '/mock/node_modules/.cache/storybook';
     vi.mocked(pkg.cache).mockReturnValue(mockCacheDir);

     // Mock the versions module to return a falsy value
     vi.mocked(versions).storybook = '' as any;

     const result = resolvePathInStorybookCache('test-file', 'test-sub');

     expect(result).toContain('unknown');
     expect(result).toBe(join(mockCacheDir, 'unknown', 'test-sub', 'test-file'));
-
-    // Reset the mock
-    vi.mocked(versions).storybook = '10.3.0-alpha.1';
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/common/utils/resolve-path-in-sb-cache.test.ts` around lines 92
- 106, The test mutates versions.storybook directly which is brittle; instead,
mock and restore it properly around the test: replace the direct assignment to
vi.mocked(versions).storybook with a scoped mock (e.g., vi.spyOn(versions,
'storybook', 'get').mockReturnValueOnce('') or vi.mocked(versions).storybook =
'' using vi.fn/mockReturnValueOnce) and add an afterEach or beforeEach that
calls vi.restoreAllMocks() or vi.resetAllMocks() so
resolvePathInStorybookCache('test-file','test-sub') runs with a controlled mock
and the original versions value is automatically restored; reference the
versions symbol and the resolvePathInStorybookCache function to locate where to
apply the spy/reset.
code/builders/builder-vite/src/plugins/vite-inject-mocker/plugin.test.ts (1)

15-23: Use the repo-standard Vitest mock shape in this new file.

Because this file is new, I’d switch the node:url mock to vi.mock(..., { spy: true }) and drive fileURLToPath through vi.mocked(...) instead of an inline factory mock. That keeps it aligned with the suite’s mocking conventions while still letting you set the import-time behavior before loading the plugin.

As per coding guidelines, "Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests" and "Use vi.mocked() to type and access the mocked functions in Vitest tests".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/plugins/vite-inject-mocker/plugin.test.ts`
around lines 15 - 23, Replace the inline factory mock for 'node:url' with the
repo-standard spy-style mock: call vi.mock('node:url', { spy: true }) and then
obtain a typed handle using vi.mocked(fileURLToPath) to set its return value;
also keep the import.meta.resolve stub via vi.stubGlobal but ensure it is set
before dynamically importing './plugin.js' so viteInjectMockerRuntime loads with
the mocked behavior. Use vi.mocked(fileURLToPath) to configure the mock return
(instead of the inline factory) and then await import('./plugin.js') to retrieve
viteInjectMockerRuntime.
code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts (1)

372-408: Add the named-reexport-only preview case to this fallback coverage.

These new assertions cover empty and side-effect-only previews, but the same “missing default export” branch also needs a regression for preview files that only re-export named symbols (for example export { withTheme } from './decorators'). A small snapshot here would lock down that repo-specific edge case too.

Based on learnings, preview files with only named exports (including re-exports) still require a generated export default definePreview({}) because story files import the preview default.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts`
around lines 372 - 408, Add a test case covering the "named re-export only"
preview scenario in the same suite: call the existing transform helper with a
preview string like "export { withTheme } from './decorators'" and assert it
resolves to a snapshot that imports definePreview from '@storybook/react-vite'
and emits "export default definePreview({});". Update the test file
codemod/helpers/config-to-csf-factory.test.ts by adding an it(...) similar to
the existing empty/side-effect tests, referencing the transform helper so the
regression for preview files that only re-export named symbols is locked down.
code/lib/create-storybook/src/commands/AddonConfigurationCommand.test.ts (2)

56-63: Mocks cleared after initialization creates potential timing issues.

The command is instantiated at lines 56-61, but vi.clearAllMocks() is called at line 63 after the command creation. This clears any mock calls that may have occurred during construction. If this is intentional, consider adding a comment to clarify.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/create-storybook/src/commands/AddonConfigurationCommand.test.ts`
around lines 56 - 63, The test instantiates AddonConfigurationCommand while
vi.clearAllMocks() is called afterwards, which can accidentally wipe out mock
call history produced during construction; move the vi.clearAllMocks() call to
before the new AddonConfigurationCommand(...) creation (or if you intentionally
want to drop constructor calls, add an explicit comment explaining that) so that
mockPackageManager, mockAddonVitestService, and mockTelemetryService call
history is preserved or clearly documented.

17-19: Consider using spy: true for mock consistency.

The mock for postinstallAddon uses a factory function instead of { spy: true }. While this works, it's inconsistent with the other mocks in this file and deviates from the coding guidelines.

As per coding guidelines: "Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests."

♻️ Suggested refactor
-vi.mock('../../../cli-storybook/src/postinstallAddon', () => ({
-  postinstallAddon: vi.fn().mockResolvedValue(undefined),
-}));
+vi.mock('../../../cli-storybook/src/postinstallAddon', { spy: true });

Then in beforeEach:

const { postinstallAddon } = await import('../../../cli-storybook/src/postinstallAddon');
vi.mocked(postinstallAddon).mockResolvedValue(undefined);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/create-storybook/src/commands/AddonConfigurationCommand.test.ts`
around lines 17 - 19, The current mock for postinstallAddon is created with a
factory function which is inconsistent with the repo guideline—replace the
inline factory mock with vi.mock('../../../cli-storybook/src/postinstallAddon',
{ spy: true }) and then in the test setup (e.g., in beforeEach) import {
postinstallAddon } from '../../../cli-storybook/src/postinstallAddon' and call
vi.mocked(postinstallAddon).mockResolvedValue(undefined) so the mock uses
Vitest's spy mode and matches the other mocks in
AddonConfigurationCommand.test.ts.
code/addons/vitest/src/postinstall.ts (1)

165-165: Minor: redundant double negation.

options.skipInstall is already a boolean (from PostinstallOptions), so !!options.skipInstall is redundant. However, this is harmless and may be intentional for explicitness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/addons/vitest/src/postinstall.ts` at line 165, The property assignment
uses a redundant double negation: change useRemotePkg: !!options.skipInstall to
useRemotePkg: options.skipInstall; locate the object where useRemotePkg is set
(in postinstall.ts) and remove the extra !! so it directly uses the boolean from
PostinstallOptions (options.skipInstall) to keep the code simpler and
equivalent.
code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts (1)

7-9: Missing file extensions in relative imports.

The coding guidelines specify using explicit file extensions for relative imports in TypeScript files.

As per coding guidelines: "Use explicit file extensions for relative code imports and exports in TypeScript (e.g., ./foo.ts or ./bar.tsx)".

♻️ Suggested fix
 import type { FileInfo } from '../../automigrate/codemod';
-import { addImportToTop, cleanupTypeImports } from './csf-factories-utils';
-import { removeUnusedTypes } from './remove-unused-types';
+import { addImportToTop, cleanupTypeImports } from './csf-factories-utils.ts';
+import { removeUnusedTypes } from './remove-unused-types.ts';

Note: The type import from ../../automigrate/codemod may also need an extension, depending on how it's structured.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts` around
lines 7 - 9, The relative imports in story-to-csf-factory.ts are missing
explicit file extensions; update the import statements for FileInfo (from
'../../automigrate/codemod') and the local helpers (addImportToTop,
cleanupTypeImports from './csf-factories-utils' and removeUnusedTypes from
'./remove-unused-types') to include the correct TypeScript file extensions
(e.g., .ts or .tsx) so they follow the project's guideline for explicit relative
import extensions; ensure you pick the actual extension used by each target
module and adjust the import paths accordingly.
code/addons/vitest/src/node/vitest-manager.ts (1)

102-112: Consider simplifying the file existence check.

The find.any call at line 104 with last: getProjectRoot() is potentially redundant since you're already iterating through specific locations. A direct existsSync(join(location, file)) would be more efficient.

♻️ Suggested optimization
     for (const location of potentialConfigFileLocations) {
       for (const file of configFiles) {
-        const maybe = find.any([file], { cwd: location, last: getProjectRoot() });
-        if (maybe && existsSync(maybe)) {
+        const maybe = join(location, file);
+        if (existsSync(maybe)) {
           firstVitestConfig ??= maybe;
           const content = readFileSync(maybe, 'utf8');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/addons/vitest/src/node/vitest-manager.ts` around lines 102 - 112, The
loop uses find.any with last: getProjectRoot() redundantly when iterating
potentialConfigFileLocations; replace the find.any + existsSync pattern by
constructing the path with path.join(location, file) and checking
existsSync(joinedPath), then use that path for readFileSync and dirname
assignments (affecting potentialConfigFileLocations, configFiles,
firstVitestConfig, vitestWorkspaceConfig and the usage of readFileSync/dirname);
remove the find.any call and its getProjectRoot dependency to simplify and
slightly improve performance.
code/lib/create-storybook/src/commands/AddonConfigurationCommand.ts (1)

8-9: Direct imports from addon source directories.

These relative imports cross package boundaries within the monorepo. This pattern works but creates tight coupling. Consider whether these should be exposed through the addon's public API instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/create-storybook/src/commands/AddonConfigurationCommand.ts` around
lines 8 - 9, The code in AddonConfigurationCommand.ts imports
addonA11yPostinstall and addonVitestPostinstall via relative paths into addon
source trees, creating cross-package coupling; change these to import the
postinstall functions from each addon's public API (re-export the postinstall
symbols from the addon's package entry if not already exported) and update
AddonConfigurationCommand.ts to import from the addon package name (e.g., the
addon's main export) instead of '../../../../addons/.../postinstall' so
consumers use the public surface rather than internal file paths.
code/core/src/core-server/server-channel/telemetry-channel.test.ts (1)

3-3: Add explicit file extension to the relative import.

As per coding guidelines, TypeScript files should use explicit file extensions for relative imports.

♻️ Suggested fix
-import { makePayload } from './telemetry-channel';
+import { makePayload } from './telemetry-channel.ts';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/core-server/server-channel/telemetry-channel.test.ts` at line
3, Update the relative import to include the explicit TypeScript file extension:
replace the import of makePayload from './telemetry-channel' with an explicit
extension (e.g., './telemetry-channel.ts') in telemetry-channel.test.ts so the
import statement points to the exact module file; ensure the symbol makePayload
remains imported from that updated path.
code/core/src/telemetry/telemetry.ts (1)

14-14: Use explicit extensions on the new relative imports in this change set.

Line 14 is one instance; the same repo rule also applies to the new imports in code/core/src/telemetry/types.ts, code/core/src/core-server/presets/common-preset.ts, and code/core/src/telemetry/detect-agent.test.ts.

As per coding guidelines, **/*.{ts,tsx}: Use explicit file extensions for relative code imports and exports in TypeScript (e.g., ./foo.ts or ./bar.tsx).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/telemetry/telemetry.ts` at line 14, Update the relative
TypeScript imports to include explicit file extensions: change the import of
detectAgent (import { detectAgent } from './detect-agent') in telemetry.ts to
use './detect-agent.ts' and apply the same fix for the new relative imports
added in code/core/src/telemetry/types.ts,
code/core/src/core-server/presets/common-preset.ts, and
code/core/src/telemetry/detect-agent.test.ts so all relative imports/exports
follow the repo rule of using explicit .ts/.tsx extensions.
code/core/src/telemetry/detect-agent.ts (1)

1-8: 'opencode' is declared but never produced

KnownAgentName includes 'opencode', but no detection branch returns it. This creates type/runtime drift and makes downstream handling misleading. Either add detection for it (e.g., specific env marker or AGENT === 'opencode') or remove it from the union until supported.

Proposed fix (if `AGENT=opencode` is intended)
 function detectExplicitAgent(env: NodeJS.ProcessEnv): AgentInfo | undefined {
@@
+  // OpenCode
+  if (env.AGENT === 'opencode') {
+    return {
+      name: 'opencode',
+    };
+  }
+
   // Amp
   if (env.AGENT === 'amp') {

Also applies to: 21-60

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/telemetry/detect-agent.ts` around lines 1 - 8, KnownAgentName
includes 'opencode' but no code returns it; either remove 'opencode' from the
KnownAgentName union or add a detection branch that actually yields 'opencode'
(e.g., check for process.env.AGENT === 'opencode' or a specific env/marker) in
the agent detection logic (the function that maps runtime markers to
KnownAgentName). Update the type or detection function accordingly so the union
and runtime detection stay in sync (refer to KnownAgentName and the
agent-detection function that returns KnownAgentName).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a6a0b8b9-93f0-4ee9-8ff4-3cc437196664

📥 Commits

Reviewing files that changed from the base of the PR and between 95aa114 and e22f6a0.

⛔ Files ignored due to path filters (2)
  • .yarn/patches/@testing-library-user-event-npm-14.6.1-5da7e1d4e2.patch is excluded by !**/.yarn/**
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (141)
  • CHANGELOG.md
  • code/.eslintrc.js
  • code/addons/a11y/package.json
  • code/addons/a11y/src/postinstall.ts
  • code/addons/docs/package.json
  • code/addons/links/package.json
  • code/addons/onboarding/package.json
  • code/addons/pseudo-states/package.json
  • code/addons/themes/package.json
  • code/addons/vitest/package.json
  • code/addons/vitest/src/node/vitest-manager.ts
  • code/addons/vitest/src/postinstall.test.ts
  • code/addons/vitest/src/postinstall.ts
  • code/addons/vitest/src/typings.d.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/addons/vitest/src/updateVitestFile.ts
  • code/addons/vitest/src/vitest-plugin/index.ts
  • code/builders/builder-vite/package.json
  • code/builders/builder-vite/src/build.ts
  • code/builders/builder-vite/src/plugins/vite-inject-mocker/plugin.test.ts
  • code/builders/builder-vite/src/plugins/vite-inject-mocker/plugin.ts
  • code/builders/builder-vite/src/plugins/vite-mock/plugin.ts
  • code/builders/builder-webpack5/package.json
  • code/builders/builder-webpack5/src/plugins/webpack-mock-plugin.ts
  • code/core/build-config.ts
  • code/core/package.json
  • code/core/src/bin/core.ts
  • code/core/src/cli/AddonVitestService.test.ts
  • code/core/src/cli/AddonVitestService.ts
  • code/core/src/common/js-package-manager/JsPackageManager.ts
  • code/core/src/common/js-package-manager/PNPMProxy.ts
  • code/core/src/common/utils/get-storybook-refs.test.ts
  • code/core/src/common/utils/get-storybook-refs.ts
  • code/core/src/common/utils/resolve-path-in-sb-cache.test.ts
  • code/core/src/common/utils/resolve-path-in-sb-cache.ts
  • code/core/src/common/versions.ts
  • code/core/src/components/components/Select/Select.stories.tsx
  • code/core/src/components/components/Select/Select.tsx
  • code/core/src/core-events/index.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/server-channel/telemetry-channel.test.ts
  • code/core/src/core-server/server-channel/telemetry-channel.ts
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/csf-tools/CsfFile.test.ts
  • code/core/src/csf-tools/CsfFile.ts
  • code/core/src/manager-api/modules/refs.ts
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager-api/tests/refs.test.ts
  • code/core/src/manager-api/tests/url.test.js
  • code/core/src/manager-api/version.ts
  • code/core/src/manager/components/preview/Viewport.tsx
  • code/core/src/manager/components/preview/tools/share.stories.tsx
  • code/core/src/manager/components/preview/tools/share.tsx
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
  • code/core/src/manager/components/preview/tools/zoom.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/mocking-utils/index.ts
  • code/core/src/mocking-utils/mocker-runtime.js
  • code/core/src/mocking-utils/redirect.ts
  • code/core/src/mocking-utils/runtime.ts
  • code/core/src/node-logger/index.test.ts
  • code/core/src/node-logger/index.ts
  • code/core/src/server-errors.ts
  • code/core/src/telemetry/detect-agent.test.ts
  • code/core/src/telemetry/detect-agent.ts
  • code/core/src/telemetry/storybook-metadata.ts
  • code/core/src/telemetry/telemetry.ts
  • code/core/src/telemetry/types.ts
  • code/core/src/toolbar/components/ToolbarMenuSelect.tsx
  • code/e2e-tests/preview-api.spec.ts
  • code/frameworks/angular/build-schema.json
  • code/frameworks/angular/package.json
  • code/frameworks/angular/start-schema.json
  • code/frameworks/ember/package.json
  • code/frameworks/html-vite/package.json
  • code/frameworks/nextjs-vite/package.json
  • code/frameworks/nextjs/build-config.ts
  • code/frameworks/nextjs/package.json
  • code/frameworks/nextjs/src/aliases/webpack.ts
  • code/frameworks/nextjs/src/export-mocks/link/index.tsx
  • code/frameworks/nextjs/src/export-mocks/webpack.ts
  • code/frameworks/preact-vite/package.json
  • code/frameworks/react-native-web-vite/package.json
  • code/frameworks/react-vite/package.json
  • code/frameworks/react-webpack5/package.json
  • code/frameworks/server-webpack5/package.json
  • code/frameworks/svelte-vite/package.json
  • code/frameworks/sveltekit/package.json
  • code/frameworks/vue3-vite/package.json
  • code/frameworks/web-components-vite/package.json
  • code/lib/cli-sb/package.json
  • code/lib/cli-storybook/package.json
  • code/lib/cli-storybook/src/automigrate/index.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.ts
  • code/lib/cli-storybook/src/automigrate/types.ts
  • code/lib/cli-storybook/src/bin/run.ts
  • code/lib/cli-storybook/src/codemod/csf-factories.ts
  • code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts
  • code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.ts
  • code/lib/cli-storybook/src/codemod/helpers/csf-factories-utils.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
  • code/lib/cli-storybook/src/sandbox.ts
  • code/lib/codemod/package.json
  • code/lib/codemod/src/index.test.ts
  • code/lib/codemod/src/index.ts
  • code/lib/core-webpack/package.json
  • code/lib/create-storybook/package.json
  • code/lib/create-storybook/src/commands/AddonConfigurationCommand.test.ts
  • code/lib/create-storybook/src/commands/AddonConfigurationCommand.ts
  • code/lib/create-storybook/src/commands/ProjectDetectionCommand.ts
  • code/lib/create-storybook/src/initiate.ts
  • code/lib/create-storybook/src/scaffold-new-project.ts
  • code/lib/create-storybook/src/services/VersionService.test.ts
  • code/lib/create-storybook/src/services/VersionService.ts
  • code/lib/csf-plugin/package.json
  • code/lib/eslint-plugin/package.json
  • code/lib/react-dom-shim/package.json
  • code/package.json
  • code/presets/create-react-app/package.json
  • code/presets/react-webpack/package.json
  • code/presets/server-webpack/package.json
  • code/renderers/html/package.json
  • code/renderers/preact/package.json
  • code/renderers/react/package.json
  • code/renderers/server/package.json
  • code/renderers/svelte/package.json
  • code/renderers/vue3/package.json
  • code/renderers/web-components/package.json
  • docs/_snippets/csf-factories-automigrate-with-config-directory.md
  • docs/_snippets/storybook-preview-configure-globaltypes.md
  • docs/api/cli-options.mdx
  • docs/api/csf/csf-next.mdx
  • docs/get-started/frameworks/angular.mdx
  • docs/versions/latest.json
  • docs/versions/next.json
  • docs/writing-stories/typescript.mdx
  • scripts/bench/bench-packages.ts
  • scripts/build/utils/entry-utils.ts
  • scripts/build/utils/generate-bundle.ts
  • scripts/package.json
💤 Files with no reviewable changes (2)
  • code/core/src/server-errors.ts
  • code/lib/create-storybook/src/initiate.ts

Comment thread code/.eslintrc.js
Comment on lines +163 to +165
// Our codebase is mostly TypeScript, and typescript will warn when imports are not found.
// It IS set to 'error' for JS files.
'import-x/named': 'warn',
'import-x/named': 'off',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

In eslint-plugin-import-x (or eslint-plugin-import equivalent), does the named rule catch invalid named imports that TypeScript may miss (especially when importing from untyped JS/CJS modules)? Please provide current docs/recommendations.

💡 Result:

Yes, the import-x/named rule in eslint-plugin-import-x (and the equivalent import/named in eslint-plugin-import) catches invalid named imports from untyped JS/CJS modules that TypeScript may miss. The rule statically analyzes the targeted module file to verify that named imports match actual named exports declared there, regardless of TypeScript typing. Key points from official docs: - It verifies "named imports are part of the set of named exports in the referenced module" by inspecting the file contents {1, high}. - For untyped JS/CJS modules (especially node_modules), it checks package.json for jsnext:main (deprecated) or module fields to find the ES module entry, then analyzes exports {1, high}. - Modules can be ignored via import-x/ignore setting (defaults include node_modules), but when not ignored, it analyzes them {1, high}. - Note: "A module path that is ignored or not unambiguously an ES module will not be reported" {1, high}. CJS modules with module.exports may not be treated as ES modules, potentially skipping analysis, but pure JS ES modules or those with module field are checked. - In TypeScript configs (flat/typescript, typescript), the rule is disabled (🚫) because TypeScript handles named import validation via types, but it can be manually enabled and works with eslint-import-resolver-typescript for path resolution {1,3,4,5, high}. TypeScript itself may miss runtime exports in untyped JS if no .d.ts declares them exhaustively. Current recommendations (2026): - Use eslint-plugin-import-x with flat/recommended or recommended configs where named is enabled by default {3,4,5, high}. - For TS projects, extend plugin:import-x/typescript + resolver, but enable import-x/named explicitly if you want checks on untyped JS {4,16, high}. - Pair with eslint-import-resolver-typescript to properly resolve paths and prefer @types but fall back to JS analysis {16, high}.

Citations:


🏁 Script executed:

cat -n code/.eslintrc.js | sed -n '150,180p'

Repository: storybookjs/storybook

Length of output: 1433


🏁 Script executed:

rg -A 5 -B 5 "import-x/named" code/.eslintrc.js

Repository: storybookjs/storybook

Length of output: 413


🏁 Script executed:

rg -n "^\s*rules:\s*{" code/.eslintrc.js | head -20

Repository: storybookjs/storybook

Length of output: 156


🏁 Script executed:

rg -n "rules:" code/.eslintrc.js | head -20

Repository: storybookjs/storybook

Length of output: 379


🏁 Script executed:

sed -n '16,68p' code/.eslintrc.js

Repository: storybookjs/storybook

Length of output: 2247


Clarify the misleading comment about JS file configuration

Lines 163–164: The comment states "It IS set to 'error' for JS files," but this ESLint config has no explicit import-x/named rule for JS files. The rule is only mentioned here, where it's disabled for TypeScript files. Revise the comment to accurately reflect what the config does—disable the check for TypeScript (where TypeScript validates named imports) while relying on plugin defaults for JS files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/.eslintrc.js` around lines 163 - 165, Update the misleading comment
above the 'import-x/named' rule to state that the rule is disabled for
TypeScript files because the TypeScript compiler handles named-import
validation, and that JS behavior will follow plugin/default settings (i.e.,
there is no explicit 'error' setting for JS here); reference the
'import-x/named' entry so reviewers know which rule the comment describes and
ensure the revised comment clearly distinguishes TypeScript vs JS handling.

@@ -0,0 +1,49 @@
import { describe, expect, it } from 'vitest';

import { isConfigAlreadySetup } from './postinstall';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Missing file extension on relative import.

As per coding guidelines, TypeScript files should use explicit file extensions for relative imports.

🔧 Suggested fix
-import { isConfigAlreadySetup } from './postinstall';
+import { isConfigAlreadySetup } from './postinstall.ts';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { isConfigAlreadySetup } from './postinstall';
import { isConfigAlreadySetup } from './postinstall.ts';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/addons/vitest/src/postinstall.test.ts` at line 3, The import in
postinstall.test.ts uses a relative path without an explicit extension; update
the import statement that references isConfigAlreadySetup so it includes the
TypeScript extension (change './postinstall' to './postinstall.ts') to comply
with the TypeScript explicit-extension guideline and ensure module resolution
for the isConfigAlreadySetup import works correctly.

Comment on lines +50 to +55
config: (config) => ({
...config,
build: {
outDir: options.outputDir,
},
}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The config hook incorrectly spreads config while overwriting build.

The spread ...config followed by a new build object loses all other build.* properties (e.g., emptyOutDir, rollupOptions). Vite's config hooks perform deep merging on the returned object, so you only need to return the partial override.

Proposed fix
    config: (config) => ({
-     ...config,
      build: {
        outDir: options.outputDir,
      },
    }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/build.ts` around lines 50 - 55, The config
hook is incorrectly spreading the incoming config which then fully replaces
nested build properties; in the config: (config) => ({ ...config, build: {
outDir: options.outputDir } }) block, remove the spread and return only the
partial override so Vite's deep merge preserves existing build.* fields—i.e.,
change the config hook to return { build: { outDir: options.outputDir } }
(referencing the config hook and options.outputDir).

Comment on lines +56 to +63
// configEnvironment is a new method in Vite 6
// It is used to configure configs based on the environment
// E.g. Nitro uses this method to set the output directory to .output/public/
configEnvironment: () => ({
build: {
outDir: options.outputDir,
},
}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

What is the Vite 6 configEnvironment plugin hook API signature?

💡 Result:

The Vite 6 configEnvironment plugin hook API signature is: configEnvironment(name: string, options: EnvironmentOptions): EnvironmentOptions | void | Promise<EnvironmentOptions | void> This hook is called for each environment after the config hook, receiving the environment name and its partially resolved options. Plugins can mutate options in place or return partial EnvironmentOptions to merge. Kind: async, sequential Example from official docs: configEnvironment(name: string, options: EnvironmentOptions) { if (name === 'rsc') { return { resolve: { conditions: ['workerd'] } } } } EnvironmentOptions interface includes: interface EnvironmentOptions { define?: Record<string, any> resolve?: EnvironmentResolveOptions optimizeDeps?: DepOptimizationOptions consumer?: 'client' | 'server' dev?: DevOptions build?: BuildOptions } Sources confirm this across Vite 6 docs and main branch (Vite 6+).

Citations:


🏁 Script executed:

# Find and read the package.json for builder-vite
find . -path "*/code/builders/builder-vite/package.json" -type f -exec cat {} \;

Repository: storybookjs/storybook

Length of output: 1764


🏁 Script executed:

# Search for all usages of configEnvironment in the codebase
rg "configEnvironment" --type ts --type tsx -B 2 -A 5

Repository: storybookjs/storybook

Length of output: 92


🏁 Script executed:

# Search for configEnvironment usages across TypeScript files
rg "configEnvironment" -B 2 -A 5

Repository: storybookjs/storybook

Length of output: 823


🏁 Script executed:

# Check the build.ts file around the lines in question to understand context
sed -n '50,70p' code/builders/builder-vite/src/build.ts

Repository: storybookjs/storybook

Length of output: 636


Fix configEnvironment hook signature to accept required parameters.

The configEnvironment hook signature is confirmed as a Vite 6 feature with API: configEnvironment(name: string, options: EnvironmentOptions). The current implementation configEnvironment: () => ({...}) does not accept the required name and options parameters. Update to: configEnvironment: (name, options) => ({ build: { outDir: options.outputDir } }) to properly implement the Vite 6 plugin hook contract. Storybook's Vite peer dependency correctly includes Vite 6 (^6.0.0).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/build.ts` around lines 56 - 63, The
configEnvironment hook currently uses the zero-arg signature but Vite 6 requires
configEnvironment(name: string, options: EnvironmentOptions); update the
exported plugin's configEnvironment implementation (symbol: configEnvironment)
to accept (name, options) and return the build override using options.outputDir
(e.g., configEnvironment: (name, options) => ({ build: { outDir:
options.outputDir } })), ensuring the hook matches the Vite 6 plugin contract
and uses options.outputDir rather than relying on closure-only values.

@@ -0,0 +1,35 @@
import { afterEach, describe, expect, it, vi } from 'vitest';

import { checkRef } from './get-storybook-refs';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use explicit extension for this relative TypeScript import.

Please include the .ts extension in this import path.

🔧 Proposed fix
-import { checkRef } from './get-storybook-refs';
+import { checkRef } from './get-storybook-refs.ts';

As per coding guidelines, **/*.{ts,tsx} must use explicit file extensions for relative imports/exports.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { checkRef } from './get-storybook-refs';
import { checkRef } from './get-storybook-refs.ts';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/common/utils/get-storybook-refs.test.ts` at line 3, The import
in get-storybook-refs.test.ts uses a relative TypeScript path without an
explicit extension; update the import statement for checkRef to reference
'./get-storybook-refs.ts' (include the .ts extension) and ensure any other
relative imports/exports in this test file follow the same explicit .ts/.tsx
rule; verify the test still compiles after changing the import.

Comment on lines +25 to +33
const { dryRun, packageManager, yes, glob, ...codemodOptions } = options;
try {
let globString = '{stories,src}/**/{Button,Header,Page,button,header,page}.stories.*';
if (!optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX)) {
const inSandbox = optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX) ?? false;
let globString = glob ?? '**/*.{stories,story}.{js,jsx,ts,tsx,mjs,mjsx,mts,mtsx}';

if (!glob && inSandbox) {
// Sandbox uses limited glob for faster testing (unless glob explicitly provided)
globString = '{stories,src}/**/{Button,Header,Page,button,header,page}.stories.*';
} else if (!glob && !yes) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This retry path never terminates for non-interactive runs.

If glob came from --glob, --yes suppresses prompts, or the sandbox default glob misses, the catch block just calls runStoriesCodemod(options) with the same inputs. That throws No files matched again and recurses forever. Please fail fast for non-interactive cases, or clear glob before retrying so the next pass can actually recover.

🛠️ Suggested fix
 async function runStoriesCodemod(options: {
   dryRun: boolean | undefined;
   packageManager: JsPackageManager;
   useSubPathImports: boolean;
   previewConfigPath: string;
   yes: boolean | undefined;
   glob: string | undefined;
 }) {
   const { dryRun, packageManager, yes, glob, ...codemodOptions } = options;
+  const inSandbox = optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX) ?? false;
   try {
-    const inSandbox = optionalEnvToBoolean(process.env.IN_STORYBOOK_SANDBOX) ?? false;
     let globString = glob ?? '**/*.{stories,story}.{js,jsx,ts,tsx,mjs,mjsx,mts,mtsx}';
 
     if (!glob && inSandbox) {
       // Sandbox uses limited glob for faster testing (unless glob explicitly provided)
       globString = '{stories,src}/**/{Button,Header,Page,button,header,page}.stories.*';
@@
   } catch (err: any) {
     if (err.message === 'No files matched') {
-      await runStoriesCodemod(options);
+      if (glob || yes || inSandbox) {
+        throw err;
+      }
+      await runStoriesCodemod({ ...options, glob: undefined });
     } else {
       throw err;
     }
   }
 }

Also applies to: 50-53

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/cli-storybook/src/codemod/csf-factories.ts` around lines 25 - 33,
The retry in the catch of runStoriesCodemod causes an infinite loop when runs
are non-interactive or a glob was explicitly provided; update the catch so that
if options.yes is true or options.glob is present it fails immediately (rethrow
or return an error) instead of retrying, and only attempt a retry by clearing
options.glob (e.g., delete options.glob or set it to undefined) when the run is
interactive and no explicit glob was supplied; adjust the logic around
runStoriesCodemod(options), options.glob, and the IN_STORYBOOK_SANDBOX behavior
accordingly so a non-interactive invocation fails fast while interactive runs
can retry with a cleared glob.

Comment on lines +83 to +94
try {
logger.logBox(
[messages.welcome]
.concat(isOutdated && !isPrerelease ? [messages.notLatest] : [])
.concat(init && (isOutdated || isPrerelease) ? [messages.longInitTime] : [])
.concat(isPrerelease ? [messages.prerelease] : [])
.join('\n'),
{
rounded: true,
}
);
} catch {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Empty catch block silently swallows errors.

The try { ... } catch {} around logger.logBox completely silences any errors. While the intent seems to be preventing log rendering failures from blocking sandbox creation, silently discarding errors makes debugging difficult.

Consider logging at debug level to preserve visibility:

🛡️ Proposed fix to log caught errors
   try {
     logger.logBox(
       [messages.welcome]
         .concat(isOutdated && !isPrerelease ? [messages.notLatest] : [])
         .concat(init && (isOutdated || isPrerelease) ? [messages.longInitTime] : [])
         .concat(isPrerelease ? [messages.prerelease] : [])
         .join('\n'),
       {
         rounded: true,
       }
     );
-  } catch {}
+  } catch (err) {
+    logger.debug(`Failed to render welcome box: ${err}`);
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
logger.logBox(
[messages.welcome]
.concat(isOutdated && !isPrerelease ? [messages.notLatest] : [])
.concat(init && (isOutdated || isPrerelease) ? [messages.longInitTime] : [])
.concat(isPrerelease ? [messages.prerelease] : [])
.join('\n'),
{
rounded: true,
}
);
} catch {}
try {
logger.logBox(
[messages.welcome]
.concat(isOutdated && !isPrerelease ? [messages.notLatest] : [])
.concat(init && (isOutdated || isPrerelease) ? [messages.longInitTime] : [])
.concat(isPrerelease ? [messages.prerelease] : [])
.join('\n'),
{
rounded: true,
}
);
} catch (err) {
logger.debug(`Failed to render welcome box: ${err}`);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/cli-storybook/src/sandbox.ts` around lines 83 - 94, The empty catch
around logger.logBox swallows errors; update the catch to log the caught error
(e.g., using logger.debug or logger.error) so failures in the welcome box are
visible but do not throw—keep the try/catch to avoid blocking sandbox creation,
but in the catch callback call logger.debug or logger.error with a clear message
and include the caught error object; locate the instance of logger.logBox in
sandbox.ts (the block that builds
messages.welcome/messages.notLatest/messages.longInitTime/messages.prerelease)
and replace the empty catch with a call that records the error for debugging
while preserving existing behavior.

Comment on lines +59 to +65
const rsbuildMatch = ancestor.command?.match(/(?:^|\s)create[\s\-]rsbuild/i);
if (rsbuildMatch) {
return 'create-rsbuild';
}
// Check for @tanstack/start
if (ancestor.command?.includes('@tanstack/start')) {
return '@tanstack/start';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Tighten matcher boundaries to prevent false CLI attribution.

Line 59 and Line 64 can overmatch (create-rsbuilder, @tanstack/startup) and return incorrect integration values.

Suggested fix
-      const rsbuildMatch = ancestor.command?.match(/(?:^|\s)create[\s\-]rsbuild/i);
+      const rsbuildMatch = ancestor.command?.match(
+        /(?:^|\s)create[\s\-]rsbuild(?:@[^ ]+)?(?:\s|$)/i
+      );
       if (rsbuildMatch) {
         return 'create-rsbuild';
       }
       // Check for `@tanstack/start`
-      if (ancestor.command?.includes('@tanstack/start')) {
+      if (/(?:^|\s)@tanstack\/start(?:@[^ ]+)?(?:\s|$)/i.test(ancestor.command ?? '')) {
         return '@tanstack/start';
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/lib/create-storybook/src/services/VersionService.ts` around lines 59 -
65, The matcher logic in VersionService that checks ancestor.command is too
loose: change the rsbuild detection (currently using
ancestor.command?.match(/(?:^|\s)create[\s\-]rsbuild/i) referenced as
rsbuildMatch) to require exact token boundaries (e.g. use word-boundary or
anchor-based regex like requiring end or whitespace after "rsbuild") so it won't
match things like "create-rsbuilder"; likewise replace the simple
includes('@tanstack/start') check with a boundary-aware test (regex or token
split) that ensures "@tanstack/start" is a standalone token (not part of "startu
p" or "startup"); update both checks in VersionService to use these stricter
regex/token checks.

## Vue specific tips

Vue has excellent support for TypeScript, and we have done our utmost to take advantage of that in the stories files. For example, consider the following strongly typed Vue 3 single file component (SFC):
## Vue specific tips
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor grammar fixes: use hyphens in compound modifiers.

The static analysis correctly identifies two hyphenation issues:

  • Line 73: "Vue specific tips" → "Vue-specific tips" (compound adjective)
  • Line 96: "type check SFC files" → "type-check SFC files" (compound verb)
📝 Proposed fix
-## Vue specific tips
+## Vue-specific tips
-You can type check SFC files with `vue-tsc` and get editor support in VSCode by installing the official [Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar).
+You can type-check SFC files with `vue-tsc` and get editor support in VSCode by installing the official [Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar).

Also applies to: 96-96

🧰 Tools
🪛 LanguageTool

[grammar] ~73-~73: Use a hyphen to join words.
Context: ...*/} ## Vue specific tips Vue has excellent support...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/writing-stories/typescript.mdx` at line 73, Update the two compound
modifiers to use hyphens: change the heading text "Vue specific tips" to
"Vue-specific tips" and update the sentence "type check SFC files" to
"type-check SFC files"; locate the occurrences by searching for those exact
phrases in the document (e.g., the heading string and the sentence containing
"type check SFC files") and replace them accordingly.

Comment on lines +407 to 412
'-b, --base-branch [branchName]',
'The base branch to compare the results with. Requires GCP_CREDENTIALS env var'
)
.option(
'-p, --pull-request <number>',
'-p, --pull-request [pullRequestNumber]',
'The PR number to add compare results to. Only used together with --baseBranch',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Commander.js, for .option('-b, --base-branch [branchName]'), what runtime value does program.opts().baseBranch have when the flag is present without an argument?

💡 Result:

In Commander.js, for .option('-b, --base-branch [branchName]'), when the flag is present without an argument (e.g., --base-branch), the runtime value of program.opts.baseBranch is true.

Citations:


🏁 Script executed:

cd scripts/bench && head -n 520 bench-packages.ts | tail -n 120 | cat -n

Repository: storybookjs/storybook

Length of output: 5097


--base-branch optional arg with [branchName] syntax resolves to boolean true at runtime, causing type mismatch.

When --base-branch is provided without a value (e.g., --base-branch alone), Commander.js sets options.baseBranch to true instead of a string. The code at line 446 properly checks typeof options.baseBranch === 'string', but line 498 uses a truthy check (if (options.baseBranch)) that passes for the boolean value, allowing compareResults() at line 499 to receive true instead of the expected string type. Additionally, if only --base-branch is used without a value and --upload is false, the GCP credentials validation at lines 446–51 is bypassed entirely.

Suggested fix
     .option(
-      '-b, --base-branch [branchName]',
+      '-b, --base-branch <branchName>',
       'The base branch to compare the results with. Requires GCP_CREDENTIALS env var'
     )

Alternatively, replace the truthy check at line 498:

-  if (options.baseBranch) {
+  if (typeof options.baseBranch === 'string') {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/bench/bench-packages.ts` around lines 407 - 412, The bug is that
options.baseBranch uses Commander optional-arg syntax so when passed without a
value it becomes boolean true; replace any truthy checks with an explicit type
check (e.g., use typeof options.baseBranch === 'string') before calling
compareResults() so compareResults() never receives boolean true, and ensure the
GCP_CREDENTIALS validation (the existing typeof options.baseBranch === 'string'
block) is used to gate both credential validation and the compareResults() call
(or normalize options.baseBranch to a string earlier) so the flow for
--base-branch with no value does not bypass the credentials check and does not
pass a boolean into compareResults().

@valentinpalkovic
Copy link
Copy Markdown
Contributor

The PR seems to be malformed. Please create a new one!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Telemetry is sent despite disableTelemetry: true

8 participants