@matchkit.io/cli 0.1.1 → 0.1.3

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.
@@ -1,9 +1,143 @@
1
1
  import * as p from "@clack/prompts";
2
2
  import pc from "picocolors";
3
- import { writeFileSync, mkdirSync, existsSync } from "node:fs";
3
+ import { writeFileSync, mkdirSync, existsSync, readFileSync } from "node:fs";
4
4
  import { join, dirname } from "node:path";
5
5
  import { readConfig, configExists, writeConfig, createDefaultConfig } from "../utils/config.js";
6
6
  import { getApiKey, authFetch, API_BASE_URL } from "../utils/auth.js";
7
+ // Marker used to detect MatchKit-managed AI rules files
8
+ const MATCHKIT_MARKER = "<!-- managed:matchkit -->";
9
+ /**
10
+ * Read registry.json from the extracted skill directory and build
11
+ * a markdown import map table for AI rules files.
12
+ */
13
+ function buildImportMap(fullSkillDir) {
14
+ const registryPath = join(fullSkillDir, "registry.json");
15
+ if (!existsSync(registryPath))
16
+ return "";
17
+ try {
18
+ const registry = JSON.parse(readFileSync(registryPath, "utf-8"));
19
+ const lines = [];
20
+ for (const comp of registry.components) {
21
+ if (!comp.exportName || !comp.file)
22
+ continue;
23
+ // Skip non-component entries (layouts, patterns)
24
+ if (comp.type === "registry:layout" || comp.type === "registry:pattern")
25
+ continue;
26
+ const file = comp.file.replace(/\.tsx$/, "");
27
+ const exports = Array.isArray(comp.exportName) ? comp.exportName : [comp.exportName];
28
+ const importStr = `{ ${exports.join(", ")} }`;
29
+ lines.push(`| ${comp.name} | \`import ${importStr} from "@/components/ui/${file}"\` |`);
30
+ }
31
+ if (lines.length === 0)
32
+ return "";
33
+ return `\n### Component imports — use these EXACT names:\n\n| Component | Import |\n|-----------|--------|\n${lines.join("\n")}\n`;
34
+ }
35
+ catch {
36
+ return "";
37
+ }
38
+ }
39
+ /**
40
+ * Build the content for AI rules files.
41
+ * This is the mandatory instruction set that tells AI tools to use the design system.
42
+ */
43
+ function buildAiRulesContent(skillDir, theme, importMap) {
44
+ return `${MATCHKIT_MARKER}
45
+ # MatchKit Design System — ${theme}-ui
46
+
47
+ > Auto-generated by \`matchkit pull\`. Do not edit the marker line above.
48
+
49
+ ## MANDATORY — This project uses a complete design system
50
+
51
+ A full design system is installed at \`${skillDir}/\`. You MUST use it for ALL UI work.
52
+
53
+ ### Before writing ANY UI code:
54
+
55
+ 1. **Read \`${skillDir}/SKILL.md\`** — contains design rules, component specs, and setup instructions
56
+ 2. **Read \`${skillDir}/registry.json\`** — lists all 27 available components with their dependencies
57
+ 3. **Copy \`${skillDir}/globals.css\`** to \`src/styles/globals.css\` if it doesn't exist yet
58
+ 4. **Create \`src/lib/utils.ts\`** with the \`cn()\` helper if it doesn't exist yet:
59
+ \`\`\`ts
60
+ import { clsx, type ClassValue } from "clsx";
61
+ import { twMerge } from "tailwind-merge";
62
+ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
63
+ \`\`\`
64
+ 5. **Copy component files** from \`${skillDir}/components/\` to \`src/components/ui/\` as needed
65
+
66
+ ### Rules — follow these at ALL times:
67
+
68
+ - **ALWAYS** check \`${skillDir}/registry.json\` before creating any component — if it exists there, use it
69
+ - **NEVER** create your own \`globals.css\`, Tailwind theme, or color tokens — use the provided \`globals.css\`
70
+ - **NEVER** hardcode hex colors, pixel spacing, or font sizes — use CSS variables from \`globals.css\`
71
+ - **NEVER** improvise a component if one already exists in the registry
72
+ - **NEVER** modify files inside \`${skillDir}/\` — it is the upstream source of truth
73
+ - **ALWAYS** use \`@/components/ui/{name}\` import paths for design system components
74
+ - **ALWAYS** use \`@/lib/utils\` for the \`cn()\` helper
75
+
76
+ ### Available resources in \`${skillDir}/\`:
77
+
78
+ | Path | Contents |
79
+ |------|----------|
80
+ | \`SKILL.md\` | Design rules, philosophy, component specs |
81
+ | \`registry.json\` | Component registry with types, layers, dependencies |
82
+ | \`components/*.tsx\` | 27 production-ready components |
83
+ | \`layouts/*.tsx\` | 6 page layout references |
84
+ | \`globals.css\` | Complete Tailwind v4 CSS with all tokens |
85
+ | \`tokens/*.json\` | Design token values |
86
+ | \`patterns/*.md\` | Responsive, dark mode, a11y, composition patterns |
87
+ ${importMap}`;
88
+ }
89
+ /**
90
+ * Build Cursor .mdc format content (has YAML frontmatter with alwaysApply).
91
+ */
92
+ function buildCursorMdcContent(skillDir, theme, importMap) {
93
+ return `---
94
+ description: MatchKit ${theme}-ui design system — mandatory rules for all UI work
95
+ alwaysApply: true
96
+ ---
97
+ ${buildAiRulesContent(skillDir, theme, importMap)}`;
98
+ }
99
+ /**
100
+ * Write AI rules files to the project root.
101
+ * Only writes if the file doesn't exist or is MatchKit-managed (has the marker).
102
+ * Returns the list of files that were written.
103
+ */
104
+ function writeAiRulesFiles(cwd, skillDir, theme) {
105
+ const fullSkillDir = join(cwd, skillDir);
106
+ const importMap = buildImportMap(fullSkillDir);
107
+ const content = buildAiRulesContent(skillDir, theme, importMap);
108
+ const cursorMdcContent = buildCursorMdcContent(skillDir, theme, importMap);
109
+ const written = [];
110
+ const targets = [
111
+ { path: "CLAUDE.md", label: "CLAUDE.md", content },
112
+ { path: ".cursorrules", label: ".cursorrules", content },
113
+ { path: ".cursor/rules/matchkit.mdc", label: ".cursor/rules/matchkit.mdc", content: cursorMdcContent },
114
+ { path: ".github/copilot-instructions.md", label: ".github/copilot-instructions.md", content },
115
+ { path: ".windsurfrules", label: ".windsurfrules", content },
116
+ ];
117
+ for (const target of targets) {
118
+ const fullPath = join(cwd, target.path);
119
+ let shouldWrite = false;
120
+ if (!existsSync(fullPath)) {
121
+ shouldWrite = true;
122
+ }
123
+ else {
124
+ // File exists — only overwrite if it's MatchKit-managed
125
+ const existing = readFileSync(fullPath, "utf-8");
126
+ if (existing.includes(MATCHKIT_MARKER)) {
127
+ shouldWrite = true;
128
+ }
129
+ }
130
+ if (shouldWrite) {
131
+ const dir = dirname(fullPath);
132
+ if (!existsSync(dir)) {
133
+ mkdirSync(dir, { recursive: true });
134
+ }
135
+ writeFileSync(fullPath, target.content);
136
+ written.push(target.label);
137
+ }
138
+ }
139
+ return written;
140
+ }
7
141
  export async function pullCommand(options) {
8
142
  p.intro(pc.bold("matchkit pull"));
9
143
  // Check auth
@@ -131,6 +265,11 @@ export async function pullCommand(options) {
131
265
  s.stop(`Extracted ${fileCount} files`);
132
266
  p.log.success(`${pc.bold(theme + "-ui")} v${version} → ${pc.dim(skillDir)}`);
133
267
  p.log.info(`Build: ${pc.dim(buildId)}`);
268
+ // Generate AI rules files so every AI tool uses the design system
269
+ const writtenRules = writeAiRulesFiles(process.cwd(), skillDir, theme);
270
+ if (writtenRules.length > 0) {
271
+ p.log.success(`AI rules → ${writtenRules.map((f) => pc.dim(f)).join(", ")}`);
272
+ }
134
273
  p.outro("Design system is up to date.");
135
274
  }
136
275
  catch (err) {
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ const program = new Command();
11
11
  program
12
12
  .name("matchkit")
13
13
  .description("MatchKit — style-agnostic design system CLI")
14
- .version("0.1.1");
14
+ .version("0.1.2");
15
15
  program
16
16
  .command("init")
17
17
  .description("Initialize a MatchKit design system in your project")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matchkit.io/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "CLI for MatchKit design system skills. Init projects, add components, manage your design system.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",