@usevyre/ai-context 0.2.1 → 0.2.2
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/index.js +1 -1
- package/dist/tokens.json +1 -1
- package/dist/version-info.json +1 -1
- 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.
|
package/dist/index.js
CHANGED
|
@@ -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-08T13:45:17.100Z",
|
|
5685
5685
|
"validFor": [
|
|
5686
5686
|
"@usevyre/react@0.1.1+",
|
|
5687
5687
|
"@usevyre/vue@0.1.1+"
|
package/dist/tokens.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://usevyre.com/schemas/ai-tokens-v1.json",
|
|
3
3
|
"version": "0.1.2",
|
|
4
|
-
"generatedAt": "2026-05-
|
|
4
|
+
"generatedAt": "2026-05-08T09:57:54.742Z",
|
|
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/version-info.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usevyre/ai-context",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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();
|