@omnifyjp/ui-mcp 0.5.0
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/README.md +133 -0
- package/dist/index.js +2497 -0
- package/dist/index.js.map +1 -0
- package/dist/setup.js +68 -0
- package/dist/setup.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/resources/tokens.ts","../src/resources/rules.ts","../src/resources/components.ts","../src/resources/layout.ts","../src/resources/patterns.ts","../src/tools/validate.ts","../src/prompts.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { registerTokensResource } from './resources/tokens.js';\nimport { registerRulesResource } from './resources/rules.js';\nimport { registerComponentsResource } from './resources/components.js';\nimport { registerLayoutResource } from './resources/layout.js';\nimport { registerPatternsResource } from './resources/patterns.js';\nimport { registerValidateTools } from './tools/validate.js';\nimport { registerPrompts } from './prompts.js';\n\nconst server = new McpServer({\n name: '@omnifyjp/ui-mcp',\n version: '0.1.0',\n}, {\n capabilities: {\n resources: {},\n tools: {},\n prompts: {},\n },\n});\n\n// Register resources\nregisterTokensResource(server);\nregisterRulesResource(server);\nregisterComponentsResource(server);\nregisterLayoutResource(server);\nregisterPatternsResource(server);\n\n// Register tools\nregisterValidateTools(server);\n\n// Register prompts (slash commands)\nregisterPrompts(server);\n\n// Start server\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nconst TOKENS_CONTENT = `# @omnifyjp Design Tokens\n\nAll tokens are CSS custom properties defined in \\`@omnifyjp/ui-components/styles/theme.css\\`.\nConsumers override them via CSS: \\`:root { --primary: #dc2626; }\\`\n\n**IMPORTANT**: Never wrap \\`var(--token)\\` in \\`hsl()\\`. Use \\`var(--foreground)\\` directly.\n\n---\n\n## Color Tokens\n\n### Core Colors\n| Token | Tailwind Class | Light Default | Usage |\n|-------|---------------|---------------|-------|\n| \\`--background\\` | \\`bg-background\\` | \\`#ffffff\\` | Page background |\n| \\`--foreground\\` | \\`text-foreground\\` | \\`oklch(0.145 0 0)\\` | Default text |\n| \\`--card\\` | \\`bg-card\\` | \\`#ffffff\\` | Card backgrounds |\n| \\`--card-foreground\\` | \\`text-card-foreground\\` | \\`oklch(0.145 0 0)\\` | Card text |\n| \\`--popover\\` | \\`bg-popover\\` | \\`oklch(1 0 0)\\` | Popover/dropdown bg |\n| \\`--popover-foreground\\` | \\`text-popover-foreground\\` | \\`oklch(0.145 0 0)\\` | Popover text |\n| \\`--primary\\` | \\`bg-primary\\`, \\`text-primary\\` | \\`#030213\\` | Main actions, active states |\n| \\`--primary-foreground\\` | \\`text-primary-foreground\\` | \\`oklch(1 0 0)\\` | Text on primary bg |\n| \\`--secondary\\` | \\`bg-secondary\\` | \\`oklch(0.95 0.0058 264.53)\\` | Secondary actions |\n| \\`--secondary-foreground\\` | \\`text-secondary-foreground\\` | \\`#030213\\` | Text on secondary bg |\n| \\`--muted\\` | \\`bg-muted\\` | \\`#ececf0\\` | Muted backgrounds (replaces bg-gray-50/100) |\n| \\`--muted-foreground\\` | \\`text-muted-foreground\\` | \\`#717182\\` | Subdued text (replaces text-gray-500/600) |\n| \\`--accent\\` | \\`bg-accent\\`, \\`hover:bg-accent\\` | \\`#e9ebef\\` | Hover/active backgrounds |\n| \\`--accent-foreground\\` | \\`text-accent-foreground\\` | \\`#030213\\` | Text on accent bg |\n| \\`--destructive\\` | \\`bg-destructive\\`, \\`text-destructive\\` | \\`#d4183d\\` | Delete, errors, danger |\n| \\`--destructive-foreground\\` | \\`text-destructive-foreground\\` | \\`#ffffff\\` | Text on destructive bg |\n| \\`--border\\` | \\`border-border\\` | \\`rgba(0,0,0,0.1)\\` | All borders (replaces border-gray-*) |\n| \\`--input\\` | \\`border-input\\` | \\`transparent\\` | Input borders |\n| \\`--input-background\\` | \\`bg-input-background\\` | \\`#f3f3f5\\` | Input backgrounds |\n| \\`--ring\\` | \\`ring-ring\\` | \\`oklch(0.708 0 0)\\` | Focus rings |\n\n### Semantic Colors\n| Token | Tailwind Class | Light Default | Usage |\n|-------|---------------|---------------|-------|\n| \\`--success\\` | \\`bg-success\\`, \\`text-success\\` | \\`#10b981\\` | Confirmed, approved, completed |\n| \\`--warning\\` | \\`bg-warning\\`, \\`text-warning\\` | \\`#f59e0b\\` | Caution, needs attention |\n| \\`--info\\` | \\`bg-info\\`, \\`text-info\\` | \\`#3b82f6\\` | Informational highlights |\n| \\`--error\\` | \\`bg-error\\`, \\`text-error\\` | \\`#ef4444\\` | Error states |\n\nEach has a matching \\`--*-foreground\\` for text on that background.\n\n### Sidebar Colors\n| Token | Tailwind Class | Usage |\n|-------|---------------|-------|\n| \\`--sidebar\\` | \\`bg-sidebar\\` | Sidebar background |\n| \\`--sidebar-foreground\\` | \\`text-sidebar-foreground\\` | Sidebar text |\n| \\`--sidebar-primary\\` | \\`text-sidebar-primary\\` | Active sidebar item |\n| \\`--sidebar-accent\\` | \\`bg-sidebar-accent\\` | Sidebar hover state |\n| \\`--sidebar-border\\` | \\`border-sidebar-border\\` | Sidebar borders |\n\n### Chart Colors\n\\`--chart-1\\` through \\`--chart-5\\` → \\`bg-chart-1\\` etc.\n\n---\n\n## Density Tokens\n\nThese create spacing utility classes via Tailwind v4 theme mapping.\n\n### Page Layout\n| Token | Tailwind Classes | Default | Usage |\n|-------|-----------------|---------|-------|\n| \\`--density-page\\` | \\`p-page\\`, \\`px-page\\`, \\`py-page\\`, \\`pt-page\\`, etc. | \\`1rem\\` (16px) | Page content padding |\n| \\`--density-section\\` | \\`gap-section\\`, \\`space-y-section\\` | \\`1rem\\` (16px) | Gap between sections |\n| \\`--density-page-title\\` | \\`text-page-title\\` | \\`1.25rem\\` (20px) | Page title font-size |\n\n### Element Heights\n| Token | Tailwind Classes | Default | Usage |\n|-------|-----------------|---------|-------|\n| \\`--density-element-xs\\` | \\`h-element-xs\\` | \\`1.5rem\\` (24px) | Compact tables, inline actions |\n| \\`--density-element-sm\\` | \\`h-element-sm\\` | \\`1.75rem\\` (28px) | Secondary actions |\n| \\`--density-element\\` | \\`h-element\\`, \\`min-h-element\\` | \\`2rem\\` (32px) | Standard buttons, inputs |\n| \\`--density-element-lg\\` | \\`h-element-lg\\` | \\`2.25rem\\` (36px) | Primary CTAs |\n| \\`--density-element-xl\\` | \\`h-element-xl\\` | \\`2.75rem\\` (44px) | Login forms, hero sections |\n\n### Container Spacing\n| Token | Tailwind Classes | Default | Usage |\n|-------|-----------------|---------|-------|\n| \\`--density-card\\` | \\`p-card\\`, \\`px-card\\`, \\`pt-card\\` | \\`1rem\\` (16px) | Card internal padding |\n| \\`--density-dialog\\` | \\`p-dialog\\` | \\`1.25rem\\` (20px) | Dialog internal padding |\n| \\`--density-table-head\\` | \\`h-table-head\\` | \\`2rem\\` (32px) | Table header row height |\n\n---\n\n## Layout Tokens\n\n| Token | Default | Usage |\n|-------|---------|-------|\n| \\`--header-height\\` | \\`3rem\\` (48px) | Header bar height. Tailwind: \\`h-header\\` |\n| \\`--sidebar-width\\` | \\`16rem\\` (256px) | Expanded sidebar |\n| \\`--sidebar-collapsed-width\\` | \\`4rem\\` (64px) | Collapsed sidebar |\n| \\`--content-sidebar-width\\` | \\`20rem\\` (320px) | PageContainer sidebar |\n| \\`--container-max-width\\` | \\`1280px\\` | Content max width |\n\n---\n\n## Border & Radius Tokens\n\n| Token | Default | Usage |\n|-------|---------|-------|\n| \\`--radius\\` | \\`0.375rem\\` (6px) | Base radius |\n| \\`--radius-sm\\` | calc(--radius - 4px) | Small elements |\n| \\`--radius-md\\` | calc(--radius - 2px) | Medium elements |\n| \\`--radius-lg\\` | var(--radius) | Large elements |\n| \\`--radius-xl\\` | calc(--radius + 4px) | Extra large elements |\n\n---\n\n## Shadow Tokens\n\n\\`--shadow-sm\\`, \\`--shadow\\`, \\`--shadow-md\\`, \\`--shadow-lg\\`, \\`--shadow-xl\\`, \\`--shadow-2xl\\`, \\`--shadow-inner\\`\n\n---\n\n## Transition Tokens\n\n| Token | Default | Usage |\n|-------|---------|-------|\n| \\`--transition-fast\\` | \\`150ms\\` | Hover states |\n| \\`--transition-base\\` | \\`200ms\\` | General transitions |\n| \\`--transition-slow\\` | \\`300ms\\` | Larger animations |\n| \\`--transition-slower\\` | \\`500ms\\` | Complex animations |\n\nEasing: \\`--ease-in\\`, \\`--ease-out\\`, \\`--ease-in-out\\`\n\n---\n\n## Z-Index Tokens\n\n| Token | Value | Usage |\n|-------|-------|-------|\n| \\`--z-dropdown\\` | 1000 | Dropdown menus |\n| \\`--z-sticky\\` | 1020 | Sticky headers |\n| \\`--z-fixed\\` | 1030 | Fixed elements |\n| \\`--z-modal-backdrop\\` | 1040 | Modal backdrops |\n| \\`--z-modal\\` | 1050 | Modals |\n| \\`--z-popover\\` | 1060 | Popovers |\n| \\`--z-tooltip\\` | 1070 | Tooltips |\n\n---\n\n## Shared Types\n\n\\`\\`\\`typescript\ntype UIColor = 'primary' | 'destructive' | 'success' | 'warning' | 'info';\ntype UIVariant = 'default' | 'secondary' | 'outline' | 'soft' | 'ghost' | 'link';\ntype UISize = 'xs' | 'sm' | 'default' | 'lg' | 'xl';\n\\`\\`\\`\n\n### UIColor × CSS Variables\nEach UIColor maps to \\`--{color}\\` and \\`--{color}-foreground\\` in theme.css.\n\n### UIVariant rendering\n| Variant | Solid bg? | Border? | Description |\n|---------|-----------|---------|-------------|\n| \\`default\\` | Yes | No | Filled background |\n| \\`secondary\\` | Muted | No | Gray background |\n| \\`outline\\` | No | Yes | Border + colored text |\n| \\`soft\\` | 10% opacity | No | Light tinted bg |\n| \\`ghost\\` | No → hover | No | Transparent, hover shows bg |\n| \\`link\\` | No | No | Underline on hover |\n\n### UISize × height tokens\n| Size | Height Token | Pixels |\n|------|-------------|--------|\n| \\`xs\\` | \\`h-element-xs\\` | 24px |\n| \\`sm\\` | \\`h-element-sm\\` | 28px |\n| \\`default\\` | \\`h-element\\` | 32px |\n| \\`lg\\` | \\`h-element-lg\\` | 36px |\n| \\`xl\\` | \\`h-element-xl\\` | 44px |\n`;\n\nexport function registerTokensResource(server: McpServer) {\n server.resource('design-tokens', 'design://tokens', {\n description: 'Complete design token reference for @omnifyjp/ui — colors, density, layout, shadows, z-index, transitions, and shared types (UIColor, UIVariant, UISize)',\n mimeType: 'text/markdown',\n }, async () => ({\n contents: [{\n uri: 'design://tokens',\n mimeType: 'text/markdown',\n text: TOKENS_CONTENT,\n }],\n }));\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nconst RULES_CONTENT = `# @omnifyjp Design Rules\n\n## Cheat Sheet: WRONG → RIGHT\n\n### Color Replacements\n| WRONG (never use) | RIGHT (use this) | Context |\n|-------------------|------------------|---------|\n| \\`bg-white\\` | \\`bg-background\\` or \\`bg-card\\` | Page/card backgrounds |\n| \\`bg-gray-50\\`, \\`bg-gray-100\\` | \\`bg-muted\\` | Muted/subtle backgrounds |\n| \\`hover:bg-gray-50\\` | \\`hover:bg-accent\\` | Hover states |\n| \\`text-gray-900\\`, \\`text-gray-700\\` | \\`text-foreground\\` | Primary text |\n| \\`text-gray-600\\`, \\`text-gray-500\\` | \\`text-muted-foreground\\` | Secondary/subdued text |\n| \\`border-gray-200\\`, \\`border-gray-300\\` | \\`border-border\\` | All borders |\n| \\`text-blue-600\\` | \\`text-primary\\` | Primary brand color |\n| \\`bg-blue-50 text-blue-600\\` | \\`bg-primary/10 text-primary\\` | Active/selected state |\n| \\`bg-blue-50 text-blue-600\\` (in sidebar) | \\`bg-sidebar-primary/10 text-sidebar-primary\\` | Sidebar active state |\n| \\`bg-red-50\\` | \\`bg-destructive/10\\` | Destructive soft bg |\n| \\`bg-red-500\\` | \\`bg-destructive\\` | Destructive solid bg |\n| \\`bg-green-500\\` | \\`bg-success\\` | Success solid bg |\n\n### Density Replacements\n| WRONG (never use) | RIGHT (use this) | Context |\n|-------------------|------------------|---------|\n| \\`p-6\\`, \\`p-8\\` | \\`p-page\\` | Page content padding |\n| \\`gap-6\\`, \\`space-y-6\\` | \\`gap-section\\` or \\`space-y-section\\` | Section gaps |\n| \\`h-9\\`, \\`h-10\\` | \\`h-element\\` or \\`h-element-lg\\` | Button/input heights |\n| \\`h-14\\` | \\`h-header\\` | Header height |\n| \\`text-3xl font-bold\\` | \\`text-page-title font-semibold\\` | Page titles |\n| \\`px-6 pt-6\\` | \\`px-card pt-card\\` | Card padding |\n| \\`p-6\\` (in dialog) | \\`p-dialog\\` | Dialog padding |\n\n---\n\n## Rules: DO\n\n1. **Use Tailwind classes** — never inline styles\n2. **Use semantic color tokens only** — \\`bg-primary\\`, \\`text-foreground\\`, \\`border-border\\`\n3. **Use density tokens** — \\`p-page\\`, \\`h-element\\`, \\`gap-section\\`\n4. **Every interactive element**: must have hover state + transition + focus ring\n5. **Selection pattern**: \\`border-primary bg-primary/5\\` + Check icon from lucide-react\n6. **Icons**: \\`lucide-react\\` only — never emoji, never other icon libraries\n7. **Badge text**: max 2 characters\n8. **Buttons with icons**: use \\`gap-2\\`\n9. **Pages inside \\`<Layout>\\`**: never recreate sidebar/header\n10. **Content wrapper**: \\`<div className=\"p-page\">\\`\n11. **Page title**: \\`text-page-title font-semibold\\`\n12. **Gaps between sections**: \\`gap-section\\` or \\`space-y-section\\`\n13. **Toast notifications**: \\`toast.success()\\` / \\`toast.error()\\` via sonner\n14. **TypeScript strict** — no \\`any\\`\n15. **Immutable state updates**\n16. **Import order**: React → router → types → data → ui → icons → sonner\n17. **Drawer content**: wrap in \\`<DrawerBody>\\`\n\n---\n\n## Rules: DON'T\n\n1. **NEVER** hardcoded Tailwind colors (\\`text-blue-600\\`, \\`bg-red-50\\`) for interactive/brand states\n - **Exception**: multi-color status badges where each status is a DIFFERENT color by design\n2. **NEVER** hardcoded spacing/heights (\\`p-6\\`, \\`h-10\\`) — use density tokens\n3. **NEVER** use \\`h-screen\\` on page components (Root component handles it)\n4. **NEVER** use inline styles\n5. **NEVER** wrap \\`var(--token)\\` in \\`hsl()\\` — tokens use mixed color formats (oklch, hex, rgba)\n6. **NEVER** use emoji as icons\n7. **NEVER** use \\`bg-white\\` — use \\`bg-background\\` or \\`bg-card\\`\n\n---\n\n## Sidebar Sizing (CRITICAL)\n\nAll sidebar elements MUST follow these exact sizes:\n\n| Element | Size |\n|---------|------|\n| Badges/avatars | \\`w-8 h-8\\` (32px) |\n| Mini badges | \\`w-5 h-5\\` (20px) |\n| Menu icons | \\`w-4 h-4\\` (16px) |\n| Primary text | \\`text-sm font-semibold\\` |\n| Menu text | \\`text-sm\\` |\n| Secondary text | \\`text-xs text-muted-foreground\\` |\n| Menu padding | \\`px-3 py-2 gap-2 rounded-md\\` |\n| Logo/Header | \\`h-header\\` (48px) |\n\n**NEVER** in sidebar:\n- \\`w-10 h-10\\` for badges (too large)\n- \\`w-5 h-5\\` for menu icons (too large)\n- \\`text-base\\` or \\`text-xl\\` for text (too large)\n\n---\n\n## Interaction Patterns\n\n### Hover States\nEvery clickable element needs:\n\\`\\`\\`\nhover:bg-accent transition-colors\n\\`\\`\\`\n\n### Focus Ring\nAll focusable elements:\n\\`\\`\\`\nfocus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\n\\`\\`\\`\n\n### Selection (Checkmark Pattern)\nSelected items use:\n\\`\\`\\`tsx\n<div className={cn(\n \"border rounded-lg p-4 cursor-pointer transition-colors\",\n selected\n ? \"border-primary bg-primary/5\"\n : \"border-border hover:border-primary/50\"\n)}>\n {selected && <Check className=\"w-4 h-4 text-primary\" />}\n</div>\n\\`\\`\\`\n\n### Avatar Border (Active State)\n\\`\\`\\`\nring-2 ring-primary ring-offset-2 ring-offset-background\n\\`\\`\\`\n\n---\n\n## Code Quality\n\n### TypeScript\n- Strict mode — no \\`any\\` types\n- Use immutable state updates\n- Proper discriminated unions for status types\n\n### Import Order\n\\`\\`\\`typescript\n// 1. React\nimport { useState } from 'react';\n// 2. Router\nimport { useNavigate } from 'react-router';\n// 3. Types\nimport type { Task } from '../types';\n// 4. Data\nimport { mockTasks } from '../data';\n// 5. UI components\nimport { Button, Card } from '@omnifyjp/ui';\n// 6. Icons\nimport { Plus, Trash2 } from 'lucide-react';\n// 7. Toast\nimport { toast } from 'sonner';\n\\`\\`\\`\n\n### Naming Conventions\n- Components: PascalCase (\\`TaskDetail\\`, \\`ProjectBoard\\`)\n- Files: kebab-case (\\`task-detail.tsx\\`, \\`project-board.tsx\\`)\n- Domain components: prefix by domain (\\`Workflow*\\`, \\`Calendar*\\`, \\`Scope*\\`)\n- Badges: \\`*Badge\\` suffix\n- Grids: \\`*Grid\\` suffix\n- Diagrams: \\`*Diagram\\` suffix\n- Compact variants: \\`*Mini\\` suffix\n`;\n\nexport function registerRulesResource(server: McpServer) {\n server.resource('design-rules', 'design://rules', {\n description: 'Design system rules — WRONG→RIGHT cheat sheet, DOs/DON\\'Ts, sidebar sizing, interaction patterns, code quality standards',\n mimeType: 'text/markdown',\n }, async () => ({\n contents: [{\n uri: 'design://rules',\n mimeType: 'text/markdown',\n text: RULES_CONTENT,\n }],\n }));\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nconst COMPONENTS_CONTENT = `# @omnifyjp/ui-components Component Inventory\n\n## Package: \\`@omnifyjp/ui-components\\`\n\nImport: \\`import { Button, Card, ... } from '@omnifyjp/ui-components';\\`\nUtility: \\`import { cn } from '@omnifyjp/ui-components';\\`\nTypes: \\`import type { UIColor, UIVariant, UISize } from '@omnifyjp/ui-components';\\`\n\n---\n\n## Primitives (53)\n\n### Data Entry\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`Input\\` | Text input | \\`size?: UISize\\` |\n| \\`PasswordInput\\` | Password with show/hide toggle | \\`size?: UISize\\` |\n| \\`Textarea\\` | Multi-line text | — |\n| \\`Checkbox\\` | Checkbox | \\`checked, onCheckedChange\\` |\n| \\`RadioGroup\\` + \\`RadioGroupItem\\` | Radio buttons | \\`value, onValueChange\\` |\n| \\`Select\\` + \\`SelectTrigger\\` + \\`SelectContent\\` + \\`SelectItem\\` | Dropdown select | \\`value, onValueChange\\`. **Never use empty string as value** |\n| \\`Switch\\` | Toggle switch | \\`checked, onCheckedChange\\` |\n| \\`Slider\\` | Range slider | \\`value, onValueChange\\` |\n| \\`DatePicker\\` | Date picker | \\`value, onChange, locale?\\` |\n| \\`TimePicker\\` | Time picker | \\`value, onChange\\` |\n| \\`InputOTP\\` | One-time password input | \\`maxLength, value, onChange\\` |\n| \\`Combobox\\` | Searchable select | \\`options, value, onValueChange\\` |\n| \\`ColorPicker\\` | Color picker | \\`value, onChange\\` |\n| \\`TagInput\\` | Tag/chip input | \\`tags, onTagsChange\\` |\n| \\`Rating\\` | Star rating | \\`value, onChange\\` |\n| \\`FileUpload\\` | File upload area | \\`onFilesSelected\\` |\n| \\`Form\\` + \\`FormField\\` + \\`FormItem\\` + \\`FormLabel\\` + \\`FormControl\\` + \\`FormMessage\\` | Form with react-hook-form | \\`form (useForm)\\` |\n| \\`Label\\` | Form label | \\`htmlFor\\` |\n\n### Data Display\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`Table\\` + \\`TableHeader\\` + \\`TableBody\\` + \\`TableRow\\` + \\`TableHead\\` + \\`TableCell\\` | Data table | — |\n| \\`Card\\` + \\`CardHeader\\` + \\`CardTitle\\` + \\`CardDescription\\` + \\`CardContent\\` + \\`CardFooter\\` | Card container | — |\n| \\`Badge\\` | Status/label badge | \\`variant?: UIVariant, color?: UIColor\\` |\n| \\`Avatar\\` + \\`AvatarImage\\` + \\`AvatarFallback\\` | User avatar | \\`src, alt\\` |\n| \\`Calendar\\` | Calendar grid | \\`selected, onSelect, locale?\\` |\n| \\`Progress\\` | Progress bar | \\`value (0-100)\\` |\n| \\`Skeleton\\` | Loading placeholder | \\`className\\` |\n| \\`Separator\\` | Horizontal/vertical line | \\`orientation?\\` |\n| \\`HoverCard\\` + \\`HoverCardTrigger\\` + \\`HoverCardContent\\` | Hover info card | — |\n| \\`Chart\\` | Recharts wrapper | See chart docs |\n| \\`AspectRatio\\` | Fixed aspect ratio container | \\`ratio\\` |\n| \\`Carousel\\` | Swipeable carousel | \\`opts\\` |\n\n### Actions\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`Button\\` | Button | \\`variant?: UIVariant, color?: UIColor, size?: UISize\\` |\n| \\`Toggle\\` | Toggle button | \\`pressed, onPressedChange\\` |\n| \\`ToggleGroup\\` | Toggle button group | \\`type, value, onValueChange\\` |\n\n### Navigation\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`Tabs\\` + \\`TabsList\\` + \\`TabsTrigger\\` + \\`TabsContent\\` | Tab navigation | \\`value, onValueChange\\` |\n| \\`Breadcrumb\\` + \\`BreadcrumbItem\\` + \\`BreadcrumbLink\\` + \\`BreadcrumbSeparator\\` | Breadcrumb trail | — |\n| \\`NavigationMenu\\` | Top navigation menu | — |\n| \\`Menubar\\` | Menu bar | — |\n| \\`Pagination\\` | Page navigation | — |\n| \\`Sidebar\\` + \\`SidebarProvider\\` + \\`SidebarTrigger\\` + \\`SidebarContent\\` + \\`SidebarGroup\\` + \\`SidebarMenu\\` + \\`SidebarMenuItem\\` + \\`SidebarMenuButton\\` | Sidebar navigation | — |\n| \\`Command\\` + \\`CommandInput\\` + \\`CommandList\\` + \\`CommandItem\\` | Command palette (cmdk) | — |\n\n### Overlays\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`Dialog\\` + \\`DialogTrigger\\` + \\`DialogContent\\` + \\`DialogHeader\\` + \\`DialogTitle\\` + \\`DialogDescription\\` + \\`DialogFooter\\` | Modal dialog | \\`open, onOpenChange\\` |\n| \\`Sheet\\` + \\`SheetTrigger\\` + \\`SheetContent\\` | Side panel | \\`open, onOpenChange, side\\` |\n| \\`Drawer\\` + \\`DrawerTrigger\\` + \\`DrawerContent\\` + \\`DrawerBody\\` | Bottom drawer (vaul) | \\`open, onOpenChange\\`. **Wrap content in DrawerBody** |\n| \\`AlertDialog\\` | Confirmation dialog | \\`open, onOpenChange\\` |\n| \\`Popover\\` + \\`PopoverTrigger\\` + \\`PopoverContent\\` | Popover | — |\n| \\`DropdownMenu\\` + \\`DropdownMenuTrigger\\` + \\`DropdownMenuContent\\` + \\`DropdownMenuItem\\` | Dropdown menu | — |\n| \\`ContextMenu\\` | Right-click menu | — |\n| \\`Tooltip\\` + \\`TooltipTrigger\\` + \\`TooltipContent\\` | Tooltip | Wrap in \\`TooltipProvider\\` |\n\n### Feedback\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`Alert\\` + \\`AlertTitle\\` + \\`AlertDescription\\` | Alert banner | \\`variant?: UIVariant, color?: UIColor\\` |\n| \\`Sonner\\` / \\`toast\\` | Toast notifications | \\`toast.success(msg)\\`, \\`toast.error(msg)\\` |\n\n### Layout\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`Accordion\\` + \\`AccordionItem\\` + \\`AccordionTrigger\\` + \\`AccordionContent\\` | Collapsible sections | \\`type, value\\` |\n| \\`Collapsible\\` | Single collapsible | \\`open, onOpenChange\\` |\n| \\`Resizable\\` + \\`ResizablePanel\\` + \\`ResizablePanelGroup\\` + \\`ResizableHandle\\` | Resizable panels | \\`direction\\` |\n| \\`ScrollArea\\` | Custom scrollbar area | \\`className\\` |\n\n---\n\n## Domain Components (14)\n\nAll domain components are **locale-agnostic**: they accept a \\`labels\\` prop with English defaults. Do NOT use \\`useTranslation\\` inside domain components.\n\n### Calendar Domain\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`CalendarMini\\` | Compact calendar widget | \\`selected, onSelect\\` |\n| \\`CalendarEventChip\\` | Event indicator chip | \\`event, color\\` |\n| \\`CalendarEventSheet\\` | Event detail side panel | \\`event, open, onOpenChange\\` |\n| \\`CalendarToolbar\\` | Calendar navigation toolbar | \\`date, view, onDateChange, onViewChange\\` |\n| \\`CalendarCategoryBadge\\` | Category color badge | \\`category\\` |\n\n### Workflow Domain\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`WorkflowStepper\\` | Step-by-step progress indicator | \\`steps, currentStep\\` |\n| \\`WorkflowDiagram\\` | Visual workflow flow diagram | \\`nodes, edges\\` |\n| \\`WorkflowStatusBadge\\` | Workflow status indicator | \\`status\\` |\n| \\`WorkflowCategoryBadge\\` | Workflow category badge | \\`category\\` |\n| \\`StageTypeBadge\\` | Stage type indicator | \\`type\\` |\n\n### RBAC Domain\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`PermissionGrid\\` | Permission matrix grid | \\`modules, actions, permissions, onChange\\`. Uses \\`module:action\\` ID format |\n| \\`ScopeTree\\` | Hierarchical scope tree | \\`scopes, selectedScope, onSelectScope\\` |\n| \\`ScopeTypeBadge\\` | Scope type indicator | \\`type\\` |\n\n### Utility\n| Component | Description | Key Props |\n|-----------|-------------|-----------|\n| \\`SlugInput\\` | URL slug input with auto-generation | \\`value, onChange, source\\` |\n\n---\n\n## Shared Types API\n\n### Button\n\\`\\`\\`tsx\n<Button\n variant=\"default\" | \"secondary\" | \"outline\" | \"soft\" | \"ghost\" | \"link\"\n color=\"primary\" | \"destructive\" | \"success\" | \"warning\" | \"info\"\n size=\"xs\" | \"sm\" | \"default\" | \"lg\" | \"xl\"\n>\n Label\n</Button>\n\\`\\`\\`\n\n### Badge\n\\`\\`\\`tsx\n<Badge\n variant=\"default\" | \"secondary\" | \"outline\" | \"soft\"\n color=\"primary\" | \"destructive\" | \"success\" | \"warning\" | \"info\"\n>\n Label\n</Badge>\n\\`\\`\\`\n\n### Alert\n\\`\\`\\`tsx\n<Alert\n variant=\"default\" | \"soft\" | \"outline\"\n color=\"primary\" | \"destructive\" | \"success\" | \"warning\" | \"info\"\n>\n <AlertTitle>Title</AlertTitle>\n <AlertDescription>Description</AlertDescription>\n</Alert>\n\\`\\`\\`\n\n---\n\n## Utility: \\`cn()\\`\n\nMerge Tailwind classes with conflict resolution:\n\\`\\`\\`tsx\nimport { cn } from '@omnifyjp/ui-components';\n\n<div className={cn(\"p-4 bg-card\", isActive && \"bg-primary/10\", className)} />\n\\`\\`\\`\n\n---\n\n## Package: \\`@omnifyjp/ui\\`\n\n| Component | Description |\n|-----------|-------------|\n| \\`AppShell\\` | Root layout wrapper (ThemeProvider → I18nextProvider → OrganizationProvider) |\n| \\`Sidebar\\` | Main navigation sidebar |\n| \\`Header\\` | Top header bar |\n| \\`StandardPageContainer\\` | Standard page layout with optional sidebar |\n| \\`SplitPageContainer\\` | Split-pane page layout |\n| \\`FullWidthPageContainer\\` | Full-width page layout |\n\ni18n: \\`initOmnifyI18n()\\` to add service namespaces.\n\n---\n\n## Package: \\`@omnifyjp/editor\\`\n\n| Component | Description |\n|-----------|-------------|\n| \\`RichTextEditor\\` | Tiptap-based rich text editor |\n| \\`BlockEditor\\` | BlockNote-based block editor |\n\nCSS import required: \\`import '@omnifyjp/editor/styles/rich-text-editor.css'\\`\n`;\n\nexport function registerComponentsResource(server: McpServer) {\n server.resource('design-components', 'design://components', {\n description: 'Complete component inventory — 53 primitives, 14 domain components, shell, editor, with props and usage examples',\n mimeType: 'text/markdown',\n }, async () => ({\n contents: [{\n uri: 'design://components',\n mimeType: 'text/markdown',\n text: COMPONENTS_CONTENT,\n }],\n }));\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nconst LAYOUT_CONTENT = `# @omnifyjp Layout Patterns\n\n## Page Layout\n\nEvery page component follows this structure:\n\n\\`\\`\\`tsx\nimport { Layout } from './Layout';\n\nexport function MyPage() {\n return (\n <Layout>\n <div className=\"p-page\">\n <h1 className=\"text-page-title font-semibold\">Page Title</h1>\n <div className=\"space-y-section\">\n {/* Page content */}\n </div>\n </div>\n </Layout>\n );\n}\n\\`\\`\\`\n\n**Rules:**\n- Always wrap in \\`<Layout>\\` — never recreate sidebar/header\n- Page content padding: \\`p-page\\` (NOT \\`p-6\\` or \\`p-8\\`)\n- Page title: \\`text-page-title font-semibold\\` (NOT \\`text-3xl font-bold\\`)\n- Section gaps: \\`gap-section\\` or \\`space-y-section\\` (NOT \\`gap-6\\` or \\`space-y-6\\`)\n- **NO \\`h-screen\\`** on page components — the root layout handles full height\n\n---\n\n## Card Pattern\n\n\\`\\`\\`tsx\n<Card>\n <CardHeader className=\"px-card pt-card\">\n <CardTitle>Title</CardTitle>\n <CardDescription>Description</CardDescription>\n </CardHeader>\n <CardContent className=\"px-card pb-card\">\n {/* Content */}\n </CardContent>\n</Card>\n\\`\\`\\`\n\n**Rules:**\n- Card padding: \\`px-card\\`, \\`pt-card\\`, \\`pb-card\\` (NOT \\`px-6\\`, \\`pt-6\\`)\n- Card background: inherits \\`bg-card\\` automatically\n\n---\n\n## Dialog Pattern\n\n\\`\\`\\`tsx\n<Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Dialog Title</DialogTitle>\n <DialogDescription>Optional description</DialogDescription>\n </DialogHeader>\n <div className=\"p-dialog space-y-4\">\n {/* Dialog body */}\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>Cancel</Button>\n <Button>Submit</Button>\n </DialogFooter>\n </DialogContent>\n</Dialog>\n\\`\\`\\`\n\n**Rules:**\n- Dialog body padding: \\`p-dialog\\` (NOT \\`p-6\\`)\n- Props: \\`{ open: boolean; onOpenChange: (open: boolean) => void }\\`\n\n---\n\n## Table Pattern\n\n\\`\\`\\`tsx\n<Table>\n <TableHeader>\n <TableRow className=\"h-table-head\">\n <TableHead>Column</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n <TableRow className=\"hover:bg-accent transition-colors\">\n <TableCell>Data</TableCell>\n </TableRow>\n </TableBody>\n</Table>\n\\`\\`\\`\n\n**Rules:**\n- Header row: \\`h-table-head\\` (NOT \\`h-10\\`)\n- Row hover: \\`hover:bg-accent transition-colors\\`\n\n---\n\n## Selection State Pattern\n\nFor selectable items (cards, list items):\n\n\\`\\`\\`tsx\n<div className={cn(\n \"border rounded-lg p-4 cursor-pointer transition-colors\",\n selected\n ? \"border-primary bg-primary/5\"\n : \"border-border hover:border-primary/50\"\n)}>\n <div className=\"flex items-center justify-between\">\n <span>{label}</span>\n {selected && <Check className=\"w-4 h-4 text-primary\" />}\n </div>\n</div>\n\\`\\`\\`\n\n**Rules:**\n- Selected: \\`border-primary bg-primary/5\\` + Check icon\n- Unselected hover: \\`hover:border-primary/50\\`\n- Always include transition: \\`transition-colors\\`\n\n---\n\n## Sidebar Layout\n\n### Sidebar Structure\n\\`\\`\\`\nSidebar (w-64 / 256px)\n├── Header (h-header / 48px)\n│ ├── Logo/Avatar (w-8 h-8)\n│ └── Title (text-sm font-semibold)\n├── Menu Groups\n│ ├── Group Label (text-xs text-muted-foreground uppercase)\n│ └── Menu Items (px-3 py-2 gap-2 rounded-md)\n│ ├── Icon (w-4 h-4)\n│ └── Text (text-sm)\n└── Footer\n └── User Info\n\\`\\`\\`\n\n### Sidebar Token Usage\n| Element | Classes |\n|---------|---------|\n| Background | \\`bg-sidebar\\` |\n| Text | \\`text-sidebar-foreground\\` |\n| Active item bg | \\`bg-sidebar-primary/10\\` |\n| Active item text | \\`text-sidebar-primary\\` |\n| Hover state | \\`hover:bg-sidebar-accent\\` |\n| Border | \\`border-sidebar-border\\` |\n| Badge/avatar | \\`w-8 h-8\\` (32px) |\n| Mini badge | \\`w-5 h-5\\` (20px) |\n| Menu icon | \\`w-4 h-4\\` (16px) |\n\n---\n\n## Header Layout\n\n\\`\\`\\`tsx\n<header className=\"h-header border-b border-border flex items-center px-page\">\n <div className=\"flex items-center gap-2\">\n {/* Header content */}\n </div>\n</header>\n\\`\\`\\`\n\n**Rules:**\n- Height: \\`h-header\\` (48px, NOT \\`h-14\\` or \\`h-16\\`)\n- Padding: \\`px-page\\`\n\n---\n\n## Drawer Pattern\n\n\\`\\`\\`tsx\n<Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent>\n <DrawerHeader>\n <DrawerTitle>Title</DrawerTitle>\n </DrawerHeader>\n <DrawerBody>\n {/* Content MUST be wrapped in DrawerBody */}\n </DrawerBody>\n </DrawerContent>\n</Drawer>\n\\`\\`\\`\n\n**Rules:**\n- Content MUST be wrapped in \\`<DrawerBody>\\`\n\n---\n\n## Avatar Border Pattern (Active User)\n\n\\`\\`\\`tsx\n<Avatar className={cn(\n \"w-8 h-8\",\n isActive && \"ring-2 ring-primary ring-offset-2 ring-offset-background\"\n)}>\n <AvatarImage src={user.avatar} />\n <AvatarFallback>{user.initials}</AvatarFallback>\n</Avatar>\n\\`\\`\\`\n\n---\n\n## Responsive Considerations\n\n- Sidebar collapses at mobile breakpoint (use \\`useIsMobile()\\` hook from \\`@omnifyjp/ui-components\\`)\n- Page padding \\`p-page\\` adjusts via density tokens\n- Element heights via \\`h-element-*\\` tokens scale consistently\n`;\n\nexport function registerLayoutResource(server: McpServer) {\n server.resource('design-layout', 'design://layout', {\n description: 'Layout patterns — page, card, dialog, table, sidebar, header, drawer, selection state, avatar borders',\n mimeType: 'text/markdown',\n }, async () => ({\n contents: [{\n uri: 'design://layout',\n mimeType: 'text/markdown',\n text: LAYOUT_CONTENT,\n }],\n }));\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nconst PATTERNS_CONTENT = `# @omnifyjp Page Patterns (from Demo App)\n\nThese are real-world patterns extracted from the reference demo app.\nEvery consumer page MUST follow these structures to maintain visual consistency.\n\n---\n\n## 1. LIST PAGE (Card Grid)\n\nUsed for: project lists, organization lists, product catalogs.\n\n\\`\\`\\`tsx\n<div className=\"p-page space-y-section\">\n {/* Header: title + action */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-page-title font-semibold mb-2\">Page Title</h1>\n <p className=\"text-sm text-muted-foreground\">Description text</p>\n </div>\n <Button><Plus className=\"w-4 h-4\" /> Create</Button>\n </div>\n\n {/* Card Grid: responsive columns */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-section\">\n {items.map((item) => (\n <Card key={item.id} className=\"hover:shadow-lg transition-shadow\">\n <CardHeader>\n <div className=\"flex items-start justify-between mb-3\">\n <div\n className=\"w-12 h-12 rounded-lg flex items-center justify-center text-white font-bold\"\n style={{ backgroundColor: item.color }}\n >\n {item.initials}\n </div>\n <Badge variant=\"secondary\">{item.count}</Badge>\n </div>\n <CardTitle>\n <Link to={item.url} className=\"hover:text-primary transition-colors\">\n {item.name}\n </Link>\n </CardTitle>\n <CardDescription className=\"line-clamp-2\">{item.description}</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {/* Progress bar */}\n <div className=\"w-full bg-muted rounded-full h-2\">\n <div\n className=\"h-2 rounded-full bg-primary transition-all\"\n style={{ width: \\`\\${item.progress}%\\` }}\n />\n </div>\n {/* Stats row */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Label</p>\n <p className=\"text-lg font-semibold\">{item.value}</p>\n </div>\n </div>\n {/* Avatar stack */}\n <div className=\"flex -space-x-2\">\n {item.members.map((m) => (\n <Avatar key={m.id} className=\"w-8 h-8 border-2 border-background\">\n <AvatarImage src={m.avatar} />\n <AvatarFallback>{m.initials}</AvatarFallback>\n </Avatar>\n ))}\n </div>\n {/* Meta info */}\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Calendar className=\"w-4 h-4\" />\n <span>{item.date}</span>\n </div>\n {/* Action footer */}\n <div className=\"pt-2 border-t\">\n <Button variant=\"outline\" className=\"w-full\">View Details</Button>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n</div>\n\\`\\`\\`\n\n---\n\n## 2. LIST PAGE (Data Table)\n\nUsed for: issue lists, user lists, audit logs, subscription lists.\n\n\\`\\`\\`tsx\n<div className=\"p-page\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-section\">\n <div>\n <h1 className=\"text-page-title font-semibold mb-2\">Page Title</h1>\n <p className=\"text-sm text-muted-foreground\">Description</p>\n </div>\n <div className=\"flex items-center gap-3\">\n <Button variant=\"outline\" size=\"sm\">\n <Filter className=\"w-4 h-4\" /> Filter\n </Button>\n <Button><Plus className=\"w-4 h-4\" /> Create</Button>\n </div>\n </div>\n\n {/* Table card container */}\n <div className=\"bg-card rounded-lg border\">\n {/* Search + filter bar */}\n <div className=\"p-4 border-b\">\n <div className=\"flex items-center gap-4\">\n <div className=\"flex-1 relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground\" />\n <Input placeholder=\"Search...\" className=\"pl-10\" />\n </div>\n <Select>\n <SelectTrigger className=\"w-[180px]\">\n <SelectValue placeholder=\"Status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All</SelectItem>\n {/* filter options */}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {/* Table */}\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[100px]\">ID</TableHead>\n <TableHead>Title</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Assignee</TableHead>\n <TableHead className=\"w-[50px]\" />\n </TableRow>\n </TableHeader>\n <TableBody>\n {items.map((item) => (\n <TableRow key={item.id} className=\"hover:bg-accent\">\n <TableCell className=\"font-medium\">\n <Link to={item.url} className=\"text-primary hover:underline\">#{item.id}</Link>\n </TableCell>\n <TableCell>\n <Link to={item.url} className=\"hover:text-primary transition-colors\">\n <p className=\"font-medium\">{item.title}</p>\n <p className=\"text-sm text-muted-foreground truncate max-w-md\">{item.desc}</p>\n </Link>\n </TableCell>\n <TableCell><Badge variant=\"outline\">{item.status}</Badge></TableCell>\n <TableCell>\n <div className=\"flex items-center gap-2\">\n <Avatar className=\"w-8 h-8\" />\n <span className=\"text-sm\">{item.assignee}</span>\n </div>\n </TableCell>\n <TableCell>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" className=\"h-8 w-8 p-0\">\n <MoreVertical className=\"w-4 h-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem>Edit</DropdownMenuItem>\n <DropdownMenuItem className=\"text-destructive\">Delete</DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n\n {/* Footer */}\n <div className=\"p-4 border-t bg-muted\">\n <p className=\"text-sm text-muted-foreground\">\n Showing {filtered} of {total} results\n </p>\n </div>\n </div>\n</div>\n\\`\\`\\`\n\n**Key rules:**\n- Table wrapped in \\`bg-card rounded-lg border\\` (NOT inside \\`<Card>\\`)\n- Search icon: \\`absolute left-3 top-1/2 -translate-y-1/2\\`, input gets \\`pl-10\\`\n- Filter selects: fixed width \\`w-[180px]\\`\n- Row hover: \\`hover:bg-accent\\`\n- ID links: \\`text-primary hover:underline\\`\n- Row action trigger: \\`Button variant=\"ghost\" size=\"sm\" className=\"h-8 w-8 p-0\"\\`\n- Destructive menu item: \\`className=\"text-destructive\"\\`\n\n---\n\n## 3. DETAIL PAGE\n\nUsed for: task detail, user detail, project detail, order detail.\n\n\\`\\`\\`tsx\n<div className=\"p-page space-y-section\">\n {/* Back nav + header */}\n <div className=\"flex items-center gap-4\">\n <Link to=\"..\">\n <Button variant=\"ghost\" size=\"icon\"><ArrowLeft className=\"w-5 h-5\" /></Button>\n </Link>\n <div\n className=\"w-10 h-10 rounded-lg flex items-center justify-center text-white font-semibold text-sm\"\n style={{ backgroundColor: entity.color }}\n >\n {entity.initials}\n </div>\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2 mb-1\">\n <Badge variant=\"outline\">KEY-123</Badge>\n <Badge className={statusColor}>{entity.status}</Badge>\n </div>\n <h1 className=\"text-page-title font-semibold\">{entity.title}</h1>\n </div>\n <Button variant=\"ghost\" size=\"icon\">\n <MoreVertical className=\"w-5 h-5\" />\n </Button>\n </div>\n\n {/* 2/3 + 1/3 layout */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-section\">\n {/* Main content */}\n <div className=\"lg:col-span-2 space-y-section\">\n <Card>\n <CardHeader><CardTitle>Description</CardTitle></CardHeader>\n <CardContent>\n <p className=\"text-foreground whitespace-pre-wrap\">{entity.description}</p>\n </CardContent>\n </Card>\n\n {/* Comments */}\n <Card>\n <CardHeader><CardTitle>Comments ({comments.length})</CardTitle></CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Textarea placeholder=\"Add a comment...\" rows={3} />\n <div className=\"flex justify-end\">\n <Button><Send className=\"w-4 h-4\" /> Send</Button>\n </div>\n </div>\n <Separator />\n <div className=\"space-y-4\">\n {comments.map((c) => (\n <div key={c.id} className=\"flex gap-3\">\n <Avatar className=\"w-8 h-8\" />\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span className=\"font-medium text-sm\">{c.author}</span>\n <span className=\"text-xs text-muted-foreground\">{c.time}</span>\n </div>\n <p className=\"text-sm text-foreground\">{c.content}</p>\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-4\">\n <Card>\n <CardHeader><CardTitle className=\"text-base\">Details</CardTitle></CardHeader>\n <CardContent className=\"space-y-4\">\n {/* Field pattern: icon+label then value, separated by Separator */}\n <div>\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground mb-2\">\n <User className=\"w-4 h-4\" /><span>Assignee</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <Avatar className=\"w-6 h-6\" />\n <span className=\"text-sm\">{entity.assignee}</span>\n </div>\n </div>\n <Separator />\n <div>\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground mb-2\">\n <Calendar className=\"w-4 h-4\" /><span>Due Date</span>\n </div>\n <span className=\"text-sm\">{entity.dueDate}</span>\n </div>\n </CardContent>\n </Card>\n\n {/* Actions */}\n <Card>\n <CardContent className=\"p-4 space-y-2\">\n <Button variant=\"outline\" className=\"w-full justify-start\">\n <Edit className=\"w-4 h-4\" /> Edit\n </Button>\n <Button variant=\"outline\" className=\"w-full justify-start text-destructive hover:text-destructive\">\n <Trash2 className=\"w-4 h-4\" /> Delete\n </Button>\n </CardContent>\n </Card>\n </div>\n </div>\n</div>\n\\`\\`\\`\n\n**Key rules:**\n- Layout: \\`grid grid-cols-1 lg:grid-cols-3 gap-section\\`, main is \\`lg:col-span-2\\`\n- Back button: \\`Button variant=\"ghost\" size=\"icon\"\\` with \\`ArrowLeft w-5 h-5\\`\n- Sidebar CardTitle: \\`className=\"text-base\"\\` (smaller)\n- Detail field: icon + label in \\`text-sm text-muted-foreground\\`, value below, \\`<Separator />\\` between fields\n- Action buttons: \\`variant=\"outline\" className=\"w-full justify-start\"\\`\n- Destructive: \\`text-destructive hover:text-destructive\\`\n\n---\n\n## 4. DASHBOARD\n\nUsed for: main dashboard, project dashboard, module overview.\n\n\\`\\`\\`tsx\n<StandardPageContainer\n title=\"Dashboard\"\n subtitle=\"Overview and statistics\"\n contentClassName=\"space-y-section\"\n>\n {/* Stat cards: 4 columns */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-section\">\n {stats.map((stat) => (\n <Card key={stat.label}>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">{stat.label}</CardTitle>\n <stat.icon className=\"h-4 w-4 text-muted-foreground\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">{stat.value}</div>\n <p className=\"text-xs text-muted-foreground mt-1\">{stat.subtext}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n {/* Content: 2/3 main + 1/3 sidebar */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-section\">\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <div>\n <CardTitle>Recent Items</CardTitle>\n <CardDescription>Latest activity</CardDescription>\n </div>\n <Link to=\"/items\" className=\"text-sm text-primary hover:text-primary/80 font-medium\">\n View all\n </Link>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {items.map((item) => (\n <Link\n key={item.id}\n to={item.url}\n className=\"flex items-center gap-4 p-3 rounded-lg hover:bg-accent transition-colors\"\n >\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span className=\"text-sm font-medium text-muted-foreground\">{item.id}</span>\n <Badge className={item.priorityColor}>{item.priority}</Badge>\n </div>\n <h4 className=\"font-medium truncate\">{item.title}</h4>\n </div>\n <Avatar className=\"w-8 h-8 flex-shrink-0\" />\n <Badge variant=\"outline\">{item.status}</Badge>\n </Link>\n ))}\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Activity className=\"w-5 h-5\" /> Recent Activity\n </CardTitle>\n </CardHeader>\n <CardContent>{/* Activity feed */}</CardContent>\n </Card>\n </div>\n</StandardPageContainer>\n\\`\\`\\`\n\n**Key rules:**\n- Stat cards: \\`grid lg:grid-cols-4 gap-section\\`\n- Stat CardHeader: \\`flex flex-row items-center justify-between space-y-0 pb-2\\`\n- Stat CardTitle: \\`text-sm font-medium\\` (NOT large)\n- Stat value: \\`text-2xl font-bold\\`\n- Stat sub-text: \\`text-xs text-muted-foreground mt-1\\`\n- Stat icon: \\`h-4 w-4 text-muted-foreground\\`\n- \"View all\" link: \\`text-sm text-primary hover:text-primary/80 font-medium\\`\n- List items: \\`p-3 rounded-lg hover:bg-accent transition-colors\\`\n\n---\n\n## 5. SETTINGS PAGE\n\nUsed for: settings, profile, preferences.\n\n\\`\\`\\`tsx\n<StandardPageContainer\n title=\"Settings\"\n subtitle=\"Manage your preferences\"\n contentClassName=\"max-w-4xl mx-auto space-y-section\"\n>\n <Tabs defaultValue=\"profile\" className=\"space-y-section\">\n <TabsList>\n <TabsTrigger value=\"profile\"><User className=\"w-4 h-4\" /> Profile</TabsTrigger>\n <TabsTrigger value=\"notifications\"><Bell className=\"w-4 h-4\" /> Notifications</TabsTrigger>\n <TabsTrigger value=\"security\"><Shield className=\"w-4 h-4\" /> Security</TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"profile\" className=\"space-y-section\">\n <Card>\n <CardHeader>\n <CardTitle>Personal Info</CardTitle>\n <CardDescription>Update your profile</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n {/* Avatar section */}\n <div className=\"flex items-center gap-4\">\n <Avatar className=\"w-20 h-20\" />\n <div>\n <Button variant=\"outline\" size=\"sm\">Change</Button>\n <p className=\"text-xs text-muted-foreground mt-2\">JPG, PNG. Max 5MB.</p>\n </div>\n </div>\n <Separator />\n {/* Form fields */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"firstName\">First Name</Label>\n <Input id=\"firstName\" />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"lastName\">Last Name</Label>\n <Input id=\"lastName\" />\n </div>\n </div>\n <Button>Save Changes</Button>\n </CardContent>\n </Card>\n </TabsContent>\n\n <TabsContent value=\"notifications\" className=\"space-y-section\">\n <Card>\n <CardHeader>\n <CardTitle>Notification Preferences</CardTitle>\n <CardDescription>Choose what to be notified about</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {/* Toggle row pattern */}\n <div className=\"flex items-center justify-between\">\n <div className=\"space-y-0.5\">\n <Label>Email Notifications</Label>\n <p className=\"text-sm text-muted-foreground\">Receive updates via email</p>\n </div>\n <Switch />\n </div>\n <Separator />\n {/* Repeat toggle rows */}\n </CardContent>\n </Card>\n </TabsContent>\n </Tabs>\n</StandardPageContainer>\n\\`\\`\\`\n\n**Key rules:**\n- Content width: \\`max-w-4xl mx-auto\\`\n- Tab triggers include icons: \\`<Icon className=\"w-4 h-4\" />\\`\n- Form fields: \\`space-y-2\\` wrapping \\`Label\\` + \\`Input\\`\n- Field groups: \\`grid grid-cols-2 gap-4\\`\n- Toggle row: \\`flex items-center justify-between\\`, label left (\\`space-y-0.5\\`), Switch right\n- Toggle description: \\`text-sm text-muted-foreground\\`\n\n---\n\n## 6. KANBAN BOARD\n\nUsed for: task boards, pipeline views.\n\n\\`\\`\\`tsx\n<div className=\"p-page\">\n <div className=\"flex items-center justify-between mb-section\">\n <div>\n <h1 className=\"text-page-title font-semibold mb-2\">Board</h1>\n <p className=\"text-sm text-muted-foreground\">Drag tasks between columns</p>\n </div>\n <div className=\"flex items-center gap-3\">\n <Button variant=\"outline\" size=\"sm\"><Filter className=\"w-4 h-4\" /> Filter</Button>\n <Button><Plus className=\"w-4 h-4\" /> Create</Button>\n </div>\n </div>\n\n {/* Horizontal scrolling columns */}\n <div className=\"flex gap-4 overflow-x-auto pb-4\">\n {columns.map((col) => (\n <div key={col.id} className=\"flex-1 min-w-[280px] bg-muted rounded-lg p-3\">\n {/* Column header */}\n <div className=\"flex items-center justify-between mb-3\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold\">{col.title}</h3>\n <Badge variant=\"secondary\">{col.tasks.length}</Badge>\n </div>\n <Button variant=\"ghost\" size=\"sm\" className=\"h-8 w-8 p-0\">\n <Plus className=\"w-4 h-4\" />\n </Button>\n </div>\n {/* Task cards */}\n <div className=\"space-y-2\">\n {col.tasks.map((task) => (\n <Card key={task.id} className=\"p-3 hover:shadow-md transition-shadow cursor-pointer group\">\n <div className=\"flex items-start justify-between\">\n <span className=\"text-sm text-muted-foreground\">#{task.id}</span>\n <Button variant=\"ghost\" size=\"sm\" className=\"h-6 w-6 p-0 opacity-0 group-hover:opacity-100\">\n <MoreVertical className=\"w-4 h-4\" />\n </Button>\n </div>\n <h4 className=\"font-medium mt-1\">{task.title}</h4>\n <div className=\"flex items-center gap-2 mt-2\">\n <div className={\\`w-2 h-2 rounded-full \\${priorityColor}\\`} />\n <span className=\"text-xs text-muted-foreground capitalize\">{task.priority}</span>\n </div>\n <div className=\"flex items-center justify-between mt-2\">\n <Avatar className=\"w-6 h-6\" />\n <span className=\"text-xs text-muted-foreground\">{task.date}</span>\n </div>\n </Card>\n ))}\n </div>\n </div>\n ))}\n </div>\n</div>\n\\`\\`\\`\n\n**Key rules:**\n- Columns: \\`flex gap-4 overflow-x-auto\\`, each \\`flex-1 min-w-[280px] bg-muted rounded-lg p-3\\`\n- Drop highlight: \\`bg-primary/5 ring-2 ring-primary\\`\n- Cards: \\`p-3\\` (compact), hover: \\`hover:shadow-md\\`\n- Action menu: hidden until hover (\\`opacity-0 group-hover:opacity-100\\`)\n- Priority dot: \\`w-2 h-2 rounded-full\\` (not badge)\n- Compact avatars: \\`w-6 h-6\\`\n\n---\n\n## 7. OVERVIEW / MODULE INDEX\n\nUsed for: module overview pages (workflow, CMS, RBAC, attendance).\nEach module overview has stat cards + a quick list of items.\n\n\\`\\`\\`tsx\n<StandardPageContainer\n title=\"Module Name\"\n subtitle=\"Module overview\"\n extra={<Button><Plus className=\"w-4 h-4\" /> Create</Button>}\n contentClassName=\"space-y-section\"\n>\n {/* Stat cards with colored icons — each stat is a DIFFERENT color */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-section\">\n {stats.map((stat) => (\n <Card key={stat.label}>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-muted-foreground\">{stat.label}</p>\n <p className=\"text-2xl font-bold mt-1\">{stat.value}</p>\n </div>\n <div className={\\`p-3 rounded-lg \\${stat.bgColor}\\`}>\n <stat.icon className={\\`w-5 h-5 \\${stat.iconColor}\\`} />\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n {/* Quick list card */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle>Recent Items</CardTitle>\n <Link to=\"/module/all\" className=\"text-sm text-primary hover:text-primary/80\">\n View all\n </Link>\n </div>\n </CardHeader>\n <CardContent>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Date</TableHead>\n <TableHead className=\"w-[50px]\" />\n </TableRow>\n </TableHeader>\n <TableBody>\n {items.map((item) => (\n <TableRow key={item.id} className=\"hover:bg-accent\">\n <TableCell className=\"font-medium\">{item.name}</TableCell>\n <TableCell><Badge>{item.status}</Badge></TableCell>\n <TableCell className=\"text-sm text-muted-foreground\">{item.date}</TableCell>\n <TableCell>\n <Button variant=\"ghost\" size=\"sm\" className=\"h-8 w-8 p-0\">\n <MoreVertical className=\"w-4 h-4\" />\n </Button>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n</StandardPageContainer>\n\\`\\`\\`\n\n**Note on stat icon colors:** Overview stat cards use DIFFERENT colors per stat (blue, green, amber, purple) — this is the multi-color exception. Each stat intentionally has a unique color.\n\n---\n\n## 8. COMMON PATTERNS\n\n### Page Header (two variants)\n\n**Variant A — Manual** (most pages):\n\\`\\`\\`tsx\n<div className=\"p-page space-y-section\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-page-title font-semibold mb-2\">Title</h1>\n <p className=\"text-sm text-muted-foreground\">Subtitle</p>\n </div>\n <div className=\"flex items-center gap-3\">\n <Button variant=\"outline\" size=\"sm\"><Filter className=\"w-4 h-4\" /> Filter</Button>\n <Button><Plus className=\"w-4 h-4\" /> Create</Button>\n </div>\n </div>\n {/* ... page content ... */}\n</div>\n\\`\\`\\`\n\n**Variant B — StandardPageContainer** (dashboard, settings):\n\\`\\`\\`tsx\n<StandardPageContainer\n title=\"Title\"\n subtitle=\"Subtitle\"\n extra={<Button>Action</Button>}\n contentClassName=\"space-y-section\"\n>\n {/* ... page content ... */}\n</StandardPageContainer>\n\\`\\`\\`\n\n### Action Button Group\n\\`\\`\\`tsx\n<div className=\"flex items-center gap-3\">\n <Button variant=\"outline\" size=\"sm\"><Filter className=\"w-4 h-4\" /> Filter</Button>\n <Button><Plus className=\"w-4 h-4\" /> Create</Button>\n</div>\n\\`\\`\\`\n- Secondary: \\`variant=\"outline\" size=\"sm\"\\`\n- Primary: default variant (solid)\n\n### Comment / Activity Item\n\\`\\`\\`tsx\n<div className=\"flex gap-3\">\n <Avatar className=\"w-8 h-8\" />\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span className=\"font-medium text-sm\">{name}</span>\n <span className=\"text-xs text-muted-foreground\">{time}</span>\n </div>\n <p className=\"text-sm text-foreground\">{content}</p>\n </div>\n</div>\n\\`\\`\\`\n\n### Avatar Stack\n\\`\\`\\`tsx\n<div className=\"flex -space-x-2\">\n {users.map((u) => (\n <Avatar key={u.id} className=\"w-8 h-8 border-2 border-background\">\n <AvatarImage src={u.avatar} />\n <AvatarFallback>{u.initials}</AvatarFallback>\n </Avatar>\n ))}\n</div>\n\\`\\`\\`\n\n### Empty State\n\\`\\`\\`tsx\n<TableRow>\n <TableCell colSpan={columns} className=\"text-center py-8 text-muted-foreground\">\n No items found\n </TableCell>\n</TableRow>\n\\`\\`\\`\n\n### \"View All\" Link\n\\`\\`\\`tsx\n<Link to=\"/path\" className=\"text-sm text-primary hover:text-primary/80 font-medium\">\n View all\n</Link>\n\\`\\`\\`\n\n### Multi-Color Status Badge Map\nThis is the EXCEPTION to the \"no hardcoded colors\" rule:\n\\`\\`\\`tsx\nconst statusColors: Record<string, string> = {\n active: 'bg-green-50 dark:bg-green-500/15 text-green-600 dark:text-green-400 border-green-200 dark:border-green-500/30',\n pending: 'bg-blue-50 dark:bg-blue-500/15 text-blue-600 dark:text-blue-400 border-blue-200 dark:border-blue-500/30',\n suspended: 'bg-red-50 dark:bg-red-500/15 text-red-600 dark:text-red-400 border-red-200 dark:border-red-500/30',\n cancelled: 'bg-yellow-50 dark:bg-yellow-500/15 text-yellow-600 dark:text-yellow-400 border-yellow-200 dark:border-yellow-500/30',\n};\n\\`\\`\\`\n**Rule**: ALWAYS include dark mode variants (\\`dark:bg-{color}-500/15 dark:text-{color}-400\\`).\n`;\n\nexport function registerPatternsResource(server: McpServer) {\n server.resource('design-patterns', 'design://patterns', {\n description: 'Real-world page patterns from the demo app — list pages, detail pages, dashboards, settings, kanban boards, data tables, stat cards, and common UI patterns',\n mimeType: 'text/markdown',\n }, async () => ({\n contents: [{\n uri: 'design://patterns',\n mimeType: 'text/markdown',\n text: PATTERNS_CONTENT,\n }],\n }));\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\n// ── Violation patterns ──\n\ninterface ViolationRule {\n pattern: RegExp;\n message: string;\n suggestion: string;\n category: 'color' | 'density' | 'layout' | 'pattern';\n}\n\nconst VIOLATION_RULES: ViolationRule[] = [\n // Color violations\n { pattern: /\\bbg-white\\b/, message: 'Hardcoded `bg-white`', suggestion: 'Use `bg-background` or `bg-card`', category: 'color' },\n { pattern: /\\bbg-gray-50\\b/, message: 'Hardcoded `bg-gray-50`', suggestion: 'Use `bg-muted`', category: 'color' },\n { pattern: /\\bbg-gray-100\\b/, message: 'Hardcoded `bg-gray-100`', suggestion: 'Use `bg-muted`', category: 'color' },\n { pattern: /\\btext-gray-900\\b/, message: 'Hardcoded `text-gray-900`', suggestion: 'Use `text-foreground`', category: 'color' },\n { pattern: /\\btext-gray-700\\b/, message: 'Hardcoded `text-gray-700`', suggestion: 'Use `text-foreground`', category: 'color' },\n { pattern: /\\btext-gray-600\\b/, message: 'Hardcoded `text-gray-600`', suggestion: 'Use `text-muted-foreground`', category: 'color' },\n { pattern: /\\btext-gray-500\\b/, message: 'Hardcoded `text-gray-500`', suggestion: 'Use `text-muted-foreground`', category: 'color' },\n { pattern: /\\btext-gray-400\\b/, message: 'Hardcoded `text-gray-400`', suggestion: 'Use `text-muted-foreground`', category: 'color' },\n { pattern: /\\bborder-gray-\\d+\\b/, message: 'Hardcoded `border-gray-*`', suggestion: 'Use `border-border`', category: 'color' },\n { pattern: /\\btext-blue-\\d+\\b/, message: 'Hardcoded `text-blue-*`', suggestion: 'Use `text-primary` or `text-info`', category: 'color' },\n { pattern: /\\bbg-blue-\\d+\\b/, message: 'Hardcoded `bg-blue-*`', suggestion: 'Use `bg-primary/10` or `bg-info/10`', category: 'color' },\n { pattern: /\\bbg-red-\\d+\\b/, message: 'Hardcoded `bg-red-*`', suggestion: 'Use `bg-destructive` or `bg-destructive/10`', category: 'color' },\n { pattern: /\\btext-red-\\d+\\b/, message: 'Hardcoded `text-red-*`', suggestion: 'Use `text-destructive`', category: 'color' },\n { pattern: /\\bbg-green-\\d+\\b/, message: 'Hardcoded `bg-green-*`', suggestion: 'Use `bg-success` or `bg-success/10`', category: 'color' },\n { pattern: /\\btext-green-\\d+\\b/, message: 'Hardcoded `text-green-*`', suggestion: 'Use `text-success`', category: 'color' },\n { pattern: /\\bbg-yellow-\\d+\\b/, message: 'Hardcoded `bg-yellow-*`', suggestion: 'Use `bg-warning` or `bg-warning/10`', category: 'color' },\n { pattern: /\\btext-yellow-\\d+\\b/, message: 'Hardcoded `text-yellow-*`', suggestion: 'Use `text-warning`', category: 'color' },\n { pattern: /\\bbg-orange-\\d+\\b/, message: 'Hardcoded `bg-orange-*`', suggestion: 'Use `bg-warning` or `bg-warning/10`', category: 'color' },\n { pattern: /\\bhover:bg-gray-\\d+\\b/, message: 'Hardcoded `hover:bg-gray-*`', suggestion: 'Use `hover:bg-accent`', category: 'color' },\n { pattern: /\\btext-black\\b/, message: 'Hardcoded `text-black`', suggestion: 'Use `text-foreground`', category: 'color' },\n { pattern: /\\bbg-black\\b/, message: 'Hardcoded `bg-black`', suggestion: 'Use `bg-foreground` or `bg-primary`', category: 'color' },\n\n // Density violations\n { pattern: /\\bp-6\\b/, message: 'Hardcoded `p-6`', suggestion: 'Use `p-page` for page padding or `p-dialog` for dialog', category: 'density' },\n { pattern: /\\bp-8\\b/, message: 'Hardcoded `p-8`', suggestion: 'Use `p-page` for page padding', category: 'density' },\n { pattern: /\\bpx-6\\b/, message: 'Hardcoded `px-6`', suggestion: 'Use `px-page` or `px-card`', category: 'density' },\n { pattern: /\\bpy-6\\b/, message: 'Hardcoded `py-6`', suggestion: 'Use `py-page` or `py-card`', category: 'density' },\n { pattern: /\\bgap-6\\b/, message: 'Hardcoded `gap-6`', suggestion: 'Use `gap-section`', category: 'density' },\n { pattern: /\\bspace-y-6\\b/, message: 'Hardcoded `space-y-6`', suggestion: 'Use `space-y-section`', category: 'density' },\n { pattern: /\\bh-9\\b/, message: 'Hardcoded `h-9`', suggestion: 'Use `h-element` (32px)', category: 'density' },\n { pattern: /\\bh-10\\b/, message: 'Hardcoded `h-10`', suggestion: 'Use `h-element-lg` (36px)', category: 'density' },\n { pattern: /\\bh-14\\b/, message: 'Hardcoded `h-14`', suggestion: 'Use `h-header` (48px)', category: 'density' },\n\n // Layout violations\n { pattern: /\\bh-screen\\b/, message: '`h-screen` on page component', suggestion: 'Remove — root layout handles full height', category: 'layout' },\n\n // Pattern violations\n { pattern: /\\btext-3xl\\s+font-bold\\b/, message: '`text-3xl font-bold` for page title', suggestion: 'Use `text-page-title font-semibold`', category: 'pattern' },\n { pattern: /\\btext-2xl\\s+font-bold\\b/, message: '`text-2xl font-bold` for page title', suggestion: 'Use `text-page-title font-semibold`', category: 'pattern' },\n { pattern: /\\bhsl\\(var\\(--/, message: '`hsl(var(--*))` wrapping', suggestion: 'Use `var(--token)` directly — tokens use mixed color formats', category: 'pattern' },\n { pattern: /style=\\{/, message: 'Inline style object', suggestion: 'Use Tailwind classes instead of inline styles', category: 'pattern' },\n];\n\n// ── Token database ──\n\ninterface TokenEntry {\n token: string;\n tailwind: string;\n category: string;\n description: string;\n}\n\nconst TOKEN_DB: TokenEntry[] = [\n // Colors\n { token: '--background', tailwind: 'bg-background', category: 'color', description: 'Page background' },\n { token: '--foreground', tailwind: 'text-foreground', category: 'color', description: 'Default text color' },\n { token: '--card', tailwind: 'bg-card', category: 'color', description: 'Card background' },\n { token: '--primary', tailwind: 'bg-primary / text-primary', category: 'color', description: 'Main brand, active states' },\n { token: '--secondary', tailwind: 'bg-secondary', category: 'color', description: 'Secondary backgrounds' },\n { token: '--muted', tailwind: 'bg-muted', category: 'color', description: 'Muted/subtle backgrounds (replaces bg-gray-50/100)' },\n { token: '--muted-foreground', tailwind: 'text-muted-foreground', category: 'color', description: 'Subdued text (replaces text-gray-500/600)' },\n { token: '--accent', tailwind: 'bg-accent / hover:bg-accent', category: 'color', description: 'Hover backgrounds (replaces hover:bg-gray-50)' },\n { token: '--destructive', tailwind: 'bg-destructive / text-destructive', category: 'color', description: 'Delete, errors, danger' },\n { token: '--border', tailwind: 'border-border', category: 'color', description: 'All borders (replaces border-gray-200/300)' },\n { token: '--success', tailwind: 'bg-success / text-success', category: 'color', description: 'Success states' },\n { token: '--warning', tailwind: 'bg-warning / text-warning', category: 'color', description: 'Warning states' },\n { token: '--info', tailwind: 'bg-info / text-info', category: 'color', description: 'Info states' },\n { token: '--sidebar', tailwind: 'bg-sidebar', category: 'color', description: 'Sidebar background' },\n { token: '--sidebar-primary', tailwind: 'text-sidebar-primary', category: 'color', description: 'Sidebar active item' },\n // Density\n { token: '--density-page', tailwind: 'p-page', category: 'density', description: 'Page content padding (16px)' },\n { token: '--density-section', tailwind: 'gap-section / space-y-section', category: 'density', description: 'Section gap (16px)' },\n { token: '--density-page-title', tailwind: 'text-page-title', category: 'density', description: 'Page title size (20px)' },\n { token: '--density-element', tailwind: 'h-element', category: 'density', description: 'Standard element height (32px)' },\n { token: '--density-element-sm', tailwind: 'h-element-sm', category: 'density', description: 'Small element height (28px)' },\n { token: '--density-element-lg', tailwind: 'h-element-lg', category: 'density', description: 'Large element height (36px)' },\n { token: '--density-element-xl', tailwind: 'h-element-xl', category: 'density', description: 'Extra large element height (44px)' },\n { token: '--density-card', tailwind: 'p-card / px-card / pt-card', category: 'density', description: 'Card internal padding (16px)' },\n { token: '--density-dialog', tailwind: 'p-dialog', category: 'density', description: 'Dialog internal padding (20px)' },\n { token: '--density-table-head', tailwind: 'h-table-head', category: 'density', description: 'Table header row height (32px)' },\n // Layout\n { token: '--header-height', tailwind: 'h-header', category: 'layout', description: 'Header height (48px)' },\n { token: '--sidebar-width', tailwind: 'w-64', category: 'layout', description: 'Sidebar width (256px)' },\n { token: '--radius', tailwind: 'rounded-lg', category: 'border', description: 'Default border radius (6px)' },\n];\n\n// ── Component registry ──\n\ninterface ComponentInfo {\n name: string;\n package: string;\n category: string;\n description: string;\n props: string;\n example: string;\n}\n\nconst COMPONENT_REGISTRY: Record<string, ComponentInfo> = {\n button: {\n name: 'Button',\n package: '@omnifyjp/ui-components',\n category: 'action',\n description: 'Standard button with variant × color × size API',\n props: 'variant?: UIVariant (\"default\"|\"secondary\"|\"outline\"|\"soft\"|\"ghost\"|\"link\"), color?: UIColor (\"primary\"|\"destructive\"|\"success\"|\"warning\"|\"info\"), size?: UISize (\"xs\"|\"sm\"|\"default\"|\"lg\"|\"xl\"), asChild?: boolean, disabled?: boolean',\n example: '<Button variant=\"soft\" color=\"success\" size=\"sm\"><Check className=\"w-4 h-4\" /> Approve</Button>',\n },\n badge: {\n name: 'Badge',\n package: '@omnifyjp/ui-components',\n category: 'display',\n description: 'Status/label badge. Max 2 characters for icon badges.',\n props: 'variant?: \"default\"|\"secondary\"|\"outline\"|\"soft\", color?: UIColor',\n example: '<Badge variant=\"soft\" color=\"warning\">Pending</Badge>',\n },\n alert: {\n name: 'Alert',\n package: '@omnifyjp/ui-components',\n category: 'feedback',\n description: 'Alert banner with icon support',\n props: 'variant?: \"default\"|\"soft\"|\"outline\", color?: UIColor',\n example: '<Alert variant=\"soft\" color=\"info\"><AlertTitle>Tip</AlertTitle><AlertDescription>Use keyboard shortcuts</AlertDescription></Alert>',\n },\n input: {\n name: 'Input',\n package: '@omnifyjp/ui-components',\n category: 'form',\n description: 'Text input field',\n props: 'size?: UISize, type?: string, placeholder?: string, disabled?: boolean',\n example: '<Input size=\"default\" placeholder=\"Enter text...\" />',\n },\n 'password-input': {\n name: 'PasswordInput',\n package: '@omnifyjp/ui-components',\n category: 'form',\n description: 'Password input with show/hide toggle',\n props: 'size?: UISize, placeholder?: string, disabled?: boolean',\n example: '<PasswordInput size=\"xl\" placeholder=\"Enter password\" />',\n },\n select: {\n name: 'Select',\n package: '@omnifyjp/ui-components',\n category: 'form',\n description: 'Dropdown select. NEVER use empty string as SelectItem value — Radix crashes. Use value=\"none\" and map to null.',\n props: 'value?: string, onValueChange?: (value: string) => void',\n example: '<Select value={value} onValueChange={setValue}><SelectTrigger><SelectValue placeholder=\"Choose...\" /></SelectTrigger><SelectContent><SelectItem value=\"a\">Option A</SelectItem></SelectContent></Select>',\n },\n dialog: {\n name: 'Dialog',\n package: '@omnifyjp/ui-components',\n category: 'overlay',\n description: 'Modal dialog. Use p-dialog for body padding.',\n props: 'open?: boolean, onOpenChange?: (open: boolean) => void',\n example: '<Dialog open={open} onOpenChange={setOpen}><DialogContent><DialogHeader><DialogTitle>Title</DialogTitle></DialogHeader><div className=\"p-dialog\">Content</div><DialogFooter><Button>Save</Button></DialogFooter></DialogContent></Dialog>',\n },\n card: {\n name: 'Card',\n package: '@omnifyjp/ui-components',\n category: 'display',\n description: 'Card container. Use px-card/pt-card for padding.',\n props: 'className?: string',\n example: '<Card><CardHeader className=\"px-card pt-card\"><CardTitle>Title</CardTitle></CardHeader><CardContent className=\"px-card pb-card\">Content</CardContent></Card>',\n },\n table: {\n name: 'Table',\n package: '@omnifyjp/ui-components',\n category: 'display',\n description: 'Data table. Use h-table-head for header row height.',\n props: 'className?: string',\n example: '<Table><TableHeader><TableRow className=\"h-table-head\"><TableHead>Name</TableHead></TableRow></TableHeader><TableBody><TableRow>...</TableRow></TableBody></Table>',\n },\n drawer: {\n name: 'Drawer',\n package: '@omnifyjp/ui-components',\n category: 'overlay',\n description: 'Bottom drawer (vaul). Content MUST be wrapped in DrawerBody.',\n props: 'open?: boolean, onOpenChange?: (open: boolean) => void',\n example: '<Drawer open={open} onOpenChange={setOpen}><DrawerContent><DrawerHeader><DrawerTitle>Title</DrawerTitle></DrawerHeader><DrawerBody>Content here</DrawerBody></DrawerContent></Drawer>',\n },\n tabs: {\n name: 'Tabs',\n package: '@omnifyjp/ui-components',\n category: 'navigation',\n description: 'Tab navigation',\n props: 'value?: string, onValueChange?: (value: string) => void, defaultValue?: string',\n example: '<Tabs value={tab} onValueChange={setTab}><TabsList><TabsTrigger value=\"a\">Tab A</TabsTrigger></TabsList><TabsContent value=\"a\">Content A</TabsContent></Tabs>',\n },\n 'permission-grid': {\n name: 'PermissionGrid',\n package: '@omnifyjp/ui-components',\n category: 'domain',\n description: 'Permission matrix grid. Uses module:action ID format via buildPermissionId().',\n props: 'modules: Module[], actions: Action[], permissions: Record<string, boolean>, onChange: (id: string, checked: boolean) => void, labels?: { ... }',\n example: '<PermissionGrid modules={modules} actions={actions} permissions={perms} onChange={handleChange} />',\n },\n 'scope-tree': {\n name: 'ScopeTree',\n package: '@omnifyjp/ui-components',\n category: 'domain',\n description: 'Hierarchical scope tree. Note: ScopeTreeSelectedScope.type is string (generic), cast to ScopeType in consumers.',\n props: 'scopes: ScopeNode[], selectedScope?: SelectedScope, onSelectScope?: (scope: SelectedScope) => void, labels?: { ... }',\n example: '<ScopeTree scopes={scopes} selectedScope={selected} onSelectScope={setSelected} />',\n },\n 'rich-text-editor': {\n name: 'RichTextEditor',\n package: '@omnifyjp/editor',\n category: 'editor',\n description: 'Tiptap-based rich text editor. Requires CSS: import \"@omnifyjp/editor/styles/rich-text-editor.css\"',\n props: 'content?: string, onChange?: (html: string) => void, placeholder?: string',\n example: 'import { RichTextEditor } from \"@omnifyjp/editor\";\\nimport \"@omnifyjp/editor/styles/rich-text-editor.css\";\\n\\n<RichTextEditor content={html} onChange={setHtml} />',\n },\n};\n\n// ── Tool registration ──\n\nexport function registerValidateTools(server: McpServer) {\n // Tool: validate_component\n server.tool(\n 'validate_component',\n 'Validate React component code against @omnifyjp design system rules. Checks for hardcoded colors, wrong spacing tokens, layout anti-patterns, and other violations.',\n { code: z.string().describe('React component source code to validate') },\n async ({ code }) => {\n const violations: Array<{ line: number; message: string; suggestion: string; category: string }> = [];\n const lines = code.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n for (const rule of VIOLATION_RULES) {\n if (rule.pattern.test(line)) {\n violations.push({\n line: i + 1,\n message: rule.message,\n suggestion: rule.suggestion,\n category: rule.category,\n });\n }\n }\n }\n\n if (violations.length === 0) {\n return {\n content: [{\n type: 'text' as const,\n text: 'No design system violations found. The code follows @omnifyjp conventions.',\n }],\n };\n }\n\n const grouped = {\n color: violations.filter(v => v.category === 'color'),\n density: violations.filter(v => v.category === 'density'),\n layout: violations.filter(v => v.category === 'layout'),\n pattern: violations.filter(v => v.category === 'pattern'),\n };\n\n let output = `Found ${violations.length} design system violation(s):\\n\\n`;\n\n for (const [category, items] of Object.entries(grouped)) {\n if (items.length === 0) continue;\n output += `### ${category.charAt(0).toUpperCase() + category.slice(1)} Issues\\n`;\n for (const v of items) {\n output += `- **Line ${v.line}**: ${v.message} → ${v.suggestion}\\n`;\n }\n output += '\\n';\n }\n\n return {\n content: [{ type: 'text' as const, text: output }],\n };\n },\n );\n\n // Tool: suggest_token\n server.tool(\n 'suggest_token',\n 'Suggest the correct @omnifyjp design token for a given need. Describe what you need (e.g., \"page background\", \"button height\", \"section spacing\") and get the right token.',\n { need: z.string().describe('Description of what design token is needed (e.g., \"subdued text color\", \"page padding\", \"button height\")') },\n async ({ need }) => {\n const query = need.toLowerCase();\n const matches = TOKEN_DB.filter(t =>\n t.description.toLowerCase().includes(query) ||\n t.category.includes(query) ||\n t.token.includes(query) ||\n t.tailwind.toLowerCase().includes(query),\n );\n\n // Keyword-based fallback matching\n const keywordMatches: TokenEntry[] = [];\n if (matches.length === 0) {\n const keywords = query.split(/\\s+/);\n for (const token of TOKEN_DB) {\n const searchText = `${token.description} ${token.token} ${token.tailwind} ${token.category}`.toLowerCase();\n if (keywords.some(k => searchText.includes(k))) {\n keywordMatches.push(token);\n }\n }\n }\n\n const results = matches.length > 0 ? matches : keywordMatches;\n\n if (results.length === 0) {\n return {\n content: [{\n type: 'text' as const,\n text: `No matching token found for \"${need}\". Check design://tokens resource for the full token reference.`,\n }],\n };\n }\n\n let output = `Recommended token(s) for \"${need}\":\\n\\n`;\n for (const t of results) {\n output += `- **${t.tailwind}** (CSS: \\`${t.token}\\`) — ${t.description}\\n`;\n }\n\n return {\n content: [{ type: 'text' as const, text: output }],\n };\n },\n );\n\n // Tool: get_component_api\n server.tool(\n 'get_component_api',\n 'Get the API reference for a specific @omnifyjp component — props, variants, sizes, and usage example.',\n { component: z.string().describe('Component name (e.g., \"Button\", \"Badge\", \"Dialog\", \"PermissionGrid\")') },\n async ({ component }) => {\n const key = component.toLowerCase().replace(/\\s+/g, '-');\n const info = COMPONENT_REGISTRY[key];\n\n if (!info) {\n // Try fuzzy match\n const keys = Object.keys(COMPONENT_REGISTRY);\n const fuzzy = keys.filter(k => k.includes(key) || key.includes(k));\n\n if (fuzzy.length > 0) {\n const match = COMPONENT_REGISTRY[fuzzy[0]];\n return {\n content: [{\n type: 'text' as const,\n text: formatComponentInfo(match),\n }],\n };\n }\n\n return {\n content: [{\n type: 'text' as const,\n text: `Component \"${component}\" not found. Available: ${keys.map(k => COMPONENT_REGISTRY[k].name).join(', ')}. Check design://components for the full inventory.`,\n }],\n };\n }\n\n return {\n content: [{\n type: 'text' as const,\n text: formatComponentInfo(info),\n }],\n };\n },\n );\n}\n\nfunction formatComponentInfo(info: ComponentInfo): string {\n return `## ${info.name}\n\n**Package**: \\`${info.package}\\`\n**Category**: ${info.category}\n\n${info.description}\n\n### Props\n${info.props}\n\n### Example\n\\`\\`\\`tsx\n${info.example}\n\\`\\`\\``;\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nexport function registerPrompts(server: McpServer) {\n // ── /create-page ──────────────────────────────────────────\n server.registerPrompt('create-page', {\n title: 'Create Page',\n description: 'Create a new page component following @omnifyjp design patterns (list, detail, dashboard, settings, kanban, overview)',\n argsSchema: {\n name: z.string().describe('Page component name in PascalCase (e.g., \"UserList\", \"OrderDetail\")'),\n type: z.enum(['list-card', 'list-table', 'detail', 'dashboard', 'settings', 'kanban', 'overview'])\n .describe('Page type: list-card (card grid), list-table (data table), detail (entity detail), dashboard (stat cards + content), settings (tabs + forms), kanban (board columns), overview (module index)'),\n },\n }, async ({ name, type }) => {\n const patterns: Record<string, string> = {\n 'list-card': LIST_CARD_PROMPT,\n 'list-table': LIST_TABLE_PROMPT,\n 'detail': DETAIL_PROMPT,\n 'dashboard': DASHBOARD_PROMPT,\n 'settings': SETTINGS_PROMPT,\n 'kanban': KANBAN_PROMPT,\n 'overview': OVERVIEW_PROMPT,\n };\n\n return {\n messages: [{\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `Create a page component called \\`${name}\\` using the **${type}** pattern.\n\n## Required Structure\n\n${patterns[type]}\n\n## Rules (MUST follow)\n\n1. Use \\`p-page\\` for page padding (NOT \\`p-6\\` or \\`p-8\\`)\n2. Use \\`text-page-title font-semibold\\` for page title (NOT \\`text-3xl font-bold\\`)\n3. Use \\`gap-section\\` or \\`space-y-section\\` for section gaps (NOT \\`gap-6\\`)\n4. Use semantic colors: \\`bg-background\\`, \\`text-foreground\\`, \\`text-muted-foreground\\`, \\`border-border\\`, \\`bg-primary\\`, \\`text-primary\\`, \\`bg-muted\\`, \\`hover:bg-accent\\`\n5. NEVER hardcode colors: no \\`bg-white\\`, \\`text-gray-500\\`, \\`border-gray-200\\`, \\`text-blue-600\\`\n6. Icons from \\`lucide-react\\` only, sized \\`w-4 h-4\\` (standard) or \\`w-5 h-5\\` (header)\n7. Import order: React → router → types → data → UI components → icons → sonner\n8. Every interactive element needs: \\`hover:bg-accent transition-colors\\` or equivalent\n9. Row actions: \\`<Button variant=\"ghost\" size=\"sm\" className=\"h-8 w-8 p-0\">\\`\n10. Destructive actions: \\`text-destructive hover:text-destructive\\`\n11. Links: \\`text-primary hover:text-primary/80\\` or \\`hover:text-primary transition-colors\\`\n12. Badge variant for status: use \\`variant=\"outline\"\\` or \\`variant=\"soft\"\\` with \\`color\\` prop\n\nRead the \\`design://patterns\\` resource for full pattern details before generating code.`,\n },\n }],\n };\n });\n\n // ── /check-design ─────────────────────────────────────────\n server.registerPrompt('check-design', {\n title: 'Check Design',\n description: 'Audit current codebase for @omnifyjp design system violations — hardcoded colors, wrong spacing, layout anti-patterns',\n }, async () => ({\n messages: [{\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `Audit the current project for @omnifyjp design system violations.\n\n## What to Check\n\n### 1. Hardcoded Colors (CRITICAL)\nSearch all \\`.tsx\\` and \\`.ts\\` files for these patterns:\n- \\`bg-white\\` → should be \\`bg-background\\` or \\`bg-card\\`\n- \\`bg-gray-50\\`, \\`bg-gray-100\\` → should be \\`bg-muted\\`\n- \\`text-gray-900\\`, \\`text-gray-700\\` → should be \\`text-foreground\\`\n- \\`text-gray-600\\`, \\`text-gray-500\\`, \\`text-gray-400\\` → should be \\`text-muted-foreground\\`\n- \\`border-gray-*\\` → should be \\`border-border\\`\n- \\`text-blue-600\\`, \\`bg-blue-50\\` → should be \\`text-primary\\`, \\`bg-primary/10\\`\n- \\`hover:bg-gray-*\\` → should be \\`hover:bg-accent\\`\n- \\`focus:ring-blue-*\\` → should be \\`focus:ring-ring\\`\n\n**Exception**: Multi-color status badge maps where each status is a DIFFERENT color (e.g., active=green, pending=blue, rejected=red) — these are intentionally hardcoded.\n\n### 2. Hardcoded Spacing\n- \\`p-6\\`, \\`p-8\\` → should be \\`p-page\\`\n- \\`gap-6\\`, \\`space-y-6\\` → should be \\`gap-section\\` / \\`space-y-section\\`\n- \\`h-9\\`, \\`h-10\\` → should be \\`h-element\\` / \\`h-element-lg\\`\n- \\`h-14\\` → should be \\`h-header\\`\n- \\`px-6\\` in cards → should be \\`px-card\\`\n- \\`p-6\\` in dialogs → should be \\`p-dialog\\`\n\n### 3. Layout Anti-Patterns\n- \\`h-screen\\` on page components → remove (root handles it)\n- \\`text-3xl font-bold\\` → should be \\`text-page-title font-semibold\\`\n- \\`hsl(var(--token))\\` → should be \\`var(--token)\\` directly\n\n### 4. Page Structure\n- Pages must use \\`p-page\\` wrapper or \\`StandardPageContainer\\`\n- Page title must be \\`text-page-title font-semibold\\`\n- Section gaps must be \\`gap-section\\` or \\`space-y-section\\`\n\n## How to Report\nFor each violation found, report:\n- File path and line number\n- Current code (wrong)\n- Suggested fix (right)\n- Category (color / density / layout / pattern)\n\nUse the \\`validate_component\\` tool to check individual components.\nUse the \\`design://rules\\` resource for the full WRONG→RIGHT cheat sheet.`,\n },\n }],\n }));\n\n // ── /fix-colors ───────────────────────────────────────────\n server.registerPrompt('fix-colors', {\n title: 'Fix Hardcoded Colors',\n description: 'Find and fix all hardcoded Tailwind colors in the codebase, replacing with @omnifyjp semantic tokens',\n }, async () => ({\n messages: [{\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `Find and fix ALL hardcoded Tailwind colors in the current project's \\`.tsx\\` and \\`.ts\\` files.\n\n## Replacement Map\n\n| Find (regex) | Replace with |\n|---|---|\n| \\`bg-white\\` | \\`bg-background\\` or \\`bg-card\\` |\n| \\`bg-gray-50\\`, \\`bg-gray-100\\` | \\`bg-muted\\` |\n| \\`hover:bg-gray-50\\`, \\`hover:bg-gray-100\\` | \\`hover:bg-accent\\` |\n| \\`text-gray-900\\`, \\`text-gray-800\\`, \\`text-gray-700\\` | \\`text-foreground\\` |\n| \\`text-gray-600\\`, \\`text-gray-500\\` | \\`text-muted-foreground\\` |\n| \\`text-gray-400\\`, \\`text-gray-300\\` | \\`text-muted-foreground\\` |\n| \\`border-gray-200\\`, \\`border-gray-300\\` | \\`border-border\\` |\n| \\`text-blue-600\\`, \\`text-blue-500\\` | \\`text-primary\\` |\n| \\`bg-blue-600\\`, \\`bg-blue-500\\` | \\`bg-primary\\` |\n| \\`bg-blue-50\\` (active state) | \\`bg-primary/10\\` |\n| \\`bg-blue-50 text-blue-600\\` (sidebar active) | \\`bg-sidebar-primary/10 text-sidebar-primary\\` |\n| \\`hover:text-blue-600\\`, \\`hover:text-blue-700\\` | \\`hover:text-primary\\` |\n| \\`hover:bg-blue-700\\` | \\`hover:bg-primary/90\\` |\n| \\`bg-red-600\\`, \\`bg-red-500\\` | \\`bg-destructive\\` |\n| \\`text-red-600\\`, \\`text-red-500\\` | \\`text-destructive\\` |\n| \\`bg-red-50\\` | \\`bg-destructive/10\\` |\n| \\`bg-green-600\\`, \\`bg-green-500\\` | \\`bg-success\\` |\n| \\`text-green-600\\`, \\`text-green-500\\` | \\`text-success\\` |\n| \\`focus:ring-blue-*\\` | \\`focus:ring-ring\\` |\n| \\`focus-visible:ring-blue-*\\` | \\`focus-visible:ring-ring\\` |\n\n## Dark mode pattern\nWhen removing dark: variants like \\`dark:bg-blue-500/15 dark:text-blue-400\\`, the semantic tokens handle dark mode automatically — just remove them:\n- \\`bg-blue-50 dark:bg-blue-500/15 text-blue-600 dark:text-blue-400\\` → \\`bg-primary/10 text-primary\\`\n\n## EXCEPTIONS — Do NOT change:\n1. **Multi-color status badge maps** where each status is a DIFFERENT color:\n \\`\\`\\`tsx\n const statusColors = {\n active: 'bg-green-50 text-green-600 ...',\n pending: 'bg-blue-50 text-blue-600 ...',\n rejected: 'bg-red-50 text-red-600 ...',\n };\n \\`\\`\\`\n2. **Intentionally dark sections** (chat sidebar with \\`bg-gray-900\\`)\n3. **Code block styling** (\\`bg-slate-900\\`)\n4. **\\`text-white\\` on dynamically colored backgrounds** (\\`style={{ backgroundColor: item.color }}\\`)\n5. **Chart hex colors** (recharts requires hex)\n6. **Gradient decorations** (\\`from-blue-500 to-purple-600\\` for visual flair)\n\n## Process\n1. Search for hardcoded color patterns using grep/ripgrep\n2. For each file, read it and identify which matches are violations vs exceptions\n3. Fix violations with the replacement map above\n4. Run \\`validate_component\\` on modified files to verify\n5. Report summary: files changed, violations fixed, exceptions kept`,\n },\n }],\n }));\n\n // ── /create-component ─────────────────────────────────────\n server.registerPrompt('create-component', {\n title: 'Create Component',\n description: 'Create a new UI component following @omnifyjp conventions (cn(), forwardRef, cva variants, kebab-case filename)',\n argsSchema: {\n name: z.string().describe('Component name in PascalCase (e.g., \"StatusCard\", \"UserAvatar\")'),\n },\n }, async ({ name }) => ({\n messages: [{\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `Create a new UI component called \\`${name}\\`.\n\n## File Convention\n- Filename: \\`${toKebabCase(name)}.tsx\\`\n- Export: named export \\`${name}\\`\n\n## Template\n\n\\`\\`\\`tsx\nimport { type HTMLAttributes, forwardRef } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '@omnifyjp/ui-components';\n\nconst ${toCamelCase(name)}Variants = cva(\n // Base styles\n 'inline-flex items-center rounded-md transition-colors',\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground',\n secondary: 'bg-secondary text-secondary-foreground',\n outline: 'border border-border bg-background',\n },\n size: {\n sm: 'h-element-sm px-3 text-sm',\n default: 'h-element px-4 text-sm',\n lg: 'h-element-lg px-6',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\ninterface ${name}Props\n extends HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof ${toCamelCase(name)}Variants> {\n // Add custom props here\n}\n\nconst ${name} = forwardRef<HTMLDivElement, ${name}Props>(\n ({ className, variant, size, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(${toCamelCase(name)}Variants({ variant, size }), className)}\n {...props}\n />\n ),\n);\n${name}.displayName = '${name}';\n\nexport { ${name} };\nexport type { ${name}Props };\n\\`\\`\\`\n\n## Rules\n1. Use \\`cn()\\` from \\`@omnifyjp/ui\\` for class merging\n2. Use \\`forwardRef\\` for DOM element refs\n3. Use \\`cva()\\` for variant definitions\n4. Use semantic tokens only — no hardcoded colors\n5. Heights: \\`h-element-sm\\`, \\`h-element\\`, \\`h-element-lg\\` (not \\`h-8\\`, \\`h-9\\`, \\`h-10\\`)\n6. Export both component and its Props type\n7. Set \\`displayName\\` for DevTools`,\n },\n }],\n }));\n\n // ── /create-dialog ────────────────────────────────────────\n server.registerPrompt('create-dialog', {\n title: 'Create Dialog',\n description: 'Create a dialog/modal component following @omnifyjp patterns (Dialog, DialogContent, form, actions)',\n argsSchema: {\n name: z.string().describe('Dialog component name in PascalCase (e.g., \"CreateUserDialog\", \"ConfirmDeleteDialog\")'),\n },\n }, async ({ name }) => ({\n messages: [{\n role: 'user' as const,\n content: {\n type: 'text' as const,\n text: `Create a dialog component called \\`${name}\\`.\n\n## Template\n\n\\`\\`\\`tsx\nimport { useState } from 'react';\nimport {\n Dialog, DialogContent, DialogHeader, DialogTitle,\n DialogDescription, DialogFooter,\n} from '@omnifyjp/ui-components/components/dialog';\nimport { Button } from '@omnifyjp/ui-components/components/button';\nimport { Input } from '@omnifyjp/ui-components/components/input';\nimport { Label } from '@omnifyjp/ui-components/components/label';\nimport { toast } from 'sonner';\n\ninterface ${name}Props {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\nexport function ${name}({ open, onOpenChange }: ${name}Props) {\n const [loading, setLoading] = useState(false);\n\n const handleSubmit = async () => {\n setLoading(true);\n try {\n // TODO: implement\n toast.success('Created successfully');\n onOpenChange(false);\n } catch {\n toast.error('Something went wrong');\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Dialog Title</DialogTitle>\n <DialogDescription>Optional description</DialogDescription>\n </DialogHeader>\n <div className=\"p-dialog space-y-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"field\">Field Label</Label>\n <Input id=\"field\" placeholder=\"Enter value...\" />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? 'Creating...' : 'Create'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\\`\\`\\`\n\n## Rules\n1. Props: \\`{ open: boolean; onOpenChange: (open: boolean) => void }\\`\n2. Dialog body padding: \\`p-dialog\\` (NOT \\`p-6\\`)\n3. Form fields: \\`space-y-2\\` wrapping \\`Label\\` + \\`Input\\`\n4. Form sections: \\`space-y-4\\` between fields\n5. Footer: Cancel (outline) left, Submit (primary) right\n6. Toast: \\`toast.success()\\` / \\`toast.error()\\` from sonner\n7. Loading state on submit button`,\n },\n }],\n }));\n}\n\n// ── Utility ──\n\nfunction toKebabCase(str: string): string {\n return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\n}\n\nfunction toCamelCase(str: string): string {\n return str.charAt(0).toLowerCase() + str.slice(1);\n}\n\n// ── Page Pattern Prompts ──\n\nconst LIST_CARD_PROMPT = `\n\\`\\`\\`tsx\n<div className=\"p-page space-y-section\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-page-title font-semibold mb-2\">Title</h1>\n <p className=\"text-sm text-muted-foreground\">Description</p>\n </div>\n <Button><Plus className=\"w-4 h-4\" /> Create</Button>\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-section\">\n {items.map((item) => (\n <Card className=\"hover:shadow-lg transition-shadow\">\n <CardHeader>\n <div className=\"flex items-start justify-between mb-3\">\n <div className=\"w-12 h-12 rounded-lg ...\" />\n <Badge variant=\"secondary\">{item.count}</Badge>\n </div>\n <CardTitle>\n <Link className=\"hover:text-primary transition-colors\">{item.name}</Link>\n </CardTitle>\n <CardDescription className=\"line-clamp-2\">{item.desc}</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {/* Progress, stats, avatars, meta, action footer */}\n </CardContent>\n </Card>\n ))}\n </div>\n</div>\n\\`\\`\\``;\n\nconst LIST_TABLE_PROMPT = `\n\\`\\`\\`tsx\n<div className=\"p-page\">\n <div className=\"flex items-center justify-between mb-section\">\n <div>\n <h1 className=\"text-page-title font-semibold mb-2\">Title</h1>\n <p className=\"text-sm text-muted-foreground\">Description</p>\n </div>\n <div className=\"flex items-center gap-3\">\n <Button variant=\"outline\" size=\"sm\"><Filter className=\"w-4 h-4\" /> Filter</Button>\n <Button><Plus className=\"w-4 h-4\" /> Create</Button>\n </div>\n </div>\n\n <div className=\"bg-card rounded-lg border\">\n {/* Search bar */}\n <div className=\"p-4 border-b\">\n <div className=\"flex items-center gap-4\">\n <div className=\"flex-1 relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground\" />\n <Input placeholder=\"Search...\" className=\"pl-10\" />\n </div>\n <Select><SelectTrigger className=\"w-[180px]\">...</SelectTrigger></Select>\n </div>\n </div>\n {/* Table */}\n <Table>...</Table>\n {/* Footer */}\n <div className=\"p-4 border-t bg-muted\">\n <p className=\"text-sm text-muted-foreground\">Showing N of M</p>\n </div>\n </div>\n</div>\n\\`\\`\\``;\n\nconst DETAIL_PROMPT = `\n\\`\\`\\`tsx\n<div className=\"p-page space-y-section\">\n {/* Back + header */}\n <div className=\"flex items-center gap-4\">\n <Link to=\"..\">\n <Button variant=\"ghost\" size=\"icon\"><ArrowLeft className=\"w-5 h-5\" /></Button>\n </Link>\n <div className=\"w-10 h-10 rounded-lg ...\" />\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2 mb-1\">\n <Badge variant=\"outline\">ID</Badge>\n <Badge className={statusColor}>Status</Badge>\n </div>\n <h1 className=\"text-page-title font-semibold\">Title</h1>\n </div>\n <Button variant=\"ghost\" size=\"icon\"><MoreVertical className=\"w-5 h-5\" /></Button>\n </div>\n\n {/* 2/3 + 1/3 */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-section\">\n <div className=\"lg:col-span-2 space-y-section\">\n <Card>/* Description */</Card>\n <Card>/* Comments */</Card>\n </div>\n <div className=\"space-y-4\">\n <Card>/* Details sidebar with field pattern */</Card>\n <Card>/* Actions */</Card>\n </div>\n </div>\n</div>\n\\`\\`\\``;\n\nconst DASHBOARD_PROMPT = `\n\\`\\`\\`tsx\n<StandardPageContainer\n title=\"Dashboard\"\n subtitle=\"Overview\"\n contentClassName=\"space-y-section\"\n>\n {/* Stat cards */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-section\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Label</CardTitle>\n <Icon className=\"h-4 w-4 text-muted-foreground\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">Value</div>\n <p className=\"text-xs text-muted-foreground mt-1\">Sub-text</p>\n </CardContent>\n </Card>\n </div>\n\n {/* 2/3 + 1/3 content */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-section\">\n <Card className=\"lg:col-span-2\">/* List with hover:bg-accent items */</Card>\n <Card>/* Activity sidebar */</Card>\n </div>\n</StandardPageContainer>\n\\`\\`\\``;\n\nconst SETTINGS_PROMPT = `\n\\`\\`\\`tsx\n<StandardPageContainer\n title=\"Settings\"\n subtitle=\"Preferences\"\n contentClassName=\"max-w-4xl mx-auto space-y-section\"\n>\n <Tabs defaultValue=\"profile\" className=\"space-y-section\">\n <TabsList>\n <TabsTrigger value=\"profile\"><User className=\"w-4 h-4\" /> Profile</TabsTrigger>\n <TabsTrigger value=\"notifications\"><Bell className=\"w-4 h-4\" /> Notifications</TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"profile\" className=\"space-y-section\">\n <Card>\n <CardHeader><CardTitle>Section</CardTitle></CardHeader>\n <CardContent className=\"space-y-6\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label>Field</Label>\n <Input />\n </div>\n </div>\n <Button>Save</Button>\n </CardContent>\n </Card>\n </TabsContent>\n\n <TabsContent value=\"notifications\" className=\"space-y-section\">\n <Card>\n <CardContent className=\"space-y-4\">\n {/* Toggle row */}\n <div className=\"flex items-center justify-between\">\n <div className=\"space-y-0.5\">\n <Label>Setting</Label>\n <p className=\"text-sm text-muted-foreground\">Description</p>\n </div>\n <Switch />\n </div>\n <Separator />\n </CardContent>\n </Card>\n </TabsContent>\n </Tabs>\n</StandardPageContainer>\n\\`\\`\\``;\n\nconst KANBAN_PROMPT = `\n\\`\\`\\`tsx\n<div className=\"p-page\">\n <div className=\"flex items-center justify-between mb-section\">\n <div>\n <h1 className=\"text-page-title font-semibold mb-2\">Board</h1>\n <p className=\"text-sm text-muted-foreground\">Description</p>\n </div>\n <div className=\"flex items-center gap-3\">\n <Button variant=\"outline\" size=\"sm\"><Filter className=\"w-4 h-4\" /> Filter</Button>\n <Button><Plus className=\"w-4 h-4\" /> Create</Button>\n </div>\n </div>\n\n <div className=\"flex gap-4 overflow-x-auto pb-4\">\n {columns.map((col) => (\n <div className=\"flex-1 min-w-[280px] bg-muted rounded-lg p-3\">\n <div className=\"flex items-center justify-between mb-3\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold\">{col.title}</h3>\n <Badge variant=\"secondary\">{col.count}</Badge>\n </div>\n </div>\n <div className=\"space-y-2\">\n {col.tasks.map((task) => (\n <Card className=\"p-3 hover:shadow-md transition-shadow cursor-pointer group\">\n {/* task card content */}\n </Card>\n ))}\n </div>\n </div>\n ))}\n </div>\n</div>\n\\`\\`\\``;\n\nconst OVERVIEW_PROMPT = `\n\\`\\`\\`tsx\n<StandardPageContainer\n title=\"Module Name\"\n subtitle=\"Overview\"\n extra={<Button><Plus className=\"w-4 h-4\" /> Create</Button>}\n contentClassName=\"space-y-section\"\n>\n {/* Stat cards with colored icons */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-section\">\n {stats.map((stat) => (\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-muted-foreground\">{stat.label}</p>\n <p className=\"text-2xl font-bold mt-1\">{stat.value}</p>\n </div>\n <div className={\\`p-3 rounded-lg \\${stat.bgColor}\\`}>\n <stat.icon className={\\`w-5 h-5 \\${stat.iconColor}\\`} />\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n {/* Quick list */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle>Recent</CardTitle>\n <Link className=\"text-sm text-primary hover:text-primary/80\">View all</Link>\n </div>\n </CardHeader>\n <CardContent>\n <Table>...</Table>\n </CardContent>\n </Card>\n</StandardPageContainer>\n\\`\\`\\``;\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAutBlB,SAAS,yBAAyBC,SAAmB;AAC1D,EAAAA,QAAO,SAAS,mBAAmB,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","names":["server","server","server","server","server","server","z","server"]}
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/setup.ts
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
var MCP_CONFIG = {
|
|
7
|
+
mcpServers: {
|
|
8
|
+
"omnify-ui": {
|
|
9
|
+
type: "stdio",
|
|
10
|
+
command: "npx",
|
|
11
|
+
args: ["@omnifyjp/ui-mcp"]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var ROOT_MARKERS = [
|
|
16
|
+
"pnpm-workspace.yaml",
|
|
17
|
+
"pnpm-lock.yaml",
|
|
18
|
+
"yarn.lock",
|
|
19
|
+
"package-lock.json",
|
|
20
|
+
"bun.lockb",
|
|
21
|
+
".git"
|
|
22
|
+
];
|
|
23
|
+
function findProjectRoot() {
|
|
24
|
+
let dir = process.cwd();
|
|
25
|
+
let firstPkg = null;
|
|
26
|
+
while (dir !== "/" && dir !== resolve(dir, "..")) {
|
|
27
|
+
if (dir.includes("/node_modules")) {
|
|
28
|
+
dir = resolve(dir, "..");
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const hasPkg = existsSync(resolve(dir, "package.json"));
|
|
32
|
+
if (hasPkg && !firstPkg) {
|
|
33
|
+
firstPkg = dir;
|
|
34
|
+
}
|
|
35
|
+
if (hasPkg && ROOT_MARKERS.some((m) => existsSync(resolve(dir, m)))) {
|
|
36
|
+
return dir;
|
|
37
|
+
}
|
|
38
|
+
dir = resolve(dir, "..");
|
|
39
|
+
}
|
|
40
|
+
return firstPkg ?? process.cwd();
|
|
41
|
+
}
|
|
42
|
+
function setup() {
|
|
43
|
+
const root = findProjectRoot();
|
|
44
|
+
const mcpPath = resolve(root, ".mcp.json");
|
|
45
|
+
if (existsSync(mcpPath)) {
|
|
46
|
+
try {
|
|
47
|
+
const existing = JSON.parse(readFileSync(mcpPath, "utf-8"));
|
|
48
|
+
if (existing.mcpServers?.["omnify-ui"]) {
|
|
49
|
+
console.log("[ui-mcp] Already configured in .mcp.json");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
existing.mcpServers = {
|
|
53
|
+
...existing.mcpServers,
|
|
54
|
+
...MCP_CONFIG.mcpServers
|
|
55
|
+
};
|
|
56
|
+
writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + "\n");
|
|
57
|
+
console.log("[ui-mcp] Added omnify-ui to existing .mcp.json");
|
|
58
|
+
} catch {
|
|
59
|
+
console.log("[ui-mcp] Could not parse .mcp.json, skipping");
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
writeFileSync(mcpPath, JSON.stringify(MCP_CONFIG, null, 2) + "\n");
|
|
64
|
+
console.log("[ui-mcp] Created .mcp.json with omnify-ui MCP server");
|
|
65
|
+
console.log("[ui-mcp] AI tools (Claude Code, Cursor) will auto-detect design system rules");
|
|
66
|
+
}
|
|
67
|
+
setup();
|
|
68
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/setup.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nconst MCP_CONFIG = {\n mcpServers: {\n 'omnify-ui': {\n type: 'stdio',\n command: 'npx',\n args: ['@omnifyjp/ui-mcp'],\n },\n },\n};\n\n/** Markers that indicate a project root (not a sub-package) */\nconst ROOT_MARKERS = [\n 'pnpm-workspace.yaml',\n 'pnpm-lock.yaml',\n 'yarn.lock',\n 'package-lock.json',\n 'bun.lockb',\n '.git',\n];\n\n/**\n * Find the consumer's project root by walking up from cwd.\n * - Skips anything inside node_modules\n * - Prefers directories with lockfiles or .git (true project root)\n * - Falls back to first package.json outside node_modules\n */\nfunction findProjectRoot(): string {\n let dir = process.cwd();\n let firstPkg: string | null = null;\n\n while (dir !== '/' && dir !== resolve(dir, '..')) {\n if (dir.includes('/node_modules')) {\n dir = resolve(dir, '..');\n continue;\n }\n\n const hasPkg = existsSync(resolve(dir, 'package.json'));\n\n if (hasPkg && !firstPkg) {\n firstPkg = dir;\n }\n\n // Check for root markers — this is the true project root\n if (hasPkg && ROOT_MARKERS.some(m => existsSync(resolve(dir, m)))) {\n return dir;\n }\n\n dir = resolve(dir, '..');\n }\n\n return firstPkg ?? process.cwd();\n}\n\nfunction setup() {\n const root = findProjectRoot();\n const mcpPath = resolve(root, '.mcp.json');\n\n if (existsSync(mcpPath)) {\n try {\n const existing = JSON.parse(readFileSync(mcpPath, 'utf-8'));\n if (existing.mcpServers?.['omnify-ui']) {\n console.log('[ui-mcp] Already configured in .mcp.json');\n return;\n }\n existing.mcpServers = {\n ...existing.mcpServers,\n ...MCP_CONFIG.mcpServers,\n };\n writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + '\\n');\n console.log('[ui-mcp] Added omnify-ui to existing .mcp.json');\n } catch {\n console.log('[ui-mcp] Could not parse .mcp.json, skipping');\n }\n return;\n }\n\n writeFileSync(mcpPath, JSON.stringify(MCP_CONFIG, null, 2) + '\\n');\n console.log('[ui-mcp] Created .mcp.json with omnify-ui MCP server');\n console.log('[ui-mcp] AI tools (Claude Code, Cursor) will auto-detect design system rules');\n}\n\nsetup();\n"],"mappings":";;;AAAA,SAAS,YAAY,cAAc,qBAAqB;AACxD,SAAS,eAAe;AAExB,IAAM,aAAa;AAAA,EACjB,YAAY;AAAA,IACV,aAAa;AAAA,MACX,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM,CAAC,kBAAkB;AAAA,IAC3B;AAAA,EACF;AACF;AAGA,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQA,SAAS,kBAA0B;AACjC,MAAI,MAAM,QAAQ,IAAI;AACtB,MAAI,WAA0B;AAE9B,SAAO,QAAQ,OAAO,QAAQ,QAAQ,KAAK,IAAI,GAAG;AAChD,QAAI,IAAI,SAAS,eAAe,GAAG;AACjC,YAAM,QAAQ,KAAK,IAAI;AACvB;AAAA,IACF;AAEA,UAAM,SAAS,WAAW,QAAQ,KAAK,cAAc,CAAC;AAEtD,QAAI,UAAU,CAAC,UAAU;AACvB,iBAAW;AAAA,IACb;AAGA,QAAI,UAAU,aAAa,KAAK,OAAK,WAAW,QAAQ,KAAK,CAAC,CAAC,CAAC,GAAG;AACjE,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,KAAK,IAAI;AAAA,EACzB;AAEA,SAAO,YAAY,QAAQ,IAAI;AACjC;AAEA,SAAS,QAAQ;AACf,QAAM,OAAO,gBAAgB;AAC7B,QAAM,UAAU,QAAQ,MAAM,WAAW;AAEzC,MAAI,WAAW,OAAO,GAAG;AACvB,QAAI;AACF,YAAM,WAAW,KAAK,MAAM,aAAa,SAAS,OAAO,CAAC;AAC1D,UAAI,SAAS,aAAa,WAAW,GAAG;AACtC,gBAAQ,IAAI,0CAA0C;AACtD;AAAA,MACF;AACA,eAAS,aAAa;AAAA,QACpB,GAAG,SAAS;AAAA,QACZ,GAAG,WAAW;AAAA,MAChB;AACA,oBAAc,SAAS,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AAC/D,cAAQ,IAAI,gDAAgD;AAAA,IAC9D,QAAQ;AACN,cAAQ,IAAI,8CAA8C;AAAA,IAC5D;AACA;AAAA,EACF;AAEA,gBAAc,SAAS,KAAK,UAAU,YAAY,MAAM,CAAC,IAAI,IAAI;AACjE,UAAQ,IAAI,sDAAsD;AAClE,UAAQ,IAAI,8EAA8E;AAC5F;AAEA,MAAM;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@omnifyjp/ui-mcp",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "MCP server exposing @omnifyjp design system tokens, rules, and component APIs to AI tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"omnifyjp-ui-mcp": "./dist/index.js",
|
|
9
|
+
"omnifyjp-ui-mcp-setup": "./dist/setup.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"postinstall": "node ./dist/setup.js || true"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
26
|
+
"zod": "^3.24.4"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"tsup": "^8.0.0",
|
|
30
|
+
"typescript": "^5.9.3"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"mcp",
|
|
34
|
+
"design-system",
|
|
35
|
+
"omnify",
|
|
36
|
+
"ai-tools",
|
|
37
|
+
"claude",
|
|
38
|
+
"cursor"
|
|
39
|
+
],
|
|
40
|
+
"license": "MIT"
|
|
41
|
+
}
|