@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 +21 -0
- package/dist/cheat-sheets/index.md +1 -1
- package/dist/cheat-sheets/toast.md +2 -2
- package/dist/claude-context.md +2 -2
- package/dist/copilot-instructions.md +2 -2
- package/dist/cursor-rules.md +1 -1
- package/dist/full-context.md +2 -2
- package/dist/index.js +13 -13
- package/dist/schema.json +2 -2
- package/dist/tokens.json +2 -2
- package/dist/tokens.md +1 -1
- package/dist/version-info.json +1 -1
- package/dist/windsurf-rules.md +2 -2
- package/package.json +7 -3
- package/scripts/build.js +442 -0
- package/scripts/init.js +91 -0
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
|
package/dist/claude-context.md
CHANGED
|
@@ -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)
|
package/dist/cursor-rules.md
CHANGED
|
@@ -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:
|
package/dist/full-context.md
CHANGED
|
@@ -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-
|
|
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.
|
|
4
|
-
"generatedAt": "2026-05-
|
|
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
package/dist/version-info.json
CHANGED
package/dist/windsurf-rules.md
CHANGED
|
@@ -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.
|
|
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
|
+
}
|
package/scripts/build.js
ADDED
|
@@ -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`);
|
package/scripts/init.js
ADDED
|
@@ -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();
|