@draekien/create-d9-app 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/package.json +2 -1
  2. package/templates/base/.agents/skills/shadcn/SKILL.md +295 -0
  3. package/templates/base/.agents/skills/shadcn/agents/openai.yml +5 -0
  4. package/templates/base/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
  5. package/templates/base/.agents/skills/shadcn/assets/shadcn.png +0 -0
  6. package/templates/base/.agents/skills/shadcn/assets/showcase/color-pair.tsx +126 -0
  7. package/templates/base/.agents/skills/shadcn/assets/showcase/preview-states.css +14 -0
  8. package/templates/base/.agents/skills/shadcn/assets/showcase/state-matrix.tsx +72 -0
  9. package/templates/base/.agents/skills/shadcn/cli.md +290 -0
  10. package/templates/base/.agents/skills/shadcn/customization.md +211 -0
  11. package/templates/base/.agents/skills/shadcn/design-system-page.md +268 -0
  12. package/templates/base/.agents/skills/shadcn/design-system.md +386 -0
  13. package/templates/base/.agents/skills/shadcn/evals/evals.json +126 -0
  14. package/templates/base/.agents/skills/shadcn/mcp.md +105 -0
  15. package/templates/base/.agents/skills/shadcn/registry.md +277 -0
  16. package/templates/base/.agents/skills/shadcn/rules/base-vs-radix.md +306 -0
  17. package/templates/base/.agents/skills/shadcn/rules/chat.md +250 -0
  18. package/templates/base/.agents/skills/shadcn/rules/composition.md +213 -0
  19. package/templates/base/.agents/skills/shadcn/rules/forms.md +192 -0
  20. package/templates/base/.agents/skills/shadcn/rules/icons.md +101 -0
  21. package/templates/base/.agents/skills/shadcn/rules/styling.md +185 -0
  22. package/templates/base/.agents/skills/shadcn/scripts/showcase-coverage.mjs +53 -0
  23. package/templates/base/.agents/skills/upgrade-dependencies/SKILL.md +99 -0
  24. package/templates/base/.claude/rules/components.md +11 -0
  25. package/templates/base/.claude/rules/generated-files.md +11 -0
  26. package/templates/base/.claude/rules/testing.md +13 -0
  27. package/templates/base/.claude/rules/typescript.md +29 -0
  28. package/templates/base/.claude/skills/shadcn/SKILL.md +295 -0
  29. package/templates/base/.claude/skills/shadcn/agents/openai.yml +5 -0
  30. package/templates/base/.claude/skills/shadcn/assets/shadcn-small.png +0 -0
  31. package/templates/base/.claude/skills/shadcn/assets/shadcn.png +0 -0
  32. package/templates/base/.claude/skills/shadcn/assets/showcase/color-pair.tsx +126 -0
  33. package/templates/base/.claude/skills/shadcn/assets/showcase/preview-states.css +14 -0
  34. package/templates/base/.claude/skills/shadcn/assets/showcase/state-matrix.tsx +72 -0
  35. package/templates/base/.claude/skills/shadcn/cli.md +290 -0
  36. package/templates/base/.claude/skills/shadcn/customization.md +211 -0
  37. package/templates/base/.claude/skills/shadcn/design-system-page.md +268 -0
  38. package/templates/base/.claude/skills/shadcn/design-system.md +386 -0
  39. package/templates/base/.claude/skills/shadcn/evals/evals.json +126 -0
  40. package/templates/base/.claude/skills/shadcn/mcp.md +105 -0
  41. package/templates/base/.claude/skills/shadcn/registry.md +277 -0
  42. package/templates/base/.claude/skills/shadcn/rules/base-vs-radix.md +306 -0
  43. package/templates/base/.claude/skills/shadcn/rules/chat.md +250 -0
  44. package/templates/base/.claude/skills/shadcn/rules/composition.md +213 -0
  45. package/templates/base/.claude/skills/shadcn/rules/forms.md +192 -0
  46. package/templates/base/.claude/skills/shadcn/rules/icons.md +101 -0
  47. package/templates/base/.claude/skills/shadcn/rules/styling.md +185 -0
  48. package/templates/base/.claude/skills/shadcn/scripts/showcase-coverage.mjs +53 -0
  49. package/templates/base/.claude/skills/upgrade-dependencies/SKILL.md +99 -0
  50. package/templates/base/AGENTS.md +9 -1
  51. package/templates/base/package.json +1 -1
  52. package/templates/base/skills-lock.json +11 -0
  53. package/templates/tools/biome/.biome/catch-binding-unknown.grit +7 -0
  54. package/templates/tools/biome/.biome/effect-cleanup.grit +5 -2
  55. package/templates/tools/biome/.biome/handler-naming.grit +19 -6
  56. package/templates/tools/biome/.biome/no-class-error-boundary.grit +11 -0
  57. package/templates/tools/biome/.biome/no-direct-component-call.grit +20 -0
  58. package/templates/tools/biome/.biome/no-fetch-in-handler.grit +12 -0
  59. package/templates/tools/biome/.biome/no-impure-render.grit +7 -2
  60. package/templates/tools/biome/.biome/no-query-client-in-render.grit +4 -1
  61. package/templates/tools/biome/.biome/no-state-mutation.grit +19 -0
  62. package/templates/tools/biome/.biome/no-try-around-jsx.grit +6 -2
  63. package/templates/tools/biome/.biome/use-assert-never.grit +6 -0
  64. package/templates/tools/biome/biome.json +41 -3
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ // Lists installed UI components that no file in the showcase imports.
3
+ // Usage: node showcase-coverage.mjs <ui-dir> <showcase-dir...>
4
+ import { readdirSync, readFileSync, statSync } from "node:fs"
5
+ import { basename, extname, join, resolve } from "node:path"
6
+
7
+ const [uiArg, ...roots] = process.argv.slice(2)
8
+
9
+ if (!uiArg || roots.length === 0) {
10
+ console.error("Usage: node showcase-coverage.mjs <ui-dir> <showcase-dir...>")
11
+ process.exit(1)
12
+ }
13
+
14
+ const uiDir = resolve(uiArg)
15
+
16
+ const components = readdirSync(uiDir)
17
+ .filter((file) => [".tsx", ".ts"].includes(extname(file)))
18
+ .map((file) => basename(file, extname(file)))
19
+
20
+ function walk(path) {
21
+ if (statSync(path).isFile()) {
22
+ return [path]
23
+ }
24
+
25
+ return readdirSync(path).flatMap((entry) => {
26
+ const child = join(path, entry)
27
+ // Skip the ui directory so components don't count as using each other.
28
+ if (entry === "node_modules" || child === uiDir) {
29
+ return []
30
+ }
31
+ return walk(child)
32
+ })
33
+ }
34
+
35
+ const source = roots
36
+ .map((root) => resolve(root))
37
+ .flatMap(walk)
38
+ .filter((file) => /\.(tsx?|jsx?)$/.test(file))
39
+ .map((file) => readFileSync(file, "utf8"))
40
+ .join("\n")
41
+
42
+ const missing = components.filter(
43
+ (name) => !new RegExp(`/ui/${name}["']`).test(source)
44
+ )
45
+
46
+ console.log(
47
+ `${components.length - missing.length}/${components.length} components shown.`
48
+ )
49
+
50
+ if (missing.length > 0) {
51
+ console.log(`Missing: ${missing.join(", ")}`)
52
+ process.exit(1)
53
+ }
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: upgrade-dependencies
3
+ description: Upgrades outdated patch and minor dependency pins in a pnpm, npm, yarn or bun workspace to exact versions, runs build, check, typecheck and test, and reports every change. Applies when asked to "upgrade dependencies", "update packages", "bump versions", or "update deps".
4
+ argument-hint: "[--accept]"
5
+ allowed-tools: Bash(npm view *), Bash(gh release view *), Bash(gh api *), Bash(pnpm *), Bash(npm *), Bash(yarn *), Bash(bun *), Bash(git diff *), Bash(git status *)
6
+ ---
7
+
8
+ # Upgrade dependencies
9
+
10
+ Arguments: `$ARGUMENTS`. `--accept` means the developer accepts the breaking-change list in advance; proceed at step 4 without stopping.
11
+
12
+ Apply patch and minor upgrades only. Never apply a major; list it as `held: major`.
13
+
14
+ ## 1. Read the workspace
15
+
16
+ - Package manager: root `package.json` field `packageManager` (`pnpm@11.25.0` means `pnpm`). Call it `<pm>` below.
17
+ - Manifests: root `package.json`, `apps/*/package.json`, `packages/*/package.json`.
18
+ - Pins: entries in `dependencies`, `devDependencies`, `optionalDependencies`. Skip `workspace:*`, `link:`, `file:`, `git`, `npm:` alias and URL specs.
19
+ - Minimum age in minutes: `minimumReleaseAge` in `pnpm-workspace.yaml` or `.npmrc`; if unset, `1440`.
20
+
21
+ ## 2. Find upgrades
22
+
23
+ Run once per distinct package name:
24
+
25
+ ```sh
26
+ npm view <pkg> versions time --json
27
+ ```
28
+
29
+ ```json
30
+ {
31
+ "versions": ["5.9.2", "5.9.3", "6.0.0-beta", "6.0.2", "6.0.3"],
32
+ "time": {
33
+ "created": "2012-10-01T15:35:39.553Z",
34
+ "modified": "2026-10-10T08:24:07.208Z",
35
+ "5.9.3": "2025-09-30T21:19:38.784Z",
36
+ "6.0.0-beta": "2026-02-11T18:26:37.557Z",
37
+ "6.0.2": "2026-03-23T16:14:45.521Z",
38
+ "6.0.3": "2026-04-16T23:38:27.905Z"
39
+ }
40
+ }
41
+ ```
42
+
43
+ A package with one version returns `versions` as a string, not an array.
44
+
45
+ Candidates: versions without a `-` prerelease suffix, with `time[version]` at least the minimum age before now.
46
+
47
+ - Same major as the pin, newer than the pin: take the highest. Type is `minor` if the minor differs, else `patch`.
48
+ - Higher major exists: record `held: major` with the highest eligible version. Do not apply it.
49
+ - No candidate newer than the pin: current.
50
+
51
+ If every pin is current, change no files, report `all pins current`, and stop.
52
+
53
+ ## 3. Read release notes
54
+
55
+ For each package to upgrade, read the notes for every release after the old version up to and including the new one.
56
+
57
+ ```sh
58
+ gh release view v<version> --repo <owner>/<repo> --json body
59
+ ```
60
+
61
+ - Repository: `npm view <pkg> repository.url`.
62
+ - No GitHub release for the tag: read `CHANGELOG.md` in the repository for the same range.
63
+ - Record each breaking change, deprecation and migration step per package.
64
+
65
+ ## 4. Surface and stop
66
+
67
+ Print one list: package, old version, new version, type, breaking changes and migrations (or `none found`). Then:
68
+
69
+ - Without `--accept`: stop and ask the developer to accept or exclude packages. Edit nothing before the answer.
70
+ - With `--accept`: continue.
71
+
72
+ ## 5. Apply
73
+
74
+ - Set each upgraded pin to the exact new version: no `^`, no `~`, no `latest`.
75
+ - Run `<pm> install`.
76
+ - Run each project script with `<pm> run <script>` in this order: `build`, `check`, `typecheck`, `test`. Skip a script the root `package.json` does not define.
77
+
78
+ ## 6. Handle a failing check
79
+
80
+ For each failure:
81
+
82
+ - The release notes read in step 3 name the change and its migration: apply the migration, then re-run `<pm> install` and all four scripts.
83
+ - Otherwise: set the package that caused the failure back to its old version, re-run `<pm> install` and all four scripts. Report it as `held: check failed` with the error text.
84
+
85
+ Repeat until `build`, `check`, `typecheck` and `test` all exit 0. Never finish with a failing check.
86
+
87
+ ## 7. Report
88
+
89
+ Print a table of changed packages:
90
+
91
+ | Package | Old | New | Type |
92
+ | --- | --- | --- | --- |
93
+ | typescript | 5.9.2 | 5.9.3 | patch |
94
+
95
+ Below it list:
96
+
97
+ - `held: major`: package, current version, newest eligible major.
98
+ - `held: check failed`: package, old version, attempted version, error text.
99
+ - Migrations applied, with the file changed.
@@ -0,0 +1,11 @@
1
+ ---
2
+ paths:
3
+ - "**/*.tsx"
4
+ ---
5
+
6
+ # Components
7
+
8
+ - Render is pure: no DOM reads, network, or subscriptions during render. Those belong in effects or handlers.
9
+ - Route errors: set `errorComponent` on the route (`defaultErrorComponent` on the router); in it, `useRouter().invalidate()` retries a failed load, `reset` retries a render. Throw `notFound()` for a missing resource.
10
+ - Subtree errors inside a route: `<CatchBoundary getResetKey={() => key} errorComponent={…}>` from `@tanstack/react-router`.
11
+ - Query errors: `throwOnError` to the boundary, reset with `useQueryErrorResetBoundary()` in its `onReset`.
@@ -0,0 +1,11 @@
1
+ ---
2
+ paths:
3
+ - "apps/*/src/**/*.gen.*"
4
+ - "apps/*/src/**/*.g.*"
5
+ - "packages/*/src/**/*.gen.*"
6
+ - "packages/*/src/**/*.g.*"
7
+ ---
8
+
9
+ # Generated files
10
+
11
+ - Never edit by hand. Rerun the generator; if the generator is unknown, ask which one.
@@ -0,0 +1,13 @@
1
+ ---
2
+ paths:
3
+ - "**/*.test.ts"
4
+ - "**/*.test.tsx"
5
+ - "**/*.spec.ts"
6
+ - "**/*.spec.tsx"
7
+ ---
8
+
9
+ # Tests
10
+
11
+ - Test behaviour through the public surface: `userEvent` and `getByRole`, assert outcomes (`toHaveBeenCalledWith`, rendered text). Never assert DOM structure or internals.
12
+ - Cover edge cases and boundaries. Prioritise business logic, external integrations and authorisation over CRUD.
13
+ - A test that breaks on a refactor with no behaviour change is rewritten or deleted.
@@ -0,0 +1,29 @@
1
+ ---
2
+ paths:
3
+ - "**/*.{ts,tsx}"
4
+ ---
5
+
6
+ # TypeScript
7
+
8
+ - Model states and results as a discriminated union on a literal field (`kind`, `success`); never optional fields, never thrown control flow.
9
+ - Narrow before use: type guard, `instanceof`, `in`, exhaustive `switch`. Close an if/else chain over a union with `return assertNever(value)`.
10
+ - Treat `null` and `undefined` as cases to handle. `?.` and `??` only where absence is a valid state; otherwise check and throw.
11
+ - Constrain every generic the body depends on (`T extends { id: string }`); leave `T` bare only for type-transparent functions. Name constrained parameters (`TBase`, `TOverride`).
12
+ - Declare return types on exported functions; let locals infer. Annotate where inference widens (`string` where a literal union was meant).
13
+ - `readonly`, `Readonly<T>`, `ReadonlyArray<T>` where mutation would be a bug; not everywhere.
14
+ - Unavoidable assertion: contain it at the integration boundary under `// biome-ignore lint/nursery/noUnsafeTypeAssertion: <invariant>`; the asserted value never leaves that function.
15
+ - Prop callbacks are `on<Event>`.
16
+
17
+ ## React state and effects
18
+
19
+ - State is immutable: spread at every changed level, `setState((prev) => ({ ...prev, profile: { ...prev.profile, name } }))`. Aliases of state are state.
20
+ - Derive during render. `useEffect` + `setState` only to sync with an external system; a form initialised once from query data is the one exception.
21
+ - An effect that starts async work returns a cleanup that cancels it: `AbortController` or an `ignore` flag.
22
+ - Lift stateful or async logic into a `use*` hook; one responsibility per component.
23
+
24
+ ## TanStack Query
25
+
26
+ - Every variable `queryFn` reads is in `queryKey`. Keys come from one factory module per entity (`userKeys.detail(id)`).
27
+ - Dependent query: `enabled: id !== undefined`. Independent queries run in parallel, never chained.
28
+ - Override `staleTime` per query only when its freshness differs from the global default.
29
+ - Render `mutation.error`. Optimistic update only for interactive, write-heavy UI: `onMutate` cancel and snapshot, `onError` restore, `onSettled` invalidate.
@@ -0,0 +1,295 @@
1
+ ---
2
+ name: shadcn
3
+ description: Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI, including chat interfaces. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for "shadcn init", "create an app with --preset", "switch to --preset", and for building a design system or applying a DESIGN.md or brand spec to shadcn/ui.
4
+ user-invocable: false
5
+ allowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)
6
+ ---
7
+
8
+ # shadcn/ui
9
+
10
+ A framework for building ui, components and design systems. Components are added as source code to the user's project via the CLI.
11
+
12
+ > **IMPORTANT:** Run all CLI commands using the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest` — based on the project's `packageManager`. Examples below use `npx shadcn@latest` but substitute the correct runner for the project.
13
+
14
+ ## Current Project Context
15
+
16
+ ```json
17
+ !`npx shadcn@latest info --json`
18
+ ```
19
+
20
+ The JSON above contains the project config and installed components. Use `npx shadcn@latest docs <component>` to get documentation and example URLs for any component.
21
+
22
+ ## Principles
23
+
24
+ 1. **Use existing components first.** Use `npx shadcn@latest search` to check registries before writing custom UI. Check community registries too.
25
+ 2. **Compose, don't reinvent.** Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.
26
+ 3. **Use built-in variants before custom styles.** `variant="outline"`, `size="sm"`, etc.
27
+ 4. **Use semantic colors.** `bg-primary`, `text-muted-foreground` — never raw values like `bg-blue-500`.
28
+
29
+ ## Critical Rules
30
+
31
+ These rules are **always enforced**. Each links to a file with Incorrect/Correct code pairs.
32
+
33
+ ### Styling & Tailwind → [styling.md](./rules/styling.md)
34
+
35
+ - **`className` for layout, not styling.** Never override component colors or typography.
36
+ - **No `space-x-*` or `space-y-*`.** Use `flex` with `gap-*`. For vertical stacks, `flex flex-col gap-*`.
37
+ - **Use `size-*` when width and height are equal.** `size-10` not `w-10 h-10`.
38
+ - **Use `truncate` shorthand.** Not `overflow-hidden text-ellipsis whitespace-nowrap`.
39
+ - **No manual `dark:` color overrides.** Use semantic tokens (`bg-background`, `text-muted-foreground`).
40
+ - **Use `cn()` for conditional classes.** Don't write manual template literal ternaries.
41
+ - **No manual `z-index` on overlay components.** Dialog, Sheet, Popover, etc. handle their own stacking.
42
+
43
+ ### Forms & Inputs → [forms.md](./rules/forms.md)
44
+
45
+ - **Forms use `FieldGroup` + `Field`.** Never use raw `div` with `space-y-*` or `grid gap-*` for form layout.
46
+ - **`InputGroup` uses `InputGroupInput`/`InputGroupTextarea`.** Never raw `Input`/`Textarea` inside `InputGroup`.
47
+ - **Buttons inside inputs use `InputGroup` + `InputGroupAddon`.**
48
+ - **Option sets (2–7 choices) use `ToggleGroup`.** Don't loop `Button` with manual active state.
49
+ - **`FieldSet` + `FieldLegend` for grouping related checkboxes/radios.** Don't use a `div` with a heading.
50
+ - **Field validation uses `data-invalid` + `aria-invalid`.** `data-invalid` on `Field`, `aria-invalid` on the control. For disabled: `data-disabled` on `Field`, `disabled` on the control.
51
+
52
+ ### Component Structure → [composition.md](./rules/composition.md)
53
+
54
+ - **Items always inside their Group.** `SelectItem` → `SelectGroup`. `DropdownMenuItem` → `DropdownMenuGroup`. `CommandItem` → `CommandGroup`.
55
+ - **Use `asChild` (radix) or `render` (base) for custom triggers.** Check `base` field from `npx shadcn@latest info`. → [base-vs-radix.md](./rules/base-vs-radix.md)
56
+ - **Dialog, Sheet, and Drawer always need a Title.** `DialogTitle`, `SheetTitle`, `DrawerTitle` required for accessibility. Use `className="sr-only"` if visually hidden.
57
+ - **Use full Card composition.** `CardHeader`/`CardTitle`/`CardDescription`/`CardContent`/`CardFooter`. Don't dump everything in `CardContent`.
58
+ - **Button has no `isPending`/`isLoading`.** Compose with `Spinner` + `data-icon` + `disabled`.
59
+ - **`TabsTrigger` must be inside `TabsList`.** Never render triggers directly in `Tabs`.
60
+ - **`Avatar` always needs `AvatarFallback`.** For when the image fails to load.
61
+
62
+ ### Use Components, Not Custom Markup → [composition.md](./rules/composition.md)
63
+
64
+ - **Use existing components before custom markup.** Check if a component exists before writing a styled `div`.
65
+ - **Callouts use `Alert`.** Don't build custom styled divs.
66
+ - **Empty states use `Empty`.** Don't build custom empty state markup.
67
+ - **Toast follows the project base.** Use `toast` from the `toast` component for
68
+ Base UI projects. Use `toast()` from `sonner` for Radix and React Aria
69
+ projects.
70
+ - **Use `Separator`** instead of `<hr>` or `<div className="border-t">`.
71
+ - **Use `Skeleton`** for loading placeholders. No custom `animate-pulse` divs.
72
+ - **Use `Badge`** instead of custom styled spans.
73
+
74
+ ### Icons → [icons.md](./rules/icons.md)
75
+
76
+ - **Icons in `Button` use `data-icon`.** `data-icon="inline-start"` or `data-icon="inline-end"` on the icon.
77
+ - **No sizing classes on icons inside components.** Components handle icon sizing via CSS. No `size-4` or `w-4 h-4`.
78
+ - **Pass icons as objects, not string keys.** `icon={CheckIcon}`, not a string lookup.
79
+
80
+ ### Chat & Messaging → [chat.md](./rules/chat.md)
81
+
82
+ - **Chat UI composes the chat primitives.** Conversations use `MessageScroller`, rows use `Message`, surfaces use `Bubble`. Never hand-rolled bubble `div`s or a raw scroll container.
83
+ - **`MessageScroller` owns scroll behavior.** Streaming follow, anchoring, and jump-to-latest (`MessageScrollerButton`) are built in. Don't write a `useStickToBottom`/`ResizeObserver` hook.
84
+ - **Attachments use `Attachment`; system notes and dividers use `Marker`.** Not `Item` cards or `Separator` + a label.
85
+
86
+ ### CLI
87
+
88
+ - **Never decode preset codes or build preset URLs manually.** Use `npx shadcn@latest preset decode <code>`, `preset url <code>`, or `preset open <code>`. For project-aware preset detection, use `npx shadcn@latest preset resolve`.
89
+ - **Apply preset codes directly with the CLI.** Use `npx shadcn@latest apply <code>` for existing projects, or `npx shadcn@latest init --preset <code>` when initializing.
90
+
91
+ ## Key Patterns
92
+
93
+ These are the most common patterns that differentiate correct shadcn/ui code. For edge cases, see the linked rule files above.
94
+
95
+ ```tsx
96
+ // Form layout: FieldGroup + Field, not div + Label.
97
+ <FieldGroup>
98
+ <Field>
99
+ <FieldLabel htmlFor="email">Email</FieldLabel>
100
+ <Input id="email" />
101
+ </Field>
102
+ </FieldGroup>
103
+
104
+ // Validation: data-invalid on Field, aria-invalid on the control.
105
+ <Field data-invalid>
106
+ <FieldLabel>Email</FieldLabel>
107
+ <Input aria-invalid />
108
+ <FieldDescription>Invalid email.</FieldDescription>
109
+ </Field>
110
+
111
+ // Icons in buttons: data-icon, no sizing classes.
112
+ <Button>
113
+ <SearchIcon data-icon="inline-start" />
114
+ Search
115
+ </Button>
116
+
117
+ // Spacing: gap-*, not space-y-*.
118
+ <div className="flex flex-col gap-4"> // correct
119
+ <div className="space-y-4"> // wrong
120
+
121
+ // Equal dimensions: size-*, not w-* h-*.
122
+ <Avatar className="size-10"> // correct
123
+ <Avatar className="w-10 h-10"> // wrong
124
+
125
+ // Status colors: Badge variants or semantic tokens, not raw colors.
126
+ <Badge variant="secondary">+20.1%</Badge> // correct
127
+ <span className="text-emerald-600">+20.1%</span> // wrong
128
+ ```
129
+
130
+ ## Component Selection
131
+
132
+ | Need | Use |
133
+ | -------------------------- | --------------------------------------------------------------------------------------------------- |
134
+ | Button/action | `Button` with appropriate variant |
135
+ | Form inputs | `Input`, `Select`, `Combobox`, `Switch`, `Checkbox`, `RadioGroup`, `Textarea`, `InputOTP`, `Slider` |
136
+ | Toggle between 2–5 options | `ToggleGroup` + `ToggleGroupItem` |
137
+ | Data display | `Table`, `Card`, `Badge`, `Avatar` |
138
+ | Navigation | `Sidebar`, `NavigationMenu`, `Breadcrumb`, `Tabs`, `Pagination` |
139
+ | Overlays | `Dialog` (modal), `Sheet` (side panel), `Drawer` (bottom sheet), `AlertDialog` (confirmation) |
140
+ | Feedback | `toast` (Base UI), `sonner` (Radix/Aria), `Alert`, `Progress`, `Skeleton`, `Spinner` |
141
+ | Command palette | `Command` inside `Dialog` |
142
+ | Charts | `Chart` (wraps Recharts) |
143
+ | Layout | `Card`, `Separator`, `Resizable`, `ScrollArea`, `Accordion`, `Collapsible` |
144
+ | Empty states | `Empty` |
145
+ | Menus | `DropdownMenu`, `ContextMenu`, `Menubar` |
146
+ | Tooltips/info | `Tooltip`, `HoverCard`, `Popover` |
147
+ | Chat / conversation UI | `MessageScroller`, `Message`, `Bubble`, `Attachment`, `Marker` |
148
+
149
+ ## Key Fields
150
+
151
+ The injected project context contains these key fields:
152
+
153
+ - **`aliases`** → use the actual alias prefix for imports (e.g. `@/`, `~/`), never hardcode.
154
+ - **`isRSC`** → when `true`, components using `useState`, `useEffect`, event handlers, or browser APIs need `"use client"` at the top of the file. Always reference this field when advising on the directive.
155
+ - **`tailwindVersion`** → `"v4"` uses `@theme inline` blocks; `"v3"` uses `tailwind.config.js`.
156
+ - **`tailwindCssFile`** → the global CSS file where custom CSS variables are defined. Always edit this file, never create a new one.
157
+ - **`style`** → component visual treatment (e.g. `nova`, `vega`).
158
+ - **`base`** → primitive library (`radix` or `base`). Affects component APIs and available props.
159
+ - **`iconLibrary`** → determines icon imports. Use `lucide-react` for `lucide`, `@tabler/icons-react` for `tabler`, etc. Never assume `lucide-react`.
160
+ - **`resolvedPaths`** → exact file-system destinations for components, utils, hooks, etc.
161
+ - **`framework`** → routing and file conventions (e.g. Next.js App Router vs Vite SPA).
162
+ - **`packageManager`** → use this for any non-shadcn dependency installs (e.g. `pnpm add date-fns` vs `npm install date-fns`).
163
+ - **`preset`** → resolved preset code and values for the current project. Use `npx shadcn@latest preset resolve --json` when you only need preset information.
164
+
165
+ See [cli.md — `info` command](./cli.md) for the full field reference.
166
+
167
+ ## Component Docs, Examples, and Usage
168
+
169
+ Run `npx shadcn@latest docs <component>` to get the URLs for a component's documentation, examples, and API reference. Fetch these URLs to get the actual content.
170
+
171
+ ```bash
172
+ npx shadcn@latest docs button dialog select
173
+ ```
174
+
175
+ **When creating, fixing, debugging, or using a component, always run `npx shadcn@latest docs` and fetch the URLs first.** This ensures you're working with the correct API and usage patterns rather than guessing.
176
+
177
+ ## Workflow
178
+
179
+ 1. **Get project context** — already injected above. Run `npx shadcn@latest info` again if you need to refresh.
180
+ 2. **Check installed components first** — before running `add`, always check the `components` list from project context or list the `resolvedPaths.ui` directory. Don't import components that haven't been added, and don't re-add ones already installed.
181
+ 3. **Find components** — `npx shadcn@latest search`.
182
+ 4. **Get docs and examples** — run `npx shadcn@latest docs <component>` to get URLs, then fetch them. Use `npx shadcn@latest view` to browse registry items you haven't installed. To preview changes to installed components, use `npx shadcn@latest add --diff`.
183
+ 5. **Install or update** — `npx shadcn@latest add`. When updating existing components, use `--dry-run` and `--diff` to preview changes first (see [Updating Components](#updating-components) below).
184
+ 6. **Fix imports in third-party components** — After adding components from community registries (e.g. `@bundui`, `@magicui`), check the added non-UI files for hardcoded import paths like `@/components/ui/...`. These won't match the project's actual aliases. Use `npx shadcn@latest info` to get the correct `ui` alias (e.g. `@workspace/ui/components`) and rewrite the imports accordingly. The CLI rewrites imports for its own UI files, but third-party registry components may use default paths that don't match the project.
185
+ 7. **Review added components** — After adding a component or block from any registry, **always read the added files and verify they are correct**. Check for missing sub-components (e.g. `SelectItem` without `SelectGroup`), missing imports, incorrect composition, or violations of the [Critical Rules](#critical-rules). Also replace any icon imports with the project's `iconLibrary` from the project context (e.g. if the registry item uses `lucide-react` but the project uses `hugeicons`, swap the imports and icon names accordingly). Fix all issues before moving on.
186
+ 8. **Registry must be explicit** — When the user asks to add a block or component, **do not guess the registry**. If no registry is specified (e.g. user says "add a login block" without specifying `@shadcn`, `@tailark`, `owner/repo`, etc.), ask which registry to use. Never default to a registry on behalf of the user.
187
+ 9. **Switching presets** — Ask the user first: **overwrite**, **partial**, **merge**, or **skip**?
188
+ - **Inspect current preset**: `npx shadcn@latest preset resolve`. Use `--json` when you need structured values.
189
+ - **Inspect incoming preset**: `npx shadcn@latest preset decode <code>`. Use `preset url <code>` or `preset open <code>` to share or open the preset builder.
190
+ - **Overwrite**: `npx shadcn@latest apply <code>`. Overwrites detected components, fonts, and CSS variables.
191
+ - **Partial**: `npx shadcn@latest apply <code> --only theme,font`. Updates only the selected preset parts without reinstalling UI components. Supported values are `theme` and `font`; comma-separated combinations are allowed. `icon` is intentionally not supported, because icon changes may require full component reinstall and transforms.
192
+ - **Merge**: `npx shadcn@latest init --preset <code> --force --no-reinstall`, then run `npx shadcn@latest info` to list installed components, then for each installed component use `--dry-run` and `--diff` to [smart merge](#updating-components) it individually.
193
+ - **Skip**: `npx shadcn@latest init --preset <code> --force --no-reinstall`. Only updates config and CSS, leaves components as-is.
194
+ - **Important**: Always run preset commands inside the user's project directory. `apply` only works in an existing project with a `components.json` file. The CLI automatically preserves the current base (`base` vs `radix`) from `components.json`. If you must use a scratch/temp directory (e.g. for `--dry-run` comparisons), pass `--base <current-base>` explicitly — preset codes do not encode the base.
195
+
196
+ ## Design Systems from DESIGN.md
197
+
198
+ When the user asks for a design system from a `DESIGN.md` (or a brand spec), with or without a preset, follow [design-system.md](./design-system.md) end to end:
199
+
200
+ 1. `init --preset <code> --template <vite|next> --name <app> --no-monorepo -y`. Without a preset code, pick the closest style and pass `--base base --preset <style>`. Then `add --all -y`.
201
+ 2. Read the DESIGN.md frontmatter, Do's and Don'ts, and Known Gaps before editing.
202
+ 3. Map its tokens onto the shadcn variables in `tailwindCssFile`. Pin the radius scale, shadows, fonts and `type-*` utilities.
203
+ 4. Restyle components by editing their `cva` variants. Change control heights together.
204
+ 5. Build the design system page per [design-system-page.md](./design-system-page.md): contrast-checked color roles, type specimens, pinned state matrices, edge cases, do/don't pairs, recipes. Check coverage with `scripts/showcase-coverage.mjs`.
205
+ 6. Verify in a browser: typecheck, no console errors, loads at the top, no horizontal overflow, dark mode.
206
+
207
+ ## Updating Components
208
+
209
+ When the user asks to update a component from upstream while keeping their local changes, use `--dry-run` and `--diff` to intelligently merge. **NEVER fetch raw files from GitHub manually — always use the CLI.**
210
+
211
+ 1. Run `npx shadcn@latest add <component> --dry-run` to see all files that would be affected.
212
+ 2. For each file, run `npx shadcn@latest add <component> --diff <file>` to see what changed upstream vs local.
213
+ 3. Decide per file based on the diff:
214
+ - No local changes → safe to overwrite.
215
+ - Has local changes → read the local file, analyze the diff, and apply upstream updates while preserving local modifications.
216
+ - User says "just update everything" → use `--overwrite`, but confirm first.
217
+ 4. **Never use `--overwrite` without the user's explicit approval.**
218
+
219
+ ## Quick Reference
220
+
221
+ ```bash
222
+ # Create a new project.
223
+ npx shadcn@latest init --name my-app --preset base-nova
224
+ npx shadcn@latest init --name my-app --preset a2r6bw --template vite
225
+
226
+ # Create a design system app (then follow design-system.md).
227
+ npx shadcn@latest init --name my-ds --preset b0 --template vite --no-monorepo -y
228
+ npx shadcn@latest init --name my-ds --base base --preset maia --template vite --no-monorepo -y
229
+ npx shadcn@latest add --all -y
230
+
231
+ # Create a monorepo project.
232
+ npx shadcn@latest init --name my-app --preset base-nova --monorepo
233
+ npx shadcn@latest init --name my-app --preset base-nova --template next --monorepo
234
+
235
+ # Initialize existing project.
236
+ npx shadcn@latest init --preset base-nova
237
+ npx shadcn@latest init --defaults # shortcut: --template=next --preset=nova (base style implied)
238
+
239
+ # Apply a preset to an existing project.
240
+ npx shadcn@latest apply a2r6bw
241
+ npx shadcn@latest apply a2r6bw --only theme
242
+ npx shadcn@latest apply a2r6bw --only font
243
+ npx shadcn@latest apply a2r6bw --only theme,font
244
+
245
+ # Inspect preset codes and project preset state.
246
+ npx shadcn@latest preset decode a2r6bw
247
+ npx shadcn@latest preset url a2r6bw
248
+ npx shadcn@latest preset open a2r6bw
249
+ npx shadcn@latest preset resolve
250
+ npx shadcn@latest preset resolve --json
251
+
252
+ # Add components.
253
+ npx shadcn@latest add button card dialog
254
+ npx shadcn@latest add @magicui/shimmer-button
255
+ npx shadcn@latest add owner/repo/item
256
+ npx shadcn@latest add --all
257
+
258
+ # Preview changes before adding/updating.
259
+ npx shadcn@latest add button --dry-run
260
+ npx shadcn@latest add button --diff button.tsx
261
+ npx shadcn@latest add @acme/form --view button.tsx
262
+ npx shadcn@latest add owner/repo/item --dry-run
263
+
264
+ # Search registries.
265
+ npx shadcn@latest search @shadcn -q "sidebar"
266
+ npx shadcn@latest search @tailark -q "stats"
267
+ npx shadcn@latest search owner/repo -q "login"
268
+ npx shadcn@latest search # all configured registries
269
+ npx shadcn@latest search @shadcn -q "menu" -t ui # filter by item type
270
+
271
+ # Get component docs and example URLs.
272
+ npx shadcn@latest docs button dialog select
273
+
274
+ # View registry item details (for items not yet installed).
275
+ npx shadcn@latest view @shadcn/button
276
+ npx shadcn@latest view owner/repo/item
277
+ ```
278
+
279
+ **Named presets:** `nova`, `vega`, `maia`, `lyra`, `mira`, `luma`
280
+ **Templates:** `next`, `vite`, `start`, `react-router`, `astro` (all support `--monorepo`) and `laravel` (not supported for monorepo)
281
+ **Preset codes:** Version-prefixed base62 strings (e.g. `a2r6bw` or `b0`), from [ui.shadcn.com](https://ui.shadcn.com).
282
+
283
+ ## Detailed References
284
+
285
+ - [rules/forms.md](./rules/forms.md) — FieldGroup, Field, InputGroup, ToggleGroup, FieldSet, validation states
286
+ - [rules/composition.md](./rules/composition.md) — Groups, overlays, Card, Tabs, Avatar, Alert, Empty, Toast, Separator, Skeleton, Badge, Button loading
287
+ - [rules/chat.md](./rules/chat.md) — MessageScroller, Message, Bubble, Attachment, Marker; streaming, anchoring, jump-to-latest
288
+ - [rules/icons.md](./rules/icons.md) — data-icon, icon sizing, passing icons as objects
289
+ - [rules/styling.md](./rules/styling.md) — Semantic colors, variants, className, spacing, size, truncate, dark mode, cn(), z-index
290
+ - [rules/base-vs-radix.md](./rules/base-vs-radix.md) — asChild vs render, Select, ToggleGroup, Slider, Accordion
291
+ - [cli.md](./cli.md) — Commands, flags, presets, templates
292
+ - [registry.md](./registry.md) — Authoring source registries, `include`, item definitions, dependencies, GitHub registry rules
293
+ - [customization.md](./customization.md) — Theming, CSS variables, extending components
294
+ - [design-system.md](./design-system.md): DESIGN.md (with or without a preset) to a themed app: style picking, token mapping, radius, fonts, type scale, control heights, showcase traps, verification
295
+ - [design-system-page.md](./design-system-page.md): What goes on a design system page and how: foundations, component tiers, pinned state matrices, contrast pairs, edge cases, do/don't, recipes
@@ -0,0 +1,5 @@
1
+ interface:
2
+ display_name: "shadcn/ui"
3
+ short_description: "Manages shadcn/ui components — adding, searching, fixing, debugging, styling, and composing UI."
4
+ icon_small: "./assets/shadcn-small.png"
5
+ icon_large: "./assets/shadcn.png"
@@ -0,0 +1,126 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ // Resolves any CSS color (hex, rgb, oklch, color-mix) to sRGB by painting one pixel.
6
+ // Translucent colors are composited over the backdrop, as the browser would.
7
+ function toRgb(color: string, backdrop = "#fff") {
8
+ const canvas = document.createElement("canvas")
9
+ canvas.width = canvas.height = 1
10
+ const context = canvas.getContext("2d", { willReadFrequently: true })
11
+ if (!context) {
12
+ return null
13
+ }
14
+ context.fillStyle = backdrop
15
+ context.fillRect(0, 0, 1, 1)
16
+ context.fillStyle = color
17
+ context.fillRect(0, 0, 1, 1)
18
+ const [r, g, b] = context.getImageData(0, 0, 1, 1).data
19
+ return [r, g, b]
20
+ }
21
+
22
+ function toHex(rgb: number[]) {
23
+ return `#${rgb.map((value) => value.toString(16).padStart(2, "0")).join("")}`
24
+ }
25
+
26
+ function luminance([r, g, b]: number[]) {
27
+ const [R, G, B] = [r, g, b].map((value) => {
28
+ const channel = value / 255
29
+ return channel <= 0.03928
30
+ ? channel / 12.92
31
+ : ((channel + 0.055) / 1.055) ** 2.4
32
+ })
33
+ return 0.2126 * R + 0.7152 * G + 0.0722 * B
34
+ }
35
+
36
+ function contrast(a: number[], b: number[]) {
37
+ const [light, dark] = [luminance(a), luminance(b)].sort((x, y) => y - x)
38
+ return (light + 0.05) / (dark + 0.05)
39
+ }
40
+
41
+ export function ColorPair({
42
+ name,
43
+ background,
44
+ foreground,
45
+ className,
46
+ }: {
47
+ name: string
48
+ background: string
49
+ foreground: string
50
+ className?: string
51
+ }) {
52
+ const ref = React.useRef<HTMLDivElement>(null)
53
+ const [info, setInfo] = React.useState<{ ratio: number; bg: string } | null>(
54
+ null
55
+ )
56
+
57
+ React.useEffect(() => {
58
+ const element = ref.current
59
+ if (!element) {
60
+ return
61
+ }
62
+ // Re-measure when the theme class on <html> changes.
63
+ const measure = () => {
64
+ const style = getComputedStyle(element)
65
+ const page = getComputedStyle(document.body).backgroundColor
66
+ const bg = toRgb(style.backgroundColor, page)
67
+ if (!bg) {
68
+ return
69
+ }
70
+ const fg = toRgb(style.color, toHex(bg))
71
+ if (fg) {
72
+ setInfo({ ratio: contrast(bg, fg), bg: toHex(bg) })
73
+ }
74
+ }
75
+ measure()
76
+ const observer = new MutationObserver(measure)
77
+ observer.observe(document.documentElement, { attributes: true })
78
+ return () => observer.disconnect()
79
+ }, [])
80
+
81
+ const level = !info
82
+ ? ""
83
+ : info.ratio >= 7
84
+ ? "AAA"
85
+ : info.ratio >= 4.5
86
+ ? "AA"
87
+ : info.ratio >= 3
88
+ ? "AA Large"
89
+ : "Fail"
90
+
91
+ return (
92
+ <div
93
+ className={cn(
94
+ "flex flex-col overflow-hidden rounded-xl border",
95
+ className
96
+ )}
97
+ >
98
+ <div
99
+ ref={ref}
100
+ style={{
101
+ backgroundColor: `var(--${background})`,
102
+ color: `var(--${foreground})`,
103
+ }}
104
+ className="flex h-24 items-end justify-between p-4"
105
+ >
106
+ <span className="text-2xl font-medium">Aa</span>
107
+ {info && (
108
+ <span className="rounded-full border border-current/20 px-2 py-0.5 font-mono text-xs">
109
+ {info.ratio.toFixed(2)} {level}
110
+ </span>
111
+ )}
112
+ </div>
113
+ <div className="flex flex-col gap-0.5 bg-background p-3">
114
+ <span className="text-sm font-medium">{name}</span>
115
+ <span className="font-mono text-xs text-muted-foreground">
116
+ --{background} / --{foreground}
117
+ </span>
118
+ {info && (
119
+ <span className="font-mono text-xs text-muted-foreground">
120
+ {info.bg}
121
+ </span>
122
+ )}
123
+ </div>
124
+ </div>
125
+ )
126
+ }