@wangs-ui/skills 1.3.0-alpha.18 → 1.3.0-alpha.20

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.
package/dist/bin.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { i as listSkills, n as updateSkills, r as addSkills, t as removeSkills } from "./src-sLQjegSQ.js";
2
+ import { i as listSkills, n as updateSkills, r as addSkills, t as removeSkills } from "./src-C0l8elLu.js";
3
3
  import path from "node:path";
4
4
  import { parseArgs } from "node:util";
5
5
  //#region bin.ts
6
- var VERSION = "1.3.0-alpha.17";
6
+ var VERSION = "1.3.0-alpha.12";
7
7
  var HELP_TEXT = `
8
8
  \x1b[1m\x1b[36mšŸš€ Wangs UI Skills & Rules CLI\x1b[0m
9
9
  Install, update, and manage modular AI agent skills and rules for Wangs UI React applications.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as loadAllRules, a as getAgentRuleDirs, c as getInstalledSkills, d as isRuleInstalled, f as isSkillInstalled, g as getSkill, h as getRule, i as listSkills, l as installRule, m as removeSkill, n as updateSkills, o as getAgentSkillDirs, p as removeRule, r as addSkills, s as getInstalledRules, t as removeSkills, u as installSkill, v as loadAllSkills } from "./src-sLQjegSQ.js";
1
+ import { _ as loadAllRules, a as getAgentRuleDirs, c as getInstalledSkills, d as isRuleInstalled, f as isSkillInstalled, g as getSkill, h as getRule, i as listSkills, l as installRule, m as removeSkill, n as updateSkills, o as getAgentSkillDirs, p as removeRule, r as addSkills, s as getInstalledRules, t as removeSkills, u as installSkill, v as loadAllSkills } from "./src-C0l8elLu.js";
2
2
  export { addSkills, getAgentRuleDirs, getAgentSkillDirs, getInstalledRules, getInstalledSkills, getRule, getSkill, installRule, installSkill, isRuleInstalled, isSkillInstalled, listSkills, loadAllRules, loadAllSkills, removeRule, removeSkill, removeSkills, updateSkills };
@@ -9,12 +9,13 @@ metadata:
9
9
 
10
10
  ## Checking Compiler Optimization
11
11
 
12
- - **React DevTools** — an optimized component shows a "Memo ✨" badge next to its name in the component tree.
12
+ - **Build output** — compiled files import `react/compiler-runtime` and contain `Symbol.for("react.memo_cache_sentinel")`; if absent, the compiler never ran.
13
13
  - **ESLint / Oxlint** — compiler's recommended rules flag Rules-of-React violations at lint time.
14
14
 
15
15
  ## Review Checklist
16
16
 
17
- - [ ] Compiler confirmed active ("Memo ✨" badge) before removing any _existing_ manual memoization
17
+ - [ ] Compiler is wired up and confirmed active — set it up if missing (see `react19-tooling-and-opt-out.md`); never treat a missing compiler as licence to hand-roll memoization
18
+ - [ ] Compiler confirmed active (build output imports `react/compiler-runtime`) before removing any _existing_ manual memoization
18
19
  - [ ] No new `useMemo`/`useCallback`/`React.memo` added without documented reason (confirmed bail-out, or external boundary)
19
20
  - [ ] No prop/state/context mutation anywhere in render
20
21
  - [ ] All hooks called unconditionally at top level, same order every render
@@ -34,4 +35,4 @@ metadata:
34
35
  | Form/async state with distinct outcomes | Discriminated union via `useActionState`, not optional fields |
35
36
  | Callback needs latest props/state without re-running effect | `useEffectEvent` |
36
37
  | A hook/library is known-incompatible with compiler | `"use no memo"` at top of that function, with comment |
37
- | Checking if optimization is happening | React DevTools "Memo ✨" badge + compiler lint rules |
38
+ | Checking if optimization is happening | Build output imports `react/compiler-runtime` (has `react.memo_cache_sentinel`) + compiler lint rules |
@@ -7,7 +7,9 @@ metadata:
7
7
  ---
8
8
  # Rule: React 19 — Stop Hand-Rolling Memoization
9
9
 
10
- Don't reach for `useMemo`, `useCallback`, or `React.memo` by default — the compiler adds this automatically wherever it determines it helps.
10
+ > **Prerequisite:** this rule holds only once the React Compiler is enabled. If the project isn't wired up yet, set it up first (see `react19-tooling-and-opt-out.md`) — the absence of the compiler is never a reason to hand-roll memoization.
11
+
12
+ Don't reach for `useMemo`, `useCallback`, or `React.memo` — the compiler adds this automatically wherever it determines it helps. This is a requirement, not a stylistic preference: manual memoization is the exception and must be justified.
11
13
 
12
14
  ```tsx
13
15
  // āŒ Old habit — noisy, and a mismatched dependency array is a whole class of bugs
@@ -7,13 +7,23 @@ metadata:
7
7
  ---
8
8
  # Rule: React 19 — Tooling Setup & Compiler Opt-Out
9
9
 
10
- ## Tooling Setup
10
+ ## Compiler Setup Is Mandatory
11
11
 
12
- **The compiler is opt-in — no default setup enables it automatically.** Plain `@vitejs/plugin-react` (`react()`), plain Next.js, plain Babel/webpack config, etc. do **not** run the compiler on their own. Verify it's actually wired up before assuming manual memoization can be dropped.
12
+ **The compiler is required, not opt-in.** Every Wangs UI project must run it. The entire "write plain code, no manual memoization" rule set only holds once the compiler is actually active, so wiring it up is a prerequisite — not an optional enhancement.
13
+
14
+ Plain `@vitejs/plugin-react` (`react()`), plain Next.js, plain Babel/webpack config, etc. do **not** run the compiler on their own. **Verify it is active before assuming manual memoization can be dropped — and if it isn't wired up, set it up first** instead of falling back to hand-rolled `useMemo`/`useCallback`/`React.memo`.
15
+
16
+ ### Verify it's active
17
+
18
+ - The build passes the compiler preset through Babel, and `babel-plugin-react-compiler` is installed (e.g. `@vitejs/plugin-react`'s `reactCompilerPreset()` via `@rolldown/plugin-babel`).
19
+ - The build output actually contains compiler artifacts — `import { c as _c } from "react/compiler-runtime"` and `Symbol.for("react.memo_cache_sentinel")` in `dist`. Config being present is **not** proof it ran; the emitted output is.
20
+ - The `react/react-compiler` lint rule is enabled.
21
+
22
+ ### Setup (when missing)
13
23
 
14
24
  ```bash
15
25
  # Compiler (build-time transform)
16
- npm install --save-dev --save-exact babel-plugin-react-compiler@latest
26
+ pnpm add -D --save-exact babel-plugin-react-compiler@latest
17
27
  ```
18
28
 
19
29
  ### Lint rules — oxlint
@@ -37,8 +47,8 @@ This single rule reports:
37
47
 
38
48
  ### Wiring into Vite 8
39
49
 
40
- ```js
41
- // vite.config.js
50
+ ```ts
51
+ // vite.config.ts
42
52
  import { defineConfig } from 'vite';
43
53
  import react, { reactCompilerPreset } from '@vitejs/plugin-react';
44
54
  import babel from '@rolldown/plugin-babel';
@@ -52,7 +62,7 @@ export default defineConfig({
52
62
  ```
53
63
 
54
64
  ```bash
55
- npm install --save-dev @rolldown/plugin-babel @babel/core babel-plugin-react-compiler
65
+ pnpm add -D @rolldown/plugin-babel @babel/core babel-plugin-react-compiler
56
66
  ```
57
67
 
58
68
  ## Incompatibility Escape Hatch (`"use no memo"`)
@@ -15,7 +15,7 @@ Use this skill whenever the user asks to create, customize, or rebrand the color
15
15
  ## 1. Generate the palette
16
16
 
17
17
  ```bash
18
- npx wangs-ui-generate-palette --name=<id> --primary=<#hex> [options] --out=<path>
18
+ pnpm exec wangs-ui-generate-palette --name=<id> --primary=<#hex> [options] --out=<path>
19
19
  ```
20
20
 
21
21
  - `--name` (required): identifier for the palette, e.g. `sunset`. Used for the exported const name (`sunsetPalette`) and, unless `--out` is given, the output filename (`sunset.ts`).
@@ -51,7 +51,7 @@ const App = () => (
51
51
  To change a color, re-run the same command with a new hex for the family that changed — **always pass every family you want to keep**, not just the one changing, since each run is a full regeneration from the anchors you give it:
52
52
 
53
53
  ```bash
54
- npx wangs-ui-generate-palette --name=sunset --primary=#ff6b35 --tertiary=#2a9d8f --out=src/theme/sunset.ts
54
+ pnpm exec wangs-ui-generate-palette --name=sunset --primary=#ff6b35 --tertiary=#2a9d8f --out=src/theme/sunset.ts
55
55
  ```
56
56
 
57
57
  ## 4. If you're extending Wangs UI itself (contributing a new official palette)
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  import os from "node:os";
5
5
  import { cancel, intro, isCancel, multiselect, outro } from "@clack/prompts";
6
6
  //#region skills/wangs-ui/craft-theme/SKILL.md?raw
7
- var SKILL_default$8 = "---\nname: craft-theme\ndescription: Generate a brand-color theme (13-shade tonal palette + semantic tokens) for a Wangs UI app via the `wangs-ui-generate-palette` CLI — never hand-write hex shade ramps.\nmetadata:\n owner: wangs-ui\n---\n# Skill: Craft Theme\n\nUse this skill whenever the user asks to create, customize, or rebrand the color theme of an app built on `@wangs-ui/react-core` / `@wangs-ui/foundation` — \"make the app's theme orange\", \"use our brand color #ff6b35\", \"add a new palette called sunset\", etc.\n\n## The rule this skill exists to enforce\n\n**Never hand-write a 13-shade tonal ramp, and never hand-pick which shade pairs with which as a text/foreground color.** Wangs UI's own design system shipped with exactly that mistake for a while: hand-tuned palette files drifted from what the perceptual (OKLCH) generator would produce, which caused white text to render at 2.54:1 contrast on one brand's primary color — invisible-adjacent, and only caught by a dedicated audit. The generator and the contrast math it's paired with exist specifically so this class of bug can't happen again. Always reach for the CLI below instead of writing hex values yourself.\n\n## 1. Generate the palette\n\n```bash\nnpx wangs-ui-generate-palette --name=<id> --primary=<#hex> [options] --out=<path>\n```\n\n- `--name` (required): identifier for the palette, e.g. `sunset`. Used for the exported const name (`sunsetPalette`) and, unless `--out` is given, the output filename (`sunset.ts`).\n- `--primary` (required): the brand/key hex color, e.g. `#ff6b35`.\n- Optional per-family overrides — omit any of these to let the generator harmoniously auto-derive it from `--primary` (secondary: boosted-lightness variant of primary's hue; tertiary: +60° hue rotation; general: near-neutral variant of primary's hue) or fall back to the generator's calibrated defaults (success/danger/warning/info):\n `--secondary=#hex --tertiary=#hex --general=#hex --success=#hex --danger=#hex --warning=#hex --info=#hex`\n- `--out=<path>` (optional): where to write the file. Defaults to `./<name>.ts` in the current working directory — always pass an explicit `--out` pointing into the consumer app's own theme directory (e.g. `src/theme/sunset.ts`), don't rely on the default.\n\nIf `@wangs-ui/foundation` isn't already a dependency of the project you're working in, install it first (`pnpm add @wangs-ui/foundation` or the project's equivalent) — the CLI ships as its `bin`.\n\n**The written file is chmod'd read-only (0o444) and headed with an AUTO-GENERATED / DO NOT EDIT BY HAND comment.** If a color needs to change, re-run the command (it clears the read-only bit, rewrites, and re-locks it) — never hand-edit a shade in the output, and never `chmod` it writable to bypass this. Note the read-only bit is a local filesystem attribute only; it is not preserved by git across clones, so it is a deterrent for the person/agent working in this checkout right now, not a hard guarantee for every future contributor.\n\n## 2. Wire it into the app\n\nThe generated file exports a plain `Palette` object — pass it directly to `WangsUiProvider`'s `theme.palette` (or `theme.defaultPalette` for uncontrolled mode):\n\n```tsx\nimport { WangsUiProvider } from '@wangs-ui/react-core/api';\nimport preset from '@wangs-ui/react-presets/fixedasset'; // or whichever preset the app uses\nimport { sunsetPalette } from './theme/sunset';\n\nconst App = () => (\n <WangsUiProvider configOptions={{ preset }} theme={{ palette: sunsetPalette, mode: 'light' }}>\n <YourApp />\n </WangsUiProvider>\n);\n```\n\n`theme.palette` also accepts a built-in palette name (`'blue' | 'emerald' | 'crimson' | 'carbon' | 'gold'`) or a raw hex string — a generated `Palette` object is the right choice once you have brand-specific secondary/tertiary/status colors to preserve, not just a single key color.\n\n## 3. Regenerating / evolving an existing custom palette\n\nTo change a color, re-run the same command with a new hex for the family that changed — **always pass every family you want to keep**, not just the one changing, since each run is a full regeneration from the anchors you give it:\n\n```bash\nnpx wangs-ui-generate-palette --name=sunset --primary=#ff6b35 --tertiary=#2a9d8f --out=src/theme/sunset.ts\n```\n\n## 4. If you're extending Wangs UI itself (contributing a new official palette)\n\nThis is a different, rarer case than theming a consumer app — only relevant if you're working inside the `wangs-ui-react` monorepo itself and adding a 6th built-in palette alongside blue/emerald/crimson/carbon/gold:\n\n1. Run the generator with `--out` pointing at `packages/foundation/theme/tokens/palettes/<name>.ts`.\n2. Register it: add `'<name>'` to the `PaletteName` union and `paletteLoaders` map in `packages/foundation/theme/context/ThemeContext.tsx`, and re-export it from `packages/foundation/theme/tokens/palettes/index.ts`.\n3. Run the contrast regression suite before considering it done: `pnpm exec vitest run --config packages/foundation/vitest.config.ts` — it checks every semantic token pairing (including the new palette) against WCAG AA across both light and dark mode. See `packages/foundation/theme/tokens/COLOR_TOKEN_CONTRACT.md` for the full rule set this is checked against.\n";
7
+ var SKILL_default$8 = "---\nname: craft-theme\ndescription: Generate a brand-color theme (13-shade tonal palette + semantic tokens) for a Wangs UI app via the `wangs-ui-generate-palette` CLI — never hand-write hex shade ramps.\nmetadata:\n owner: wangs-ui\n---\n# Skill: Craft Theme\n\nUse this skill whenever the user asks to create, customize, or rebrand the color theme of an app built on `@wangs-ui/react-core` / `@wangs-ui/foundation` — \"make the app's theme orange\", \"use our brand color #ff6b35\", \"add a new palette called sunset\", etc.\n\n## The rule this skill exists to enforce\n\n**Never hand-write a 13-shade tonal ramp, and never hand-pick which shade pairs with which as a text/foreground color.** Wangs UI's own design system shipped with exactly that mistake for a while: hand-tuned palette files drifted from what the perceptual (OKLCH) generator would produce, which caused white text to render at 2.54:1 contrast on one brand's primary color — invisible-adjacent, and only caught by a dedicated audit. The generator and the contrast math it's paired with exist specifically so this class of bug can't happen again. Always reach for the CLI below instead of writing hex values yourself.\n\n## 1. Generate the palette\n\n```bash\npnpm exec wangs-ui-generate-palette --name=<id> --primary=<#hex> [options] --out=<path>\n```\n\n- `--name` (required): identifier for the palette, e.g. `sunset`. Used for the exported const name (`sunsetPalette`) and, unless `--out` is given, the output filename (`sunset.ts`).\n- `--primary` (required): the brand/key hex color, e.g. `#ff6b35`.\n- Optional per-family overrides — omit any of these to let the generator harmoniously auto-derive it from `--primary` (secondary: boosted-lightness variant of primary's hue; tertiary: +60° hue rotation; general: near-neutral variant of primary's hue) or fall back to the generator's calibrated defaults (success/danger/warning/info):\n `--secondary=#hex --tertiary=#hex --general=#hex --success=#hex --danger=#hex --warning=#hex --info=#hex`\n- `--out=<path>` (optional): where to write the file. Defaults to `./<name>.ts` in the current working directory — always pass an explicit `--out` pointing into the consumer app's own theme directory (e.g. `src/theme/sunset.ts`), don't rely on the default.\n\nIf `@wangs-ui/foundation` isn't already a dependency of the project you're working in, install it first (`pnpm add @wangs-ui/foundation` or the project's equivalent) — the CLI ships as its `bin`.\n\n**The written file is chmod'd read-only (0o444) and headed with an AUTO-GENERATED / DO NOT EDIT BY HAND comment.** If a color needs to change, re-run the command (it clears the read-only bit, rewrites, and re-locks it) — never hand-edit a shade in the output, and never `chmod` it writable to bypass this. Note the read-only bit is a local filesystem attribute only; it is not preserved by git across clones, so it is a deterrent for the person/agent working in this checkout right now, not a hard guarantee for every future contributor.\n\n## 2. Wire it into the app\n\nThe generated file exports a plain `Palette` object — pass it directly to `WangsUiProvider`'s `theme.palette` (or `theme.defaultPalette` for uncontrolled mode):\n\n```tsx\nimport { WangsUiProvider } from '@wangs-ui/react-core/api';\nimport preset from '@wangs-ui/react-presets/fixedasset'; // or whichever preset the app uses\nimport { sunsetPalette } from './theme/sunset';\n\nconst App = () => (\n <WangsUiProvider configOptions={{ preset }} theme={{ palette: sunsetPalette, mode: 'light' }}>\n <YourApp />\n </WangsUiProvider>\n);\n```\n\n`theme.palette` also accepts a built-in palette name (`'blue' | 'emerald' | 'crimson' | 'carbon' | 'gold'`) or a raw hex string — a generated `Palette` object is the right choice once you have brand-specific secondary/tertiary/status colors to preserve, not just a single key color.\n\n## 3. Regenerating / evolving an existing custom palette\n\nTo change a color, re-run the same command with a new hex for the family that changed — **always pass every family you want to keep**, not just the one changing, since each run is a full regeneration from the anchors you give it:\n\n```bash\npnpm exec wangs-ui-generate-palette --name=sunset --primary=#ff6b35 --tertiary=#2a9d8f --out=src/theme/sunset.ts\n```\n\n## 4. If you're extending Wangs UI itself (contributing a new official palette)\n\nThis is a different, rarer case than theming a consumer app — only relevant if you're working inside the `wangs-ui-react` monorepo itself and adding a 6th built-in palette alongside blue/emerald/crimson/carbon/gold:\n\n1. Run the generator with `--out` pointing at `packages/foundation/theme/tokens/palettes/<name>.ts`.\n2. Register it: add `'<name>'` to the `PaletteName` union and `paletteLoaders` map in `packages/foundation/theme/context/ThemeContext.tsx`, and re-export it from `packages/foundation/theme/tokens/palettes/index.ts`.\n3. Run the contrast regression suite before considering it done: `pnpm exec vitest run --config packages/foundation/vitest.config.ts` — it checks every semantic token pairing (including the new palette) against WCAG AA across both light and dark mode. See `packages/foundation/theme/tokens/COLOR_TOKEN_CONTRACT.md` for the full rule set this is checked against.\n";
8
8
  //#endregion
9
9
  //#region skills/wangs-ui/create-form/SKILL.md?raw
10
10
  var SKILL_default$7 = "---\nname: create-form\ndescription: Form architecture, validation workflows, strongly-typed forms (useForm, useDialogForm, useWatchField), initialValues/reset lifecycle, and MCP discovery protocol for building forms and input controls with @wangs-ui/react-core.\nmetadata:\n owner: wangs-ui\n---\n# Skill: Form Architecture & Validation Workflows\n\nUse this skill when building forms, data entry panels, modal forms, settings pages, or multipart forms in Wangs UI applications.\n\n---\n\n## 1. MCP Protocol & Component Rules (Mandatory Single Source of Truth)\n\nDo **NOT** hardcode or guess prop names, component options, preset variations, or Storybook patterns in this document. Always retrieve component definitions, active props, and live Storybook implementations directly via MCP:\n\n### Component & Form API Protocol:\n\n```json\nget_component_api({ \"component\": \"form\" })\nget_component_api({ \"component\": \"field\" })\nget_component_api({ \"component\": \"dialogform\" })\nget_component_api({ \"component\": \"input\" })\nget_component_api({ \"component\": \"numberinput\" })\nget_component_api({ \"component\": \"select\" })\nget_component_api({ \"component\": \"multiselect\" })\nget_component_api({ \"component\": \"datepicker\" })\nget_component_api({ \"component\": \"fileupload\" })\n```\n\n### Curated Form Documentation:\n\n```json\nget_documentation({ \"id\": \"form\" })\nget_documentation({ \"id\": \"dialogform\" })\nget_documentation({ \"id\": \"field\" })\n```\n\n### Live Storybook & Interactive Behavior Protocol:\n\n```json\nget_component_examples({ \"component\": \"form\", \"variant\": \"Default\" })\nget_component_examples({ \"component\": \"form\", \"variant\": \"AsyncInitialValues\" })\nget_component_examples({ \"component\": \"form\", \"variant\": \"ConditionalFields\" })\nget_component_examples({ \"component\": \"form\", \"variant\": \"CascadingOptions\" })\nget_component_examples({ \"component\": \"dialogform\", \"variant\": \"Default\" })\n```\n\n### Knowledge Graph & Symbol Usages:\n\n```json\nquery_graph({ \"query\": \"useForm\" })\nquery_graph({ \"query\": \"useDialogForm\" })\nquery_graph({ \"query\": \"useWatchField\" })\n```\n\n---\n\n## 2. Core Form Concepts & Lifecycle Mechanics\n\n### A. Strongly Typed Form Instance (`useForm<TForm>()`)\n\n`useForm<TForm>()` instantiates a `FormControl` natively bound to model type `TForm`.\n\n- `Field`: `name` is strictly typed to `Path<TForm>` dot-paths.\n- `useWatchField`: `name` is strictly typed to `Path<TForm>`.\n- `control`: Provides `setInitialValues`, `setValues`, `setFieldError`, `setErrors`, and `reset`.\n\n### B. Dynamic Initial Values & Baseline Reset (`setInitialValues` vs `setValues`)\n\n1. **Async Initial Values (`control.setInitialValues(values)`)**:\n - Accepts a `Partial<TForm>` JSON object (e.g. fetched from an API).\n - Establishes an **immutable baseline** for registered fields. Once set for a field path, subsequent calls to `setInitialValues` for that path are ignored.\n2. **Batch Value Updates (`control.setValues(values)`)**:\n - Accepts a `Partial<TForm>` JSON object to update current input values without altering the initial baseline.\n3. **Reset Behavior (`control.reset()`)**:\n - Restores all fields back to their registered initial baseline values (set via `setInitialValues` or field `initialValue`) and clears all field-level validation errors.\n\n### C. Primitive Component Integration Architecture\n\n`Field` serves as the form integration wrapper for primitive UI input components (`Input`, `Select`, `MultiSelect`, `DatePicker`, `NumberInput`, `FileUpload`, `Calendar`, etc.):\n\n- **Children Render Callback**: `Field` yields `{ fieldProps, fieldState }`.\n- **`fieldProps`**: Pass directly to primitive inputs (`<Input {...fieldProps} />`). Contains `name`, `value`, `ref`, `onChange`.\n- **`fieldState`**: Provides `invalid`, `error`, `isDirty`, `isPending`. Pass `invalid={fieldState.invalid}` to primitive components for accessibility and validation styling.\n- **āš ļø Mandatory Destructuring (Never `(field) => ...`)**: `<Field>`'s callback render prop must destructure as `{({ fieldProps, fieldState })}`. Passing a single parameter instead (`{(field) => ...}`) produces `undefined` `value`/`onChange` and permanently locks the input.\n- **Pass `fieldProps.onChange` directly**: `<Field>` is generic and `fieldProps.onChange` is already typed to the field's value type. Do not wrap it in unnecessary `e?.target?.value` extractions.\n\n---\n\n## 3. High-Level Form Architecture & Usage Patterns\n\n### Pattern 1: Page Forms (`useForm<T>()`)\n\n```tsx\nimport Button from '@wangs-ui/react-core/primitive/button';\nimport { useForm } from '@wangs-ui/react-core/primitive/form';\nimport Input from '@wangs-ui/react-core/primitive/input';\nimport { useI18n } from '@wangs-ui/react-i18n';\nimport { useEffect } from 'react';\n\ninterface UserProfile {\n name: string;\n email: string;\n}\n\nexport function UserProfilePage({ userId }: { userId: string }) {\n const { t } = useI18n();\n const { Form, Field, control } = useForm<UserProfile>();\n\n useEffect(() => {\n async function loadData() {\n const data = await fetchUserData(userId);\n // Establish immutable initial baseline from async response\n control.setInitialValues(data);\n }\n loadData();\n }, [userId, control]);\n\n return (\n <Form control={control} onSubmit={(values) => saveUserData(values)}>\n <Field required label={t('Full Name')} name=\"name\">\n {({ fieldProps, fieldState }) => (\n <Input {...fieldProps} invalid={fieldState.invalid} placeholder={t('Enter full name')} />\n )}\n </Field>\n\n <div className=\"flex gap-2\">\n <Button\n label={t('Reset')}\n type=\"button\"\n variant=\"outlined\"\n onClick={() => control.reset()}\n />\n <Button label={t('Save')} type=\"submit\" />\n </div>\n </Form>\n );\n}\n```\n\n### Pattern 2: Modal Forms (`useDialogForm<T>()`)\n\n```tsx\nimport Button from '@wangs-ui/react-core/primitive/button';\nimport { useDialogForm } from '@wangs-ui/react-core/primitive/dialogform';\nimport Input from '@wangs-ui/react-core/primitive/input';\nimport { useI18n } from '@wangs-ui/react-i18n';\nimport { useState } from 'react';\n\ninterface EditUserForm {\n name: string;\n}\n\nexport function EditUserModal() {\n const { t } = useI18n();\n const [open, setOpen] = useState(false);\n const { DialogForm, Field, control } = useDialogForm<EditUserForm>();\n\n return (\n <>\n <Button label={t('Edit')} onClick={() => setOpen(true)} />\n <DialogForm\n closeOnSubmit\n control={control}\n header={t('Edit User')}\n open={open}\n onOpenChange={setOpen}\n onSubmit={(values) => handleSave(values)}\n >\n <Field required label={t('Full Name')} name=\"name\">\n {({ fieldProps, fieldState }) => <Input {...fieldProps} invalid={fieldState.invalid} />}\n </Field>\n </DialogForm>\n </>\n );\n}\n```\n\n---\n\n## 4. Mandatory Implementation Guidelines\n\n1. **Query MCP First**: Never guess component props or story examples — inspect via MCP tools.\n2. **Granular Primitive Subpaths**: Import primitives via exact subpath modules (`@wangs-ui/react-core/primitive/form`, `@wangs-ui/react-core/primitive/dialogform`, `@wangs-ui/react-core/primitive/input`).\n3. **i18n Localization**: Wrap all user-visible labels, placeholders, and error strings in `t('...')` from `@wangs-ui/react-i18n`.\n4. **Server Error Mapping**: Map HTTP validation errors (e.g. 422 response) into the form using `control.setErrors(apiErrors)`.\n";
@@ -43,7 +43,7 @@ var no_raw_html_default = "---\ntrigger: model_decision\ndescription: \"Apply wh
43
43
  var no_redundant_wrappers_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing JSX layout for redundant wrapper containers.\"\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: No Redundant Layout & Container Wrappers\n\n> **Severity**: **ARCHITECTURAL CODE SMELL / LINT FAILURE**.\n\nComponents must not be wrapped in an unnecessary layout element. A `<div>` (or `Box`/`Stack`) wrapping a single component without active layout coordination is strictly prohibited — see `.agents/skills/universal-layout` and `.agents/rules/no-inline-component-styling.md` §2 for which primitive replaces a raw `<div>` when layout coordination is genuinely needed.\n\n---\n\n## 1. Prohibited Anti-Patterns\n\n1. **Single-Child Wrapper `<div>`**:\n Never wrap a single component (e.g. `<DataTable />`, `<Card />`, `<Tabs />`, `<Input />`, `<Button />`) inside an isolated `<div>` that has no layout siblings.\n2. **Intermediate Tab/Card Wrappers**:\n Do not insert pass-through `<div>` wrappers between `<Card>`/`<Tabs>` and feature content:\n ```tsx\n // āŒ Bad — Redundant intermediate div wrapping a single tab component\n function TabbedPage() {\n return (\n <Card>\n <Tabs ... />\n <div>\n {activeTab === 'general' ? <GeneralTab /> : <AdvancedTab />}\n </div>\n </Card>\n );\n }\n\n // āœ… Good — Render active tab directly as Card child\n function TabbedPage() {\n return (\n <Card>\n <Tabs ... />\n {activeTab === 'general' ? <GeneralTab /> : <AdvancedTab />}\n </Card>\n );\n }\n ```\n3. **Redundant Table Wrappers**:\n Never wrap `<DataTable />` in a `Box`/`div` solely to set width or margin. Wangs UI DataTable manages its own container scroll and dimensions out of the box.\n\n---\n\n## 2. When a Layout Wrapper Is Permitted\n\nA layout wrapper is **ONLY** warranted when coordinating **2 or more siblings** in a structural layout — and even then it's a `universal-layout` primitive, not a raw `<div>`:\n\n1. **Alignment bars**: header title + search + action buttons → `<HStack justify=\"between\">`.\n2. **Multi-column/card grids**: responsive multi-card or multi-field layouts → `<Grid columns={{ compact: 1, medium: 2, expanded: 3 }}>`.\n3. **Fragment Alternative**: when returning multiple adjacent elements without layout coordination, use React Fragment (`<>...</>`) — not an empty wrapper.\n";
44
44
  //#endregion
45
45
  //#region rules/wangs-ui/react19-checklist-and-reference.md?raw
46
- var react19_checklist_and_reference_default = "---\ntrigger: model_decision\ndescription: \"Apply when doing a final review pass on React 19 component or hook code for compiler-optimization compliance.\"\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Review Checklist & Quick Reference\n\n## Checking Compiler Optimization\n\n- **React DevTools** — an optimized component shows a \"Memo ✨\" badge next to its name in the component tree.\n- **ESLint / Oxlint** — compiler's recommended rules flag Rules-of-React violations at lint time.\n\n## Review Checklist\n\n- [ ] Compiler confirmed active (\"Memo ✨\" badge) before removing any _existing_ manual memoization\n- [ ] No new `useMemo`/`useCallback`/`React.memo` added without documented reason (confirmed bail-out, or external boundary)\n- [ ] No prop/state/context mutation anywhere in render\n- [ ] All hooks called unconditionally at top level, same order every render\n- [ ] Side effects live in `useEffect`/event handlers, never during render\n- [ ] Components are `PascalCase`; hooks are `camelCase` and prefixed `use`\n- [ ] `ref` accepted as normal prop instead of `forwardRef`\n- [ ] Action/optimistic-update state modeled as discriminated union, not optional fields\n- [ ] Mutually exclusive prop combinations modeled as discriminated union `Props` type\n- [ ] Any `\"use no memo\"` usage has a comment explaining why\n\n## Quick Reference\n\n| Situation | Do |\n| ----------------------------------------------------------- | ------------------------------------------------------------- |\n| Tempted to write `useMemo`/`useCallback` | Don't — write plain expression, let compiler decide |\n| Need a ref on a function component | Accept `ref` as a prop, skip `forwardRef` |\n| Form/async state with distinct outcomes | Discriminated union via `useActionState`, not optional fields |\n| Callback needs latest props/state without re-running effect | `useEffectEvent` |\n| A hook/library is known-incompatible with compiler | `\"use no memo\"` at top of that function, with comment |\n| Checking if optimization is happening | React DevTools \"Memo ✨\" badge + compiler lint rules |\n";
46
+ var react19_checklist_and_reference_default = "---\ntrigger: model_decision\ndescription: \"Apply when doing a final review pass on React 19 component or hook code for compiler-optimization compliance.\"\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Review Checklist & Quick Reference\n\n## Checking Compiler Optimization\n\n- **Build output** — compiled files import `react/compiler-runtime` and contain `Symbol.for(\"react.memo_cache_sentinel\")`; if absent, the compiler never ran.\n- **ESLint / Oxlint** — compiler's recommended rules flag Rules-of-React violations at lint time.\n\n## Review Checklist\n\n- [ ] Compiler is wired up and confirmed active — set it up if missing (see `react19-tooling-and-opt-out.md`); never treat a missing compiler as licence to hand-roll memoization\n- [ ] Compiler confirmed active (build output imports `react/compiler-runtime`) before removing any _existing_ manual memoization\n- [ ] No new `useMemo`/`useCallback`/`React.memo` added without documented reason (confirmed bail-out, or external boundary)\n- [ ] No prop/state/context mutation anywhere in render\n- [ ] All hooks called unconditionally at top level, same order every render\n- [ ] Side effects live in `useEffect`/event handlers, never during render\n- [ ] Components are `PascalCase`; hooks are `camelCase` and prefixed `use`\n- [ ] `ref` accepted as normal prop instead of `forwardRef`\n- [ ] Action/optimistic-update state modeled as discriminated union, not optional fields\n- [ ] Mutually exclusive prop combinations modeled as discriminated union `Props` type\n- [ ] Any `\"use no memo\"` usage has a comment explaining why\n\n## Quick Reference\n\n| Situation | Do |\n| ----------------------------------------------------------- | ------------------------------------------------------------- |\n| Tempted to write `useMemo`/`useCallback` | Don't — write plain expression, let compiler decide |\n| Need a ref on a function component | Accept `ref` as a prop, skip `forwardRef` |\n| Form/async state with distinct outcomes | Discriminated union via `useActionState`, not optional fields |\n| Callback needs latest props/state without re-running effect | `useEffectEvent` |\n| A hook/library is known-incompatible with compiler | `\"use no memo\"` at top of that function, with comment |\n| Checking if optimization is happening | Build output imports `react/compiler-runtime` (has `react.memo_cache_sentinel`) + compiler lint rules |\n";
47
47
  //#endregion
48
48
  //#region rules/wangs-ui/react19-compiler-render-patterns.md?raw
49
49
  var react19_compiler_render_patterns_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing render logic in a React 19 component.\"\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Compiler-Friendly Render Patterns\n\n- Creating new object/array/function literals inline in render (`style={{ color }}`, `onClick={() => ...}`) is fine — stop manually hoisting or `useMemo`-wrapping these preemptively; the compiler memoizes them if it determines it's worthwhile.\n- Avoid module-level mutable variables read or written during render — that state is invisible to the compiler and breaks idempotence.\n- Don't use `useRef` to store a value that should trigger a re-render when it changes — refs are an imperative escape hatch, not state, and the compiler treats them as such.\n- Keep components small and composable. The compiler optimizes per component/hook boundary, so a single 300-line component gives it far less to work with than several focused ones.\n";
@@ -52,7 +52,7 @@ var react19_compiler_render_patterns_default = "---\ntrigger: model_decision\nde
52
52
  var react19_naming_conventions_default = "---\ntrigger: model_decision\ndescription: \"Apply when naming a React 19 component, hook, or prop.\"\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Naming Conventions\n\nThe compiler identifies what to optimize by naming heuristics, same as the Rules of Hooks linter:\n\n| Kind | Convention | Notes |\n| ------------------------------------------------------------------------ | ------------------------------- | ----------------------------------------------------------------------------- |\n| Components | `PascalCase`, returns JSX | Compiler treats it as a component to optimize |\n| Custom hooks | `camelCase`, prefixed `use` | Required for both Rules-of-Hooks lint and compiler analysis |\n| Plain helper functions that return JSX-like values but aren't components | Avoid `PascalCase`/`use` naming | Prevents the compiler (and other devs) from mistaking it for a component/hook |\n";
53
53
  //#endregion
54
54
  //#region rules/wangs-ui/react19-no-manual-memoization.md?raw
55
- var react19_no_manual_memoization_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing a React 19 component or hook that uses or is tempted to use useMemo/useCallback/React.memo.\"\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Stop Hand-Rolling Memoization\n\nDon't reach for `useMemo`, `useCallback`, or `React.memo` by default — the compiler adds this automatically wherever it determines it helps.\n\n```tsx\n// āŒ Old habit — noisy, and a mismatched dependency array is a whole class of bugs\nconst filteredUsers = useMemo(() => users.filter((u) => u.isActive), [users]);\nconst handleClick = useCallback(() => onSelect(user.id), [onSelect, user.id]);\n\n// āœ… New default — just write the logic; the compiler memoizes what's worth memoizing\nconst filteredUsers = users.filter((u) => u.isActive);\nconst handleClick = () => onSelect(user.id);\n```\n\n## When manual memoization is still justified:\n\n- You've **confirmed a compiler bail-out** on a genuine hot path via profiling, and fixing the underlying Rules-of-React violation isn't possible right now.\n- A value must have **stable referential identity across a boundary the compiler can't see** — e.g. passed into a non-React library, a WebSocket subscription, or a third-party hook incompatible with the compiler (`react-hook-form`'s `useForm`, `@tanstack/react-table`'s `useReactTable` are known cases).\n- Keep any manual memoization it produces isolated and commented with _why_, so it doesn't silently rot into a bail-out later when the code around it changes.\n";
55
+ var react19_no_manual_memoization_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing a React 19 component or hook that uses or is tempted to use useMemo/useCallback/React.memo.\"\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Stop Hand-Rolling Memoization\n\n> **Prerequisite:** this rule holds only once the React Compiler is enabled. If the project isn't wired up yet, set it up first (see `react19-tooling-and-opt-out.md`) — the absence of the compiler is never a reason to hand-roll memoization.\n\nDon't reach for `useMemo`, `useCallback`, or `React.memo` — the compiler adds this automatically wherever it determines it helps. This is a requirement, not a stylistic preference: manual memoization is the exception and must be justified.\n\n```tsx\n// āŒ Old habit — noisy, and a mismatched dependency array is a whole class of bugs\nconst filteredUsers = useMemo(() => users.filter((u) => u.isActive), [users]);\nconst handleClick = useCallback(() => onSelect(user.id), [onSelect, user.id]);\n\n// āœ… New default — just write the logic; the compiler memoizes what's worth memoizing\nconst filteredUsers = users.filter((u) => u.isActive);\nconst handleClick = () => onSelect(user.id);\n```\n\n## When manual memoization is still justified:\n\n- You've **confirmed a compiler bail-out** on a genuine hot path via profiling, and fixing the underlying Rules-of-React violation isn't possible right now.\n- A value must have **stable referential identity across a boundary the compiler can't see** — e.g. passed into a non-React library, a WebSocket subscription, or a third-party hook incompatible with the compiler (`react-hook-form`'s `useForm`, `@tanstack/react-table`'s `useReactTable` are known cases).\n- Keep any manual memoization it produces isolated and commented with _why_, so it doesn't silently rot into a bail-out later when the code around it changes.\n";
56
56
  //#endregion
57
57
  //#region rules/wangs-ui/react19-primitives-typing.md?raw
58
58
  var react19_primitives_typing_default = "---\ntrigger: model_decision\ndescription: \"Apply when typing React 19 primitives — ref props, useActionState, useOptimistic, use(), useEffectEvent.\"\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Typing React 19 Primitives\n\n## `ref` as a Normal Prop\n\n`forwardRef` is no longer required for most cases; function components accept `ref` directly.\n\n```tsx\ntype InputProps = {\n ref?: React.Ref<HTMLInputElement>;\n placeholder?: string;\n};\n\nfunction TextInput({ ref, placeholder }: InputProps) {\n return <input ref={ref} placeholder={placeholder} />;\n}\n```\n\n## Actions with `useActionState`\n\nType the state and payload as generics; model the result as a discriminated union rather than optional fields.\n\n```tsx\ntype FormState = { status: 'idle' } | { status: 'error'; message: string } | { status: 'success' };\n\nconst [state, formAction, isPending] = useActionState<FormState, FormData>(\n async (_previous, formData) => {\n const email = formData.get('email');\n if (typeof email !== 'string' || !email.includes('@')) {\n return { status: 'error', message: 'Invalid email' };\n }\n await submit(email);\n return { status: 'success' };\n },\n { status: 'idle' },\n);\n```\n\n## Optimistic Updates with `useOptimistic`\n\nType both the state and the update shape.\n\n```tsx\nconst [optimisticTodos, addOptimisticTodo] = useOptimistic<Todo[], Todo>(\n todos,\n (state, newTodo) => [...state, newTodo],\n);\n```\n\n## Reading Promises or Context with `use()`\n\nType the resolved value, not the promise wrapper; `use()` is not a hook and may be called conditionally.\n\n```tsx\nfunction Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {\n const comments = use(commentsPromise); // suspends until resolved\n return (\n <ul>\n {comments.map((c) => (\n <li key={c.id}>{c.text}</li>\n ))}\n </ul>\n );\n}\n```\n\n## Stable Event Callbacks with `useEffectEvent` (React 19.2+)\n\nSeparates \"event\" logic from \"reactive\" effect logic so the callback always sees latest props/state without being listed as an effect dependency.\n\n```tsx\nconst onVisit = useEffectEvent((url: string) => {\n logVisit(url, theme); // always fresh `theme`, never re-triggers the effect\n});\n\nuseEffect(() => {\n onVisit(url);\n}, [url]); // `theme` intentionally omitted — onVisit is stable\n```\n";
@@ -64,7 +64,7 @@ var react19_props_typing_default = "---\ntrigger: model_decision\ndescription: \
64
64
  var react19_purity_and_immutability_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing a React 19 component or hook body for purity and immutability.\"\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Purity and Immutability (Load-Bearing)\n\nThe compiler assumes your components and hooks are pure. Violating these rules causes the compiler to silently skip optimizing that component:\n\n- **Idempotent renders** — given the same props/state/context, a component must return the same output. No random values, no `Date.now()`, no side effects during render.\n- **Immutability** — never mutate props, state, or context directly. Always create new objects/arrays for changes.\n- **Side effects only in effects or event handlers** — never during render.\n- **Hooks called unconditionally, top-level, same order every render** — no hooks inside conditionals, loops, or nested functions.\n\n```tsx\n// āŒ Mutates a prop — breaks purity and the compiler can't safely memoize this\nfunction TodoList({ todos }: { todos: Todo[] }) {\n todos.sort((a, b) => a.priority - b.priority); // mutates caller's array\n return (\n <ul>\n {todos.map((t) => (\n <li key={t.id}>{t.title}</li>\n ))}\n </ul>\n );\n}\n\n// āœ… Creates a new array — pure, compiler-safe\nfunction TodoList({ todos }: { todos: Todo[] }) {\n const sorted = [...todos].sort((a, b) => a.priority - b.priority);\n return (\n <ul>\n {sorted.map((t) => (\n <li key={t.id}>{t.title}</li>\n ))}\n </ul>\n );\n}\n```\n";
65
65
  //#endregion
66
66
  //#region rules/wangs-ui/react19-tooling-and-opt-out.md?raw
67
- var react19_tooling_and_opt_out_default = "---\ntrigger: model_decision\ndescription: 'Apply when configuring React Compiler tooling or opting a component out via \"use no memo\".'\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Tooling Setup & Compiler Opt-Out\n\n## Tooling Setup\n\n**The compiler is opt-in — no default setup enables it automatically.** Plain `@vitejs/plugin-react` (`react()`), plain Next.js, plain Babel/webpack config, etc. do **not** run the compiler on their own. Verify it's actually wired up before assuming manual memoization can be dropped.\n\n```bash\n# Compiler (build-time transform)\nnpm install --save-dev --save-exact babel-plugin-react-compiler@latest\n```\n\n### Lint rules — oxlint\n\nOxlint ships a **native, Rust-based** `react/react-compiler` rule:\n\n```json\n// .oxlintrc.json\n{\n \"plugins\": [\"react\"],\n \"rules\": {\n \"react/react-compiler\": \"error\"\n }\n}\n```\n\nThis single rule reports:\n\n- **Rules-of-React violations** (conditional hooks, reading a ref during render, mutating props) — must-fix bugs.\n- **Compiler bail-outs** — places compiler declined to optimize without rule violation.\n\n### Wiring into Vite 8\n\n```js\n// vite.config.js\nimport { defineConfig } from 'vite';\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react';\nimport babel from '@rolldown/plugin-babel';\n\nexport default defineConfig({\n plugins: [\n babel({ presets: [reactCompilerPreset()] }), // must run before react()\n react(),\n ],\n});\n```\n\n```bash\nnpm install --save-dev @rolldown/plugin-babel @babel/core babel-plugin-react-compiler\n```\n\n## Incompatibility Escape Hatch (`\"use no memo\"`)\n\nIf a specific function is genuinely incompatible with compiler (e.g. calls `useForm` from `react-hook-form`), opt out with `\"use no memo\"` directive as **first line of function body** — leave a comment explaining why:\n\n```tsx\nfunction LegacyForm() {\n 'use no memo';\n const form = useForm(); // incompatible with the compiler today\n // ...\n}\n```\n";
67
+ var react19_tooling_and_opt_out_default = "---\ntrigger: model_decision\ndescription: 'Apply when configuring React Compiler tooling or opting a component out via \"use no memo\".'\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Tooling Setup & Compiler Opt-Out\n\n## Compiler Setup Is Mandatory\n\n**The compiler is required, not opt-in.** Every Wangs UI project must run it. The entire \"write plain code, no manual memoization\" rule set only holds once the compiler is actually active, so wiring it up is a prerequisite — not an optional enhancement.\n\nPlain `@vitejs/plugin-react` (`react()`), plain Next.js, plain Babel/webpack config, etc. do **not** run the compiler on their own. **Verify it is active before assuming manual memoization can be dropped — and if it isn't wired up, set it up first** instead of falling back to hand-rolled `useMemo`/`useCallback`/`React.memo`.\n\n### Verify it's active\n\n- The build passes the compiler preset through Babel, and `babel-plugin-react-compiler` is installed (e.g. `@vitejs/plugin-react`'s `reactCompilerPreset()` via `@rolldown/plugin-babel`).\n- The build output actually contains compiler artifacts — `import { c as _c } from \"react/compiler-runtime\"` and `Symbol.for(\"react.memo_cache_sentinel\")` in `dist`. Config being present is **not** proof it ran; the emitted output is.\n- The `react/react-compiler` lint rule is enabled.\n\n### Setup (when missing)\n\n```bash\n# Compiler (build-time transform)\npnpm add -D --save-exact babel-plugin-react-compiler@latest\n```\n\n### Lint rules — oxlint\n\nOxlint ships a **native, Rust-based** `react/react-compiler` rule:\n\n```json\n// .oxlintrc.json\n{\n \"plugins\": [\"react\"],\n \"rules\": {\n \"react/react-compiler\": \"error\"\n }\n}\n```\n\nThis single rule reports:\n\n- **Rules-of-React violations** (conditional hooks, reading a ref during render, mutating props) — must-fix bugs.\n- **Compiler bail-outs** — places compiler declined to optimize without rule violation.\n\n### Wiring into Vite 8\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite';\nimport react, { reactCompilerPreset } from '@vitejs/plugin-react';\nimport babel from '@rolldown/plugin-babel';\n\nexport default defineConfig({\n plugins: [\n babel({ presets: [reactCompilerPreset()] }), // must run before react()\n react(),\n ],\n});\n```\n\n```bash\npnpm add -D @rolldown/plugin-babel @babel/core babel-plugin-react-compiler\n```\n\n## Incompatibility Escape Hatch (`\"use no memo\"`)\n\nIf a specific function is genuinely incompatible with compiler (e.g. calls `useForm` from `react-hook-form`), opt out with `\"use no memo\"` directive as **first line of function body** — leave a comment explaining why:\n\n```tsx\nfunction LegacyForm() {\n 'use no memo';\n const form = useForm(); // incompatible with the compiler today\n // ...\n}\n```\n";
68
68
  //#endregion
69
69
  //#region rules/wangs-ui/theme-variable-override-breaks-engine.md?raw
70
70
  var theme_variable_override_breaks_engine_default = "---\ntrigger: model_decision\ndescription: \"Apply when tempted to override a Wangs UI theme CSS variable directly.\"\nowner: wangs-ui\nmetadata:\n owner: wangs-ui\n---\n# Wangs UI Fact: Theme Variables Are Engine-Internal, Never Override Directly\n\nManually defining or overriding `:root`, `[data-mode='dark']`, or `.dark` custom\nproperties owned by `@wangs-ui/foundation/theme` (`--color-*`, `--z-index-*`,\n`--spacing-*`, `--size-*`, `--shadow-*`, `--radius-*`) breaks the theme token\nengine: it destroys automatic light/dark mode transitions, corrupts sub-tree\ndensity overrides made via `<ThemeProvider>`, and causes cross-module visual\nbugs that don't reproduce consistently. This is true for any project consuming\n`@wangs-ui/foundation`, not specific to one app's setup.\n\nThe supported customization surface is `WangsUiProvider.configOptions.preset`\n— discover it via the `design-system`/`wangs-ui-components` skills' MCP calls,\nnever by reading or guessing the CSS variable names.\n";
@@ -415,7 +415,7 @@ function removeRule(ruleRelativePath, baseDir = process.cwd()) {
415
415
  //#endregion
416
416
  //#region src/commands/list.ts
417
417
  function listSkills(baseDir = process.cwd()) {
418
- intro(`\x1b[1m\x1b[36mšŸ“¦ Wangs UI Skills & Rules Registry\x1b[0m (v1.3.0-alpha.17)`);
418
+ intro(`\x1b[1m\x1b[36mšŸ“¦ Wangs UI Skills & Rules Registry\x1b[0m (v1.3.0-alpha.12)`);
419
419
  const allSkills = loadAllSkills();
420
420
  const allRules = loadAllRules();
421
421
  const skillDirs = getAgentSkillDirs(baseDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wangs-ui/skills",
3
- "version": "1.3.0-alpha.18",
3
+ "version": "1.3.0-alpha.20",
4
4
  "description": "CLI to install, update, and manage modular AI agent skills for Wangs UI React applications",
5
5
  "keywords": [
6
6
  "agents",
@@ -9,12 +9,13 @@ metadata:
9
9
 
10
10
  ## Checking Compiler Optimization
11
11
 
12
- - **React DevTools** — an optimized component shows a "Memo ✨" badge next to its name in the component tree.
12
+ - **Build output** — compiled files import `react/compiler-runtime` and contain `Symbol.for("react.memo_cache_sentinel")`; if absent, the compiler never ran.
13
13
  - **ESLint / Oxlint** — compiler's recommended rules flag Rules-of-React violations at lint time.
14
14
 
15
15
  ## Review Checklist
16
16
 
17
- - [ ] Compiler confirmed active ("Memo ✨" badge) before removing any _existing_ manual memoization
17
+ - [ ] Compiler is wired up and confirmed active — set it up if missing (see `react19-tooling-and-opt-out.md`); never treat a missing compiler as licence to hand-roll memoization
18
+ - [ ] Compiler confirmed active (build output imports `react/compiler-runtime`) before removing any _existing_ manual memoization
18
19
  - [ ] No new `useMemo`/`useCallback`/`React.memo` added without documented reason (confirmed bail-out, or external boundary)
19
20
  - [ ] No prop/state/context mutation anywhere in render
20
21
  - [ ] All hooks called unconditionally at top level, same order every render
@@ -34,4 +35,4 @@ metadata:
34
35
  | Form/async state with distinct outcomes | Discriminated union via `useActionState`, not optional fields |
35
36
  | Callback needs latest props/state without re-running effect | `useEffectEvent` |
36
37
  | A hook/library is known-incompatible with compiler | `"use no memo"` at top of that function, with comment |
37
- | Checking if optimization is happening | React DevTools "Memo ✨" badge + compiler lint rules |
38
+ | Checking if optimization is happening | Build output imports `react/compiler-runtime` (has `react.memo_cache_sentinel`) + compiler lint rules |
@@ -7,7 +7,9 @@ metadata:
7
7
  ---
8
8
  # Rule: React 19 — Stop Hand-Rolling Memoization
9
9
 
10
- Don't reach for `useMemo`, `useCallback`, or `React.memo` by default — the compiler adds this automatically wherever it determines it helps.
10
+ > **Prerequisite:** this rule holds only once the React Compiler is enabled. If the project isn't wired up yet, set it up first (see `react19-tooling-and-opt-out.md`) — the absence of the compiler is never a reason to hand-roll memoization.
11
+
12
+ Don't reach for `useMemo`, `useCallback`, or `React.memo` — the compiler adds this automatically wherever it determines it helps. This is a requirement, not a stylistic preference: manual memoization is the exception and must be justified.
11
13
 
12
14
  ```tsx
13
15
  // āŒ Old habit — noisy, and a mismatched dependency array is a whole class of bugs
@@ -7,13 +7,23 @@ metadata:
7
7
  ---
8
8
  # Rule: React 19 — Tooling Setup & Compiler Opt-Out
9
9
 
10
- ## Tooling Setup
10
+ ## Compiler Setup Is Mandatory
11
11
 
12
- **The compiler is opt-in — no default setup enables it automatically.** Plain `@vitejs/plugin-react` (`react()`), plain Next.js, plain Babel/webpack config, etc. do **not** run the compiler on their own. Verify it's actually wired up before assuming manual memoization can be dropped.
12
+ **The compiler is required, not opt-in.** Every Wangs UI project must run it. The entire "write plain code, no manual memoization" rule set only holds once the compiler is actually active, so wiring it up is a prerequisite — not an optional enhancement.
13
+
14
+ Plain `@vitejs/plugin-react` (`react()`), plain Next.js, plain Babel/webpack config, etc. do **not** run the compiler on their own. **Verify it is active before assuming manual memoization can be dropped — and if it isn't wired up, set it up first** instead of falling back to hand-rolled `useMemo`/`useCallback`/`React.memo`.
15
+
16
+ ### Verify it's active
17
+
18
+ - The build passes the compiler preset through Babel, and `babel-plugin-react-compiler` is installed (e.g. `@vitejs/plugin-react`'s `reactCompilerPreset()` via `@rolldown/plugin-babel`).
19
+ - The build output actually contains compiler artifacts — `import { c as _c } from "react/compiler-runtime"` and `Symbol.for("react.memo_cache_sentinel")` in `dist`. Config being present is **not** proof it ran; the emitted output is.
20
+ - The `react/react-compiler` lint rule is enabled.
21
+
22
+ ### Setup (when missing)
13
23
 
14
24
  ```bash
15
25
  # Compiler (build-time transform)
16
- npm install --save-dev --save-exact babel-plugin-react-compiler@latest
26
+ pnpm add -D --save-exact babel-plugin-react-compiler@latest
17
27
  ```
18
28
 
19
29
  ### Lint rules — oxlint
@@ -37,8 +47,8 @@ This single rule reports:
37
47
 
38
48
  ### Wiring into Vite 8
39
49
 
40
- ```js
41
- // vite.config.js
50
+ ```ts
51
+ // vite.config.ts
42
52
  import { defineConfig } from 'vite';
43
53
  import react, { reactCompilerPreset } from '@vitejs/plugin-react';
44
54
  import babel from '@rolldown/plugin-babel';
@@ -52,7 +62,7 @@ export default defineConfig({
52
62
  ```
53
63
 
54
64
  ```bash
55
- npm install --save-dev @rolldown/plugin-babel @babel/core babel-plugin-react-compiler
65
+ pnpm add -D @rolldown/plugin-babel @babel/core babel-plugin-react-compiler
56
66
  ```
57
67
 
58
68
  ## Incompatibility Escape Hatch (`"use no memo"`)
@@ -15,7 +15,7 @@ Use this skill whenever the user asks to create, customize, or rebrand the color
15
15
  ## 1. Generate the palette
16
16
 
17
17
  ```bash
18
- npx wangs-ui-generate-palette --name=<id> --primary=<#hex> [options] --out=<path>
18
+ pnpm exec wangs-ui-generate-palette --name=<id> --primary=<#hex> [options] --out=<path>
19
19
  ```
20
20
 
21
21
  - `--name` (required): identifier for the palette, e.g. `sunset`. Used for the exported const name (`sunsetPalette`) and, unless `--out` is given, the output filename (`sunset.ts`).
@@ -51,7 +51,7 @@ const App = () => (
51
51
  To change a color, re-run the same command with a new hex for the family that changed — **always pass every family you want to keep**, not just the one changing, since each run is a full regeneration from the anchors you give it:
52
52
 
53
53
  ```bash
54
- npx wangs-ui-generate-palette --name=sunset --primary=#ff6b35 --tertiary=#2a9d8f --out=src/theme/sunset.ts
54
+ pnpm exec wangs-ui-generate-palette --name=sunset --primary=#ff6b35 --tertiary=#2a9d8f --out=src/theme/sunset.ts
55
55
  ```
56
56
 
57
57
  ## 4. If you're extending Wangs UI itself (contributing a new official palette)