mdk-skills 2.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.
- package/.claude/.install.log +4 -0
- package/.claude/settings.json +64 -0
- package/.claude/settings.local.json +7 -0
- package/.claude/skills/agentation/.meta.json +6 -0
- package/.claude/skills/agentation/SKILL.md +107 -0
- package/.claude/skills/fe-biz-patterns/.meta.json +6 -0
- package/.claude/skills/fe-biz-patterns/SKILL.md +26 -0
- package/.claude/skills/fe-biz-patterns/references/infinite-scroll.md +292 -0
- package/.claude/skills/fe-biz-patterns/references/pinia-store.md +174 -0
- package/.claude/skills/fe-biz-patterns/references/service-layer.md +198 -0
- package/.claude/skills/fe-biz-patterns/references/tab-anchor.md +1125 -0
- package/.claude/skills/fe-biz-patterns/references/use-loading.md +114 -0
- package/.claude/skills/frontend-code-review/.meta.json +6 -0
- package/.claude/skills/frontend-code-review/SKILL.md +167 -0
- package/.claude/skills/frontend-code-review/references/checklist.md +298 -0
- package/.claude/skills/frontend-design/.meta.json +6 -0
- package/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/.claude/skills/frontend-design/SKILL.md +42 -0
- package/.claude/skills/moai-framework-electron/.meta.json +6 -0
- package/.claude/skills/moai-framework-electron/SKILL.md +328 -0
- package/.claude/skills/skill-creator/.meta.json +6 -0
- package/.claude/skills/skill-creator/SKILL.md +356 -0
- package/.claude/skills/skill-creator/references/output-patterns.md +82 -0
- package/.claude/skills/skill-creator/references/workflows.md +28 -0
- package/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.claude/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/.claude/skills/ui-ux-pro-max/.meta.json +6 -0
- package/.claude/skills/ui-ux-pro-max/SKILL.md +228 -0
- package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.claude/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.claude/skills/ui-ux-pro-max/scripts/core.py +238 -0
- package/.claude/skills/ui-ux-pro-max/scripts/search.py +61 -0
- package/.claude/skills/vue/.meta.json +6 -0
- package/.claude/skills/vue/SKILL.md +103 -0
- package/.claude/skills/vue/references/components.md +323 -0
- package/.claude/skills/vue/references/composables.md +358 -0
- package/.claude/skills/vue/references/directives.md +225 -0
- package/.claude/skills/vue/references/gotchas.md +438 -0
- package/.claude/skills/vue/references/provide-inject.md +174 -0
- package/.claude/skills/vue/references/reactivity.md +289 -0
- package/.claude/skills/vue/references/router.md +181 -0
- package/.claude/skills/vue/references/testing.md +294 -0
- package/.claude/skills/vue/references/typescript.md +172 -0
- package/.claude/skills/vue/references/utils-client.md +156 -0
- package/CLAUDE.md +131 -0
- package/package.json +23 -0
- package/scripts/cli.js +260 -0
- package/scripts/copy-skills.js +86 -0
- package/scripts/core.js +256 -0
package/scripts/core.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
// 时间戳格式:YYYY-MM-DD HH:mm:ss
|
|
5
|
+
function timestamp() {
|
|
6
|
+
const d = new Date();
|
|
7
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
8
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 紧凑时间戳用于备份目录名:YYYYMMDD.HHmmss
|
|
12
|
+
function backupTimestamp() {
|
|
13
|
+
const d = new Date();
|
|
14
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
15
|
+
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}.${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 追加日志到 .claude/.install.log
|
|
20
|
+
* 需要传入 logFile 路径
|
|
21
|
+
*/
|
|
22
|
+
function appendLog(logFile, level, message) {
|
|
23
|
+
const dir = path.dirname(logFile);
|
|
24
|
+
if (!fs.existsSync(dir)) {
|
|
25
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
const line = `[${timestamp()}] [${level}] ${message}\n`;
|
|
28
|
+
fs.appendFileSync(logFile, line, "utf-8");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 备份已有目录到 dir/backups/<timestamp>/
|
|
33
|
+
* 排除 backups/ 自身,清理超过 3 份的旧备份
|
|
34
|
+
*/
|
|
35
|
+
function backupDir(dir) {
|
|
36
|
+
if (!fs.existsSync(dir)) return null;
|
|
37
|
+
|
|
38
|
+
const backupsDir = path.join(dir, "backups");
|
|
39
|
+
if (!fs.existsSync(backupsDir)) {
|
|
40
|
+
fs.mkdirSync(backupsDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const ts = backupTimestamp();
|
|
44
|
+
const bakPath = path.join(backupsDir, ts);
|
|
45
|
+
fs.mkdirSync(bakPath, { recursive: true });
|
|
46
|
+
|
|
47
|
+
for (const item of fs.readdirSync(dir)) {
|
|
48
|
+
if (item === "backups") continue;
|
|
49
|
+
copyDirSync(path.join(dir, item), path.join(bakPath, item));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 清理旧备份,只保留最近 3 份
|
|
53
|
+
const backups = fs
|
|
54
|
+
.readdirSync(backupsDir)
|
|
55
|
+
.filter((name) => /^\d{8}\.\d{6}$/.test(name))
|
|
56
|
+
.sort()
|
|
57
|
+
.reverse();
|
|
58
|
+
|
|
59
|
+
if (backups.length > 3) {
|
|
60
|
+
for (const old of backups.slice(3)) {
|
|
61
|
+
const oldPath = path.join(backupsDir, old);
|
|
62
|
+
fs.rmSync(oldPath, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return bakPath;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isPlainObject(val) {
|
|
70
|
+
return Object.prototype.toString.call(val) === "[object Object]";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 手动递归拷贝目录,绕开 Windows 中文路径下 fs.cpSync({ recursive: true }) 死锁的 bug
|
|
75
|
+
*/
|
|
76
|
+
function copyDirSync(src, dest) {
|
|
77
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
78
|
+
for (const item of fs.readdirSync(src)) {
|
|
79
|
+
const srcPath = path.join(src, item);
|
|
80
|
+
const destPath = path.join(dest, item);
|
|
81
|
+
if (fs.statSync(srcPath).isDirectory()) {
|
|
82
|
+
copyDirSync(srcPath, destPath);
|
|
83
|
+
} else {
|
|
84
|
+
fs.copyFileSync(srcPath, destPath);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 递归深合并,用户值优先,目标新增字段补充
|
|
91
|
+
*/
|
|
92
|
+
function deepMerge(userObj, pkgObj) {
|
|
93
|
+
const result = { ...userObj };
|
|
94
|
+
|
|
95
|
+
for (const key of Object.keys(pkgObj)) {
|
|
96
|
+
if (!(key in result)) {
|
|
97
|
+
result[key] = pkgObj[key];
|
|
98
|
+
} else if (isPlainObject(pkgObj[key]) && isPlainObject(result[key])) {
|
|
99
|
+
result[key] = deepMerge(result[key], pkgObj[key]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 读取技能目录下的 .meta.json,没有则返回 null
|
|
108
|
+
*/
|
|
109
|
+
function readMeta(skillDir) {
|
|
110
|
+
const metaPath = path.join(skillDir, ".meta.json");
|
|
111
|
+
if (fs.existsSync(metaPath)) {
|
|
112
|
+
try {
|
|
113
|
+
return JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 安装技能:逐个复制,补缺不覆盖
|
|
123
|
+
*/
|
|
124
|
+
function installSkills(skillsSource, skillsDest, logFile) {
|
|
125
|
+
if (!fs.existsSync(skillsSource)) return [];
|
|
126
|
+
|
|
127
|
+
if (!fs.existsSync(skillsDest)) {
|
|
128
|
+
fs.mkdirSync(skillsDest, { recursive: true });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const skillDirs = fs.readdirSync(skillsSource);
|
|
132
|
+
const results = [];
|
|
133
|
+
|
|
134
|
+
for (const dir of skillDirs) {
|
|
135
|
+
const srcSkill = path.join(skillsSource, dir);
|
|
136
|
+
const destSkill = path.join(skillsDest, dir);
|
|
137
|
+
const meta = readMeta(srcSkill);
|
|
138
|
+
const version = meta ? meta.version : "?";
|
|
139
|
+
|
|
140
|
+
if (!fs.existsSync(destSkill)) {
|
|
141
|
+
copyDirSync(srcSkill, destSkill);
|
|
142
|
+
appendLog(logFile, "SKILL", `${dir} (${version}) → installed`);
|
|
143
|
+
results.push({ name: dir, version, action: "installed" });
|
|
144
|
+
} else {
|
|
145
|
+
const destMeta = readMeta(destSkill);
|
|
146
|
+
const needUpdate = meta && destMeta && meta.version !== destMeta.version;
|
|
147
|
+
|
|
148
|
+
if (needUpdate) {
|
|
149
|
+
const bakName = `${dir}.bak.${backupTimestamp()}`;
|
|
150
|
+
const bakPath = path.join(skillsDest, bakName);
|
|
151
|
+
copyDirSync(destSkill, bakPath);
|
|
152
|
+
fs.rmSync(destSkill, { recursive: true, force: true });
|
|
153
|
+
copyDirSync(srcSkill, destSkill);
|
|
154
|
+
appendLog(logFile, "SKILL", `${dir} (${destMeta.version} → ${meta.version}) → updated`);
|
|
155
|
+
results.push({ name: dir, version: meta.version, action: "updated" });
|
|
156
|
+
} else {
|
|
157
|
+
appendLog(logFile, "SKILL", `${dir} (${version}) → skipped (exists)`);
|
|
158
|
+
results.push({ name: dir, version, action: "skipped" });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return results;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 安装 settings.json,深度合并
|
|
168
|
+
*/
|
|
169
|
+
function installSettings(sourceDir, destDir, logFile) {
|
|
170
|
+
const settingsSource = path.join(sourceDir, "settings.json");
|
|
171
|
+
const settingsDest = path.join(destDir, "settings.json");
|
|
172
|
+
const localSource = path.join(sourceDir, "settings.local.json");
|
|
173
|
+
const localDest = path.join(destDir, "settings.local.json");
|
|
174
|
+
|
|
175
|
+
for (const { src, dest, name } of [
|
|
176
|
+
{ src: settingsSource, dest: settingsDest, name: "settings.json" },
|
|
177
|
+
{ src: localSource, dest: localDest, name: "settings.local.json" },
|
|
178
|
+
]) {
|
|
179
|
+
if (!fs.existsSync(src)) continue;
|
|
180
|
+
|
|
181
|
+
if (fs.existsSync(dest)) {
|
|
182
|
+
const userData = JSON.parse(fs.readFileSync(dest, "utf-8"));
|
|
183
|
+
const pkgData = JSON.parse(fs.readFileSync(src, "utf-8"));
|
|
184
|
+
const merged = deepMerge(userData, pkgData);
|
|
185
|
+
fs.writeFileSync(dest, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
186
|
+
appendLog(logFile, "SETTINGS", `${name} merged`);
|
|
187
|
+
} else {
|
|
188
|
+
fs.copyFileSync(src, dest);
|
|
189
|
+
appendLog(logFile, "SETTINGS", `${name} created`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 获取包内的技能列表(带元信息)
|
|
196
|
+
*/
|
|
197
|
+
function getPackageSkills(packageDir) {
|
|
198
|
+
const skillsSource = path.join(packageDir, ".claude", "skills");
|
|
199
|
+
if (!fs.existsSync(skillsSource)) return [];
|
|
200
|
+
|
|
201
|
+
return fs.readdirSync(skillsSource).map((name) => {
|
|
202
|
+
const meta = readMeta(path.join(skillsSource, name));
|
|
203
|
+
return {
|
|
204
|
+
name,
|
|
205
|
+
version: meta ? meta.version : "?",
|
|
206
|
+
description: meta ? meta.description : "",
|
|
207
|
+
tags: meta ? meta.tags : [],
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 获取用户已安装的技能及其 settings 中的 enabled 状态
|
|
214
|
+
*/
|
|
215
|
+
function getUserSkills(claudeDest) {
|
|
216
|
+
const skillsDest = path.join(claudeDest, "skills");
|
|
217
|
+
if (!fs.existsSync(skillsDest)) return [];
|
|
218
|
+
|
|
219
|
+
const settingsPath = path.join(claudeDest, "settings.json");
|
|
220
|
+
let settings = {};
|
|
221
|
+
if (fs.existsSync(settingsPath)) {
|
|
222
|
+
try {
|
|
223
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
224
|
+
} catch {
|
|
225
|
+
settings = {};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const skillConfigs = settings.skills || {};
|
|
230
|
+
|
|
231
|
+
return fs.readdirSync(skillsDest).map((name) => {
|
|
232
|
+
const meta = readMeta(path.join(skillsDest, name));
|
|
233
|
+
const config = skillConfigs[name] || {};
|
|
234
|
+
return {
|
|
235
|
+
name,
|
|
236
|
+
version: meta ? meta.version : "?",
|
|
237
|
+
description: meta ? meta.description : config.description || "",
|
|
238
|
+
tags: meta ? meta.tags : [],
|
|
239
|
+
enabled: config.enabled !== false,
|
|
240
|
+
};
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = {
|
|
245
|
+
timestamp,
|
|
246
|
+
backupTimestamp,
|
|
247
|
+
appendLog,
|
|
248
|
+
backupDir,
|
|
249
|
+
copyDirSync,
|
|
250
|
+
deepMerge,
|
|
251
|
+
readMeta,
|
|
252
|
+
installSkills,
|
|
253
|
+
installSettings,
|
|
254
|
+
getPackageSkills,
|
|
255
|
+
getUserSkills,
|
|
256
|
+
};
|