@godxjp/ui-mcp 0.18.0 → 0.19.1

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/index.js CHANGED
@@ -1604,10 +1604,79 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1604
1604
  rules: []
1605
1605
  },
1606
1606
  // ─── data-entry ─────────────────────────────────────────────────────────
1607
+ {
1608
+ name: "Form",
1609
+ group: "data-entry",
1610
+ tagline: "Ant-style layout container \u2014 renders <form> and pushes layout (vertical/horizontal), labelWidth/controlWidth, label alignment, responsive collapse, and multi-column grid down to every FormField (overridable per field).",
1611
+ props: [
1612
+ {
1613
+ name: "layout",
1614
+ type: '"vertical" | "horizontal" | "inline"',
1615
+ defaultValue: '"vertical"',
1616
+ description: "Label position relative to control; applied to all FormFields."
1617
+ },
1618
+ {
1619
+ name: "labelWidth",
1620
+ type: "number | string",
1621
+ description: "Label column width in horizontal layout (number\u2192px). e.g. 120 or '8rem'."
1622
+ },
1623
+ {
1624
+ name: "controlWidth",
1625
+ type: "number | string",
1626
+ description: "Cap the control width (number\u2192px). Omit to fill the column."
1627
+ },
1628
+ {
1629
+ name: "labelAlign",
1630
+ type: '"start" | "end"',
1631
+ defaultValue: '"end"',
1632
+ description: "Horizontal alignment of the label within its column."
1633
+ },
1634
+ {
1635
+ name: "collapseBelow",
1636
+ type: '"sm" | "md" | "lg" | "xl" | false',
1637
+ defaultValue: '"md"',
1638
+ description: "Breakpoint below which horizontal collapses to vertical (mobile-first). false = always horizontal."
1639
+ },
1640
+ {
1641
+ name: "columns",
1642
+ type: "number | { sm?: number; md?: number; lg?: number }",
1643
+ description: "Lay fields out in a responsive grid (reuses ResponsiveGrid; 1 col on small)."
1644
+ },
1645
+ {
1646
+ name: "density",
1647
+ type: '"compact" | "default" | "comfortable"',
1648
+ description: "Apply a density to controls inside the form."
1649
+ }
1650
+ ],
1651
+ usage: [
1652
+ "DO set `layout`, `labelWidth`, `controlWidth` ONCE on `<Form>` \u2014 every `<FormField>` inside inherits them. Override a single field by passing the same prop on that `<FormField>` (Form \u2192 FormField priority).",
1653
+ "DO rely on mobile-first collapse: `layout='horizontal'` automatically stacks to vertical below `collapseBelow` (default `md`). Pass `collapseBelow={false}` only when a field MUST stay label-beside-control even on phones.",
1654
+ "DO use `columns` for multi-field forms (e.g. `columns={2}`) \u2014 it reuses ResponsiveGrid (1 column on small screens, more on md/lg). Span a wide field across columns with `<FormField colSpan={2}>`.",
1655
+ "DON'T hand-roll a `<form>` + Flex stack for spacing \u2014 `<Form>` provides the vertical rhythm and the layout context FormField reads. Wire react-hook-form by spreading `onSubmit={handleSubmit(...)}` onto `<Form>`."
1656
+ ],
1657
+ useCases: [
1658
+ "A settings page form where every label sits in a fixed 120px column to the left of its control (horizontal), collapsing to stacked labels on mobile.",
1659
+ "A two-column entity-edit form (`columns={2}`) where the address field spans both columns (`colSpan={2}`).",
1660
+ "A compact filter form (`layout='horizontal' density='compact'`) above a DataTable."
1661
+ ],
1662
+ related: [
1663
+ "FormField \u2014 the per-field wrapper (label + control + helper/error) that reads Form's layout context; use one per control inside a Form.",
1664
+ "ResponsiveGrid \u2014 Form `columns` reuses it; use ResponsiveGrid directly for non-form card grids."
1665
+ ],
1666
+ example: `import { Form, FormField, Input } from "@godxjp/ui/data-entry";
1667
+
1668
+ <Form layout="horizontal" labelWidth={120} columns={2} onSubmit={onSubmit}>
1669
+ <FormField id="first" label="\u59D3"><Input id="first" /></FormField>
1670
+ <FormField id="last" label="\u540D"><Input id="last" /></FormField>
1671
+ <FormField id="address" label="\u4F4F\u6240" colSpan={2}><Input id="address" /></FormField>
1672
+ </Form>`,
1673
+ storyPath: "data-entry/Form.stories.tsx",
1674
+ rules: [23, 24]
1675
+ },
1607
1676
  {
1608
1677
  name: "FormField",
1609
1678
  group: "data-entry",
1610
- tagline: "Wraps a control with label, helper, and error; injects aria-describedby/aria-invalid onto the child.",
1679
+ tagline: "Wraps a control with label, helper, and error; injects aria-describedby/aria-invalid onto the child. Reads the parent Form's layout (vertical/horizontal) \u2014 overridable per field.",
1611
1680
  props: [
1612
1681
  {
1613
1682
  name: "id",
@@ -1633,6 +1702,26 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1633
1702
  type: "string",
1634
1703
  description: "Destructive error message (role=alert); overrides helper."
1635
1704
  },
1705
+ {
1706
+ name: "layout",
1707
+ type: '"vertical" | "horizontal" | "inline"',
1708
+ description: "Override the parent Form's layout for this field only."
1709
+ },
1710
+ {
1711
+ name: "labelWidth",
1712
+ type: "number | string",
1713
+ description: "Override the Form's label width for this field."
1714
+ },
1715
+ {
1716
+ name: "controlWidth",
1717
+ type: "number | string",
1718
+ description: "Override the Form's control width for this field."
1719
+ },
1720
+ {
1721
+ name: "colSpan",
1722
+ type: "number",
1723
+ description: "Span N columns when inside a `columns` Form grid."
1724
+ },
1636
1725
  {
1637
1726
  name: "children",
1638
1727
  type: "ReactNode",
@@ -1792,6 +1881,11 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1792
1881
  defaultValue: '""',
1793
1882
  description: "Controlled selected value (data-driven API). Pass an empty string to represent no selection."
1794
1883
  },
1884
+ {
1885
+ name: "defaultValue",
1886
+ type: "string",
1887
+ description: "Uncontrolled initial value (data-driven API). The trigger shows the matching option's label at rest \u2014 including in searchable (showSearch) mode \u2014 so an edit form pre-filled from server data renders the label, not the placeholder. Selected option is marked by a background tint (no check icon)."
1888
+ },
1795
1889
  {
1796
1890
  name: "onChange",
1797
1891
  type: "(value: string, option?: SearchSelectOptionProp) => void",
@@ -2342,7 +2436,8 @@ export function InvoiceDueDateField() {
2342
2436
  usage: [
2343
2437
  "Use `Dialog` for form-style or wizard-style modal flows that need freeform content and a close action.",
2344
2438
  "DO always control open state via `open` + `onOpenChange`. Dialog has no uncontrolled shortcut \u2014 omitting `open` means the trigger alone drives state, which is fine for simple trigger-only cases, but any async submission flow must use controlled state so you can hold the dialog open while `pending=true` and close it only on success.",
2345
- "DO include `DialogHeader` with `DialogTitle` (and optionally `DialogDescription`) inside every `DialogContent`. Radix requires an accessible title for screen readers; omitting it triggers a console warning and breaks a11y."
2439
+ "DO include `DialogHeader` with `DialogTitle` (and optionally `DialogDescription`) inside every `DialogContent`. Radix requires an accessible title for screen readers; omitting it triggers a console warning and breaks a11y.",
2440
+ "DO wrap tall/scrolling content in `DialogBody` (the ring-safe scroll slot, max-height ~60vh). It insets the content to match the dialog padding so a full-width control's focus ring never clips against the scroll container \u2014 mirror of SheetBody."
2346
2441
  ],
2347
2442
  useCases: [
2348
2443
  "Inline form dialog \u2014 create or edit a record (invoice line, supplier, coupon) without navigating away. Place `FormField`/`Input`/`Select` inside `DialogContent`, wire the submit button to your mutation, and hold `open` while `pending` to prevent double-submit.",
@@ -2469,20 +2564,33 @@ function CreateDialog() {
2469
2564
  {
2470
2565
  name: "Sheet",
2471
2566
  group: "feedback",
2472
- tagline: "Side-panel drawer (Radix Dialog). Parts: Sheet/SheetTrigger/SheetContent(side=right|left|top|bottom)/SheetHeader/SheetTitle/SheetFooter.",
2567
+ tagline: "Side-panel drawer (Radix Dialog). Parts: Sheet/SheetTrigger/SheetContent(side=right|left|top|bottom)/SheetHeader/SheetBody/SheetTitle/SheetFooter.",
2473
2568
  props: [
2474
2569
  { name: "open", type: "boolean", description: "Controlled open state." },
2475
2570
  {
2476
2571
  name: "onOpenChange",
2477
2572
  type: "(open: boolean) => void",
2478
2573
  description: "Open-state change handler."
2574
+ },
2575
+ {
2576
+ name: "width",
2577
+ type: "number | string",
2578
+ description: "On SheetContent (side left/right): desired panel width (number\u2192px). Caps at the viewport \u2014 full-width on a small screen (min(width,100%)), NOT a hard fixed width. Default w-3/4 sm:max-w-md."
2579
+ },
2580
+ {
2581
+ name: "title / subtitle / extra / tone",
2582
+ type: "ReactNode / ReactNode / ReactNode / ToneProp",
2583
+ description: "On SheetHeader (Ant-style): title (\u2192 SheetTitle, accessible name), subtitle (\u2192 SheetDescription), right-aligned extra actions, and a soft semantic `tone` background band. Children still supported."
2479
2584
  }
2480
2585
  ],
2481
2586
  usage: [
2587
+ "DO build the panel with SheetHeader (pass `title`/`subtitle`/`extra`/`tone` OR children) > SheetBody (scrollable, ring-safe) > SheetFooter (pinned). SheetTitle is required for a11y \u2014 the `title` prop renders it for you. Never skip the title.",
2588
+ "DO set `width` on SheetContent for a wider/narrower panel (e.g. width={480}); it caps at the viewport so small screens still get a full-width panel.",
2482
2589
  "DO use all named sub-parts in order: Sheet (root) > SheetTrigger (opener) > SheetContent (panel) > SheetHeader > SheetTitle (required for a11y \u2014 maps to Radix DialogPrimitive.Title, announced as the accessible name) > optional SheetDescription > body content > SheetFooter. Never skip SheetTitle inside an open SheetContent.",
2483
2590
  "DO control state explicitly with open + onOpenChange on Sheet root when you need to close programmatically (e.g. after form submit). Uncontrolled (no props) works for simple trigger-only cases but gives you no hook to reset form state on close.",
2484
2591
  "DO use SheetTrigger asChild to wrap a Button or other interactive element \u2014 this avoids a nested <button> in the DOM. Never render a raw <button> as a direct child of SheetTrigger.",
2485
- "DO use SheetFooter (renders at the bottom via mt-auto) for primary/cancel action Buttons. Never float action Buttons inside the body \u2014 they will not stick to the panel bottom.",
2592
+ "DO wrap a long/scrolling body in SheetBody (between SheetHeader and a pinned SheetFooter). It is the ring-safe scroll slot: a hand-rolled <div className='overflow-y-auto'> clips the 3px focus ring of a full-width Input/Select at the scroll edges \u2014 SheetBody insets the content so the ring never clips.",
2593
+ "DO use SheetFooter (renders at the bottom via mt-auto, symmetric 16/24 padding, full-bleed top border) for primary/cancel action Buttons. Never float action Buttons inside the body \u2014 they will not stick to the panel bottom.",
2486
2594
  "DON'T set showCloseButton={false} on SheetContent unless you provide your own SheetClose element; omitting both leaves users with no keyboard-accessible close path and breaks a11y.",
2487
2595
  "DON'T put a Sheet inside a Dialog (nested Radix portals conflict). If you need a slide-over triggered from within a modal, close the Dialog first, then open the Sheet."
2488
2596
  ],
@@ -5790,51 +5898,6 @@ export default function PasswordBlock() {
5790
5898
  storyPath: "data-entry/PasswordStrength.stories.tsx",
5791
5899
  rules: [3, 6]
5792
5900
  },
5793
- {
5794
- name: "Drawer",
5795
- group: "feedback",
5796
- tagline: "Bottom-sheet (vaul) \u2014 a draggable sheet that slides up from the screen edge. DISTINCT from Sheet (the side panel); use Drawer for mobile/touch bottom sheets.",
5797
- props: [
5798
- { name: "open", type: "boolean", description: "Controlled open state." },
5799
- {
5800
- name: "onOpenChange",
5801
- type: "(open: boolean) => void",
5802
- description: "Open-state callback."
5803
- },
5804
- {
5805
- name: "shouldScaleBackground",
5806
- type: "boolean",
5807
- defaultValue: "true",
5808
- description: "Scale the page behind the sheet (iOS-style)."
5809
- }
5810
- ],
5811
- usage: [
5812
- "DO compose Drawer > DrawerTrigger > DrawerContent (> DrawerHeader/DrawerTitle + body + DrawerFooter).",
5813
- "DO use Drawer for mobile/touch bottom sheets; use Sheet for a desktop side panel and Dialog for a centered modal.",
5814
- "DON'T confuse with Sheet \u2014 Sheet slides from a side edge, Drawer is the draggable bottom sheet."
5815
- ],
5816
- useCases: [
5817
- "Mobile action sheet / menu",
5818
- "Filter panel on small screens",
5819
- "Quick-create form on touch",
5820
- "Detail peek that drags up"
5821
- ],
5822
- related: [
5823
- "Sheet (side panel, same Radix Dialog base, different placement)",
5824
- "Dialog (centered modal)"
5825
- ],
5826
- example: `import { Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerTitle } from "@godxjp/ui/feedback";
5827
-
5828
- <Drawer>
5829
- <DrawerTrigger>\u7D5E\u308A\u8FBC\u307F</DrawerTrigger>
5830
- <DrawerContent>
5831
- <DrawerHeader><DrawerTitle>\u7D5E\u308A\u8FBC\u307F</DrawerTitle></DrawerHeader>
5832
- {/* filters */}
5833
- </DrawerContent>
5834
- </Drawer>`,
5835
- storyPath: "feedback/Drawer.stories.tsx",
5836
- rules: [3, 6, 24]
5837
- },
5838
5901
  {
5839
5902
  name: "InputOTP",
5840
5903
  group: "data-entry",
@@ -6084,7 +6147,7 @@ export default function PasswordBlock() {
6084
6147
  {
6085
6148
  name: "Carousel",
6086
6149
  group: "data-display",
6087
- tagline: "Embla-backed carousel primitives including previous/next controls and context API.",
6150
+ tagline: "Embla-backed carousel primitives: previous/next controls, CarouselDots indicators, and a context API.",
6088
6151
  props: [
6089
6152
  {
6090
6153
  name: "opts",
@@ -6105,15 +6168,17 @@ export default function PasswordBlock() {
6105
6168
  useCases: ["Feature cards", "Image galleries", "Horizontal stepping lists"],
6106
6169
  storyPath: "data-display/Carousel.stories.tsx",
6107
6170
  rules: [3, 6],
6108
- example: `import { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext } from "@godxjp/ui/data-display";
6171
+ example: `import { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext, CarouselDots } from "@godxjp/ui/data-display";
6109
6172
 
6110
- <Carousel>
6173
+ // CarouselDots reads the Embla api from context \u2014 no setApi wiring needed.
6174
+ <Carousel opts={{ loop: true }}>
6111
6175
  <CarouselContent>
6112
6176
  <CarouselItem>1</CarouselItem>
6113
6177
  <CarouselItem>2</CarouselItem>
6114
6178
  </CarouselContent>
6115
6179
  <CarouselPrevious />
6116
6180
  <CarouselNext />
6181
+ <CarouselDots />
6117
6182
  </Carousel>`
6118
6183
  },
6119
6184
  {
@@ -7061,10 +7126,14 @@ function searchPatterns(query) {
7061
7126
  }
7062
7127
 
7063
7128
  // src/data/skills-index.ts
7129
+ function isConsumerSkill(s) {
7130
+ return s.audience !== "core";
7131
+ }
7064
7132
  var SKILLS = [
7065
7133
  // ── taste (foundational) ───────────────────────────────────────
7066
7134
  {
7067
7135
  id: "taste",
7136
+ audience: "both",
7068
7137
  name: "Taste baseline \u2014 Senior UI/UX engineering",
7069
7138
  whenToUse: "Default for any production app screen. Metric-based rules, strict component architecture, CSS hardware acceleration, balanced design engineering.",
7070
7139
  source: "Leonxlnx/taste-skill (root) + @godxjp/ui design-thinking.ts",
@@ -7144,6 +7213,7 @@ broken). Spinner during init is wrong (nothing to spin over).`
7144
7213
  // ── soft (Awwwards / premium agency) ───────────────────────────
7145
7214
  {
7146
7215
  id: "soft",
7216
+ audience: "consumer",
7147
7217
  name: "Awwwards-tier \u2014 $150k agency build",
7148
7218
  whenToUse: "Premium agency brief \u2014 marketing site, hero pages, product showcase. NOT every internal SaaS screen. Apply when the brief asks for 'Linear-tier', 'Apple-esque', 'Awwwards-style'.",
7149
7219
  source: "Leonxlnx/taste-skill/soft-skill",
@@ -7263,6 +7333,7 @@ or Framer Motion's whileInView. NEVER window.addEventListener("scroll")
7263
7333
  // ── minimalist (editorial workspace) ───────────────────────────
7264
7334
  {
7265
7335
  id: "minimalist",
7336
+ audience: "consumer",
7266
7337
  name: "Minimalist \u2014 editorial workspace",
7267
7338
  whenToUse: "Document-style apps (Notion-clone, knowledge base, blog admin). Warm monochrome + spot pastels. Bento grids. Editorial serif headings + sans body + monospace for data.",
7268
7339
  source: "Leonxlnx/taste-skill/minimalist-skill",
@@ -7346,6 +7417,7 @@ position:fixed pointer-events-none layer.`
7346
7417
  // ── brutalist ──────────────────────────────────────────────────
7347
7418
  {
7348
7419
  id: "brutalist",
7420
+ audience: "consumer",
7349
7421
  name: "Brutalist \u2014 Swiss print + military terminal",
7350
7422
  whenToUse: "Data-heavy dashboards, declassified-blueprint feel, portfolios needing raw mechanical aesthetic. Rigid grids, extreme type scale contrast, utilitarian color, analog degradation effects.",
7351
7423
  source: "Leonxlnx/taste-skill/brutalist-skill",
@@ -7368,6 +7440,7 @@ portfolios.`
7368
7440
  // ── gpt-tasteskill ─────────────────────────────────────────────
7369
7441
  {
7370
7442
  id: "gpt-tasteskill",
7443
+ audience: "consumer",
7371
7444
  name: "GPT taste \u2014 editorial + advanced GSAP motion",
7372
7445
  whenToUse: "Long-scroll marketing pages with cinematic scroll choreography. Pins, stacks, scrubbed timelines. AIDA structure. Wide editorial typography. Bans 6-line wraps. Gapless bento grids.",
7373
7446
  source: "Leonxlnx/taste-skill/gpt-tasteskill",
@@ -7390,6 +7463,7 @@ over current), scrubbing (animation tied to scroll progress).`
7390
7463
  // ── redesign ───────────────────────────────────────────────────
7391
7464
  {
7392
7465
  id: "redesign",
7466
+ audience: "both",
7393
7467
  name: "Redesign \u2014 audit + upgrade existing UI",
7394
7468
  whenToUse: "Working on an existing project (not greenfield). Find generic patterns, weak points, missing states. Apply fixes in priority order \u2014 font swap first, palette cleanup second, etc.",
7395
7469
  source: "Leonxlnx/taste-skill/redesign-skill + redesign-audit.ts",
@@ -7437,6 +7511,7 @@ no skip-to-content.`
7437
7511
  // ── output (full-output enforcement) ───────────────────────────
7438
7512
  {
7439
7513
  id: "output",
7514
+ audience: "both",
7440
7515
  name: "Full-output enforcement",
7441
7516
  whenToUse: "Always. Bans the // ... / // TODO / 'I'll leave this as an exercise' patterns. Treat every task as production-critical.",
7442
7517
  source: "Leonxlnx/taste-skill/output-skill + output-quality.ts",
@@ -7476,6 +7551,7 @@ of writing it.`
7476
7551
  // ── brandkit ───────────────────────────────────────────────────
7477
7552
  {
7478
7553
  id: "brandkit",
7554
+ audience: "consumer",
7479
7555
  name: "Brandkit \u2014 identity guidelines boards",
7480
7556
  whenToUse: "Designing a brand identity board first (before screens). Logo system, color palette, typography lockup, icon system, photography direction, brand voice.",
7481
7557
  source: "Leonxlnx/taste-skill/brandkit",
@@ -7497,6 +7573,7 @@ imagery. Flexible grid layouts.`
7497
7573
  // ── stitch ─────────────────────────────────────────────────────
7498
7574
  {
7499
7575
  id: "stitch",
7576
+ audience: "consumer",
7500
7577
  name: "Stitch \u2014 semantic DESIGN.md for Google Stitch",
7501
7578
  whenToUse: "Pairing with Google Stitch (or similar AI UI generator). Generate DESIGN.md files that enforce premium standards \u2014 strict typography, calibrated color, asymmetric layouts, perpetual micro-motion.",
7502
7579
  source: "Leonxlnx/taste-skill/stitch-skill",
@@ -7518,6 +7595,7 @@ expressive.`
7518
7595
  // ── imagegen-mobile ────────────────────────────────────────────
7519
7596
  {
7520
7597
  id: "imagegen-mobile",
7598
+ audience: "consumer",
7521
7599
  name: "Imagegen mobile \u2014 app screen reference images",
7522
7600
  whenToUse: "Pre-code phase. Generate mobile screen mockups before implementing. Onboarding flows, auth, home dashboards, profile, settings, chat, ecommerce, fintech, health, productivity.",
7523
7601
  source: "Leonxlnx/taste-skill/imagegen-frontend-mobile",
@@ -7544,6 +7622,7 @@ screens in flows.`
7544
7622
  // ── imagegen-web ───────────────────────────────────────────────
7545
7623
  {
7546
7624
  id: "imagegen-web",
7625
+ audience: "consumer",
7547
7626
  name: "Imagegen web \u2014 landing page section images",
7548
7627
  whenToUse: "Pre-code phase for landing / marketing sites. Generate ONE image per section (8 sections \u2192 8 images). Hero composition variety (NOT always left-text/right-image).",
7549
7628
  source: "Leonxlnx/taste-skill/imagegen-frontend-web",
@@ -7580,6 +7659,7 @@ for the brand.`
7580
7659
  // ── image-to-code ──────────────────────────────────────────────
7581
7660
  {
7582
7661
  id: "image-to-code",
7662
+ audience: "consumer",
7583
7663
  name: "Image-to-code \u2014 generate design first, then implement",
7584
7664
  whenToUse: "Visual-first brief in Codex. First generate the design image yourself, deeply analyze, THEN implement code matching it.",
7585
7665
  source: "Leonxlnx/taste-skill/image-to-code-skill",
@@ -7604,6 +7684,7 @@ visible on a small laptop.`
7604
7684
  // ── component discipline (hard contract) ───────────────────────
7605
7685
  {
7606
7686
  id: "component-discipline",
7687
+ audience: "core",
7607
7688
  name: "Component discipline \u2014 international standards (hard contract)",
7608
7689
  whenToUse: "MANDATORY before creating or changing ANY @godxjp/ui component, recipe, doc, or example. Enforces real primitives only, no duplication, i18n (Intl/CLDR/ISO/IANA/BCP-47), WAI-ARIA APG + WCAG 2.2 AA, RTL, and the controlled-vocabulary API.",
7609
7690
  source: "@godxjp/ui .claude/skills/godxjp-ui-component + international-standardization audit",
@@ -7650,12 +7731,26 @@ with expectNoA11yViolations. Prefer Radix/cmdk/vaul for ARIA.`
7650
7731
  "default"); positive booleans; tone for status; forward ref + ...props + className + id; export
7651
7732
  XProp + XProp as XProps and register in props/registry. Then: add an MCP catalog entry + a
7652
7733
  real-screen docs page; verify typecheck/lint/audit/check:*/preview:build/test all green.`
7734
+ },
7735
+ {
7736
+ id: "report-bug",
7737
+ title: "Found a library-level defect \u2192 file a gh issue (never paper over it)",
7738
+ tagline: "A missing token / wrong vocab / broken a11y in the system is a bug to report, not to work around.",
7739
+ body: `If satisfying this contract is blocked by @godxjp/ui itself \u2014 a token tier that
7740
+ doesn't exist, a primitive missing the controlled-vocabulary prop, a Radix wiring with a
7741
+ real a11y bug, a catalog example that's wrong \u2014 the contract says STOP and fix the SYSTEM,
7742
+ not the call site. Don't bake a one-off around it. If you can fix the library in this repo,
7743
+ do. If you can't (or you're a consumer agent without write access), open a detailed GitHub
7744
+ issue: use the draft_bug_report MCP tool to produce the issue body + a 'gh issue create
7745
+ --repo godx-jp/godxjp-ui \u2026' command, linking the component (get_component) and the cardinal
7746
+ rule (get_rule) involved, with a minimal repro, expected vs actual, version, and env.`
7653
7747
  }
7654
7748
  ]
7655
7749
  },
7656
7750
  // ── design-to-page (consumer: handoff → real page) ─────────────
7657
7751
  {
7658
7752
  id: "design-to-page",
7753
+ audience: "consumer",
7659
7754
  name: "Design handoff \u2192 real page (consumer build guide)",
7660
7755
  whenToUse: "You (a consumer agent) received a Claude Design handoff \u2014 a bundle/mock/screenshot/HTML prototype or a written brief \u2014 and must build it as a REAL page with @godxjp/ui. Read this BEFORE writing any JSX. It teaches: read intent, map every block to a real primitive via this MCP, consume existing tokens, apply the dxs-kintai DNA, treat tables as the centerpiece, resolve gaps by extend-or-ask, and verify.",
7661
7756
  source: "@godxjp/ui .design/research (chats-intent, tables, atomic-components) + dxs-kintai SKILL/colors_and_type.css",
@@ -7784,6 +7879,114 @@ format numbers/dates via Intl. Then run the build: pnpm typecheck + pnpm lint +
7784
7879
  pnpm audit must be green, console clean, and eyeball the page at 390 / 768 / 1280
7785
7880
  (atoms never wrap, containers wrap with row-gap, tabs horizontal-scroll, grids
7786
7881
  minmax(0,1fr), heights never break).`
7882
+ },
7883
+ {
7884
+ id: "report-bug",
7885
+ title: "godx-ui bug / can't-follow-a-rule \u2192 file a gh issue (never fake it)",
7886
+ tagline: "If the LIBRARY is wrong, report it \u2014 don't hand-roll a workaround that hides the bug.",
7887
+ body: `When you CANNOT satisfy a rule because @godxjp/ui itself is at fault \u2014 a
7888
+ primitive doesn't expose the controlled-vocabulary prop you need, a token is
7889
+ missing, a component has a real a11y/behaviour bug, an example in the catalog is
7890
+ wrong \u2014 the rule is: DO NOT silently hand-roll/fake a replacement to dodge it (that
7891
+ just buries a library bug inside every app). Instead:
7892
+ \u2022 DON'T: copy a primitive's internals into your app, swap in a raw <input>/<div>, or
7893
+ redeclare a token to route around the defect.
7894
+ \u2022 DO: open a detailed GitHub issue against the library, then apply the SMALLEST
7895
+ possible local workaround marked // TODO(godxui#<n>: <summary>) so it's grep-able
7896
+ and removed once fixed.
7897
+ \u2022 Use the MCP tool draft_bug_report to generate the issue body + a copy-paste
7898
+ 'gh issue create --repo godx-jp/godxjp-ui \u2026' command. Include: the component/rule
7899
+ (link via get_component/get_rule), a minimal repro, expected vs actual, the
7900
+ installed @godxjp/ui version, and your env. A vague "X is broken" issue is not
7901
+ enough \u2014 the report must let a maintainer reproduce in one paste.`
7902
+ }
7903
+ ]
7904
+ },
7905
+ // ── compose-a-screen (consumer: primitives → a finished screen) ──
7906
+ {
7907
+ id: "compose-a-screen",
7908
+ audience: "consumer",
7909
+ name: "Compose a screen \u2014 primitives \u2192 a finished app view (consumer)",
7910
+ whenToUse: "You (a consumer agent) are building a NEW screen/page in an app that imports @godxjp/ui \u2014 from a written brief or product requirement, not a design handoff. Read this to assemble it from real primitives via this MCP: pick the right components, lay out one-intent-per-screen, wire every state + a11y + i18n, and verify. For a Claude Design handoff bundle/mock specifically, use design-to-page instead.",
7911
+ source: "@godxjp/ui MCP (consumer surface) \u2014 taste/one-intent + component-discipline + dxs-kintai DNA",
7912
+ sections: [
7913
+ {
7914
+ id: "pick-primitives",
7915
+ title: "Pick primitives via the MCP \u2014 never hand-roll, never guess a prop",
7916
+ tagline: "Decompose the screen into a shopping list; resolve each item with list_primitives \u2192 get_component.",
7917
+ body: `Start from the requirement, not the markup. Write a shopping list of every
7918
+ block the screen needs, then resolve each through THIS MCP before writing JSX:
7919
+ list_primitives (discover the group), get_component <Name> (confirm the exact
7920
+ prop/union/default \u2014 never guess), suggest_primitive / search_components when unsure
7921
+ which fits. Hard rules: compose ONLY real @godxjp/ui primitives \u2014 no styled <div>
7922
+ faking a Card, no raw <input>/<select>/<button>/<textarea>/<table>. No duplication:
7923
+ Select (showSearch/loadOptions) is the ONLY searchable/async select (there is no
7924
+ Combobox/Autocomplete/CountrySelect); the 4 i18n pickers are one AppSettingPicker
7925
+ kind=\u2026. A table = Card + CardContent-flush + DataTable. If a block has no clean
7926
+ primitive, see gaps handling in design-to-page/gaps-extend-or-ask + report-bug.`
7927
+ },
7928
+ {
7929
+ id: "assemble-screen",
7930
+ title: "Assemble it \u2014 one intent per screen, real chrome, fixed signaling",
7931
+ tagline: "One primary question per page; AppShell/PageContainer chrome; --primary once; semantic color is fixed.",
7932
+ body: `Distil the screen to ONE primary question it answers (one-intent-per-screen):
7933
+ 1\u20132 hero facts + ONE primary list/form + contextual actions; push tertiary content to
7934
+ a Sheet/Dialog/next page. An 8-stat-card wall is a red flag. Use real page chrome
7935
+ (AppShell/Sidebar/Topbar/PageContainer) \u2014 content never touches the viewport edge,
7936
+ two bordered surfaces never touch (\u9593/breathing via Stack gap). Exactly ONE --primary
7937
+ action per view; status uses the FIXED semantic mapping (success/warning/info/
7938
+ attention/danger) \u2014 never recolor a wa-iro hue into a role, never use --primary for
7939
+ status. Pick density up front (compact 28 heavy-table / default 32 / comfortable 44
7940
+ login-mobile) and don't mix it mid-page. Hierarchy from type weight+size+color
7941
+ (20/18/14/13 \xD7 400/500/700), not colored background blocks. Mobile-first: default one
7942
+ column, add columns only at md:/lg: when each keeps \u226514px body at \u2265~280px width.`
7943
+ },
7944
+ {
7945
+ id: "state-and-a11y",
7946
+ title: "Wire every state + a11y + i18n (the part that gets skipped)",
7947
+ tagline: "default/hover/focus/active/disabled/loading/empty/error all handled; APG keyboard; t() everything.",
7948
+ body: `A screen isn't done at the happy path. Handle every state: Skeleton for INIT
7949
+ fetch (no data yet), Form loading / Spinner for active save (data present), EmptyState
7950
+ for no-data (one calm sentence, no illustration), inline error near the field via
7951
+ FormField (NOT a disappearing toast). Forms: explicit label + help + error always \u2014
7952
+ never placeholder-as-label. A11y (WAI-ARIA APG + WCAG 2.2 AA): correct roles/landmarks,
7953
+ keyboard (arrows/Home/End/Enter/Esc, visible focus, no positive tabindex, return focus
7954
+ on close), \u226524px targets, never colour-only state (add sr-only text), icon-only buttons
7955
+ need a name. i18n: zero hardcoded strings \u2014 every label + aria-label through t();
7956
+ numbers/currency/dates via Intl with the active locale (ISO 4217/8601, IANA tz),
7957
+ plurals via Intl.PluralRules. State-truthful affordances: a parent checkbox aggregates
7958
+ its children (checked/indeterminate/empty); a held value is visible when a control
7959
+ opens; controlled inputs mirror type\u2194click both ways (a controlled value with no
7960
+ synchronous onValueChange FREEZES the input).`
7961
+ },
7962
+ {
7963
+ id: "verify",
7964
+ title: "Verify \u2014 states shown, console clean, build green, 3 widths",
7965
+ tagline: "Drive every interactive control; 0 console errors/warnings; typecheck/lint green; eyeball 390/768/1280.",
7966
+ body: `Before calling the screen done: drive EVERY interactive control to its
7967
+ terminal state in a real browser (don't infer behaviour from source) and read the
7968
+ DevTools console \u2014 a <button>-in-<button>/hydration/act()/404 warning is a FINDING,
7969
+ not noise. Confirm: every prop \xD7 union \xD7 state is exercised; held values visible on
7970
+ open; multi-step selections (date range, capped multi-select) can be restarted from a
7971
+ complete state, not trapped; controlled mirrors update both directions. Run the app's
7972
+ build: typecheck + lint clean, console clean, and eyeball at 390 / 768 / 1280 (atoms
7973
+ never wrap, containers wrap with row-gap, natural-width components stay w-fit, no
7974
+ decorative edge fades, no dead grey panes). If a failure traces to @godxjp/ui itself,
7975
+ do not fake around it \u2014 see report-bug.`
7976
+ },
7977
+ {
7978
+ id: "report-bug",
7979
+ title: "godx-ui bug / can't-follow-a-rule \u2192 file a gh issue (never fake it)",
7980
+ tagline: "If the LIBRARY is wrong, report it \u2014 don't hand-roll a workaround that hides the bug.",
7981
+ body: `Same contract as design-to-page/report-bug: when a rule is impossible because
7982
+ @godxjp/ui is at fault (missing token, a primitive without the controlled-vocabulary
7983
+ prop, a real a11y/behaviour bug, a wrong catalog example) \u2014 DO NOT silently fake a
7984
+ replacement. DON'T copy primitive internals, drop to raw HTML, or redeclare a token to
7985
+ dodge it. DO open a detailed GitHub issue (use the draft_bug_report MCP tool to build
7986
+ the body + the 'gh issue create --repo godx-jp/godxjp-ui \u2026' command), then apply the
7987
+ smallest local workaround tagged // TODO(godxui#<n>: <summary>). The issue must carry a
7988
+ minimal repro, expected vs actual, the installed version, and env \u2014 enough to
7989
+ reproduce in one paste.`
7787
7990
  }
7788
7991
  ]
7789
7992
  }
@@ -7794,7 +7997,7 @@ function findSkill(id) {
7794
7997
  function findSection(skillId, sectionId) {
7795
7998
  return findSkill(skillId)?.sections.find((s) => s.id === sectionId);
7796
7999
  }
7797
- function routeTask(task) {
8000
+ function routeTask(task, opts) {
7798
8001
  const q = task.toLowerCase();
7799
8002
  const matches = [];
7800
8003
  const route = (kw, skill, section, why, alsoSee) => {
@@ -7885,13 +8088,73 @@ function routeTask(task) {
7885
8088
  "Generate design image first \u2192 analyze \u2192 implement."
7886
8089
  );
7887
8090
  route(
7888
- ["handoff", "design bundle", "claude design", "prototype", "build the page", "implement the design", "build this screen", "mockup"],
8091
+ [
8092
+ "handoff",
8093
+ "design bundle",
8094
+ "claude design",
8095
+ "prototype",
8096
+ "build the page",
8097
+ "implement the design",
8098
+ "build this screen",
8099
+ "mockup"
8100
+ ],
7889
8101
  "design-to-page",
7890
8102
  "map-to-primitives",
7891
8103
  "Map every block to a real @godxjp/ui primitive (MCP-first), consume existing tokens, apply the dxs-kintai DNA, tables central, gaps \u2192 extend-or-ask, verify.",
7892
8104
  ["design-to-page/read-intent", "design-to-page/dna", "design-to-page/tables-central"]
7893
8105
  );
7894
- if (matches.length === 0) {
8106
+ route(
8107
+ [
8108
+ "compose a screen",
8109
+ "new screen",
8110
+ "new page",
8111
+ "create a page",
8112
+ "create a screen",
8113
+ "build a view",
8114
+ "build a screen",
8115
+ "from scratch",
8116
+ "screen from a brief"
8117
+ ],
8118
+ "compose-a-screen",
8119
+ "pick-primitives",
8120
+ "Build a new app screen from real @godxjp/ui primitives (MCP-first): one-intent-per-screen, real chrome, every state + a11y + i18n, verify.",
8121
+ [
8122
+ "compose-a-screen/assemble-screen",
8123
+ "compose-a-screen/state-and-a11y",
8124
+ "taste/one-intent-per-screen"
8125
+ ]
8126
+ );
8127
+ route(
8128
+ [
8129
+ "bug in godx",
8130
+ "godx-ui bug",
8131
+ "report a bug",
8132
+ "file an issue",
8133
+ "gh issue",
8134
+ "can't follow the rule",
8135
+ "library is broken",
8136
+ "primitive is broken"
8137
+ ],
8138
+ "compose-a-screen",
8139
+ "report-bug",
8140
+ "If @godxjp/ui itself is at fault, don't fake a workaround \u2014 file a detailed gh issue (use draft_bug_report).",
8141
+ ["design-to-page/report-bug"]
8142
+ );
8143
+ const filtered = opts?.consumerOnly ? matches.filter((m) => {
8144
+ const sk = findSkill(m.skill);
8145
+ return !sk || sk.audience !== "core";
8146
+ }) : matches;
8147
+ if (filtered.length === 0) {
8148
+ if (opts?.consumerOnly) {
8149
+ return [
8150
+ {
8151
+ skill: "compose-a-screen",
8152
+ section: "pick-primitives",
8153
+ why: `No keyword match for "${task}". Default consumer path: compose the screen from real primitives via the MCP.`,
8154
+ alsoSee: ["design-to-page/map-to-primitives", "taste/one-intent-per-screen"]
8155
+ }
8156
+ ];
8157
+ }
7895
8158
  return [
7896
8159
  {
7897
8160
  skill: "taste",
@@ -7900,7 +8163,7 @@ function routeTask(task) {
7900
8163
  }
7901
8164
  ];
7902
8165
  }
7903
- return matches;
8166
+ return filtered;
7904
8167
  }
7905
8168
 
7906
8169
  // src/data/anti-ai-tells.ts
@@ -8629,27 +8892,71 @@ var TOOL_DEFINITIONS = [
8629
8892
  },
8630
8893
  {
8631
8894
  name: "get_tokens",
8632
- description: "Read design tokens, optionally filtered by category.",
8895
+ description: "Read design tokens, optionally filtered by tier category (primitive / semantic / component).",
8633
8896
  inputSchema: {
8634
8897
  type: "object",
8635
8898
  properties: {
8636
8899
  category: {
8637
8900
  type: "string",
8638
- enum: [
8639
- "color",
8640
- "spacing",
8641
- "typography",
8642
- "radius",
8643
- "shadow",
8644
- "motion",
8645
- "breakpoint",
8646
- "density",
8647
- "z-index"
8648
- ]
8901
+ enum: ["primitive", "semantic", "component"]
8649
8902
  }
8650
8903
  }
8651
8904
  }
8652
8905
  },
8906
+ // ── CONSUMER NAMESPACE (app-dev surface — core-only skills hidden) ─
8907
+ {
8908
+ name: "list_consumer_skills",
8909
+ description: "List the design skills relevant to an app-dev BUILDING WITH @godxjp/ui (audience consumer/both). Hides core library-maintenance skills. START HERE if you import @godxjp/ui and want guidance (design-to-page, compose-a-screen, taste, \u2026). Returns id + name + whenToUse + section ids.",
8910
+ inputSchema: { type: "object", properties: {} }
8911
+ },
8912
+ {
8913
+ name: "get_consumer_skill",
8914
+ description: "Fetch ONE section of ONE consumer-facing skill. Same as get_skill_section but refuses core-only skills (steers app-devs away from library-maintenance material). Use after list_consumer_skills / route_consumer_task.",
8915
+ inputSchema: {
8916
+ type: "object",
8917
+ properties: {
8918
+ skill: {
8919
+ type: "string",
8920
+ description: "Consumer skill id (e.g. 'design-to-page', 'compose-a-screen')."
8921
+ },
8922
+ section: { type: "string", description: "Section id within that skill." }
8923
+ },
8924
+ required: ["skill"]
8925
+ }
8926
+ },
8927
+ {
8928
+ name: "route_consumer_task",
8929
+ description: "Natural-language task \u2192 consumer skill+section pointer. Like route_task but only points to consumer-facing skills (never core library-maintenance). Use FIRST when you're building an app with @godxjp/ui.",
8930
+ inputSchema: {
8931
+ type: "object",
8932
+ properties: { task: { type: "string", description: "Describe what you want to build." } },
8933
+ required: ["task"]
8934
+ }
8935
+ },
8936
+ {
8937
+ name: "draft_bug_report",
8938
+ description: "When @godxjp/ui ITSELF is at fault (missing token, a primitive lacking the controlled-vocabulary prop, a real a11y/behaviour bug, a wrong catalog example) and you cannot follow a rule \u2014 DON'T fake a workaround. This drafts a detailed GitHub issue body + a copy-paste `gh issue create` command so you can report it. Prints the command only; never runs gh.",
8939
+ inputSchema: {
8940
+ type: "object",
8941
+ properties: {
8942
+ summary: { type: "string", description: "One-line title of the bug / blocked rule." },
8943
+ repro: { type: "string", description: "Minimal steps or code to reproduce." },
8944
+ expected: { type: "string", description: "What SHOULD happen (per the rule/spec)." },
8945
+ actual: { type: "string", description: "What actually happens." },
8946
+ component: {
8947
+ type: "string",
8948
+ description: "Affected component name, if any (links to get_component)."
8949
+ },
8950
+ rule: {
8951
+ type: "number",
8952
+ description: "Cardinal rule number that can't be followed, if any."
8953
+ },
8954
+ version: { type: "string", description: "Installed @godxjp/ui version (e.g. '12.1.0')." },
8955
+ env: { type: "string", description: "Environment (browser/OS/framework), if relevant." }
8956
+ },
8957
+ required: ["summary"]
8958
+ }
8959
+ },
8653
8960
  // ── TASK ROUTING (smallest response — pointer) ─────────────────
8654
8961
  {
8655
8962
  name: "route_task",
@@ -8716,9 +9023,18 @@ async function dispatchTool(name, args) {
8716
9023
  case "get_rule":
8717
9024
  return getRule(typeof args.number === "number" ? args.number : void 0);
8718
9025
  case "get_vocab":
8719
- return getVocab(args.name);
9026
+ return getVocab(args.name == null ? void 0 : String(args.name));
8720
9027
  case "get_tokens":
8721
9028
  return getTokens(args.category);
9029
+ // Consumer namespace
9030
+ case "list_consumer_skills":
9031
+ return listConsumerSkills();
9032
+ case "get_consumer_skill":
9033
+ return getConsumerSkill(String(args.skill ?? ""), String(args.section ?? ""));
9034
+ case "route_consumer_task":
9035
+ return routeTaskTool(String(args.task ?? ""), { consumerOnly: true });
9036
+ case "draft_bug_report":
9037
+ return draftBugReport(args);
8722
9038
  // Task routing
8723
9039
  case "route_task":
8724
9040
  return routeTaskTool(String(args.task ?? ""));
@@ -8736,12 +9052,15 @@ async function dispatchTool(name, args) {
8736
9052
  function listSkills() {
8737
9053
  let out = `# Available skills (${SKILLS.length})
8738
9054
 
9055
+ `;
9056
+ out += `Each is tagged \`[audience]\` \u2014 \`core\` = building @godxjp/ui itself, \`consumer\` = building an app with it, \`both\`. App-devs: use \`list_consumer_skills\` to hide core material.
9057
+
8739
9058
  `;
8740
9059
  out += `Use \`get_skill_section skill="..." section="..."\` to drill in.
8741
9060
 
8742
9061
  `;
8743
9062
  for (const s of SKILLS) {
8744
- out += `## ${s.id} \u2014 ${s.name}
9063
+ out += `## ${s.id} \u2014 ${s.name} \`[${s.audience}]\`
8745
9064
  `;
8746
9065
  out += `**When to use:** ${s.whenToUse}
8747
9066
 
@@ -8754,6 +9073,124 @@ function listSkills() {
8754
9073
  _Source: ${SKILLS.map((s) => s.source).filter((v, i, a) => a.indexOf(v) === i).slice(0, 3).join("; ")}, \u2026_`;
8755
9074
  return out;
8756
9075
  }
9076
+ function listConsumerSkills() {
9077
+ const list = SKILLS.filter(isConsumerSkill);
9078
+ let out = `# Consumer skills (${list.length}) \u2014 building an app WITH @godxjp/ui
9079
+
9080
+ `;
9081
+ out += `Core library-maintenance skills are hidden. Drill in with \`get_consumer_skill skill="..." section="..."\`, or route a task with \`route_consumer_task\`.
9082
+
9083
+ `;
9084
+ for (const s of list) {
9085
+ out += `## ${s.id} \u2014 ${s.name} \`[${s.audience}]\`
9086
+ `;
9087
+ out += `**When to use:** ${s.whenToUse}
9088
+
9089
+ `;
9090
+ out += `**Sections:** ${s.sections.map((sec) => `\`${sec.id}\``).join(", ")}
9091
+
9092
+ `;
9093
+ }
9094
+ return out;
9095
+ }
9096
+ function getConsumerSkill(skillId, sectionId) {
9097
+ const skill = findSkill(skillId);
9098
+ if (skill && !isConsumerSkill(skill)) {
9099
+ return `Skill "${skillId}" is CORE-only (building @godxjp/ui itself) and isn't served to app-devs. Use \`list_consumer_skills\` for consumer-facing guidance${skillId === "component-discipline" ? ` \u2014 the standards you need when composing/extending are folded into \`compose-a-screen/state-and-a11y\` and \`design-to-page/verify\`.` : `.`}`;
9100
+ }
9101
+ return getSkillSection(skillId, sectionId);
9102
+ }
9103
+ function draftBugReport(args) {
9104
+ const get = (k) => {
9105
+ const v = args[k];
9106
+ return typeof v === "string" ? v.trim() : "";
9107
+ };
9108
+ const summary = get("summary");
9109
+ if (!summary) {
9110
+ return "Pass at least `summary` (one-line title). For a useful report also pass `repro`, `expected`, `actual`, and ideally `component` / `rule` / `version` / `env`. A vague report a maintainer can't reproduce is not enough.";
9111
+ }
9112
+ const repro = get("repro");
9113
+ const expected = get("expected");
9114
+ const actual = get("actual");
9115
+ const component = get("component");
9116
+ const rule = typeof args.rule === "number" ? args.rule : void 0;
9117
+ const version = get("version");
9118
+ const env = get("env");
9119
+ const missing = [];
9120
+ if (!repro) missing.push("repro");
9121
+ if (!expected) missing.push("expected");
9122
+ if (!actual) missing.push("actual");
9123
+ const ph = (label, val) => val ? val : `_(TODO: ${label} \u2014 required for a reproducible report)_`;
9124
+ const title = `[bug] ${summary}`;
9125
+ let body = `## Summary
9126
+
9127
+ ${summary}
9128
+
9129
+ `;
9130
+ body += `## Affected
9131
+
9132
+ `;
9133
+ body += component ? `- Component: \`${component}\` (see \`get_component name="${component}"\`)
9134
+ ` : `- Component: _(n/a)_
9135
+ `;
9136
+ body += rule !== void 0 ? `- Cardinal rule: #${rule} (see \`get_rule number=${rule}\`)
9137
+ ` : "";
9138
+ body += `
9139
+ ## Reproduction (minimal)
9140
+
9141
+ ${ph("repro", repro)}
9142
+
9143
+ `;
9144
+ body += `## Expected
9145
+
9146
+ ${ph("expected", expected)}
9147
+
9148
+ `;
9149
+ body += `## Actual
9150
+
9151
+ ${ph("actual", actual)}
9152
+
9153
+ `;
9154
+ body += `## Environment
9155
+
9156
+ `;
9157
+ body += `- @godxjp/ui version: ${version || "_(TODO: e.g. 12.1.0)_"}
9158
+ `;
9159
+ body += `- Env: ${env || "_(TODO: browser / OS / framework)_"}
9160
+
9161
+ `;
9162
+ body += `## Proposed fix
9163
+
9164
+ _(optional \u2014 what the library should do instead)_
9165
+ `;
9166
+ const q = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
9167
+ const cmd = `gh issue create --repo godx-jp/godxjp-ui --label bug --title ${q(title)} --body ${q(body)}`;
9168
+ let out = `# Draft bug report
9169
+
9170
+ `;
9171
+ if (missing.length) {
9172
+ out += `> \u26A0\uFE0F Incomplete \u2014 fill ${missing.map((m) => `\`${m}\``).join(", ")} before filing (a report a maintainer can't reproduce will bounce).
9173
+
9174
+ `;
9175
+ }
9176
+ out += `**Title:** ${title}
9177
+
9178
+ `;
9179
+ out += `## Issue body (Markdown)
9180
+
9181
+ ${body}
9182
+ `;
9183
+ out += `## File it (copy-paste \u2014 this tool does NOT run gh)
9184
+
9185
+ \`\`\`sh
9186
+ ${cmd}
9187
+ \`\`\`
9188
+
9189
+ `;
9190
+ out += `_Reminder: don't hand-roll a fake workaround to hide the bug \u2014 file this, then mark any minimal local workaround with \`// TODO(godxui#<n>)\`._
9191
+ `;
9192
+ return out;
9193
+ }
8757
9194
  function listPrimitives(group) {
8758
9195
  const list = group ? componentsByGroup(group) : COMPONENTS;
8759
9196
  if (list.length === 0) return `No components${group ? ` in group "${group}"` : ""}.`;
@@ -9093,11 +9530,11 @@ function getTokens(cat) {
9093
9530
  }
9094
9531
  return out;
9095
9532
  }
9096
- function routeTaskTool(task) {
9533
+ function routeTaskTool(task, opts) {
9097
9534
  if (!task.trim())
9098
9535
  return "Describe the task (e.g. 'design a premium agency hero', 'audit existing settings page').";
9099
- const results = routeTask(task);
9100
- let out = `# Routing "${task}"
9536
+ const results = routeTask(task, opts);
9537
+ let out = `# Routing "${task}"${opts?.consumerOnly ? " (consumer)" : ""}
9101
9538
 
9102
9539
  `;
9103
9540
  for (const r of results) {
@@ -9196,10 +9633,19 @@ function lintJsx(jsx) {
9196
9633
  check(/<input[\s>]/, "Use `<Input>` instead of raw `<input>` (rule 29).");
9197
9634
  check(/<select[\s>]/, "Use `<Select>` instead of raw `<select>` (rule 29).");
9198
9635
  check(/<textarea[\s>]/, "Use `<Textarea>` instead of raw `<textarea>` (rule 29).");
9636
+ check(/<(table|thead|tbody)[\s>]/, "Use `<DataTable>` instead of a hand-rolled `<table>` (rule 29).");
9199
9637
  check(
9200
9638
  /bg-(red|blue|green|yellow|gray|slate|zinc|neutral|stone|orange|amber|lime|emerald|teal|cyan|sky|indigo|violet|purple|fuchsia|pink|rose)-\d{2,3}\b/,
9201
9639
  "Use semantic token utilities (`bg-primary`/`bg-destructive`) not raw color scales (rule 2)."
9202
9640
  );
9641
+ check(
9642
+ /\b(?:ml|mr|pl|pr|left|right)-(?:\d|\[|auto|px|full|screen)|\b(?:rounded-[lr]|border-[lr]|text-(?:left|right))\b/,
9643
+ "Physical direction class \u2014 use logical CSS (`ms-/me-`, `ps-/pe-`, `start-/end-`, `rounded-s/e`, `text-start/end`) so the UI flips correctly under RTL (rule: logical CSS)."
9644
+ );
9645
+ check(
9646
+ /size=["']default["']/,
9647
+ '`size="default"` is not in the controlled vocabulary \u2014 use `size` \u2208 xs|sm|md|lg.'
9648
+ );
9203
9649
  check(
9204
9650
  /<Tag[\s\S]*?color=["']error["']/i,
9205
9651
  'Tag `color="error"` \u2192 `"destructive"` (v5.0, PR #60).'
@@ -9268,6 +9714,7 @@ Note: heuristic only \u2014 not a substitute for the full CI gate.
9268
9714
  }
9269
9715
 
9270
9716
  // src/resources/registry.ts
9717
+ var RULE_COUNT = CARDINAL_RULES.length;
9271
9718
  var RESOURCE_DEFINITIONS = [
9272
9719
  {
9273
9720
  uri: "godx-ui://components",
@@ -9289,8 +9736,8 @@ var RESOURCE_DEFINITIONS = [
9289
9736
  },
9290
9737
  {
9291
9738
  uri: "godx-ui://rules",
9292
- name: "Cardinal rules (34)",
9293
- description: "The 34 binding rules from CLAUDE.md as Markdown.",
9739
+ name: `Cardinal rules (${RULE_COUNT})`,
9740
+ description: `The ${RULE_COUNT} binding rules from CLAUDE.md as Markdown.`,
9294
9741
  mimeType: "text/markdown"
9295
9742
  },
9296
9743
  {
@@ -9397,7 +9844,7 @@ ${c.example}
9397
9844
  // package.json
9398
9845
  var package_default = {
9399
9846
  name: "@godxjp/ui-mcp",
9400
- version: "0.18.0",
9847
+ version: "0.19.1",
9401
9848
  description: "Model Context Protocol server for @godxjp/ui \u2014 gives Claude Code / Codex CLI / Cursor / any MCP-aware agent live access to the component catalog, prop vocabulary, design tokens, 34 cardinal rules, copy-paste-ready patterns, 12 design / taste skills synthesised from Leonxlnx/taste-skill, 20+ anti-AI-tell patterns, and a 50-check redesign audit \u2014 token-efficient (list \u2192 drill-down).",
9402
9849
  type: "module",
9403
9850
  main: "./dist/index.js",