mdk-skills 2.4.19 → 2.4.20
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/package.json +1 -1
- package/scripts/cli.js +1 -15
- package/scripts/web-ui/server/context.js +54 -50
- package/scripts/web-ui/server/routes/others.js +25 -15
- package/scripts/web-ui/server/routes/profiles.js +16 -12
- package/scripts/web-ui/server/routes/skills.js +124 -120
- package/scripts/web-ui/server/routes/source.js +46 -42
- package/scripts/web-ui/server/utils.js +190 -12
- package/scripts/web-ui/server.js +34 -16
package/package.json
CHANGED
package/scripts/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
|
-
const { getPackageSkills, getUserSkills, getSkillsSource, backupDir, listSkillDirs } = require("./core");
|
|
5
|
+
const { getPackageSkills, getUserSkills, getSkillsSource, backupDir, listSkillDirs, copyDirSync } = require("./core");
|
|
6
6
|
|
|
7
7
|
// npx 运行时:process.cwd() 是用户项目目录
|
|
8
8
|
// __dirname 是包内 scripts/ 目录
|
|
@@ -47,20 +47,6 @@ function writeSettings(settings) {
|
|
|
47
47
|
|
|
48
48
|
// ---------- 安装勾选的技能 ----------
|
|
49
49
|
|
|
50
|
-
// 手动递归拷贝目录,绕开 Windows 中文路径下 fs.cpSync({ recursive: true }) 死锁的 bug
|
|
51
|
-
function copyDirSync(src, dest) {
|
|
52
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
53
|
-
for (const item of fs.readdirSync(src)) {
|
|
54
|
-
const srcPath = path.join(src, item);
|
|
55
|
-
const destPath = path.join(dest, item);
|
|
56
|
-
if (fs.statSync(srcPath).isDirectory()) {
|
|
57
|
-
copyDirSync(srcPath, destPath);
|
|
58
|
-
} else {
|
|
59
|
-
fs.copyFileSync(srcPath, destPath);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
50
|
function installSelectedSkills(selectedNames) {
|
|
65
51
|
// 创建 .claude/ 和 skills/ 目录
|
|
66
52
|
if (!fs.existsSync(skillsDest)) {
|
|
@@ -41,23 +41,23 @@ function refreshSource() {
|
|
|
41
41
|
pkgSkillsSource = skillsSource ? path.join(skillsSource, ".claude", "skills") : null;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
// ---------- Settings
|
|
44
|
+
// ---------- Settings(异步)----------
|
|
45
45
|
|
|
46
|
-
function readSettings() {
|
|
47
|
-
if (!
|
|
46
|
+
async function readSettings() {
|
|
47
|
+
if (!(await pathExists(settingsPath)))
|
|
48
48
|
return { skills: {}, always_apply_skills: [] };
|
|
49
49
|
try {
|
|
50
|
-
return JSON.parse(fs.
|
|
50
|
+
return JSON.parse(await fs.promises.readFile(settingsPath, "utf-8"));
|
|
51
51
|
} catch {
|
|
52
52
|
return { skills: {}, always_apply_skills: [] };
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
function writeSettings(settings) {
|
|
57
|
-
if (!
|
|
58
|
-
fs.
|
|
56
|
+
async function writeSettings(settings) {
|
|
57
|
+
if (!(await pathExists(claudeDest))) {
|
|
58
|
+
await fs.promises.mkdir(claudeDest, { recursive: true });
|
|
59
59
|
}
|
|
60
|
-
fs.
|
|
60
|
+
await fs.promises.writeFile(
|
|
61
61
|
settingsPath,
|
|
62
62
|
JSON.stringify(settings, null, 2) + "\n",
|
|
63
63
|
"utf-8",
|
|
@@ -70,25 +70,25 @@ function getPullSourcePath() {
|
|
|
70
70
|
return path.join(pkgSkillsSource, ".pull-source.json");
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
function readPullSource() {
|
|
73
|
+
async function readPullSource() {
|
|
74
74
|
if (!pkgSkillsSource) return {};
|
|
75
75
|
const filePath = getPullSourcePath();
|
|
76
|
-
if (!
|
|
76
|
+
if (!(await pathExists(filePath))) return {};
|
|
77
77
|
try {
|
|
78
|
-
return JSON.parse(fs.
|
|
78
|
+
return JSON.parse(await fs.promises.readFile(filePath, "utf-8"));
|
|
79
79
|
} catch {
|
|
80
80
|
return {};
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function writePullSource(data) {
|
|
85
|
-
fs.
|
|
84
|
+
async function writePullSource(data) {
|
|
85
|
+
await fs.promises.writeFile(getPullSourcePath(), JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
function addPullSource(skillName, url) {
|
|
89
|
-
const data = readPullSource();
|
|
88
|
+
async function addPullSource(skillName, url) {
|
|
89
|
+
const data = await readPullSource();
|
|
90
90
|
data[skillName] = { url, pulledAt: new Date().toISOString() };
|
|
91
|
-
writePullSource(data);
|
|
91
|
+
await writePullSource(data);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// 检查技能源是否已设置
|
|
@@ -102,11 +102,11 @@ function requireSource(res, utils) {
|
|
|
102
102
|
|
|
103
103
|
// ---------- profiles ----------
|
|
104
104
|
|
|
105
|
-
function loadProfiles() {
|
|
105
|
+
async function loadProfiles() {
|
|
106
106
|
const profilesPath = path.join(skillsSource, ".claude", "profiles.json");
|
|
107
|
-
if (!
|
|
107
|
+
if (!(await pathExists(profilesPath))) return null;
|
|
108
108
|
try {
|
|
109
|
-
return JSON.parse(fs.
|
|
109
|
+
return JSON.parse(await fs.promises.readFile(profilesPath, "utf-8")).profiles;
|
|
110
110
|
} catch {
|
|
111
111
|
return null;
|
|
112
112
|
}
|
|
@@ -120,9 +120,15 @@ const runningTasks = new Map();
|
|
|
120
120
|
|
|
121
121
|
const pullCache = new Map();
|
|
122
122
|
|
|
123
|
-
// ----------
|
|
123
|
+
// ---------- 路径检查工具 ----------
|
|
124
124
|
|
|
125
|
-
function
|
|
125
|
+
async function pathExists(p) {
|
|
126
|
+
try { await fs.promises.access(p); return true; } catch { return false; }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------- 应用场景 / 安装(异步)----------
|
|
130
|
+
|
|
131
|
+
async function applyProfile(profile, { copyDir, safeRm }) {
|
|
126
132
|
const pkgSkills = core.listSkillDirs(pkgSkillsSource);
|
|
127
133
|
const failedDelete = [];
|
|
128
134
|
const failedInstall = [];
|
|
@@ -136,22 +142,22 @@ function applyProfile(profile, { copyDirSync, safeRmSync, readSettings, writeSet
|
|
|
136
142
|
selected = profile.skills.filter((s) => pkgSkills.includes(s));
|
|
137
143
|
}
|
|
138
144
|
|
|
139
|
-
if (!
|
|
140
|
-
fs.
|
|
145
|
+
if (!(await pathExists(skillsDest))) {
|
|
146
|
+
await fs.promises.mkdir(skillsDest, { recursive: true });
|
|
141
147
|
}
|
|
142
148
|
for (const name of pkgSkills) {
|
|
143
149
|
const src = path.join(pkgSkillsSource, name);
|
|
144
150
|
const dest = path.join(skillsDest, name);
|
|
145
151
|
if (selected.includes(name)) {
|
|
146
|
-
if (!
|
|
147
|
-
try {
|
|
152
|
+
if (!(await pathExists(dest))) {
|
|
153
|
+
try { await copyDir(src, dest); } catch (err) {
|
|
148
154
|
console.error(`安装技能 "${name}" 失败:`, err.message);
|
|
149
155
|
failedInstall.push(name);
|
|
150
156
|
}
|
|
151
157
|
}
|
|
152
158
|
} else {
|
|
153
|
-
if (
|
|
154
|
-
const r =
|
|
159
|
+
if (await pathExists(dest)) {
|
|
160
|
+
const r = await safeRm(dest, name);
|
|
155
161
|
if (!r.removed) failedDelete.push(name);
|
|
156
162
|
}
|
|
157
163
|
}
|
|
@@ -159,7 +165,7 @@ function applyProfile(profile, { copyDirSync, safeRmSync, readSettings, writeSet
|
|
|
159
165
|
|
|
160
166
|
const hasFailed = failedDelete.length > 0 || failedInstall.length > 0;
|
|
161
167
|
if (!hasFailed) {
|
|
162
|
-
const settings = readSettings();
|
|
168
|
+
const settings = await readSettings();
|
|
163
169
|
if (!settings.skills) settings.skills = {};
|
|
164
170
|
for (const name of selected) {
|
|
165
171
|
if (!settings.skills[name]) settings.skills[name] = {};
|
|
@@ -174,18 +180,18 @@ function applyProfile(profile, { copyDirSync, safeRmSync, readSettings, writeSet
|
|
|
174
180
|
settings.always_apply_skills = profile.always_apply;
|
|
175
181
|
}
|
|
176
182
|
settings._active_profile = profile.id;
|
|
177
|
-
writeSettings(settings);
|
|
183
|
+
await writeSettings(settings);
|
|
178
184
|
|
|
179
185
|
const profilesSource = path.join(skillsSource, ".claude", "profiles.json");
|
|
180
186
|
const profilesDest = path.join(claudeDest, "profiles.json");
|
|
181
|
-
if (
|
|
182
|
-
fs.
|
|
187
|
+
if (await pathExists(profilesSource)) {
|
|
188
|
+
await fs.promises.copyFile(profilesSource, profilesDest);
|
|
183
189
|
}
|
|
184
190
|
|
|
185
191
|
const mdSource = path.join(skillsSource, "CLAUDE.md");
|
|
186
192
|
const mdDest = path.join(projectRoot, "CLAUDE.md");
|
|
187
|
-
if (
|
|
188
|
-
fs.
|
|
193
|
+
if (await pathExists(mdSource) && !(await pathExists(mdDest))) {
|
|
194
|
+
await fs.promises.copyFile(mdSource, mdDest);
|
|
189
195
|
}
|
|
190
196
|
}
|
|
191
197
|
|
|
@@ -198,10 +204,10 @@ function applyProfile(profile, { copyDirSync, safeRmSync, readSettings, writeSet
|
|
|
198
204
|
};
|
|
199
205
|
}
|
|
200
206
|
|
|
201
|
-
function installSelectedSkills(selectedNames, {
|
|
202
|
-
if (!
|
|
203
|
-
if (!
|
|
204
|
-
fs.
|
|
207
|
+
async function installSelectedSkills(selectedNames, { copyDir, safeRm }) {
|
|
208
|
+
if (!(await pathExists(pkgSkillsSource))) return { installed: 0, locked: [] };
|
|
209
|
+
if (!(await pathExists(skillsDest))) {
|
|
210
|
+
await fs.promises.mkdir(skillsDest, { recursive: true });
|
|
205
211
|
}
|
|
206
212
|
const allPkgSkills = core.listSkillDirs(pkgSkillsSource);
|
|
207
213
|
let installedCount = 0;
|
|
@@ -211,41 +217,39 @@ function installSelectedSkills(selectedNames, { copyDirSync, safeRmSync, readSet
|
|
|
211
217
|
const src = path.join(pkgSkillsSource, name);
|
|
212
218
|
const dest = path.join(skillsDest, name);
|
|
213
219
|
if (selectedNames.includes(name)) {
|
|
214
|
-
if (!
|
|
215
|
-
try {
|
|
220
|
+
if (!(await pathExists(dest))) {
|
|
221
|
+
try { await copyDir(src, dest); installedCount++; } catch (err) {
|
|
216
222
|
console.error(`安装技能 "${name}" 失败:`, err.message);
|
|
217
223
|
}
|
|
218
224
|
}
|
|
219
225
|
} else {
|
|
220
|
-
if (
|
|
221
|
-
const r =
|
|
226
|
+
if (await pathExists(dest)) {
|
|
227
|
+
const r = await safeRm(dest, name);
|
|
222
228
|
if (!r.removed && r.reason === 'locked') locked.push(name);
|
|
223
229
|
}
|
|
224
230
|
}
|
|
225
231
|
}
|
|
226
232
|
|
|
227
|
-
const settings = readSettings();
|
|
233
|
+
const settings = await readSettings();
|
|
228
234
|
if (!settings.skills) settings.skills = {};
|
|
229
235
|
for (const name of allPkgSkills) {
|
|
230
236
|
const enabled = selectedNames.includes(name);
|
|
231
237
|
if (!settings.skills[name]) settings.skills[name] = {};
|
|
232
238
|
settings.skills[name].enabled = enabled;
|
|
233
239
|
}
|
|
234
|
-
writeSettings(settings);
|
|
240
|
+
await writeSettings(settings);
|
|
235
241
|
|
|
236
242
|
const mdSource = path.join(skillsSource, "CLAUDE.md");
|
|
237
243
|
const mdDest = path.join(projectRoot, "CLAUDE.md");
|
|
238
|
-
if (
|
|
239
|
-
fs.
|
|
244
|
+
if (await pathExists(mdSource) && !(await pathExists(mdDest))) {
|
|
245
|
+
await fs.promises.copyFile(mdSource, mdDest);
|
|
240
246
|
}
|
|
241
247
|
|
|
242
248
|
return { installed: installedCount, locked };
|
|
243
249
|
}
|
|
244
250
|
|
|
245
|
-
// ----------
|
|
251
|
+
// ---------- 回调函数注册 ----------
|
|
246
252
|
|
|
247
|
-
// 由于 applyProfile / installSelectedSkills 需要在编译时传入依赖
|
|
248
|
-
// 而在拆分前它们直接引用模块级变量,因此我们在 context 模块级保留接口
|
|
249
253
|
function buildContext(utils) {
|
|
250
254
|
return {
|
|
251
255
|
projectRoot,
|
|
@@ -263,8 +267,8 @@ function buildContext(utils) {
|
|
|
263
267
|
addPullSource,
|
|
264
268
|
loadProfiles,
|
|
265
269
|
requireSource: (res) => requireSource(res, utils),
|
|
266
|
-
applyProfile: (profile) => applyProfile(profile, utils),
|
|
267
|
-
installSelectedSkills: (names) => installSelectedSkills(names, utils),
|
|
270
|
+
applyProfile: (profile) => applyProfile(profile, { copyDir: utils.copyDir, safeRm: utils.safeRm }),
|
|
271
|
+
installSelectedSkills: (names) => installSelectedSkills(names, { copyDir: utils.copyDir, safeRm: utils.safeRm }),
|
|
268
272
|
runningTasks,
|
|
269
273
|
pullCache,
|
|
270
274
|
};
|
|
@@ -6,6 +6,10 @@ const { buildContext } = require("../context");
|
|
|
6
6
|
|
|
7
7
|
const ctx = buildContext(utils);
|
|
8
8
|
|
|
9
|
+
async function pathExists(p) {
|
|
10
|
+
try { await fs.promises.access(p); return true; } catch { return false; }
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
function register(router) {
|
|
10
14
|
|
|
11
15
|
// ----------------------------------------------------------------
|
|
@@ -14,7 +18,10 @@ function register(router) {
|
|
|
14
18
|
router.get("/api/readme", async (req, res) => {
|
|
15
19
|
if (!ctx.skillsSource) return utils.sendJSON(res, { content: null, found: false });
|
|
16
20
|
const p = path.join(ctx.skillsSource, "README.md");
|
|
17
|
-
|
|
21
|
+
let content = null;
|
|
22
|
+
if (await pathExists(p)) {
|
|
23
|
+
content = await fs.promises.readFile(p, "utf-8");
|
|
24
|
+
}
|
|
18
25
|
return utils.sendJSON(res, { content, found: !!content });
|
|
19
26
|
});
|
|
20
27
|
|
|
@@ -24,7 +31,10 @@ function register(router) {
|
|
|
24
31
|
router.get("/api/skills-readme", async (req, res) => {
|
|
25
32
|
if (!ctx.skillsSource) return utils.sendJSON(res, { content: null, found: false });
|
|
26
33
|
const p = path.join(ctx.skillsSource, ".claude", "skills", "README.md");
|
|
27
|
-
|
|
34
|
+
let content = null;
|
|
35
|
+
if (await pathExists(p)) {
|
|
36
|
+
content = await fs.promises.readFile(p, "utf-8");
|
|
37
|
+
}
|
|
28
38
|
return utils.sendJSON(res, { content, found: !!content });
|
|
29
39
|
});
|
|
30
40
|
|
|
@@ -37,8 +47,8 @@ function register(router) {
|
|
|
37
47
|
: [path.join(ctx.projectRoot, "CLAUDE.md")];
|
|
38
48
|
let content = null;
|
|
39
49
|
for (const p of paths) {
|
|
40
|
-
if (
|
|
41
|
-
content = fs.
|
|
50
|
+
if (await pathExists(p)) {
|
|
51
|
+
content = await fs.promises.readFile(p, "utf-8");
|
|
42
52
|
break;
|
|
43
53
|
}
|
|
44
54
|
}
|
|
@@ -50,7 +60,7 @@ function register(router) {
|
|
|
50
60
|
// ----------------------------------------------------------------
|
|
51
61
|
router.post("/api/readme/create", async (req, res) => {
|
|
52
62
|
const body = await utils.parseBody(req);
|
|
53
|
-
const settings = ctx.readSettings();
|
|
63
|
+
const settings = await ctx.readSettings();
|
|
54
64
|
const sourcePath = settings._skill_source;
|
|
55
65
|
if (!sourcePath) return utils.sendJSON(res, { error: "未绑定本地源" }, 400);
|
|
56
66
|
|
|
@@ -61,7 +71,7 @@ function register(router) {
|
|
|
61
71
|
const skipped = [];
|
|
62
72
|
|
|
63
73
|
const skillsDir = path.join(sourcePath, ".claude", "skills");
|
|
64
|
-
const skillNames =
|
|
74
|
+
const skillNames = await pathExists(skillsDir)
|
|
65
75
|
? core.listSkillDirs(skillsDir)
|
|
66
76
|
: [];
|
|
67
77
|
const skillsList = skillNames.length
|
|
@@ -71,7 +81,7 @@ function register(router) {
|
|
|
71
81
|
// 根目录 README.md
|
|
72
82
|
if (root) {
|
|
73
83
|
const rootPath = path.join(sourcePath, "README.md");
|
|
74
|
-
if (!
|
|
84
|
+
if (!(await pathExists(rootPath))) {
|
|
75
85
|
const tmpl = `# ${name}
|
|
76
86
|
|
|
77
87
|
> 维护团队:${team}
|
|
@@ -97,7 +107,7 @@ ${skillsList}
|
|
|
97
107
|
|
|
98
108
|
<!-- 许可证信息 -->
|
|
99
109
|
`;
|
|
100
|
-
fs.
|
|
110
|
+
await fs.promises.writeFile(rootPath, tmpl, "utf-8");
|
|
101
111
|
created.push("README.md");
|
|
102
112
|
} else {
|
|
103
113
|
skipped.push("README.md");
|
|
@@ -107,7 +117,7 @@ ${skillsList}
|
|
|
107
117
|
// skills/README.md
|
|
108
118
|
if (skills) {
|
|
109
119
|
const skillsReadmePath = path.join(skillsDir, "README.md");
|
|
110
|
-
if (!
|
|
120
|
+
if (!(await pathExists(skillsReadmePath))) {
|
|
111
121
|
const tmpl = `# 技能说明
|
|
112
122
|
|
|
113
123
|
## 快速开始
|
|
@@ -132,7 +142,7 @@ ${skillsList}
|
|
|
132
142
|
技能使用过程中的注意事项。
|
|
133
143
|
-->
|
|
134
144
|
`;
|
|
135
|
-
fs.
|
|
145
|
+
await fs.promises.writeFile(skillsReadmePath, tmpl, "utf-8");
|
|
136
146
|
created.push("skills/README.md");
|
|
137
147
|
} else {
|
|
138
148
|
skipped.push("skills/README.md");
|
|
@@ -154,10 +164,10 @@ ${skillsList}
|
|
|
154
164
|
for (const skill of pkgSkills) {
|
|
155
165
|
const skillDir = path.join(ctx.skillsDest, skill.name);
|
|
156
166
|
const issues = [];
|
|
157
|
-
if (!
|
|
167
|
+
if (!(await pathExists(skillDir))) {
|
|
158
168
|
issues.push("未安装");
|
|
159
169
|
} else {
|
|
160
|
-
if (!
|
|
170
|
+
if (!(await pathExists(path.join(skillDir, "SKILL.md")))) {
|
|
161
171
|
issues.push("缺少 SKILL.md");
|
|
162
172
|
} else if (!core.parseFrontmatter(skillDir)) {
|
|
163
173
|
issues.push("SKILL.md frontmatter 格式异常");
|
|
@@ -194,13 +204,13 @@ ${skillsList}
|
|
|
194
204
|
|
|
195
205
|
if (!dirPath || dirPath === "") {
|
|
196
206
|
if (process.platform === "win32") {
|
|
197
|
-
entries = utils.getWindowsDrives();
|
|
207
|
+
entries = await utils.getWindowsDrives();
|
|
198
208
|
} else {
|
|
199
209
|
entries = [{ name: "/", isDir: true }];
|
|
200
210
|
}
|
|
201
211
|
} else {
|
|
202
212
|
try {
|
|
203
|
-
const items = fs.
|
|
213
|
+
const items = await fs.promises.readdir(dirPath, { withFileTypes: true });
|
|
204
214
|
entries = items
|
|
205
215
|
.filter((item) => item.isDirectory())
|
|
206
216
|
.map((item) => ({ name: item.name, isDir: true }))
|
|
@@ -221,7 +231,7 @@ ${skillsList}
|
|
|
221
231
|
if (!dirPath) return utils.sendJSON(res, { error: "缺少路径" }, 400);
|
|
222
232
|
|
|
223
233
|
const skillsDir = path.join(dirPath, ".claude", "skills");
|
|
224
|
-
const valid =
|
|
234
|
+
const valid = await pathExists(skillsDir);
|
|
225
235
|
let skillCount = 0;
|
|
226
236
|
if (valid) {
|
|
227
237
|
skillCount = core.listSkillDirs(skillsDir).length;
|
|
@@ -5,6 +5,10 @@ const { buildContext } = require("../context");
|
|
|
5
5
|
|
|
6
6
|
const ctx = buildContext(utils);
|
|
7
7
|
|
|
8
|
+
async function pathExists(p) {
|
|
9
|
+
try { await fs.promises.access(p); return true; } catch { return false; }
|
|
10
|
+
}
|
|
11
|
+
|
|
8
12
|
function register(router) {
|
|
9
13
|
|
|
10
14
|
// ----------------------------------------------------------------
|
|
@@ -14,8 +18,8 @@ function register(router) {
|
|
|
14
18
|
if (!ctx.skillsSource || !ctx.pkgSkillsSource) {
|
|
15
19
|
return utils.sendJSON(res, { error: "未设置技能目录,请在设置中连接" }, 400);
|
|
16
20
|
}
|
|
17
|
-
const profiles = ctx.loadProfiles();
|
|
18
|
-
const settings = ctx.readSettings();
|
|
21
|
+
const profiles = await ctx.loadProfiles();
|
|
22
|
+
const settings = await ctx.readSettings();
|
|
19
23
|
return utils.sendJSON(res, {
|
|
20
24
|
profiles: profiles || [],
|
|
21
25
|
activeProfile: settings._active_profile || null,
|
|
@@ -33,11 +37,11 @@ function register(router) {
|
|
|
33
37
|
const body = await utils.parseBody(req);
|
|
34
38
|
const { profileId } = body;
|
|
35
39
|
if (!profileId) return utils.sendJSON(res, { error: "缺少场景 ID" }, 400);
|
|
36
|
-
const profiles = ctx.loadProfiles();
|
|
40
|
+
const profiles = await ctx.loadProfiles();
|
|
37
41
|
const profile = profiles?.find((p) => p.id === profileId);
|
|
38
42
|
if (!profile)
|
|
39
43
|
return utils.sendJSON(res, { error: "场景不存在: " + profileId }, 404);
|
|
40
|
-
const result = ctx.applyProfile(profile);
|
|
44
|
+
const result = await ctx.applyProfile(profile);
|
|
41
45
|
return utils.sendJSON(res, { ok: true, ...result });
|
|
42
46
|
});
|
|
43
47
|
|
|
@@ -52,9 +56,9 @@ function register(router) {
|
|
|
52
56
|
|
|
53
57
|
const profilesPath = path.join(ctx.skillsSource, ".claude", "profiles.json");
|
|
54
58
|
let data = { version: 1, profiles: [] };
|
|
55
|
-
if (
|
|
59
|
+
if (await pathExists(profilesPath)) {
|
|
56
60
|
try {
|
|
57
|
-
data = JSON.parse(fs.
|
|
61
|
+
data = JSON.parse(await fs.promises.readFile(profilesPath, "utf-8"));
|
|
58
62
|
} catch {}
|
|
59
63
|
}
|
|
60
64
|
|
|
@@ -82,11 +86,11 @@ function register(router) {
|
|
|
82
86
|
});
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
fs.
|
|
89
|
+
await fs.promises.writeFile(profilesPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
86
90
|
|
|
87
91
|
// 同步到项目 .claude
|
|
88
92
|
const projectProfilesPath = path.join(ctx.claudeDest, "profiles.json");
|
|
89
|
-
fs.
|
|
93
|
+
await fs.promises.writeFile(projectProfilesPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
90
94
|
|
|
91
95
|
return utils.sendJSON(res, { ok: true, profiles: data.profiles });
|
|
92
96
|
});
|
|
@@ -106,19 +110,19 @@ function register(router) {
|
|
|
106
110
|
return utils.sendJSON(res, { error: "不能删除内置场景" }, 403);
|
|
107
111
|
|
|
108
112
|
const profilesPath = path.join(ctx.skillsSource, ".claude", "profiles.json");
|
|
109
|
-
if (!
|
|
113
|
+
if (!(await pathExists(profilesPath)))
|
|
110
114
|
return utils.sendJSON(res, { error: "场景文件不存在" }, 404);
|
|
111
115
|
|
|
112
|
-
const data = JSON.parse(fs.
|
|
116
|
+
const data = JSON.parse(await fs.promises.readFile(profilesPath, "utf-8"));
|
|
113
117
|
const index = data.profiles.findIndex((p) => p.id === id);
|
|
114
118
|
if (index === -1) return utils.sendJSON(res, { error: "场景不存在" }, 404);
|
|
115
119
|
|
|
116
120
|
data.profiles.splice(index, 1);
|
|
117
|
-
fs.
|
|
121
|
+
await fs.promises.writeFile(profilesPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
118
122
|
|
|
119
123
|
// 同步到项目 .claude
|
|
120
124
|
const projectProfilesPath = path.join(ctx.claudeDest, "profiles.json");
|
|
121
|
-
fs.
|
|
125
|
+
await fs.promises.writeFile(projectProfilesPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
122
126
|
|
|
123
127
|
return utils.sendJSON(res, { ok: true });
|
|
124
128
|
});
|