feat(cli): add ultracite --frameworks support for create and init#28
Conversation
Add framework id parsing helpers and thread selected frameworks from the CLI into ultracite init, including dry-run plan output and runtime guards.
Resolve ultracite framework presets from flags, interactive wizards, or -y defaults, then persist the selection in the Verno manifest.
Add unit tests for framework id parsing, flag validation, pm-exec args, and create/init input resolution.
Describe framework preset selection for create and init, including -y defaults and supported framework ids.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: e04ab9b The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Warning Review limit reached
More reviews will be available in 49 minutes and 32 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis PR adds ChangesUltracite frameworks feature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/cli/src/commands/init/index.ts (1)
154-161:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd
ultracite_frameworksto init analytics event for consistency.The
trackEventcall for init is missing theultracite_frameworksfield that is tracked in the create command (line 157 increate/index.ts). Both commands support framework selection, so both should track this metric for consistent analytics data.📊 Proposed fix to add frameworks to analytics
await trackEvent("init_project", { addons: resolved.addons, dry_run: false, linter: resolved.ultraciteLinter, package_manager: resolved.packageManager, shadcn_preset: resolved.shadcnPreset, ui: resolved.ui, + ultracite_frameworks: resolved.ultraciteFrameworks?.join(","), });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/commands/init/index.ts` around lines 154 - 161, The init analytics call to trackEvent is missing the ultracite_frameworks field; update the payload passed to trackEvent in packages/cli/src/commands/init/index.ts to include ultracite_frameworks: resolved.ultraciteFrameworks (matching the create command), so the init event records selected frameworks consistently alongside addons, linter, package_manager, shadcn_preset and ui.packages/cli/src/commands/shared/manifest.ts (1)
93-108:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winValidate optional
ultraciteFrameworksfield in manifest validation.The
isVernoManifestRecordvalidation function checks required fields and validatesaddonsandpackagesas string arrays, but does not validate the new optionalultraciteFrameworksfield. If the field is present in a persisted manifest, it should be validated as a string array to ensure data integrity.🛡️ Proposed fix to add ultraciteFrameworks validation
const isVernoManifestRecord = (value: unknown): value is VernoManifest => { if (!value || typeof value !== "object") { return false; } const record = value as Record<string, unknown>; return ( record.generator === "verno" && typeof record.projectName === "string" && typeof record.createdAt === "string" && typeof record.frontend === "string" && typeof record.packageManager === "string" && typeof record.ui === "string" && isStringArray(record.addons) && - isStringArray(record.packages) + isStringArray(record.packages) && + (record.ultraciteFrameworks === undefined || isStringArray(record.ultraciteFrameworks)) ); };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/commands/shared/manifest.ts` around lines 93 - 108, The isVernoManifestRecord type guard is missing validation for the optional ultraciteFrameworks field; update the function (isVernoManifestRecord) to ensure that if record.ultraciteFrameworks exists it passes isStringArray(record.ultraciteFrameworks) — i.e. add a boolean clause that allows undefined but validates present values with the existing isStringArray helper so persisted manifests with ultraciteFrameworks are correctly validated.
♻️ Duplicate comments (1)
packages/cli/src/commands/init/prompts.ts (1)
163-185: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winDuplicate function: extract to shared module.
This function is identical to the one in
create/prompts.ts(lines 249-271). See the review comment on that file for the recommended extraction.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/commands/init/prompts.ts` around lines 163 - 185, The readUltraciteFrameworks function is duplicated; extract its logic into a shared utility module (e.g., export a single readUltraciteFrameworks from a new shared prompts/util file) and have both packages/cli/src/commands/init/prompts.ts and create/prompts.ts import and call that shared function instead of maintaining duplicates. Ensure the shared function signature matches the current usage (takes InitCommandOptions and ultraciteOn) and that it references or accepts the same dependencies/constants (parseUltraciteFrameworksFlag, DEFAULT_ULTRACITE_FRAMEWORKS, ULTRACITE_FRAMEWORK_IDS, p.multiselect, assertValue) so callers work without changes, then remove the duplicate implementation from both locations and update imports.
🧹 Nitpick comments (3)
packages/cli/src/ultracite-framework.ts (1)
34-35: ⚡ Quick winRemove unnecessary type assertion.
The type cast
as readonly string[]is redundant. TypeScript already knows thatULTRACITE_FRAMEWORK_IDScontains strings, and theincludesmethod acceptsstringarguments.♻️ Simplify by removing the cast
export const isUltraciteFrameworkId = (value: string | undefined): value is UltraciteFrameworkId => - value !== undefined && (ULTRACITE_FRAMEWORK_IDS as readonly string[]).includes(value); + value !== undefined && ULTRACITE_FRAMEWORK_IDS.includes(value as UltraciteFrameworkId);Or better yet, use a type-safe approach:
export const isUltraciteFrameworkId = (value: string | undefined): value is UltraciteFrameworkId => - value !== undefined && (ULTRACITE_FRAMEWORK_IDS as readonly string[]).includes(value); + value !== undefined && (ULTRACITE_FRAMEWORK_IDS as readonly string[]).includes(value);Actually, the original pattern is fine but the cast can be on
valueinstead for cleaner narrowing.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/ultracite-framework.ts` around lines 34 - 35, Remove the redundant type assertion on ULTRACITE_FRAMEWORK_IDS in isUltraciteFrameworkId: change the includes call to rely on the declared type of ULTRACITE_FRAMEWORK_IDS (no "as readonly string[]"), and if you need narrowing cast the input (value) instead (e.g., narrow value to string when calling includes) so the function signature and narrowing work without the unnecessary cast; update the isUltraciteFrameworkId implementation to call (ULTRACITE_FRAMEWORK_IDS).includes(...) directly and adjust any casting to reference the parameter `value` rather than the constant.packages/cli/__tests__/ultracite-framework.test.ts (1)
25-29: ⚡ Quick winConsider testing other frontend values.
The test only verifies the
"next"frontend case. Given the implementation's conditional structure, add a test for anotherFrontendIdvalue (e.g.,"react","vue") to ensure the defaulting logic is correct and doesn't silently fail for other frontends.🧪 Suggested additional test
test("defaults to react and next for next frontend", () => { expect(defaultUltraciteFrameworksFromFrontend("next")).toEqual(DEFAULT_ULTRACITE_FRAMEWORKS); }); + + test("defaults to react and next for other frontends", () => { + expect(defaultUltraciteFrameworksFromFrontend("react")).toEqual(DEFAULT_ULTRACITE_FRAMEWORKS); + expect(defaultUltraciteFrameworksFromFrontend("vue")).toEqual(DEFAULT_ULTRACITE_FRAMEWORKS); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/__tests__/ultracite-framework.test.ts` around lines 25 - 29, Add a second unit test in packages/cli/__tests__/ultracite-framework.test.ts that calls defaultUltraciteFrameworksFromFrontend with another FrontendId (e.g., "react" or "vue") and asserts the returned value is the expected framework list; for example, add a test that expects defaultUltraciteFrameworksFromFrontend("react") toEqual DEFAULT_ULTRACITE_FRAMEWORKS to validate the non-"next" branch of defaultUltraciteFrameworksFromFrontend.packages/cli/src/commands/create/prompts.ts (1)
263-263: 💤 Low valueConsider clarifying prompt message.
The message "Ultracite frameworks (passed as --frameworks)" describes the technical flag name but doesn't explain the purpose. Users might benefit from a message like "Ultracite frameworks to configure" or "Select frameworks for Ultracite linting".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/commands/create/prompts.ts` at line 263, Update the prompt message for the frameworks question so it clearly explains purpose rather than just the CLI flag; in the prompt object where message: "Ultracite frameworks (passed as --frameworks)" is set (the prompt used in create/prompts.ts), replace it with a user-facing label like "Select Ultracite frameworks to configure" or "Select frameworks for Ultracite linting" so users understand what they're choosing (keep the existing flag help elsewhere if needed).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/ultracite-frameworks-flag.md:
- Line 5: Update the changelog sentence to reference the actual manifest field
name used by the implementation: replace “manifest persistence for selected
preset extends” with wording that explicitly states the selected frameworks are
persisted to the manifest under the ultraciteFrameworks field (e.g.,
“persistence of selected frameworks to the manifest via the ultraciteFrameworks
field in .verno/manifest.json”) so the release note matches the implemented
manifest property.
In `@packages/cli/README.md`:
- Line 76: Update the README entry for the --frameworks flag to list only the
supported framework IDs ("react" and "next") and remove or mark "solid, vue,
..." as unsupported/future; ensure the help text reflects that -y defaults to
"react,next" and that only "react" and "next" are accepted values by the CLI
(reference the --frameworks flag in docs so maintainers can find and keep this
in sync with the CLI parser).
In `@packages/cli/src/commands/create/prompts.ts`:
- Around line 249-271: Extract the duplicated readUltraciteFrameworks
implementation into a shared helper module (e.g., commands/shared/ultracite.ts)
and update both create/prompts.ts and init/prompts.ts to import and call that
single function; keep the same signature and behavior (use
parseUltraciteFrameworksFlag, DEFAULT_ULTRACITE_FRAMEWORKS,
ULTRACITE_FRAMEWORK_IDS, assertValue and the p.multiselect prompt), and if any
of those symbols (p, assertValue, parseUltraciteFrameworksFlag) are not
accessible from the new module either export them from their current module or
accept them as parameters to the helper so the shared function can run without
duplicating logic.
In `@packages/cli/src/ultracite-framework.ts`:
- Around line 37-44: The function defaultUltraciteFrameworksFromFrontend
currently has a redundant if that always returns DEFAULT_ULTRACITE_FRAMEWORKS;
replace the conditional body with a single direct return of
DEFAULT_ULTRACITE_FRAMEWORKS and add a short TODO comment explaining this is a
placeholder for future per-frontend defaults (so callers keep the same
signature), i.e. update defaultUltraciteFrameworksFromFrontend to simply "return
DEFAULT_ULTRACITE_FRAMEWORKS" and add a TODO noting eventual expansion for
different FrontendId values.
---
Outside diff comments:
In `@packages/cli/src/commands/init/index.ts`:
- Around line 154-161: The init analytics call to trackEvent is missing the
ultracite_frameworks field; update the payload passed to trackEvent in
packages/cli/src/commands/init/index.ts to include ultracite_frameworks:
resolved.ultraciteFrameworks (matching the create command), so the init event
records selected frameworks consistently alongside addons, linter,
package_manager, shadcn_preset and ui.
In `@packages/cli/src/commands/shared/manifest.ts`:
- Around line 93-108: The isVernoManifestRecord type guard is missing validation
for the optional ultraciteFrameworks field; update the function
(isVernoManifestRecord) to ensure that if record.ultraciteFrameworks exists it
passes isStringArray(record.ultraciteFrameworks) — i.e. add a boolean clause
that allows undefined but validates present values with the existing
isStringArray helper so persisted manifests with ultraciteFrameworks are
correctly validated.
---
Duplicate comments:
In `@packages/cli/src/commands/init/prompts.ts`:
- Around line 163-185: The readUltraciteFrameworks function is duplicated;
extract its logic into a shared utility module (e.g., export a single
readUltraciteFrameworks from a new shared prompts/util file) and have both
packages/cli/src/commands/init/prompts.ts and create/prompts.ts import and call
that shared function instead of maintaining duplicates. Ensure the shared
function signature matches the current usage (takes InitCommandOptions and
ultraciteOn) and that it references or accepts the same dependencies/constants
(parseUltraciteFrameworksFlag, DEFAULT_ULTRACITE_FRAMEWORKS,
ULTRACITE_FRAMEWORK_IDS, p.multiselect, assertValue) so callers work without
changes, then remove the duplicate implementation from both locations and update
imports.
---
Nitpick comments:
In `@packages/cli/__tests__/ultracite-framework.test.ts`:
- Around line 25-29: Add a second unit test in
packages/cli/__tests__/ultracite-framework.test.ts that calls
defaultUltraciteFrameworksFromFrontend with another FrontendId (e.g., "react" or
"vue") and asserts the returned value is the expected framework list; for
example, add a test that expects defaultUltraciteFrameworksFromFrontend("react")
toEqual DEFAULT_ULTRACITE_FRAMEWORKS to validate the non-"next" branch of
defaultUltraciteFrameworksFromFrontend.
In `@packages/cli/src/commands/create/prompts.ts`:
- Line 263: Update the prompt message for the frameworks question so it clearly
explains purpose rather than just the CLI flag; in the prompt object where
message: "Ultracite frameworks (passed as --frameworks)" is set (the prompt used
in create/prompts.ts), replace it with a user-facing label like "Select
Ultracite frameworks to configure" or "Select frameworks for Ultracite linting"
so users understand what they're choosing (keep the existing flag help elsewhere
if needed).
In `@packages/cli/src/ultracite-framework.ts`:
- Around line 34-35: Remove the redundant type assertion on
ULTRACITE_FRAMEWORK_IDS in isUltraciteFrameworkId: change the includes call to
rely on the declared type of ULTRACITE_FRAMEWORK_IDS (no "as readonly
string[]"), and if you need narrowing cast the input (value) instead (e.g.,
narrow value to string when calling includes) so the function signature and
narrowing work without the unnecessary cast; update the isUltraciteFrameworkId
implementation to call (ULTRACITE_FRAMEWORK_IDS).includes(...) directly and
adjust any casting to reference the parameter `value` rather than the constant.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a114b487-6f0a-4022-948c-e0cda95af6ba
📒 Files selected for processing (24)
.changeset/ultracite-frameworks-flag.mdpackages/cli/README.mdpackages/cli/__tests__/create-args.test.tspackages/cli/__tests__/create-plan.test.tspackages/cli/__tests__/init-args.test.tspackages/cli/__tests__/pm-exec.test.tspackages/cli/__tests__/ultracite-framework.test.tspackages/cli/src/commands/create/args.tspackages/cli/src/commands/create/index.tspackages/cli/src/commands/create/plan.tspackages/cli/src/commands/create/prompts.tspackages/cli/src/commands/init/args.tspackages/cli/src/commands/init/index.tspackages/cli/src/commands/init/plan.tspackages/cli/src/commands/init/prompts.tspackages/cli/src/commands/shared/command-ui.tspackages/cli/src/commands/shared/manifest.tspackages/cli/src/commands/shared/plan-steps.tspackages/cli/src/commands/shared/post-scaffold.tspackages/cli/src/commands/shared/post-setup-pipeline.tspackages/cli/src/commands/shared/ultracite.tspackages/cli/src/index.tspackages/cli/src/pm-exec.tspackages/cli/src/ultracite-framework.ts
Remove the no-op defaultUltraciteFrameworksFromFrontend helper and use DEFAULT_ULTRACITE_FRAMEWORKS directly; align init wizard calls with create.
Description
Verno CLI runs
ultracite initduring project setup but previously only forwarded--linter, not--frameworks. In non-interactive mode (-y/--quiet), Ultracite therefore could not apply framework-specific presets (React, Next.js, etc.).This PR adds
--frameworkstoverno createandverno init, wires it through the post-setup pipeline intogetUltraciteInitCommand(), and exposes an interactive multiselect when Ultracite is enabled in the wizards. Non-interactive mode defaults toreactandnext. Selected frameworks are persisted on.verno/manifest.jsonasultraciteFrameworks.Related Issues
N/A
Checklist
Screenshots (if applicable)
N/A — CLI-only change; no UI.
Additional Notes
packages/cli/src/ultracite-framework.ts(parser, defaults, allowed framework IDs).@verno/clipatch bump via.changeset/ultracite-frameworks-flag.md.bun test(103 tests) andbun x ultracite checkinpackages/cli.Made with Cursor
Summary by CodeRabbit
New Features
--frameworksflag support toverno createandverno initcommands for Ultracite configurationDocumentation
--frameworksflag and default behavior