@matchkit.io/cli 0.1.0 → 0.1.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/README.md +154 -0
- package/dist/commands/login.js +12 -11
- package/dist/commands/pull.d.ts +3 -1
- package/dist/commands/pull.js +148 -12
- package/dist/index.js +2 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# @matchkit.io/cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for MatchKit design system skills. Sync your design system, add components, and integrate with CI/CD pipelines.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔐 **Browser-based authentication** - Secure OAuth flow via matchkit.io
|
|
8
|
+
- 📦 **Component management** - Add individual components with automatic dependency resolution
|
|
9
|
+
- 🔄 **Sync & update** - Pull latest design system updates from your MatchKit configuration
|
|
10
|
+
- 🤖 **CI/CD ready** - Use API keys for automated deployments
|
|
11
|
+
- 📊 **Status tracking** - See what's installed, what's available, and what's changed
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @matchkit.io/cli
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 1. Authenticate (opens browser)
|
|
23
|
+
matchkit login
|
|
24
|
+
|
|
25
|
+
# 2. Initialize your project
|
|
26
|
+
matchkit init
|
|
27
|
+
|
|
28
|
+
# 3. Pull your design system
|
|
29
|
+
matchkit pull
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Your design system files are installed to `.claude/skills/[theme]-ui/` and work automatically with Claude Code, Cursor, VS Code, and Windsurf.
|
|
33
|
+
|
|
34
|
+
## Commands
|
|
35
|
+
|
|
36
|
+
### `matchkit login`
|
|
37
|
+
|
|
38
|
+
Authenticate with MatchKit. Opens your browser to sign in, then saves your API key locally.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
matchkit login
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### `matchkit init`
|
|
45
|
+
|
|
46
|
+
Initialize a MatchKit design system in your project. Links to your configuration on matchkit.io.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
matchkit init
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### `matchkit pull`
|
|
53
|
+
|
|
54
|
+
Download your latest resolved design system from MatchKit.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
matchkit pull
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### `matchkit add <component>`
|
|
61
|
+
|
|
62
|
+
Install a single component with its dependencies.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
matchkit add button
|
|
66
|
+
matchkit add data-table
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `matchkit list`
|
|
70
|
+
|
|
71
|
+
Show all 27 components with their installation status.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
matchkit list
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `matchkit status`
|
|
78
|
+
|
|
79
|
+
Show current configuration, theme, version, and sync status.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
matchkit status
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `matchkit diff`
|
|
86
|
+
|
|
87
|
+
Show what changed since your last pull.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
matchkit diff
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Configuration
|
|
94
|
+
|
|
95
|
+
The CLI stores:
|
|
96
|
+
- **Project config**: `.matchkit/config.json` (in your project)
|
|
97
|
+
- **API key**: `~/.matchkit/credentials` (global, never committed)
|
|
98
|
+
|
|
99
|
+
### .matchkit/config.json
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"$schema": "https://matchkit.io/schemas/config.json",
|
|
104
|
+
"configId": "cfg_a1b2c3d4e5f6",
|
|
105
|
+
"version": 3,
|
|
106
|
+
"theme": "soft",
|
|
107
|
+
"accent": "#14B8A6",
|
|
108
|
+
"overrides": { "radius": "xl", "button-style": "ghost" },
|
|
109
|
+
"componentsDir": "src/components/ui",
|
|
110
|
+
"skillDir": ".claude/skills/soft-ui",
|
|
111
|
+
"registryUrl": "https://matchkit.io/api/registry"
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## CI/CD Usage
|
|
116
|
+
|
|
117
|
+
For automated deployments, set the `MATCHKIT_API_KEY` environment variable:
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
# GitHub Actions
|
|
121
|
+
- name: Pull MatchKit design system
|
|
122
|
+
env:
|
|
123
|
+
MATCHKIT_API_KEY: ${{ secrets.MATCHKIT_API_KEY }}
|
|
124
|
+
run: |
|
|
125
|
+
npm install -g @matchkit.io/cli
|
|
126
|
+
matchkit pull
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Get your API key from https://matchkit.io/app/keys
|
|
130
|
+
|
|
131
|
+
## How It Works
|
|
132
|
+
|
|
133
|
+
1. **Authentication**: Browser OAuth flow creates an API key
|
|
134
|
+
2. **Configuration**: Links your project to your MatchKit design system
|
|
135
|
+
3. **Resolution**: Server-side resolution generates your personalized skill
|
|
136
|
+
4. **Download**: ZIP download with all components, tokens, and documentation
|
|
137
|
+
5. **Extraction**: Files land in `.claude/skills/[theme]-ui/`
|
|
138
|
+
|
|
139
|
+
## Requirements
|
|
140
|
+
|
|
141
|
+
- Node.js 18 or later
|
|
142
|
+
- A MatchKit account (free tier available at https://matchkit.io)
|
|
143
|
+
|
|
144
|
+
## Links
|
|
145
|
+
|
|
146
|
+
- **Website**: https://matchkit.io
|
|
147
|
+
- **Documentation**: https://matchkit.io/docs
|
|
148
|
+
- **Dashboard**: https://matchkit.io/app
|
|
149
|
+
- **Repository**: https://github.com/Maes9/matchkit
|
|
150
|
+
- **Issues**: https://github.com/Maes9/matchkit/issues
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
package/dist/commands/login.js
CHANGED
|
@@ -33,17 +33,7 @@ async function verifyKey(apiKey) {
|
|
|
33
33
|
}
|
|
34
34
|
export async function loginCommand(options) {
|
|
35
35
|
p.intro(pc.bold("matchkit login"));
|
|
36
|
-
|
|
37
|
-
if (existing) {
|
|
38
|
-
const overwrite = await p.confirm({
|
|
39
|
-
message: "You already have an API key stored. Replace it?",
|
|
40
|
-
});
|
|
41
|
-
if (p.isCancel(overwrite) || !overwrite) {
|
|
42
|
-
p.outro("Keeping existing key.");
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
// --key flag or non-interactive: fall back to manual key entry
|
|
36
|
+
// --key flag or non-interactive: skip all interactive prompts
|
|
47
37
|
if (options?.key || !process.stdin.isTTY) {
|
|
48
38
|
const apiKey = options?.key ?? process.env.MATCHKIT_API_KEY;
|
|
49
39
|
if (!apiKey) {
|
|
@@ -64,6 +54,17 @@ export async function loginCommand(options) {
|
|
|
64
54
|
p.outro("Key stored in " + pc.dim("~/.matchkit/credentials"));
|
|
65
55
|
return;
|
|
66
56
|
}
|
|
57
|
+
// Interactive flow below: requires TTY
|
|
58
|
+
const existing = getApiKey();
|
|
59
|
+
if (existing) {
|
|
60
|
+
const overwrite = await p.confirm({
|
|
61
|
+
message: "You already have an API key stored. Replace it?",
|
|
62
|
+
});
|
|
63
|
+
if (p.isCancel(overwrite) || !overwrite) {
|
|
64
|
+
p.outro("Keeping existing key.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
67
68
|
// Browser-based auth flow
|
|
68
69
|
const s = p.spinner();
|
|
69
70
|
const callbackPromise = new Promise((resolve, reject) => {
|
package/dist/commands/pull.d.ts
CHANGED
package/dist/commands/pull.js
CHANGED
|
@@ -1,33 +1,164 @@
|
|
|
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
|
-
import { readConfig, configExists, writeConfig } from "../utils/config.js";
|
|
5
|
+
import { readConfig, configExists, writeConfig, createDefaultConfig } from "../utils/config.js";
|
|
6
6
|
import { getApiKey, authFetch, API_BASE_URL } from "../utils/auth.js";
|
|
7
|
-
|
|
7
|
+
// Marker used to detect MatchKit-managed AI rules files
|
|
8
|
+
const MATCHKIT_MARKER = "<!-- managed:matchkit -->";
|
|
9
|
+
/**
|
|
10
|
+
* Build the content for AI rules files.
|
|
11
|
+
* This is the mandatory instruction set that tells AI tools to use the design system.
|
|
12
|
+
*/
|
|
13
|
+
function buildAiRulesContent(skillDir, theme) {
|
|
14
|
+
return `${MATCHKIT_MARKER}
|
|
15
|
+
# MatchKit Design System — ${theme}-ui
|
|
16
|
+
|
|
17
|
+
> Auto-generated by \`matchkit pull\`. Do not edit the marker line above.
|
|
18
|
+
|
|
19
|
+
## MANDATORY — This project uses a complete design system
|
|
20
|
+
|
|
21
|
+
A full design system is installed at \`${skillDir}/\`. You MUST use it for ALL UI work.
|
|
22
|
+
|
|
23
|
+
### Before writing ANY UI code:
|
|
24
|
+
|
|
25
|
+
1. **Read \`${skillDir}/SKILL.md\`** — contains design rules, component specs, and setup instructions
|
|
26
|
+
2. **Read \`${skillDir}/registry.json\`** — lists all 27 available components with their dependencies
|
|
27
|
+
3. **Copy \`${skillDir}/globals.css\`** to \`src/styles/globals.css\` if it doesn't exist yet
|
|
28
|
+
4. **Create \`src/lib/utils.ts\`** with the \`cn()\` helper if it doesn't exist yet:
|
|
29
|
+
\`\`\`ts
|
|
30
|
+
import { clsx, type ClassValue } from "clsx";
|
|
31
|
+
import { twMerge } from "tailwind-merge";
|
|
32
|
+
export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
|
|
33
|
+
\`\`\`
|
|
34
|
+
5. **Copy component files** from \`${skillDir}/components/\` to \`src/components/ui/\` as needed
|
|
35
|
+
|
|
36
|
+
### Rules — follow these at ALL times:
|
|
37
|
+
|
|
38
|
+
- **ALWAYS** check \`${skillDir}/registry.json\` before creating any component — if it exists there, use it
|
|
39
|
+
- **NEVER** create your own \`globals.css\`, Tailwind theme, or color tokens — use the provided \`globals.css\`
|
|
40
|
+
- **NEVER** hardcode hex colors, pixel spacing, or font sizes — use CSS variables from \`globals.css\`
|
|
41
|
+
- **NEVER** improvise a component if one already exists in the registry
|
|
42
|
+
- **NEVER** modify files inside \`${skillDir}/\` — it is the upstream source of truth
|
|
43
|
+
- **ALWAYS** use \`@/components/ui/{name}\` import paths for design system components
|
|
44
|
+
- **ALWAYS** use \`@/lib/utils\` for the \`cn()\` helper
|
|
45
|
+
|
|
46
|
+
### Available resources in \`${skillDir}/\`:
|
|
47
|
+
|
|
48
|
+
| Path | Contents |
|
|
49
|
+
|------|----------|
|
|
50
|
+
| \`SKILL.md\` | Design rules, philosophy, component specs |
|
|
51
|
+
| \`registry.json\` | Component registry with types, layers, dependencies |
|
|
52
|
+
| \`components/*.tsx\` | 27 production-ready components |
|
|
53
|
+
| \`layouts/*.tsx\` | 6 page layout references |
|
|
54
|
+
| \`globals.css\` | Complete Tailwind v4 CSS with all tokens |
|
|
55
|
+
| \`tokens/*.json\` | Design token values |
|
|
56
|
+
| \`patterns/*.md\` | Responsive, dark mode, a11y, composition patterns |
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build Cursor .mdc format content (has YAML frontmatter with alwaysApply).
|
|
61
|
+
*/
|
|
62
|
+
function buildCursorMdcContent(skillDir, theme) {
|
|
63
|
+
return `---
|
|
64
|
+
description: MatchKit ${theme}-ui design system — mandatory rules for all UI work
|
|
65
|
+
alwaysApply: true
|
|
66
|
+
---
|
|
67
|
+
${buildAiRulesContent(skillDir, theme)}`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Write AI rules files to the project root.
|
|
71
|
+
* Only writes if the file doesn't exist or is MatchKit-managed (has the marker).
|
|
72
|
+
* Returns the list of files that were written.
|
|
73
|
+
*/
|
|
74
|
+
function writeAiRulesFiles(cwd, skillDir, theme) {
|
|
75
|
+
const content = buildAiRulesContent(skillDir, theme);
|
|
76
|
+
const cursorMdcContent = buildCursorMdcContent(skillDir, theme);
|
|
77
|
+
const written = [];
|
|
78
|
+
const targets = [
|
|
79
|
+
{ path: "CLAUDE.md", label: "CLAUDE.md", content },
|
|
80
|
+
{ path: ".cursorrules", label: ".cursorrules", content },
|
|
81
|
+
{ path: ".cursor/rules/matchkit.mdc", label: ".cursor/rules/matchkit.mdc", content: cursorMdcContent },
|
|
82
|
+
{ path: ".github/copilot-instructions.md", label: ".github/copilot-instructions.md", content },
|
|
83
|
+
{ path: ".windsurfrules", label: ".windsurfrules", content },
|
|
84
|
+
];
|
|
85
|
+
for (const target of targets) {
|
|
86
|
+
const fullPath = join(cwd, target.path);
|
|
87
|
+
let shouldWrite = false;
|
|
88
|
+
if (!existsSync(fullPath)) {
|
|
89
|
+
shouldWrite = true;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// File exists — only overwrite if it's MatchKit-managed
|
|
93
|
+
const existing = readFileSync(fullPath, "utf-8");
|
|
94
|
+
if (existing.includes(MATCHKIT_MARKER)) {
|
|
95
|
+
shouldWrite = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (shouldWrite) {
|
|
99
|
+
const dir = dirname(fullPath);
|
|
100
|
+
if (!existsSync(dir)) {
|
|
101
|
+
mkdirSync(dir, { recursive: true });
|
|
102
|
+
}
|
|
103
|
+
writeFileSync(fullPath, target.content);
|
|
104
|
+
written.push(target.label);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return written;
|
|
108
|
+
}
|
|
109
|
+
export async function pullCommand(options) {
|
|
8
110
|
p.intro(pc.bold("matchkit pull"));
|
|
9
111
|
// Check auth
|
|
10
112
|
const apiKey = getApiKey();
|
|
11
113
|
if (!apiKey) {
|
|
12
|
-
p.log.error("Not logged in. Run " + pc.bold("matchkit login") + " first.");
|
|
114
|
+
p.log.error("Not logged in. Run " + pc.bold("matchkit login --key <key>") + " first.");
|
|
13
115
|
process.exit(1);
|
|
14
116
|
}
|
|
117
|
+
// Auto-create config.json if --config-id provided and no config exists
|
|
118
|
+
if (options?.configId) {
|
|
119
|
+
if (!configExists()) {
|
|
120
|
+
const cs = p.spinner();
|
|
121
|
+
cs.start("Fetching project details...");
|
|
122
|
+
try {
|
|
123
|
+
const res = await authFetch(`${API_BASE_URL}/api/v1/configs/${options.configId}`);
|
|
124
|
+
if (!res.ok) {
|
|
125
|
+
cs.stop("Failed");
|
|
126
|
+
p.log.error(`Project ${pc.dim(options.configId)} not found (${res.status})`);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
const data = (await res.json());
|
|
130
|
+
cs.stop("Project found");
|
|
131
|
+
const newConfig = createDefaultConfig(data.theme, data.accent ?? "#4F46E5", (data.overrides ?? {}));
|
|
132
|
+
writeConfig({ ...newConfig, configId: options.configId });
|
|
133
|
+
p.log.success(`Created .matchkit/config.json for ${pc.bold(data.theme + "-ui")}`);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
cs.stop("Failed");
|
|
137
|
+
p.log.error("Could not fetch project details. Check your connection and API key.");
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// Config exists — update configId
|
|
143
|
+
const existing = readConfig();
|
|
144
|
+
writeConfig({ ...existing, configId: options.configId });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
15
147
|
// Check config
|
|
16
148
|
if (!configExists()) {
|
|
17
|
-
p.log.error("No .matchkit/config.json found.
|
|
149
|
+
p.log.error("No .matchkit/config.json found. Use " +
|
|
150
|
+
pc.bold("--config-id <id>") +
|
|
151
|
+
" or run " +
|
|
18
152
|
pc.bold("matchkit init") +
|
|
19
|
-
"
|
|
153
|
+
".");
|
|
20
154
|
process.exit(1);
|
|
21
155
|
}
|
|
22
156
|
const config = readConfig();
|
|
23
157
|
const configId = config.configId;
|
|
24
158
|
if (!configId) {
|
|
25
|
-
p.log.error("No configId in .matchkit/config.json.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
" and run " +
|
|
29
|
-
pc.bold("matchkit init") +
|
|
30
|
-
" again.");
|
|
159
|
+
p.log.error("No configId in .matchkit/config.json. Use " +
|
|
160
|
+
pc.bold("matchkit pull --config-id <id>") +
|
|
161
|
+
" to link a project.");
|
|
31
162
|
process.exit(1);
|
|
32
163
|
}
|
|
33
164
|
const s = p.spinner();
|
|
@@ -102,6 +233,11 @@ export async function pullCommand() {
|
|
|
102
233
|
s.stop(`Extracted ${fileCount} files`);
|
|
103
234
|
p.log.success(`${pc.bold(theme + "-ui")} v${version} → ${pc.dim(skillDir)}`);
|
|
104
235
|
p.log.info(`Build: ${pc.dim(buildId)}`);
|
|
236
|
+
// Generate AI rules files so every AI tool uses the design system
|
|
237
|
+
const writtenRules = writeAiRulesFiles(process.cwd(), skillDir, theme);
|
|
238
|
+
if (writtenRules.length > 0) {
|
|
239
|
+
p.log.success(`AI rules → ${writtenRules.map((f) => pc.dim(f)).join(", ")}`);
|
|
240
|
+
}
|
|
105
241
|
p.outro("Design system is up to date.");
|
|
106
242
|
}
|
|
107
243
|
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.
|
|
14
|
+
.version("0.1.2");
|
|
15
15
|
program
|
|
16
16
|
.command("init")
|
|
17
17
|
.description("Initialize a MatchKit design system in your project")
|
|
@@ -37,6 +37,7 @@ program
|
|
|
37
37
|
program
|
|
38
38
|
.command("pull")
|
|
39
39
|
.description("Pull your latest resolved design system from the server")
|
|
40
|
+
.option("--config-id <id>", "Server config ID (skips matchkit init)")
|
|
40
41
|
.action(pullCommand);
|
|
41
42
|
program
|
|
42
43
|
.command("status")
|
package/package.json
CHANGED