@wangs-ui/skills 1.3.0-alpha.20 → 1.3.0-alpha.21
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 +2 -2
- package/dist/index.js +1 -1
- package/dist/rules/wangs-ui/default-props-precedence.md +0 -1
- package/dist/rules/wangs-ui/forms-destructuring-lock.md +0 -1
- package/dist/rules/wangs-ui/no-raw-html.md +0 -1
- package/dist/rules/wangs-ui/no-redundant-wrappers.md +0 -1
- package/dist/rules/wangs-ui/react19-checklist-and-reference.md +0 -1
- package/dist/rules/wangs-ui/react19-compiler-render-patterns.md +0 -1
- package/dist/rules/wangs-ui/react19-naming-conventions.md +0 -1
- package/dist/rules/wangs-ui/react19-no-manual-memoization.md +0 -1
- package/dist/rules/wangs-ui/react19-primitives-typing.md +0 -1
- package/dist/rules/wangs-ui/react19-props-typing.md +0 -1
- package/dist/rules/wangs-ui/react19-purity-and-immutability.md +0 -1
- package/dist/rules/wangs-ui/react19-tooling-and-opt-out.md +0 -1
- package/dist/rules/wangs-ui/theme-variable-override-breaks-engine.md +0 -1
- package/dist/rules/wangs-ui/typescript-assertions-last-resort.md +0 -1
- package/dist/rules/wangs-ui/typescript-checklist-and-reference.md +0 -1
- package/dist/rules/wangs-ui/typescript-discriminated-unions.md +0 -1
- package/dist/rules/wangs-ui/typescript-explicit-return-types.md +0 -1
- package/dist/rules/wangs-ui/typescript-interface-vs-type.md +0 -1
- package/dist/rules/wangs-ui/typescript-literal-unions-vs-enums.md +0 -1
- package/dist/rules/wangs-ui/typescript-naming-conventions.md +0 -1
- package/dist/rules/wangs-ui/typescript-narrowing-over-casting.md +0 -1
- package/dist/rules/wangs-ui/typescript-readonly-by-default.md +0 -1
- package/dist/rules/wangs-ui/typescript-tsconfig-strictness.md +0 -1
- package/dist/rules/wangs-ui/typescript-zero-any.md +0 -1
- package/dist/{src-C0l8elLu.js → src-BFwMaU8I.js} +25 -25
- package/package.json +1 -1
- package/rules/wangs-ui/default-props-precedence.md +0 -1
- package/rules/wangs-ui/forms-destructuring-lock.md +0 -1
- package/rules/wangs-ui/no-raw-html.md +0 -1
- package/rules/wangs-ui/no-redundant-wrappers.md +0 -1
- package/rules/wangs-ui/react19-checklist-and-reference.md +0 -1
- package/rules/wangs-ui/react19-compiler-render-patterns.md +0 -1
- package/rules/wangs-ui/react19-naming-conventions.md +0 -1
- package/rules/wangs-ui/react19-no-manual-memoization.md +0 -1
- package/rules/wangs-ui/react19-primitives-typing.md +0 -1
- package/rules/wangs-ui/react19-props-typing.md +0 -1
- package/rules/wangs-ui/react19-purity-and-immutability.md +0 -1
- package/rules/wangs-ui/react19-tooling-and-opt-out.md +0 -1
- package/rules/wangs-ui/theme-variable-override-breaks-engine.md +0 -1
- package/rules/wangs-ui/typescript-assertions-last-resort.md +0 -1
- package/rules/wangs-ui/typescript-checklist-and-reference.md +0 -1
- package/rules/wangs-ui/typescript-discriminated-unions.md +0 -1
- package/rules/wangs-ui/typescript-explicit-return-types.md +0 -1
- package/rules/wangs-ui/typescript-interface-vs-type.md +0 -1
- package/rules/wangs-ui/typescript-literal-unions-vs-enums.md +0 -1
- package/rules/wangs-ui/typescript-naming-conventions.md +0 -1
- package/rules/wangs-ui/typescript-narrowing-over-casting.md +0 -1
- package/rules/wangs-ui/typescript-readonly-by-default.md +0 -1
- package/rules/wangs-ui/typescript-tsconfig-strictness.md +0 -1
- package/rules/wangs-ui/typescript-zero-any.md +0 -1
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-
|
|
2
|
+
import { i as listSkills, n as updateSkills, r as addSkills, t as removeSkills } from "./src-BFwMaU8I.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.
|
|
6
|
+
var VERSION = "1.3.0-alpha.20";
|
|
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-
|
|
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-BFwMaU8I.js";
|
|
2
2
|
export { addSkills, getAgentRuleDirs, getAgentSkillDirs, getInstalledRules, getInstalledSkills, getRule, getSkill, installRule, installSkill, isRuleInstalled, isSkillInstalled, listSkills, loadAllRules, loadAllSkills, removeRule, removeSkill, removeSkills, updateSkills };
|
|
@@ -31,76 +31,76 @@ var SKILL_default$1 = "---\nname: universal-layout\ndescription: Universal layou
|
|
|
31
31
|
var SKILL_default = "---\nname: wangs-ui-components\ndescription: MCP Discovery Protocol, modular subpath import map, and primitive substitution map for Wangs UI applications.\nmetadata:\n owner: wangs-ui\n---\n\n# Skill: Wangs UI Component Fundamentals & MCP Protocol\n\nUse this skill to navigate the Wangs UI ecosystem. **MCP is the single source of truth** for all component APIs, props, variants, slots, and examples. Do not hardcode or assume props — always query MCP dynamically.\n\n---\n\n## 1. The MCP Discovery Protocol (Single Source of Truth)\n\nDo **NOT** guess component props, Pass-Through (`pt`) slots, or event names. Always query the MCP server dynamically:\n\n```mermaid\ngraph TD\n A[Identify Component / Primitive Needed] --> B[list_catalog to confirm name/id]\n B --> C[get_component_api for typed contract]\n B --> D[get_documentation for curated guidance]\n C --> E{Need live story / variant code?}\n D --> E\n E -->|Yes| F[get_component_examples]\n E -->|No| G[Implement with Subpath Import Map]\n F --> G\n```\n\n### Discovery Steps:\n\n1. **Discover Catalog Entries**:\n ```json\n list_catalog({ \"query\": \"button\" })\n list_catalog({ \"category\": \"component\" })\n ```\n2. **Inspect Typed Contracts & Props** (`component` parameter):\n ```json\n get_component_api({ \"component\": \"button\" })\n get_component_api({ \"component\": \"datatable\" })\n get_component_api({ \"component\": \"field\" })\n ```\n3. **Read Curated Narrative Documentation** (`id` parameter):\n ```json\n get_documentation({ \"id\": \"button\" })\n get_documentation({ \"id\": \"foundation\" })\n get_documentation({ \"id\": \"form\" })\n ```\n4. **Fetch Live Storybook TSX Examples**:\n ```json\n get_component_examples({ \"component\": \"button\", \"variant\": \"Sizes\" })\n get_component_examples({ \"component\": \"datatable\", \"variant\": \"Basic\" })\n ```\n5. **Explore Knowledge Graph Relationships**:\n ```json\n query_graph({ \"query\": \"DataTable\" })\n query_graph({ \"query\": \"useForm\" })\n ```\n\n---\n\n## 2. Modular Subpath Import Map\n\nAlways import via specific subpath modules to guarantee tree-shaking and avoid bundling entire packages:\n\n| Ecosystem Layer | Subpath Pattern | Example Imports |\n| :--- | :--- | :--- |\n| **Core Primitives** | `@wangs-ui/react-core/primitive/<name>` | `Button`, `Input`, `Select`, `DataTable`, `Dialog`, `Modal`, `Field`, `Form`, `Card`, `Badge` |\n| **Layout Primitives** | `@wangs-ui/foundation/layout` | `Stack`, `HStack`, `VStack`, `Flex`, `Grid`, `Box`, `Container`, `Section`, `ScrollArea` |\n| **Typography Primitives** | `@wangs-ui/foundation/theme` | `Text`, `Code`, `Kbd`, `Link`, `Blockquote`, `List`, `Mark` |\n| **Blocks & Shells** | `@wangs-ui/react-core/blocks/<name>` | `AppLayout`, `Sidebar`, `TableToolbar` |\n| **Form Engine** | `@wangs-ui/form/core`, `@wangs-ui/form/react` | Headless form state, hooks, validation adapters |\n| **Universal Navigation** | `@wangs-ui/react-navigation/web`, `@wangs-ui/react-navigation/native` | `buildGraph`, `paramRoute`, platform router bridges |\n| **Theme & Providers** | `@wangs-ui/react-core/api`, `@wangs-ui/foundation/theme` | `WangsUiProvider`, `ThemeProvider`, `useTheme` |\n| **Vector Icons** | `@wangs-ui/react-icons` | `SearchLine`, `AddLine`, `DeleteBin6Line`, `CheckLine` |\n| **i18n & Localization** | `@wangs-ui/react-i18n` | `useI18n`, `t`, `WangsUiI18nProvider` |\n\n---\n\n## 3. Primitive Substitution Map\n\nZero raw HTML elements. Never write raw HTML tags when a Wangs UI primitive exists:\n\n| Forbidden Raw HTML | Mandatory Wangs UI Primitive | Import Path | MCP Catalog / Doc ID |\n| :--- | :--- | :--- | :--- |\n| `<button>` | `Button` | `@wangs-ui/react-core/primitive/button` | `button` |\n| `<input type=\"text\">` | `Input` | `@wangs-ui/react-core/primitive/input` | `input` |\n| `<input type=\"number\">` | `NumberInput` | `@wangs-ui/react-core/primitive/numberinput` | `numberinput` |\n| `<input type=\"checkbox\">` | `Checkbox` | `@wangs-ui/react-core/primitive/checkbox` | `checkbox` |\n| `<select>` | `Select` / `MultiSelect` | `@wangs-ui/react-core/primitive/select` | `select`, `multiselect` |\n| `<textarea>` | `Textarea` | `@wangs-ui/react-core/primitive/textarea` | `textarea` |\n| `<form>` | `Form` / `DialogForm` | `@wangs-ui/react-core/primitive/form` | `form`, `dialogform` |\n| `<dialog>` / alert modal | `Modal` / `Dialog` | `@wangs-ui/react-core/primitive/modal` | `modal`, `dialog` |\n| `<table>` | `DataTable` | `@wangs-ui/react-core/primitive/datatable` | `datatable` |\n| `<div>` (layout / flex / grid) | `Stack`, `HStack`, `Flex`, `Grid`, `Box` | `@wangs-ui/foundation/layout` | `universal-layout` |\n| `<div>` (surface card) | `Card` | `@wangs-ui/react-core/primitive/card` | `card` |\n| Pill / status chip | `Badge` | `@wangs-ui/react-core/primitive/badge` | `badge` |\n| `<h1>` - `<h6>` | `<Text variant=\"...\">` | `@wangs-ui/foundation/theme` | `foundation` |\n| `<p>`, `<span>` (body text) | `<Text variant=\"...\">` | `@wangs-ui/foundation/theme` | `foundation` |\n| `<a>` | `Link` | `@wangs-ui/foundation/theme` | `foundation` |\n| `<code>` | `Code` | `@wangs-ui/foundation/theme` | `foundation` |\n| `<kbd>` | `Kbd` | `@wangs-ui/foundation/theme` | `foundation` |\n| `<blockquote>` | `Blockquote` | `@wangs-ui/foundation/theme` | `foundation` |\n| `<ul>`, `<ol>` | `List` | `@wangs-ui/foundation/theme` | `foundation` |\n";
|
|
32
32
|
//#endregion
|
|
33
33
|
//#region rules/wangs-ui/default-props-precedence.md?raw
|
|
34
|
-
var default_props_precedence_default = "---\ntrigger: model_decision\ndescription: \"Apply when deciding whether a prop belongs in global defaultProps config or per-instance JSX.\"\
|
|
34
|
+
var default_props_precedence_default = "---\ntrigger: model_decision\ndescription: \"Apply when deciding whether a prop belongs in global defaultProps config or per-instance JSX.\"\nmetadata:\n owner: wangs-ui\n---\n# Wangs UI Fact: `configOptions.defaultProps` Precedence\n\n`WangsUiProvider.configOptions.defaultProps` lets any project set global\ndefault prop values per component. Per-instance JSX props always win over\nthat global default — the merge order is global-default-then-instance, never\nthe reverse. This is a `WangsUiProvider` mechanism, true in any project that\nuses it, independent of what values a given project actually configures.\n";
|
|
35
35
|
//#endregion
|
|
36
36
|
//#region rules/wangs-ui/forms-destructuring-lock.md?raw
|
|
37
|
-
var forms_destructuring_lock_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing a <Field> render-prop callback in a Wangs UI Form/DialogForm.\"\
|
|
37
|
+
var forms_destructuring_lock_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing a <Field> render-prop callback in a Wangs UI Form/DialogForm.\"\nmetadata:\n owner: wangs-ui\n---\n# Wangs UI Fact: `<Field>` Render-Prop Must Destructure `{ fieldProps, fieldState }`\n\n`<Field>`'s callback render prop must destructure as `{({ fieldProps, fieldState })}`.\nPassing a single parameter instead (`{(field) => ...}`) produces `undefined`\n`value`/`onChange` and permanently locks the input — the field never becomes\ninteractive, even on initial render. This is a real `@wangs-ui/form` quirk,\nnot a project convention; it reproduces the same way in any consumer.\n\n`<Field>` is generic — `fieldProps.onChange` is already typed to the field's\nactual value type. Pass it straight through (`fieldProps.onChange`); don't\nwrap it in a defensive `typeof`/`e?.target?.value` extraction, that's fighting\na type the field already guarantees.\n";
|
|
38
38
|
//#endregion
|
|
39
39
|
//#region rules/wangs-ui/no-raw-html.md?raw
|
|
40
|
-
var no_raw_html_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing JSX markup — enforce Wangs UI primitives over raw HTML elements.\"\
|
|
40
|
+
var no_raw_html_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing JSX markup — enforce Wangs UI primitives over raw HTML elements.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: No Raw HTML Controls\n\nZero raw HTML elements. Use Wangs UI primitives and Foundation theme typography.\n\n## Prohibitions\n\n- No `<button>`, `<input>`, `<select>`, `<textarea>`, `<form>`, `<table>`, `<dialog>`.\n- No `<h1>`-`<h6>`, `<p>`, `<span>` for text typography without Wangs UI theme primitives.\n\n## Mandatory Imports\n\n- Primitives via subpath:\n - `@wangs-ui/react-core/primitive/button`\n - `@wangs-ui/react-core/primitive/input`\n - `@wangs-ui/react-core/primitive/datatable`\n - `@wangs-ui/react-core/primitive/modal`\n - `@wangs-ui/react-core/primitive/dialogform`\n - `@wangs-ui/react-core/primitive/card`\n - `@wangs-ui/react-core/primitive/badge`\n- Typography via Foundation theme:\n - `@wangs-ui/foundation/theme` (`Text`, `Code`, `Kbd`, `Link`, `Mark`, `Blockquote`, `List`)\n - Use variants: `display*`, `headline*`, `title*`, `body*`, `label*`.\n";
|
|
41
41
|
//#endregion
|
|
42
42
|
//#region rules/wangs-ui/no-redundant-wrappers.md?raw
|
|
43
|
-
var no_redundant_wrappers_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing JSX layout for redundant wrapper containers.\"\
|
|
43
|
+
var no_redundant_wrappers_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing JSX layout for redundant wrapper containers.\"\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.\"\
|
|
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.\"\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
|
-
var react19_compiler_render_patterns_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing render logic in a React 19 component.\"\
|
|
49
|
+
var react19_compiler_render_patterns_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing render logic in a React 19 component.\"\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";
|
|
50
50
|
//#endregion
|
|
51
51
|
//#region rules/wangs-ui/react19-naming-conventions.md?raw
|
|
52
|
-
var react19_naming_conventions_default = "---\ntrigger: model_decision\ndescription: \"Apply when naming a React 19 component, hook, or prop.\"\
|
|
52
|
+
var react19_naming_conventions_default = "---\ntrigger: model_decision\ndescription: \"Apply when naming a React 19 component, hook, or prop.\"\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.\"\
|
|
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.\"\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
|
-
var react19_primitives_typing_default = "---\ntrigger: model_decision\ndescription: \"Apply when typing React 19 primitives — ref props, useActionState, useOptimistic, use(), useEffectEvent.\"\
|
|
58
|
+
var react19_primitives_typing_default = "---\ntrigger: model_decision\ndescription: \"Apply when typing React 19 primitives — ref props, useActionState, useOptimistic, use(), useEffectEvent.\"\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";
|
|
59
59
|
//#endregion
|
|
60
60
|
//#region rules/wangs-ui/react19-props-typing.md?raw
|
|
61
|
-
var react19_props_typing_default = "---\ntrigger: model_decision\ndescription: \"Apply when defining a component's Props type or interface.\"\
|
|
61
|
+
var react19_props_typing_default = "---\ntrigger: model_decision\ndescription: \"Apply when defining a component's Props type or interface.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: React 19 — Typing Props\n\n- `interface` for a component's `Props` — it's an entity shape, often extended.\n- A discriminated union when a component has mutually exclusive prop combinations, instead of a pile of optional props that can contradict each other.\n\n```tsx\n// ❌ Bad — nothing stops passing both `href` and `onClick` incoherently\ninterface ButtonProps {\n label: string;\n href?: string;\n onClick?: () => void;\n}\n\n// ✅ Good — the two variants can't be mixed\ntype ButtonProps =\n | { variant: 'link'; label: string; href: string }\n | { variant: 'action'; label: string; onClick: () => void };\n```\n";
|
|
62
62
|
//#endregion
|
|
63
63
|
//#region rules/wangs-ui/react19-purity-and-immutability.md?raw
|
|
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.\"\
|
|
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.\"\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\".'\
|
|
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\".'\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
|
-
var theme_variable_override_breaks_engine_default = "---\ntrigger: model_decision\ndescription: \"Apply when tempted to override a Wangs UI theme CSS variable directly.\"\
|
|
70
|
+
var theme_variable_override_breaks_engine_default = "---\ntrigger: model_decision\ndescription: \"Apply when tempted to override a Wangs UI theme CSS variable directly.\"\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";
|
|
71
71
|
//#endregion
|
|
72
72
|
//#region rules/wangs-ui/typescript-assertions-last-resort.md?raw
|
|
73
|
-
var typescript_assertions_last_resort_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing a type assertion (`as X`) or non-null assertion (`x!`).\"\
|
|
73
|
+
var typescript_assertions_last_resort_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing a type assertion (`as X`) or non-null assertion (`x!`).\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — Type Assertions & Non-Null Assertions are a Last Resort\n\n- `as X` and `x!` tell the compiler \"trust me\" — they produce zero runtime safety and actively hide bugs if wrong.\n- Acceptable only when the compiler genuinely cannot know something you do (e.g. a DOM query you've already null-checked, or narrowing a third-party type at a well-tested boundary) — and even then, prefer a type guard or a runtime check over a bare assertion.\n- Never use `as any` or `as unknown as X` to force an incompatible cast — that's `any` wearing a disguise.\n- `x!` should almost always be replaceable by an actual null check or optional chaining (`x?.y`) plus a real fallback.\n";
|
|
74
74
|
//#endregion
|
|
75
75
|
//#region rules/wangs-ui/typescript-checklist-and-reference.md?raw
|
|
76
|
-
var typescript_checklist_and_reference_default = "---\ntrigger: model_decision\ndescription: \"Apply when doing a final TypeScript review pass on a file.\"\
|
|
76
|
+
var typescript_checklist_and_reference_default = "---\ntrigger: model_decision\ndescription: \"Apply when doing a final TypeScript review pass on a file.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — Review Checklist & Quick Reference\n\n## Review Checklist\n\nBefore considering TypeScript code \"done,\" verify:\n\n- [ ] No `any` anywhere (including implicit `any` from missing annotations)\n- [ ] External/uncertain data enters as `unknown` and is narrowed before use\n- [ ] Variant state is a discriminated union, not optional fields + booleans\n- [ ] `interface` used for object/entity shapes; `type` used for unions/aliases/intersections\n- [ ] No stray `I` prefixes on interfaces\n- [ ] Naming follows casing conventions consistently\n- [ ] `as` / `!` are rare, justified, and can't be replaced by a guard or null check\n- [ ] Exported functions/methods have explicit return types\n- [ ] Switch statements over unions have an exhaustiveness (`never`) check\n- [ ] `tsconfig.json` includes the strictness baseline\n\n## Quick Reference\n\n| Situation | Use |\n| ---------------------------------------------- | ------------------------------------------------------------- |\n| External/uncertain data | `unknown` + narrowing |\n| \"This value is definitely one of these shapes\" | Discriminated union (`type`) |\n| Object with identity, may be extended | `interface` |\n| Union, intersection, tuple, mapped type | `type` |\n| Need to prove a type through logic | Type guard / narrowing |\n| Tempted to write `any` | Stop — use `unknown`, a generic, or a local interface instead |\n";
|
|
77
77
|
//#endregion
|
|
78
78
|
//#region rules/wangs-ui/typescript-discriminated-unions.md?raw
|
|
79
|
-
var typescript_discriminated_unions_default = "---\ntrigger: model_decision\ndescription: \"Apply when modeling a variant or multi-shape state in TypeScript.\"\
|
|
79
|
+
var typescript_discriminated_unions_default = "---\ntrigger: model_decision\ndescription: \"Apply when modeling a variant or multi-shape state in TypeScript.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — Discriminated Unions for Variant State\n\nWhenever a value can be one of several distinct \"shapes\" (loading/success/error states, event types, API response variants), model it as a **discriminated union** with a literal tag field — never as a loose object with optional fields or boolean flags.\n\n```ts\n// ❌ Bad — booleans can contradict each other; unclear which fields are valid together\ninterface FetchState {\n isLoading: boolean;\n isError: boolean;\n data?: User;\n error?: string;\n}\n\n// ✅ Good — only one shape is possible at a time, and the compiler enforces it\ntype FetchState =\n | { status: 'idle' }\n | { status: 'loading' }\n | { status: 'success'; data: User }\n | { status: 'error'; error: string };\n\nfunction render(state: FetchState) {\n switch (state.status) {\n case 'success':\n return state.data.name; // `data` is guaranteed to exist here\n case 'error':\n return state.error; // `error` is guaranteed to exist here\n default:\n return null;\n }\n}\n```\n\nUse a consistent tag field name across a codebase (`kind`, `type`, or `status` — pick one and stick with it) so narrowing patterns stay predictable.\n";
|
|
80
80
|
//#endregion
|
|
81
81
|
//#region rules/wangs-ui/typescript-explicit-return-types.md?raw
|
|
82
|
-
var typescript_explicit_return_types_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing an exported function, class method, or public API surface in TypeScript.\"\
|
|
82
|
+
var typescript_explicit_return_types_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing an exported function, class method, or public API surface in TypeScript.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — Explicit Return Types on Exported Functions\n\nInference is fine for local, private helpers, but exported functions, class methods, and anything forming a public API should declare an explicit return type. This prevents an internal implementation change from silently widening/narrowing the public contract.\n\n```ts\n// ❌ Return type is inferred and can silently drift\nexport function getActiveUsers(users: User[]) {\n return users.filter((u) => u.active);\n}\n\n// ✅ Explicit, intentional contract\nexport function getActiveUsers(users: User[]): User[] {\n return users.filter((u) => u.active);\n}\n```\n";
|
|
83
83
|
//#endregion
|
|
84
84
|
//#region rules/wangs-ui/typescript-interface-vs-type.md?raw
|
|
85
|
-
var typescript_interface_vs_type_default = "---\ntrigger: model_decision\ndescription: \"Apply when deciding between `interface` and `type` for a new TypeScript declaration.\"\
|
|
85
|
+
var typescript_interface_vs_type_default = "---\ntrigger: model_decision\ndescription: \"Apply when deciding between `interface` and `type` for a new TypeScript declaration.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — `interface` vs `type`\n\nBoth can describe object shapes, but they signal different intent. Default rule:\n\n| Use `interface` for... | Use `type` for... |\n| -------------------------------------------------------------------------- | -------------------------------------------------------------------------- |\n| Object / entity shapes (a `User`, a `Product`, a component's `Props`) | Unions (`\"a\" \\| \"b\"`) and discriminated unions |\n| Public API contracts meant to be `implements`-ed by classes | Intersections (`A & B`) |\n| Shapes that consumers may want to **extend/augment** (declaration merging) | Tuples (`[string, number]`) |\n| | Function types / callback signatures |\n| | Mapped, conditional, or utility-derived types (`Partial<T>`, `Pick<T, K>`) |\n| | Aliasing a primitive or another type for readability |\n\n```ts\n// ✅ interface — an entity with identity, extendable\ninterface User {\n id: string;\n email: string;\n role: UserRole;\n}\n\ninterface AdminUser extends User {\n permissions: Permission[];\n}\n\n// ✅ type — union, alias, derived shape\ntype UserRole = 'admin' | 'editor' | 'viewer';\ntype UserId = User['id'];\ntype PartialUser = Partial<User>;\ntype Callback<T> = (value: T) => void;\n```\n\nDon't mix conventions arbitrarily within one file — if a shape is a plain data object that will never need a union/intersection, `interface` is the default; the moment it needs to express \"one of several shapes,\" reach for `type`.\n";
|
|
86
86
|
//#endregion
|
|
87
87
|
//#region rules/wangs-ui/typescript-literal-unions-vs-enums.md?raw
|
|
88
|
-
var typescript_literal_unions_vs_enums_default = "---\ntrigger: model_decision\ndescription: \"Apply when modeling a fixed set of string or numeric variants in TypeScript.\"\
|
|
88
|
+
var typescript_literal_unions_vs_enums_default = "---\ntrigger: model_decision\ndescription: \"Apply when modeling a fixed set of string or numeric variants in TypeScript.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — Prefer Literal Unions Over Numeric Enums\n\nString literal unions are simpler, tree-shake better, and produce clearer error messages than TypeScript `enum`. Reserve `enum` (or `as const` object maps) for cases that need reverse lookup or genuinely benefit from a namespaced runtime value.\n\n```ts\n// ✅ Preferred\ntype OrderStatus = 'pending' | 'shipped' | 'delivered' | 'cancelled';\n\n// Acceptable when a namespaced runtime object is actually needed\nconst OrderStatus = {\n Pending: 'pending',\n Shipped: 'shipped',\n} as const;\ntype OrderStatus = (typeof OrderStatus)[keyof typeof OrderStatus];\n```\n";
|
|
89
89
|
//#endregion
|
|
90
90
|
//#region rules/wangs-ui/typescript-naming-conventions.md?raw
|
|
91
|
-
var typescript_naming_conventions_default = "---\ntrigger: model_decision\ndescription: \"Apply when naming a TypeScript type, variable, or constant.\"\
|
|
91
|
+
var typescript_naming_conventions_default = "---\ntrigger: model_decision\ndescription: \"Apply when naming a TypeScript type, variable, or constant.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — Naming Conventions\n\n| Kind | Convention | Example |\n| ---------------------------------------------------------- | ------------------------------------------- | ----------------------------------- |\n| Types, interfaces, classes, enums | `PascalCase` | `UserProfile`, `OrderStatus` |\n| Interfaces | `PascalCase`, **no `I` prefix** | `User`, not `IUser` |\n| Type aliases | `PascalCase` | `type ApiResponse<T> = ...` |\n| Variables, functions, methods, properties | `camelCase` | `getUserById`, `isValid` |\n| Booleans | `camelCase` with `is/has/should/can` prefix | `isLoading`, `hasPermission` |\n| True constants (module-level, never reassigned, primitive) | `UPPER_SNAKE_CASE` | `MAX_RETRIES`, `DEFAULT_TIMEOUT_MS` |\n| Enum members | `PascalCase` | `enum Status { Active, Archived }` |\n| Generic type parameters (simple, single-purpose) | Single uppercase letter | `T`, `K`, `V`, `E` for errors |\n| Generic type parameters (multiple / non-obvious) | Descriptive, prefixed with `T` | `TInput`, `TOutput`, `TContext` |\n| Discriminated union tag field | Consistent across the codebase | `kind`, `type`, or `status` |\n| Files with a single exported entity | Match the entity name | `UserProfile.ts`, `useAuth.ts` |\n\nNaming should describe **intent**, not implementation — `fetchUser` not `getUserFromApiEndpoint`; `retryCount` not `numRetries2`.\n";
|
|
92
92
|
//#endregion
|
|
93
93
|
//#region rules/wangs-ui/typescript-narrowing-over-casting.md?raw
|
|
94
|
-
var typescript_narrowing_over_casting_default = "---\ntrigger: model_decision\ndescription: \"Apply when narrowing an `unknown` or union-typed value in TypeScript.\"\
|
|
94
|
+
var typescript_narrowing_over_casting_default = "---\ntrigger: model_decision\ndescription: \"Apply when narrowing an `unknown` or union-typed value in TypeScript.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — `unknown` + Narrowing, Not Casting\n\nPrefer proving a type through control flow over asserting it with `as`.\n\n## Narrowing techniques, in order of preference:\n\n1. **`typeof`** — primitives (`string`, `number`, `boolean`, `undefined`, `function`)\n2. **`instanceof`** — class instances, `Error`, `Date`, custom classes\n3. **`in`** — checking a property exists before accessing it on a union/unknown\n4. **User-defined type guards** — `function isUser(x: unknown): x is User`\n5. **Discriminated union tag checks** — `switch (value.kind) { ... }`\n6. **Exhaustiveness checks** — a `never`-typed default branch so adding a new variant is a compile error until every switch/if-chain handles it\n\n```ts\n// ✅ Type guard\nfunction isUser(value: unknown): value is User {\n return typeof value === 'object' && value !== null && 'id' in value && 'email' in value;\n}\n\n// ✅ Exhaustiveness check\nfunction assertNever(x: never): never {\n throw new Error(`Unhandled case: ${JSON.stringify(x)}`);\n}\n\nfunction area(shape: Shape): number {\n switch (shape.kind) {\n case 'circle':\n return Math.PI * shape.radius ** 2;\n case 'square':\n return shape.side ** 2;\n default:\n return assertNever(shape); // compile error if a variant is missed\n }\n}\n```\n\nType assertions (`as X`) and the non-null assertion (`!`) bypass this entirely — treat them as a last resort, not a shortcut.\n";
|
|
95
95
|
//#endregion
|
|
96
96
|
//#region rules/wangs-ui/typescript-readonly-by-default.md?raw
|
|
97
|
-
var typescript_readonly_by_default_default = "---\ntrigger: model_decision\ndescription: \"Apply when declaring a TypeScript collection or object shape.\"\
|
|
97
|
+
var typescript_readonly_by_default_default = "---\ntrigger: model_decision\ndescription: \"Apply when declaring a TypeScript collection or object shape.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — Readonly by Default\n\nPrefer immutable shapes unless mutation is intentional and localized.\n\n```ts\ninterface Point {\n readonly x: number;\n readonly y: number;\n}\n\nfunction config(values: readonly string[]) {\n /* ... */\n}\n\nconst ROLES = ['admin', 'editor', 'viewer'] as const;\ntype UserRole = (typeof ROLES)[number];\n```\n";
|
|
98
98
|
//#endregion
|
|
99
99
|
//#region rules/wangs-ui/typescript-tsconfig-strictness.md?raw
|
|
100
|
-
var typescript_tsconfig_strictness_default = "---\ntrigger: model_decision\ndescription: \"Apply when configuring or reviewing tsconfig.json compiler strictness options.\"\
|
|
100
|
+
var typescript_tsconfig_strictness_default = "---\ntrigger: model_decision\ndescription: \"Apply when configuring or reviewing tsconfig.json compiler strictness options.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — Baseline `tsconfig.json` Strictness\n\nTreat these as the non-negotiable floor for any project this rule touches:\n\n```json\n{\n \"compilerOptions\": {\n \"strict\": true,\n \"noImplicitAny\": true,\n \"strictNullChecks\": true,\n \"strictFunctionTypes\": true,\n \"strictPropertyInitialization\": true,\n \"noUncheckedIndexedAccess\": true,\n \"exactOptionalPropertyTypes\": true,\n \"noImplicitOverride\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"forceConsistentCasingInFileNames\": true\n }\n}\n```\n\n`strict: true` alone enables the core group (`noImplicitAny`, `strictNullChecks`, etc.), but `noUncheckedIndexedAccess` and `exactOptionalPropertyTypes` are commonly missed and close real gaps (array/object index access returning `T` instead of `T | undefined`; optional properties silently accepting `undefined` as an explicit value).\n";
|
|
101
101
|
//#endregion
|
|
102
102
|
//#region rules/wangs-ui/typescript-zero-any.md?raw
|
|
103
|
-
var typescript_zero_any_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing any TypeScript type annotation.\"\
|
|
103
|
+
var typescript_zero_any_default = "---\ntrigger: model_decision\ndescription: \"Apply when writing or reviewing any TypeScript type annotation.\"\nmetadata:\n owner: wangs-ui\n---\n# Rule: TypeScript — Never Use `any`\n\n`any` is not \"unknown type,\" it's \"type checking off.\" It's contagious — once a value is `any`, everything it touches becomes unchecked too.\n\n- Never write `any` for parameters, return types, variables, or generics.\n- Use `unknown` for genuinely unknown external data (API responses, `JSON.parse`, catch clauses, third-party callbacks) and narrow it before use.\n- Use generics (`<T>`) when a function needs to work across types but preserve the relationship between input and output.\n- If a library ships untyped, write a minimal local type/interface for the surface area you actually use instead of reaching for `any`.\n\n```ts\n// ❌ Bad\nfunction parseConfig(json: any) {\n return json.settings.theme; // no safety, no autocomplete, silent runtime crash\n}\n\n// ✅ Good\nfunction parseConfig(json: unknown): string {\n if (\n typeof json === 'object' &&\n json !== null &&\n 'settings' in json &&\n typeof (json as { settings: unknown }).settings === 'object'\n ) {\n // still narrow further or validate with a schema library (zod, valibot, etc.)\n }\n throw new Error('Invalid config shape');\n}\n```\n\nThe only acceptable `any` is a well-justified, isolated, and commented one (e.g. interfacing with a genuinely untyped legacy module) — never a default.\n";
|
|
104
104
|
//#endregion
|
|
105
105
|
//#region src/registry.ts
|
|
106
106
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -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 [2m(v1.3.0-alpha.
|
|
418
|
+
intro(`\x1b[1m\x1b[36m📦 Wangs UI Skills & Rules Registry\x1b[0m [2m(v1.3.0-alpha.20)[0m`);
|
|
419
419
|
const allSkills = loadAllSkills();
|
|
420
420
|
const allRules = loadAllRules();
|
|
421
421
|
const skillDirs = getAgentSkillDirs(baseDir);
|
package/package.json
CHANGED