@usevyre/ai-context 0.2.1 → 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Galih Pranowo (gapra.dev)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -29,6 +29,6 @@ Quick reference for AI agents — one file per component.
29
29
  - [Switch](switch.md) — Toggle switch for boolean on/off settings.
30
30
  - [Table](table.md) — Data table with optional sorting. Compose with TableHeader, TableRow, TableCell.
31
31
  - [Tabs](tabs.md) — Tabbed navigation for switching between content panels.
32
- - [Toast](toast.md) — Transient notification. Use the useToast hook to trigger toasts imperatively.
32
+ - [Toast](toast.md) — Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
33
33
  - [Tooltip](tooltip.md) — Short label that appears on hover/focus. For rich content use Popover instead.
34
34
  - [Typography](typography.md) — Text rendering components with semantic scale. Includes Text, Heading, Lead, Code, Blockquote.
@@ -1,10 +1,10 @@
1
1
  # Toast — AI Cheat Sheet
2
2
  > Quick reference for Claude / Cursor / Copilot
3
3
 
4
- **Transient notification. Use the useToast hook to trigger toasts imperatively.**
4
+ **Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.**
5
5
 
6
6
  ```ts
7
- import { useToast, ToastProvider } from "@usevyre/react"
7
+ import { useToast, ToastProvider } from "@usevyre/react" // Vue: import { useToast, ToastViewport } from "@usevyre/vue"
8
8
  ```
9
9
 
10
10
  ## Valid Props
@@ -815,10 +815,10 @@ import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@usevyre/react"
815
815
 
816
816
  ### Toast
817
817
 
818
- Transient notification. Use the useToast hook to trigger toasts imperatively.
818
+ Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
819
819
 
820
820
  ```tsx
821
- import { useToast, ToastProvider } from "@usevyre/react"
821
+ import { useToast, ToastProvider } from "@usevyre/react" // Vue: import { useToast, ToastViewport } from "@usevyre/vue"
822
822
 
823
823
  // Props:
824
824
  // variant = "default" | "success" | "warning" | "danger" (default: default)
@@ -814,10 +814,10 @@ import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@usevyre/react"
814
814
 
815
815
  ### Toast
816
816
 
817
- Transient notification. Use the useToast hook to trigger toasts imperatively.
817
+ Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
818
818
 
819
819
  ```tsx
820
- import { useToast, ToastProvider } from "@usevyre/react"
820
+ import { useToast, ToastProvider } from "@usevyre/react" // Vue: import { useToast, ToastViewport } from "@usevyre/vue"
821
821
 
822
822
  // Props:
823
823
  // variant = "default" | "success" | "warning" | "danger" (default: default)
@@ -255,7 +255,7 @@ Import: `import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@usevyre/react
255
255
  Valid props:
256
256
 
257
257
  ## Toast
258
- Transient notification. Use the useToast hook to trigger toasts imperatively.
258
+ Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
259
259
  Import: `import { useToast, ToastProvider } from "@usevyre/react"`
260
260
 
261
261
  Valid props:
@@ -809,10 +809,10 @@ import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@usevyre/react"
809
809
 
810
810
  ### Toast
811
811
 
812
- Transient notification. Use the useToast hook to trigger toasts imperatively.
812
+ Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
813
813
 
814
814
  ```tsx
815
- import { useToast, ToastProvider } from "@usevyre/react"
815
+ import { useToast, ToastProvider } from "@usevyre/react" // Vue: import { useToast, ToastViewport } from "@usevyre/vue"
816
816
 
817
817
  // Props:
818
818
  // variant = "default" | "success" | "warning" | "danger" (default: default)
package/dist/index.js CHANGED
@@ -814,10 +814,10 @@ import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@usevyre/react"
814
814
 
815
815
  ### Toast
816
816
 
817
- Transient notification. Use the useToast hook to trigger toasts imperatively.
817
+ Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
818
818
 
819
819
  \`\`\`tsx
820
- import { useToast, ToastProvider } from "@usevyre/react"
820
+ import { useToast, ToastProvider } from "@usevyre/react" // Vue: import { useToast, ToastViewport } from "@usevyre/vue"
821
821
 
822
822
  // Props:
823
823
  // variant = "default" | "success" | "warning" | "danger" (default: default)
@@ -1227,7 +1227,7 @@ Import: \`import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@usevyre/reac
1227
1227
  Valid props:
1228
1228
 
1229
1229
  ## Toast
1230
- Transient notification. Use the useToast hook to trigger toasts imperatively.
1230
+ Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
1231
1231
  Import: \`import { useToast, ToastProvider } from "@usevyre/react"\`
1232
1232
 
1233
1233
  Valid props:
@@ -2077,10 +2077,10 @@ import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@usevyre/react"
2077
2077
 
2078
2078
  ### Toast
2079
2079
 
2080
- Transient notification. Use the useToast hook to trigger toasts imperatively.
2080
+ Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
2081
2081
 
2082
2082
  \`\`\`tsx
2083
- import { useToast, ToastProvider } from "@usevyre/react"
2083
+ import { useToast, ToastProvider } from "@usevyre/react" // Vue: import { useToast, ToastViewport } from "@usevyre/vue"
2084
2084
 
2085
2085
  // Props:
2086
2086
  // variant = "default" | "success" | "warning" | "danger" (default: default)
@@ -3047,10 +3047,10 @@ import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@usevyre/react"
3047
3047
 
3048
3048
  ### Toast
3049
3049
 
3050
- Transient notification. Use the useToast hook to trigger toasts imperatively.
3050
+ Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
3051
3051
 
3052
3052
  \`\`\`tsx
3053
- import { useToast, ToastProvider } from "@usevyre/react"
3053
+ import { useToast, ToastProvider } from "@usevyre/react" // Vue: import { useToast, ToastViewport } from "@usevyre/vue"
3054
3054
 
3055
3055
  // Props:
3056
3056
  // variant = "default" | "success" | "warning" | "danger" (default: default)
@@ -4019,10 +4019,10 @@ import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@usevyre/react"
4019
4019
 
4020
4020
  ### Toast
4021
4021
 
4022
- Transient notification. Use the useToast hook to trigger toasts imperatively.
4022
+ Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
4023
4023
 
4024
4024
  \`\`\`tsx
4025
- import { useToast, ToastProvider } from "@usevyre/react"
4025
+ import { useToast, ToastProvider } from "@usevyre/react" // Vue: import { useToast, ToastViewport } from "@usevyre/vue"
4026
4026
 
4027
4027
  // Props:
4028
4028
  // variant = "default" | "success" | "warning" | "danger" (default: default)
@@ -5294,8 +5294,8 @@ export const schema = {
5294
5294
  ]
5295
5295
  },
5296
5296
  "Toast": {
5297
- "description": "Transient notification. Use the useToast hook to trigger toasts imperatively.",
5298
- "import": "import { useToast, ToastProvider } from \"@usevyre/react\"",
5297
+ "description": "Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.",
5298
+ "import": "import { useToast, ToastProvider } from \"@usevyre/react\" // Vue: import { useToast, ToastViewport } from \"@usevyre/vue\"",
5299
5299
  "props": {
5300
5300
  "variant": {
5301
5301
  "type": "enum",
@@ -5681,7 +5681,7 @@ export const antiPatterns = {
5681
5681
  export const versionInfo = {
5682
5682
  "version": "0.2.0",
5683
5683
  "packageVersion": "0.1.1",
5684
- "generatedAt": "2026-05-08T07:42:19.123Z",
5684
+ "generatedAt": "2026-05-10T13:15:02.040Z",
5685
5685
  "validFor": [
5686
5686
  "@usevyre/react@0.1.1+",
5687
5687
  "@usevyre/vue@0.1.1+"
@@ -5940,7 +5940,7 @@ export const cheatSheets = {
5940
5940
  "Switch": "# Switch — AI Cheat Sheet\n> Quick reference for Claude / Cursor / Copilot\n\n**Toggle switch for boolean on/off settings.**\n\n```ts\nimport { Switch } from \"@usevyre/react\"\n```\n\n## Valid Props\n\n| Prop | Values | Default |\n|------|--------|---------|\n| `size` | `\"sm\"` \\| `\"md\"` | `md` |\n| `checked` | `true` \\| `false` | — |\n| `disabled` | `true` \\| `false` | `false` |\n\n## Examples\n\n**Notifications toggle**\n```tsx\n<label style={{ display: 'flex', alignItems: 'center', gap: 'var(--vyre-spacing-2)' }}>\n <Switch checked={notifications} onChange={setNotifications} />\n Enable notifications\n</label>\n```\n",
5941
5941
  "Table": "# Table — AI Cheat Sheet\n> Quick reference for Claude / Cursor / Copilot\n\n**Data table with optional sorting. Compose with TableHeader, TableRow, TableCell.**\n\n```ts\nimport { Table, TableHead, TableBody, TableRow, TableHeader, TableCell } from \"@usevyre/react\"\n```\n\n## Examples\n\n**Basic data table**\n```tsx\n<Table>\n <TableHead>\n <TableRow>\n <TableHeader>Name</TableHeader>\n <TableHeader>Status</TableHeader>\n </TableRow>\n </TableHead>\n <TableBody>\n <TableRow>\n <TableCell>Alice</TableCell>\n <TableCell><Badge variant=\"success\">Active</Badge></TableCell>\n </TableRow>\n </TableBody>\n</Table>\n```\n",
5942
5942
  "Tabs": "# Tabs — AI Cheat Sheet\n> Quick reference for Claude / Cursor / Copilot\n\n**Tabbed navigation for switching between content panels.**\n\n```ts\nimport { Tabs, TabList, Tab, TabPanels, TabPanel } from \"@usevyre/react\"\n```\n\n## Examples\n\n**Basic tabs**\n```tsx\n<Tabs defaultIndex={0}>\n <TabList>\n <Tab>Overview</Tab>\n <Tab>Settings</Tab>\n </TabList>\n <TabPanels>\n <TabPanel>Overview content</TabPanel>\n <TabPanel>Settings content</TabPanel>\n </TabPanels>\n</Tabs>\n```\n",
5943
- "Toast": "# Toast — AI Cheat Sheet\n> Quick reference for Claude / Cursor / Copilot\n\n**Transient notification. Use the useToast hook to trigger toasts imperatively.**\n\n```ts\nimport { useToast, ToastProvider } from \"@usevyre/react\"\n```\n\n## Valid Props\n\n| Prop | Values | Default |\n|------|--------|---------|\n| `variant` | `\"default\"` \\| `\"success\"` \\| `\"warning\"` \\| `\"danger\"` | `default` |\n\n## Common AI Mistakes\n\n- ❌ `Rendering <Toast> directly in JSX`\n → Use: const { toast } = useToast(); then toast({ title, variant })\n- ❌ `variant=\"error\"`\n → Use variant=\"danger\"\n- ❌ `variant=\"info\"`\n → Use variant=\"default\"\n\n## Examples\n\n**Trigger a success toast**\n```tsx\nconst { toast } = useToast();\n\n<Button onClick={() => toast({ title: 'Saved!', variant: 'success', duration: 3000 })}>\n Save\n</Button>\n```\n\n**Setup: wrap app with ToastProvider**\n```tsx\n<ToastProvider>\n <App />\n</ToastProvider>\n```\n",
5943
+ "Toast": "# Toast — AI Cheat Sheet\n> Quick reference for Claude / Cursor / Copilot\n\n**Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.**\n\n```ts\nimport { useToast, ToastProvider } from \"@usevyre/react\" // Vue: import { useToast, ToastViewport } from \"@usevyre/vue\"\n```\n\n## Valid Props\n\n| Prop | Values | Default |\n|------|--------|---------|\n| `variant` | `\"default\"` \\| `\"success\"` \\| `\"warning\"` \\| `\"danger\"` | `default` |\n\n## Common AI Mistakes\n\n- ❌ `Rendering <Toast> directly in JSX`\n → Use: const { toast } = useToast(); then toast({ title, variant })\n- ❌ `variant=\"error\"`\n → Use variant=\"danger\"\n- ❌ `variant=\"info\"`\n → Use variant=\"default\"\n\n## Examples\n\n**Trigger a success toast**\n```tsx\nconst { toast } = useToast();\n\n<Button onClick={() => toast({ title: 'Saved!', variant: 'success', duration: 3000 })}>\n Save\n</Button>\n```\n\n**Setup: wrap app with ToastProvider**\n```tsx\n<ToastProvider>\n <App />\n</ToastProvider>\n```\n",
5944
5944
  "Tooltip": "# Tooltip — AI Cheat Sheet\n> Quick reference for Claude / Cursor / Copilot\n\n**Short label that appears on hover/focus. For rich content use Popover instead.**\n\n```ts\nimport { Tooltip } from \"@usevyre/react\"\n```\n\n## Valid Props\n\n| Prop | Values | Default |\n|------|--------|---------|\n| `placement` | `\"top\"` \\| `\"bottom\"` \\| `\"left\"` \\| `\"right\"` | `top` |\n\n## Common AI Mistakes\n\n- ❌ `Using Tooltip for rich content (forms, buttons, etc.)`\n → Use Popover for rich interactive content\n\n## Examples\n\n**Tooltip on an icon button**\n```tsx\n<Tooltip content=\"Close dialog\" placement=\"bottom\">\n <Button variant=\"ghost\" size=\"icon\" aria-label=\"Close\">\n <X size={16} />\n </Button>\n</Tooltip>\n```\n",
5945
5945
  "Typography": "# Typography — AI Cheat Sheet\n> Quick reference for Claude / Cursor / Copilot\n\n**Text rendering components with semantic scale. Includes Text, Heading, Lead, Code, Blockquote.**\n\n```ts\nimport { Text, Heading, Lead, Code, Blockquote } from \"@usevyre/react\"\n```\n\n## Common AI Mistakes\n\n- ❌ `Using raw <h1>, <p> tags instead of Typography components`\n → Use <Heading>, <Text>, <Lead> from @usevyre/react\n\n## Examples\n\n**Page heading and body text**\n```tsx\n<Heading size=\"2xl\" as=\"h1\">Dashboard</Heading>\n<Lead>Welcome back. Here's what's happening today.</Lead>\n<Text size=\"sm\" style={{ color: 'var(--vyre-color-semantic-text-muted)' }}>Last updated 5 minutes ago.</Text>\n```\n"
5946
5946
  };
package/dist/schema.json CHANGED
@@ -1116,8 +1116,8 @@
1116
1116
  ]
1117
1117
  },
1118
1118
  "Toast": {
1119
- "description": "Transient notification. Use the useToast hook to trigger toasts imperatively.",
1120
- "import": "import { useToast, ToastProvider } from \"@usevyre/react\"",
1119
+ "description": "Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.",
1120
+ "import": "import { useToast, ToastProvider } from \"@usevyre/react\" // Vue: import { useToast, ToastViewport } from \"@usevyre/vue\"",
1121
1121
  "props": {
1122
1122
  "variant": {
1123
1123
  "type": "enum",
package/dist/tokens.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://usevyre.com/schemas/ai-tokens-v1.json",
3
- "version": "0.1.2",
4
- "generatedAt": "2026-05-08T07:41:02.398Z",
3
+ "version": "1.0.0",
4
+ "generatedAt": "2026-05-10T13:14:24.865Z",
5
5
  "description": "useVyre design tokens — machine-readable reference for AI agents. Use semantic color tokens; never use primitive tokens directly in components.",
6
6
  "naming": {
7
7
  "convention": "--vyre-[category]-[subcategory]-[variant]",
package/dist/tokens.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # useVyre Token Reference for AI Agents
2
- # Version: 0.1.2 | Generated: 2026-05-08
2
+ # Version: 1.0.0 | Generated: 2026-05-10
3
3
  # Machine-readable version: @usevyre/tokens/ai (ai-tokens.json)
4
4
 
5
5
  ## Usage
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "0.2.0",
3
3
  "packageVersion": "0.1.1",
4
- "generatedAt": "2026-05-08T07:42:19.123Z",
4
+ "generatedAt": "2026-05-10T13:15:02.040Z",
5
5
  "validFor": [
6
6
  "@usevyre/react@0.1.1+",
7
7
  "@usevyre/vue@0.1.1+"
@@ -812,10 +812,10 @@ import { Tabs, TabList, Tab, TabPanels, TabPanel } from "@usevyre/react"
812
812
 
813
813
  ### Toast
814
814
 
815
- Transient notification. Use the useToast hook to trigger toasts imperatively.
815
+ Transient notification. Use the useToast hook to trigger toasts imperatively. React: wrap app in <ToastProvider>. Vue: place <ToastViewport /> once in app root.
816
816
 
817
817
  ```tsx
818
- import { useToast, ToastProvider } from "@usevyre/react"
818
+ import { useToast, ToastProvider } from "@usevyre/react" // Vue: import { useToast, ToastViewport } from "@usevyre/vue"
819
819
 
820
820
  // Props:
821
821
  // variant = "default" | "success" | "warning" | "danger" (default: default)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usevyre/ai-context",
3
- "version": "0.2.1",
3
+ "version": "1.0.0",
4
4
  "description": "useVyre AI context — inject into LLM system prompts to eliminate UI hallucinations",
5
5
  "keywords": [
6
6
  "design-system",
@@ -31,10 +31,14 @@
31
31
  "./tokens-md": "./dist/tokens.md"
32
32
  },
33
33
  "files": [
34
- "dist"
34
+ "dist",
35
+ "scripts"
35
36
  ],
37
+ "bin": {
38
+ "usevyre-ai-context": "./scripts/init.js"
39
+ },
36
40
  "scripts": {
37
41
  "build": "node scripts/build.js",
38
42
  "clean": "rm -rf dist"
39
43
  }
40
- }
44
+ }
@@ -0,0 +1,442 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
2
+ import { resolve, dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const root = resolve(__dirname, "..");
7
+ const src = resolve(root, "src");
8
+ const dist = resolve(root, "dist");
9
+
10
+ mkdirSync(dist, { recursive: true });
11
+
12
+ // ── Load source data ──────────────────────────────────────────────────────────
13
+
14
+ const schema = JSON.parse(readFileSync(resolve(src, "schema/components.json"), "utf8"));
15
+ const staticContext = readFileSync(resolve(src, "full-context.md"), "utf8");
16
+
17
+ // Load AI tokens from @usevyre/tokens (sibling package in monorepo)
18
+ const tokensDistPath = resolve(__dirname, "../../tokens/dist/ai-tokens.json");
19
+ const tokensMDPath = resolve(__dirname, "../../tokens/dist/ai-tokens.md");
20
+ const aiTokens = existsSync(tokensDistPath) ? JSON.parse(readFileSync(tokensDistPath, "utf8")) : null;
21
+ const aiTokensMD = existsSync(tokensMDPath) ? readFileSync(tokensMDPath, "utf8") : null;
22
+
23
+ // ── Helpers ───────────────────────────────────────────────────────────────────
24
+
25
+ function buildComponentSection(name, comp) {
26
+ const lines = [];
27
+
28
+ lines.push(`### ${name}`);
29
+ lines.push("");
30
+ lines.push(comp.description);
31
+ lines.push("");
32
+
33
+ lines.push("```tsx");
34
+ lines.push(comp.import);
35
+ lines.push("");
36
+
37
+ const props = comp.props || {};
38
+ const propNames = Object.keys(props);
39
+ if (propNames.length > 0) {
40
+ lines.push("// Props:");
41
+ for (const [propName, prop] of Object.entries(props)) {
42
+ const vals = prop.values ? `"${prop.values.join('" | "')}"` : prop.type;
43
+ const def = prop.default !== undefined ? ` (default: ${prop.default})` : "";
44
+ lines.push(`// ${propName.padEnd(14)} = ${vals}${def}`);
45
+ }
46
+ lines.push("");
47
+ }
48
+
49
+ if (comp.examples?.length) {
50
+ lines.push("// Examples:");
51
+ for (const ex of comp.examples) {
52
+ lines.push(ex.code);
53
+ }
54
+ }
55
+
56
+ lines.push("```");
57
+ lines.push("");
58
+
59
+ if (comp.antiPatterns?.length) {
60
+ lines.push("**Common mistakes:**");
61
+ for (const ap of comp.antiPatterns) {
62
+ lines.push(`- ❌ \`${ap.pattern}\` → ${ap.fix}`);
63
+ }
64
+ lines.push("");
65
+ }
66
+
67
+ return lines.join("\n");
68
+ }
69
+
70
+ function buildCursorRulesSection(name, comp) {
71
+ const lines = [];
72
+ lines.push(`## ${name}`);
73
+ lines.push(comp.description);
74
+
75
+ // Extract package name from import string
76
+ const pkgMatch = comp.import.match(/"([^"]+)"/);
77
+ const pkg = pkgMatch ? pkgMatch[1] : "@usevyre/react";
78
+ const symbolMatch = comp.import.match(/import \{([^}]+)\}/);
79
+ const symbols = symbolMatch ? symbolMatch[1].trim() : name;
80
+ lines.push(`Import: \`import { ${symbols} } from "${pkg}"\``);
81
+ lines.push("");
82
+
83
+ const props = comp.props || {};
84
+ if (Object.keys(props).length > 0) {
85
+ lines.push("Valid props:");
86
+ for (const [propName, prop] of Object.entries(props)) {
87
+ if (prop.values) {
88
+ const def = prop.default !== undefined ? ` [default: ${prop.default}]` : "";
89
+ lines.push(`- ${propName}: ${prop.values.map(v => `"${v}"`).join(" | ")}${def}`);
90
+ }
91
+ }
92
+ lines.push("");
93
+ }
94
+
95
+ if (comp.antiPatterns?.length) {
96
+ lines.push("Never do:");
97
+ for (const ap of comp.antiPatterns) {
98
+ lines.push(`- ❌ ${ap.pattern} → ✅ ${ap.fix}`);
99
+ }
100
+ lines.push("");
101
+ }
102
+
103
+ return lines.join("\n");
104
+ }
105
+
106
+ // ── Generate component sections from schema ───────────────────────────────────
107
+
108
+ const componentSections = Object.entries(schema.components)
109
+ .map(([name, comp]) => buildComponentSection(name, comp))
110
+ .join("\n---\n\n");
111
+
112
+ const cursorSections = Object.entries(schema.components)
113
+ .map(([name, comp]) => buildCursorRulesSection(name, comp))
114
+ .join("\n");
115
+
116
+ // Collect all anti-patterns for hallucination guard section
117
+ const allAntiPatterns = Object.entries(schema.components).flatMap(([name, comp]) =>
118
+ (comp.antiPatterns || []).map(ap => ({ component: name, ...ap }))
119
+ );
120
+
121
+ const antiPatternBlock = allAntiPatterns
122
+ .map(ap => `- ❌ \`<${ap.component} ${ap.pattern}>\` → ${ap.fix}`)
123
+ .join("\n");
124
+
125
+ // ── Build full-context.md (schema-driven component section, static tokens/rules) ──
126
+
127
+ // Split static content at the Component API Reference section boundary
128
+ const splitMarker = "## Component API Reference";
129
+ const stylingMarker = "## Styling Rules for AI Agents";
130
+
131
+ const staticUpToComponents = staticContext.includes(splitMarker)
132
+ ? staticContext.split(splitMarker)[0].trimEnd()
133
+ : staticContext.split(stylingMarker)[0].trimEnd();
134
+
135
+ const staticStylingOnward = staticContext.includes(stylingMarker)
136
+ ? staticContext.split(stylingMarker)[1].trimStart()
137
+ : "";
138
+
139
+ const generatedFullContext = [
140
+ staticUpToComponents,
141
+ "",
142
+ "## Component API Reference",
143
+ "",
144
+ componentSections,
145
+ "---",
146
+ "",
147
+ "## Hallucination Guard — Common AI Mistakes",
148
+ "",
149
+ "The following prop values and patterns do NOT exist in useVyre.",
150
+ "If you generate these, you are hallucinating.",
151
+ "",
152
+ antiPatternBlock,
153
+ "",
154
+ "---",
155
+ "",
156
+ "## Styling Rules for AI Agents",
157
+ "",
158
+ staticStylingOnward,
159
+ ].join("\n");
160
+
161
+ // Write back to src so full-context.md stays in sync (schema is source of truth for components)
162
+ writeFileSync(resolve(src, "full-context.md"), generatedFullContext);
163
+ writeFileSync(resolve(dist, "full-context.md"), generatedFullContext);
164
+
165
+ // ── cursor-rules.md ───────────────────────────────────────────────────────────
166
+
167
+ const cursorRules = [
168
+ "---",
169
+ "description: useVyre design system rules — follow these when writing UI code",
170
+ "alwaysApply: true",
171
+ "---",
172
+ "",
173
+ "# useVyre Design System — Cursor Rules",
174
+ "# Version: " + schema.version,
175
+ "",
176
+ "You are working in a project using the useVyre design system (@usevyre/react).",
177
+ "Follow these rules strictly when generating any UI code.",
178
+ "",
179
+ "## Critical Rules",
180
+ "- NEVER use color/size values that are not in the valid prop lists below",
181
+ "- ALWAYS use semantic tokens (--vyre-color-semantic-*), never hardcode colors",
182
+ "- ALWAYS add aria-label to Button size=\"icon\" (no visible text)",
183
+ "- NEVER render <Toast> directly — use useToast() hook",
184
+ "- NEVER use <Input type=\"search\"> for search UI — use Command component",
185
+ "",
186
+ "## Component Rules",
187
+ "",
188
+ cursorSections,
189
+ "## Token Rules",
190
+ "",
191
+ "Use --vyre-color-semantic-* for all colors. Never use primitive tokens.",
192
+ "Use --vyre-spacing-* for all spacing. Never use raw px in component code.",
193
+ "Use --vyre-border-radius-* for border radius.",
194
+ ].join("\n");
195
+
196
+ writeFileSync(resolve(dist, "cursor-rules.md"), cursorRules);
197
+
198
+ // ── claude-context.md ─────────────────────────────────────────────────────────
199
+
200
+ const claudeContext = [
201
+ "# useVyre Design System Context",
202
+ "# Version: " + schema.version,
203
+ "",
204
+ "You are working in a codebase that uses the useVyre design system.",
205
+ "Follow the rules below strictly when writing any UI code.",
206
+ "",
207
+ generatedFullContext,
208
+ ].join("\n");
209
+
210
+ writeFileSync(resolve(dist, "claude-context.md"), claudeContext);
211
+
212
+ // ── windsurf-rules.md ─────────────────────────────────────────────────────────
213
+
214
+ const windsurfRules = [
215
+ "# useVyre Rules for Windsurf",
216
+ "# Version: " + schema.version,
217
+ "",
218
+ generatedFullContext,
219
+ ].join("\n");
220
+
221
+ writeFileSync(resolve(dist, "windsurf-rules.md"), windsurfRules);
222
+
223
+ // ── copilot-instructions.md ───────────────────────────────────────────────────
224
+
225
+ const copilotInstructions = [
226
+ "# useVyre Copilot Instructions",
227
+ "# Version: " + schema.version,
228
+ "",
229
+ "When generating UI code in this project, follow the useVyre design system rules below.",
230
+ "",
231
+ generatedFullContext,
232
+ ].join("\n");
233
+
234
+ writeFileSync(resolve(dist, "copilot-instructions.md"), copilotInstructions);
235
+
236
+ // ── schema.json — machine-readable component schema ───────────────────────────
237
+
238
+ writeFileSync(resolve(dist, "schema.json"), JSON.stringify(schema, null, 2));
239
+
240
+ // ── version-info.json — 1.4 AI Context Versioning ────────────────────────────
241
+
242
+ const versionInfo = {
243
+ version: schema.version,
244
+ packageVersion: schema.packageVersion ?? "0.1.1",
245
+ generatedAt: new Date().toISOString(),
246
+ validFor: schema.validFor ?? [],
247
+ changelog: schema.changelog ?? {},
248
+ components: Object.fromEntries(
249
+ Object.entries(schema.components).map(([name, comp]) => [
250
+ name,
251
+ {
252
+ version: comp.meta?.version ?? schema.packageVersion ?? "0.1.1",
253
+ lastUpdated: comp.meta?.lastUpdated ?? schema.generatedAt,
254
+ breaking: comp.meta?.breaking ?? false,
255
+ stable: comp.meta?.stable ?? true,
256
+ changelog: comp.meta?.changelog ?? null,
257
+ },
258
+ ])
259
+ ),
260
+ };
261
+
262
+ writeFileSync(resolve(dist, "version-info.json"), JSON.stringify(versionInfo, null, 2));
263
+
264
+ // ── cheat-sheets/ — 2.1 per-component AI Cheat Sheet (markdown) ──────────────
265
+
266
+ const cheatSheetsDir = resolve(dist, "cheat-sheets");
267
+ mkdirSync(cheatSheetsDir, { recursive: true });
268
+
269
+ function buildCheatSheet(name, comp) {
270
+ const props = comp.props ?? {};
271
+ const enumProps = Object.entries(props).filter(([, p]) => p.values);
272
+ const boolProps = Object.entries(props).filter(([, p]) => p.type === "boolean");
273
+
274
+ const lines = [];
275
+ lines.push(`# ${name} — AI Cheat Sheet`);
276
+ lines.push(`> Quick reference for Claude / Cursor / Copilot`);
277
+ lines.push("");
278
+ lines.push(`**${comp.description}**`);
279
+ lines.push("");
280
+ lines.push(`\`\`\`ts`);
281
+ lines.push(comp.import);
282
+ lines.push(`\`\`\``);
283
+ lines.push("");
284
+
285
+ if (enumProps.length > 0 || boolProps.length > 0) {
286
+ lines.push("## Valid Props");
287
+ lines.push("");
288
+ lines.push("| Prop | Values | Default |");
289
+ lines.push("|------|--------|---------|");
290
+ for (const [propName, prop] of enumProps) {
291
+ const vals = prop.values.map(v => `\`"${v}"\``).join(" \\| ");
292
+ const def = prop.default !== undefined ? `\`${prop.default}\`` : "—";
293
+ lines.push(`| \`${propName}\` | ${vals} | ${def} |`);
294
+ }
295
+ for (const [propName, prop] of boolProps) {
296
+ const def = prop.default !== undefined ? `\`${prop.default}\`` : "—";
297
+ lines.push(`| \`${propName}\` | \`true\` \\| \`false\` | ${def} |`);
298
+ }
299
+ lines.push("");
300
+ }
301
+
302
+ if (comp.antiPatterns?.length) {
303
+ lines.push("## Common AI Mistakes");
304
+ lines.push("");
305
+ for (const ap of comp.antiPatterns) {
306
+ lines.push(`- ❌ \`${ap.pattern}\``);
307
+ lines.push(` → ${ap.fix}`);
308
+ }
309
+ lines.push("");
310
+ }
311
+
312
+ if (comp.examples?.length) {
313
+ lines.push("## Examples");
314
+ lines.push("");
315
+ for (const ex of comp.examples) {
316
+ lines.push(`**${ex.description}**`);
317
+ lines.push("```tsx");
318
+ lines.push(ex.code);
319
+ lines.push("```");
320
+ lines.push("");
321
+ }
322
+ }
323
+
324
+ if (comp.accessibility?.notes?.length) {
325
+ lines.push("## Accessibility");
326
+ lines.push("");
327
+ for (const note of comp.accessibility.notes) {
328
+ lines.push(`- ${note}`);
329
+ }
330
+ lines.push("");
331
+ }
332
+
333
+ return lines.join("\n");
334
+ }
335
+
336
+ // Index of all cheat sheets
337
+ const cheatSheetIndex = ["# useVyre Component Cheat Sheets", "", "Quick reference for AI agents — one file per component.", ""];
338
+
339
+ for (const [name, comp] of Object.entries(schema.components)) {
340
+ const content = buildCheatSheet(name, comp);
341
+ const filename = `${name.toLowerCase()}.md`;
342
+ writeFileSync(resolve(cheatSheetsDir, filename), content);
343
+ cheatSheetIndex.push(`- [${name}](${filename}) — ${comp.description}`);
344
+ }
345
+
346
+ writeFileSync(resolve(cheatSheetsDir, "index.md"), cheatSheetIndex.join("\n") + "\n");
347
+
348
+ // ── anti-patterns.json — hallucination guard, usable by ESLint/validate CLI ──
349
+
350
+ const antiPatternsExport = {
351
+ version: schema.version,
352
+ rules: allAntiPatterns.map(ap => ({
353
+ component: ap.component,
354
+ pattern: ap.pattern,
355
+ reason: ap.reason,
356
+ fix: ap.fix,
357
+ severity: ap.severity ?? "error",
358
+ })),
359
+ };
360
+
361
+ writeFileSync(resolve(dist, "anti-patterns.json"), JSON.stringify(antiPatternsExport, null, 2));
362
+
363
+ // ── index.js — JS entry exporting all context strings + structured data ───────
364
+
365
+ const escape = (s) => s.replace(/`/g, "\\`").replace(/\$/g, "\\$");
366
+
367
+ // Build cheat sheets map for index.js export
368
+ const cheatSheetsMap = Object.fromEntries(
369
+ Object.entries(schema.components).map(([name, comp]) => [name, buildCheatSheet(name, comp)])
370
+ );
371
+
372
+ const indexContent = [
373
+ `// @usevyre/ai-context v${schema.version}`,
374
+ `// Auto-generated — do not edit directly. Edit src/schema/components.json instead.`,
375
+ "",
376
+ `export const version = "${schema.version}";`,
377
+ "",
378
+ `export const fullContext = \`${escape(generatedFullContext)}\`;`,
379
+ `export const cursorRules = \`${escape(cursorRules)}\`;`,
380
+ `export const claudeContext = \`${escape(claudeContext)}\`;`,
381
+ `export const windsurfRules = \`${escape(windsurfRules)}\`;`,
382
+ `export const copilotInstructions = \`${escape(copilotInstructions)}\`;`,
383
+ "",
384
+ `export const schema = ${JSON.stringify(schema, null, 2)};`,
385
+ "",
386
+ `export const antiPatterns = ${JSON.stringify(antiPatternsExport, null, 2)};`,
387
+ "",
388
+ `export const versionInfo = ${JSON.stringify(versionInfo, null, 2)};`,
389
+ "",
390
+ `export const cheatSheets = ${JSON.stringify(cheatSheetsMap, null, 2)};`,
391
+ "",
392
+ ].join("\n");
393
+
394
+ writeFileSync(resolve(dist, "index.js"), indexContent);
395
+
396
+ // ── Copy AI tokens from @usevyre/tokens ──────────────────────────────────────
397
+
398
+ if (aiTokens) {
399
+ writeFileSync(resolve(dist, "tokens.json"), JSON.stringify(aiTokens, null, 2));
400
+ }
401
+ if (aiTokensMD) {
402
+ writeFileSync(resolve(dist, "tokens.md"), aiTokensMD);
403
+ }
404
+
405
+ // ── Update package.json exports to include new files ─────────────────────────
406
+
407
+ const pkg = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"));
408
+ pkg.exports = {
409
+ ".": "./dist/index.js",
410
+ "./cursor": "./dist/cursor-rules.md",
411
+ "./claude": "./dist/claude-context.md",
412
+ "./windsurf": "./dist/windsurf-rules.md",
413
+ "./copilot": "./dist/copilot-instructions.md",
414
+ "./full": "./dist/full-context.md",
415
+ "./schema": "./dist/schema.json",
416
+ "./anti-patterns": "./dist/anti-patterns.json",
417
+ "./version-info": "./dist/version-info.json",
418
+ "./cheat-sheets": "./dist/cheat-sheets/index.md",
419
+ "./tokens": "./dist/tokens.json",
420
+ "./tokens-md": "./dist/tokens.md",
421
+ };
422
+ writeFileSync(resolve(root, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
423
+
424
+ // ── Summary ───────────────────────────────────────────────────────────────────
425
+
426
+ const componentCount = Object.keys(schema.components).length;
427
+ const antiPatternCount = allAntiPatterns.length;
428
+
429
+ console.log(`@usevyre/ai-context v${schema.version} built successfully`);
430
+ console.log(` ${componentCount} components · ${antiPatternCount} anti-patterns`);
431
+ console.log(` dist/schema.json`);
432
+ console.log(` dist/anti-patterns.json`);
433
+ console.log(` dist/version-info.json`);
434
+ console.log(` dist/cheat-sheets/ (${componentCount} files + index.md)`);
435
+ console.log(` dist/full-context.md`);
436
+ console.log(` dist/cursor-rules.md`);
437
+ console.log(` dist/claude-context.md`);
438
+ console.log(` dist/windsurf-rules.md`);
439
+ console.log(` dist/copilot-instructions.md`);
440
+ console.log(` dist/tokens.json — AI token reference (from @usevyre/tokens)`);
441
+ console.log(` dist/tokens.md — AI token reference markdown`);
442
+ console.log(` dist/index.js`);
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ // npx @usevyre/ai-context init --claude|--cursor|--windsurf|--copilot
3
+
4
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
+ import { resolve, dirname } from "path";
6
+ import { fileURLToPath } from "url";
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const distDir = resolve(__dirname, "../dist");
10
+
11
+ const TARGETS = {
12
+ "--claude": { src: "claude-context.md", dest: "CLAUDE.md", label: "CLAUDE.md" },
13
+ "--cursor": { src: "cursor-rules.md", dest: ".cursor/rules", label: ".cursor/rules" },
14
+ "--windsurf": { src: "windsurf-rules.md", dest: ".windsurf/rules", label: ".windsurf/rules" },
15
+ "--copilot": { src: "copilot-instructions.md", dest: ".github/copilot-instructions.md", label: ".github/copilot-instructions.md" },
16
+ };
17
+
18
+ const MARKER_START = "<!-- usevyre-context:start -->";
19
+ const MARKER_END = "<!-- usevyre-context:end -->";
20
+
21
+ function run() {
22
+ const args = process.argv.slice(2);
23
+ const flag = args.find((a) => a.startsWith("--"));
24
+
25
+ if (!flag || flag === "--help") {
26
+ console.log(`
27
+ Usage: npx @usevyre/ai-context init <flag>
28
+
29
+ Flags:
30
+ --claude Write context to CLAUDE.md
31
+ --cursor Write context to .cursor/rules
32
+ --windsurf Write context to .windsurf/rules
33
+ --copilot Write context to .github/copilot-instructions.md
34
+ `);
35
+ process.exit(flag === "--help" ? 0 : 1);
36
+ }
37
+
38
+ const target = TARGETS[flag];
39
+ if (!target) {
40
+ console.error(`Unknown flag: ${flag}`);
41
+ console.error(`Valid flags: ${Object.keys(TARGETS).join(", ")}`);
42
+ process.exit(1);
43
+ }
44
+
45
+ const srcPath = resolve(distDir, target.src);
46
+ const destPath = resolve(process.cwd(), target.dest);
47
+
48
+ if (!existsSync(srcPath)) {
49
+ console.error(`Source file not found: ${srcPath}`);
50
+ console.error("Run 'pnpm build' in packages/ai-context first.");
51
+ process.exit(1);
52
+ }
53
+
54
+ const content = readFileSync(srcPath, "utf-8");
55
+ const block = `${MARKER_START}\n${content.trim()}\n${MARKER_END}`;
56
+
57
+ // Ensure destination directory exists
58
+ const destDir = dirname(destPath);
59
+ if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
60
+
61
+ if (existsSync(destPath)) {
62
+ let existing = readFileSync(destPath, "utf-8");
63
+
64
+ if (existing.includes(MARKER_START)) {
65
+ // Replace existing block between markers
66
+ const re = new RegExp(
67
+ `${escapeRe(MARKER_START)}[\\s\\S]*?${escapeRe(MARKER_END)}`,
68
+ "m"
69
+ );
70
+ existing = existing.replace(re, block);
71
+ writeFileSync(destPath, existing, "utf-8");
72
+ console.log(`✓ Updated useVyre context block in ${target.label}`);
73
+ } else {
74
+ // Append to existing file
75
+ writeFileSync(destPath, existing.trimEnd() + "\n\n" + block + "\n", "utf-8");
76
+ console.log(`✓ Appended useVyre context block to ${target.label}`);
77
+ }
78
+ } else {
79
+ // Create new file
80
+ writeFileSync(destPath, block + "\n", "utf-8");
81
+ console.log(`✓ Created ${target.label} with useVyre context`);
82
+ }
83
+
84
+ console.log(` Re-run this command after upgrading @usevyre packages.`);
85
+ }
86
+
87
+ function escapeRe(str) {
88
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
89
+ }
90
+
91
+ run();