@matchkit.io/cli 0.1.3 → 0.1.5
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/dist/commands/pull.js +65 -4
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/commands/pull.js
CHANGED
|
@@ -23,10 +23,25 @@ function buildImportMap(fullSkillDir) {
|
|
|
23
23
|
// Skip non-component entries (layouts, patterns)
|
|
24
24
|
if (comp.type === "registry:layout" || comp.type === "registry:pattern")
|
|
25
25
|
continue;
|
|
26
|
-
|
|
26
|
+
// Strip directory prefix from file path (e.g. "components/button.tsx" → "button")
|
|
27
|
+
const basename = comp.file.replace(/^(components|lib)\//, "").replace(/\.tsx?$/, "");
|
|
27
28
|
const exports = Array.isArray(comp.exportName) ? comp.exportName : [comp.exportName];
|
|
28
29
|
const importStr = `{ ${exports.join(", ")} }`;
|
|
29
|
-
|
|
30
|
+
// Use the registry category to determine the correct import prefix:
|
|
31
|
+
// lib → @/lib/{name} (utils.ts)
|
|
32
|
+
// layout → @/components/layout/{name} (sidebar-nav, top-bar, page-header)
|
|
33
|
+
// ui/chart/other → @/components/ui/{name} (everything else)
|
|
34
|
+
let importPath;
|
|
35
|
+
if (comp.category === "lib") {
|
|
36
|
+
importPath = `@/lib/${basename}`;
|
|
37
|
+
}
|
|
38
|
+
else if (comp.category === "layout") {
|
|
39
|
+
importPath = `@/components/layout/${basename}`;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
importPath = `@/components/ui/${basename}`;
|
|
43
|
+
}
|
|
44
|
+
lines.push(`| ${comp.name} | \`import ${importStr} from "${importPath}"\` |`);
|
|
30
45
|
}
|
|
31
46
|
if (lines.length === 0)
|
|
32
47
|
return "";
|
|
@@ -62,6 +77,9 @@ A full design system is installed at \`${skillDir}/\`. You MUST use it for ALL U
|
|
|
62
77
|
export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
|
|
63
78
|
\`\`\`
|
|
64
79
|
5. **Copy component files** from \`${skillDir}/components/\` to \`src/components/ui/\` as needed
|
|
80
|
+
6. **Copy layout files** from \`${skillDir}/layouts/\` to \`src/components/layout/\` as needed (sidebar-nav, top-bar, page-header)
|
|
81
|
+
7. **Read \`${skillDir}/layouts/app-shell.tsx\`** — reference implementation showing how to compose the app shell
|
|
82
|
+
8. **Read the .tsx source** of any component before using it — the file contains the exact props interface
|
|
65
83
|
|
|
66
84
|
### Rules — follow these at ALL times:
|
|
67
85
|
|
|
@@ -70,8 +88,10 @@ A full design system is installed at \`${skillDir}/\`. You MUST use it for ALL U
|
|
|
70
88
|
- **NEVER** hardcode hex colors, pixel spacing, or font sizes — use CSS variables from \`globals.css\`
|
|
71
89
|
- **NEVER** improvise a component if one already exists in the registry
|
|
72
90
|
- **NEVER** modify files inside \`${skillDir}/\` — it is the upstream source of truth
|
|
73
|
-
- **ALWAYS** use \`@/components/ui/{name}\` import paths for
|
|
91
|
+
- **ALWAYS** use \`@/components/ui/{name}\` import paths for UI components
|
|
92
|
+
- **ALWAYS** use \`@/components/layout/{name}\` import paths for layout components (sidebar-nav, top-bar, page-header)
|
|
74
93
|
- **ALWAYS** use \`@/lib/utils\` for the \`cn()\` helper
|
|
94
|
+
- **ALWAYS** read the component .tsx source to see its props interface before using it — do NOT guess props
|
|
75
95
|
|
|
76
96
|
### Available resources in \`${skillDir}/\`:
|
|
77
97
|
|
|
@@ -138,6 +158,37 @@ function writeAiRulesFiles(cwd, skillDir, theme) {
|
|
|
138
158
|
}
|
|
139
159
|
return written;
|
|
140
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Detect if ZIP paths share a common prefix that matches the skillDir.
|
|
163
|
+
* e.g., if skillDir is ".claude/skills/glass-ui" and ZIP paths start with
|
|
164
|
+
* ".claude/skills/glass-ui/components/...", return ".claude/skills/glass-ui/"
|
|
165
|
+
* so we can strip it and avoid double nesting.
|
|
166
|
+
*
|
|
167
|
+
* Returns the prefix string (with trailing slash) or null if paths are flat.
|
|
168
|
+
*/
|
|
169
|
+
function detectPrefix(paths, skillDir) {
|
|
170
|
+
if (paths.length === 0)
|
|
171
|
+
return null;
|
|
172
|
+
// Normalize skillDir: remove leading/trailing slashes, then add trailing slash
|
|
173
|
+
const normalized = skillDir.replace(/^\/+|\/+$/g, "") + "/";
|
|
174
|
+
// Check if most paths start with the skillDir prefix
|
|
175
|
+
const matchCount = paths.filter((p) => p.startsWith(normalized)).length;
|
|
176
|
+
// If >50% of paths match, it's a prefixed ZIP
|
|
177
|
+
if (matchCount > paths.length / 2) {
|
|
178
|
+
return normalized;
|
|
179
|
+
}
|
|
180
|
+
// Also check for .claude/skills/<theme>-ui/ pattern in case skillDir differs
|
|
181
|
+
const firstPath = paths[0];
|
|
182
|
+
const match = firstPath.match(/^(\.claude\/skills\/[^/]+\/)/);
|
|
183
|
+
if (match) {
|
|
184
|
+
const candidatePrefix = match[1];
|
|
185
|
+
const prefixCount = paths.filter((p) => p.startsWith(candidatePrefix)).length;
|
|
186
|
+
if (prefixCount > paths.length / 2) {
|
|
187
|
+
return candidatePrefix;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
141
192
|
export async function pullCommand(options) {
|
|
142
193
|
p.intro(pc.bold("matchkit pull"));
|
|
143
194
|
// Check auth
|
|
@@ -243,12 +294,22 @@ export async function pullCommand(options) {
|
|
|
243
294
|
if (!existsSync(fullSkillDir)) {
|
|
244
295
|
mkdirSync(fullSkillDir, { recursive: true });
|
|
245
296
|
}
|
|
297
|
+
// Detect if ZIP paths are prefixed with a skill directory
|
|
298
|
+
// (e.g., ".claude/skills/glass-ui/components/..." vs "components/...")
|
|
299
|
+
// The server may include the full skillDir prefix — we need to strip it
|
|
300
|
+
// so files land directly in fullSkillDir, not double-nested.
|
|
301
|
+
const allPaths = Object.keys(zip.files).filter((p) => !zip.files[p].dir);
|
|
302
|
+
const skillDirPrefix = detectPrefix(allPaths, config.skillDir);
|
|
246
303
|
let fileCount = 0;
|
|
247
304
|
for (const [path, entry] of Object.entries(zip.files)) {
|
|
248
305
|
if (entry.dir)
|
|
249
306
|
continue;
|
|
307
|
+
// Strip the detected prefix to avoid double nesting
|
|
308
|
+
const relativePath = skillDirPrefix && path.startsWith(skillDirPrefix)
|
|
309
|
+
? path.slice(skillDirPrefix.length)
|
|
310
|
+
: path;
|
|
250
311
|
const content = await entry.async("string");
|
|
251
|
-
const fullPath = join(fullSkillDir,
|
|
312
|
+
const fullPath = join(fullSkillDir, relativePath);
|
|
252
313
|
const dir = dirname(fullPath);
|
|
253
314
|
if (!existsSync(dir)) {
|
|
254
315
|
mkdirSync(dir, { recursive: true });
|
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.4");
|
|
15
15
|
program
|
|
16
16
|
.command("init")
|
|
17
17
|
.description("Initialize a MatchKit design system in your project")
|
package/package.json
CHANGED