codexmate 0.0.17 → 0.0.19
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.en.md +65 -33
- package/README.md +61 -37
- package/cli.js +1877 -408
- package/lib/text-diff.js +303 -0
- package/package.json +1 -1
- package/web-ui/app.js +1749 -447
- package/web-ui/index.html +580 -199
- package/web-ui/logic.mjs +390 -0
- package/web-ui/modules/config-mode.computed.mjs +1 -0
- package/web-ui/modules/skills.computed.mjs +26 -1
- package/web-ui/modules/skills.methods.mjs +160 -23
- package/web-ui/session-helpers.mjs +362 -0
- package/web-ui/styles.css +652 -13
- package/doc/CHANGELOG.md +0 -32
- package/doc/CHANGELOG.zh-CN.md +0 -34
|
@@ -1,11 +1,98 @@
|
|
|
1
|
-
|
|
1
|
+
function createUnsupportedSkillsTargetAppError(app) {
|
|
2
|
+
return new Error(`Unsupported skills target app: ${String(app)}`);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function showUnsupportedSkillsTargetMessage(vm, app) {
|
|
6
|
+
vm.showMessage(`不支持的 Skills 安装目标:${String(app)}`, 'error');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createSkillsMethods({ api }) {
|
|
2
10
|
return {
|
|
3
|
-
|
|
11
|
+
normalizeSkillsTargetApp(app) {
|
|
12
|
+
if (app == null) {
|
|
13
|
+
return 'codex';
|
|
14
|
+
}
|
|
15
|
+
if (app === 'codex' || app === 'claude') {
|
|
16
|
+
return app;
|
|
17
|
+
}
|
|
18
|
+
throw createUnsupportedSkillsTargetAppError(app);
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
resetSkillsTargetState() {
|
|
4
22
|
this.skillsSelectedNames = [];
|
|
5
23
|
this.skillsKeyword = '';
|
|
6
24
|
this.skillsStatusFilter = 'all';
|
|
25
|
+
this.skillsRootPath = '';
|
|
26
|
+
this.skillsList = [];
|
|
7
27
|
this.skillsImportList = [];
|
|
8
28
|
this.skillsImportSelectedKeys = [];
|
|
29
|
+
this.skillsMarketLocalLoadedOnce = false;
|
|
30
|
+
this.skillsMarketImportLoadedOnce = false;
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
async setSkillsTargetApp(app, options = {}) {
|
|
34
|
+
if (
|
|
35
|
+
this.skillsLoading
|
|
36
|
+
|| this.skillsDeleting
|
|
37
|
+
|| this.skillsScanningImports
|
|
38
|
+
|| this.skillsImporting
|
|
39
|
+
|| this.skillsZipImporting
|
|
40
|
+
|| this.skillsExporting
|
|
41
|
+
|| this.skillsMarketLoading
|
|
42
|
+
) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
let nextTarget;
|
|
46
|
+
try {
|
|
47
|
+
nextTarget = this.normalizeSkillsTargetApp(app);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
showUnsupportedSkillsTargetMessage(this, app);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const refresh = !(options && options.refresh === false);
|
|
53
|
+
const silent = !!(options && options.silent);
|
|
54
|
+
if (nextTarget !== this.skillsTargetApp) {
|
|
55
|
+
this.skillsTargetApp = nextTarget;
|
|
56
|
+
this.resetSkillsTargetState();
|
|
57
|
+
}
|
|
58
|
+
if (!refresh) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
return await this.loadSkillsMarketOverview({
|
|
62
|
+
forceRefresh: true,
|
|
63
|
+
silent
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
async openSkillsManager(options = {}) {
|
|
68
|
+
if (
|
|
69
|
+
this.skillsLoading
|
|
70
|
+
|| this.skillsDeleting
|
|
71
|
+
|| this.skillsScanningImports
|
|
72
|
+
|| this.skillsImporting
|
|
73
|
+
|| this.skillsZipImporting
|
|
74
|
+
|| this.skillsExporting
|
|
75
|
+
|| this.skillsMarketLoading
|
|
76
|
+
) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
let targetApp;
|
|
80
|
+
try {
|
|
81
|
+
targetApp = this.normalizeSkillsTargetApp(options && options.targetApp ? options.targetApp : this.skillsTargetApp);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
showUnsupportedSkillsTargetMessage(this, options && options.targetApp);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
const targetChanged = targetApp !== this.skillsTargetApp;
|
|
87
|
+
if (targetChanged) {
|
|
88
|
+
this.skillsTargetApp = targetApp;
|
|
89
|
+
this.resetSkillsTargetState();
|
|
90
|
+
} else {
|
|
91
|
+
this.skillsSelectedNames = [];
|
|
92
|
+
this.skillsKeyword = '';
|
|
93
|
+
this.skillsStatusFilter = 'all';
|
|
94
|
+
this.skillsImportSelectedKeys = [];
|
|
95
|
+
}
|
|
9
96
|
this.showSkillsModal = true;
|
|
10
97
|
await this.refreshSkillsList({ silent: false });
|
|
11
98
|
},
|
|
@@ -28,23 +115,25 @@
|
|
|
28
115
|
async refreshSkillsList(options = {}) {
|
|
29
116
|
this.skillsLoading = true;
|
|
30
117
|
try {
|
|
31
|
-
const res = await api('list-
|
|
118
|
+
const res = await api('list-skills', {
|
|
119
|
+
targetApp: this.skillsTargetApp
|
|
120
|
+
});
|
|
32
121
|
if (res.error) {
|
|
33
122
|
this.skillsRootPath = '';
|
|
34
123
|
this.skillsList = [];
|
|
35
124
|
this.skillsSelectedNames = [];
|
|
36
125
|
this.showMessage(res.error, 'error');
|
|
37
|
-
return;
|
|
126
|
+
return false;
|
|
38
127
|
}
|
|
39
128
|
const exists = res.exists !== false;
|
|
40
129
|
if (!exists) {
|
|
41
|
-
this.skillsRootPath = '';
|
|
130
|
+
this.skillsRootPath = res.root || '';
|
|
42
131
|
this.skillsList = [];
|
|
43
132
|
this.skillsSelectedNames = [];
|
|
44
133
|
if (!options.silent) {
|
|
45
|
-
this.showMessage(
|
|
134
|
+
this.showMessage(`${this.skillsTargetLabel} skills 目录不存在,已按空列表显示`, 'info');
|
|
46
135
|
}
|
|
47
|
-
return;
|
|
136
|
+
return true;
|
|
48
137
|
}
|
|
49
138
|
this.skillsRootPath = res.root || '';
|
|
50
139
|
this.skillsList = Array.isArray(res.items) ? res.items : [];
|
|
@@ -53,16 +142,40 @@
|
|
|
53
142
|
.filter(Boolean));
|
|
54
143
|
this.skillsSelectedNames = (Array.isArray(this.skillsSelectedNames) ? this.skillsSelectedNames : [])
|
|
55
144
|
.filter((name) => currentNames.has(name));
|
|
145
|
+
return true;
|
|
56
146
|
} catch (e) {
|
|
57
147
|
this.skillsRootPath = '';
|
|
58
148
|
this.skillsList = [];
|
|
59
149
|
this.skillsSelectedNames = [];
|
|
60
150
|
this.showMessage('加载 skills 失败', 'error');
|
|
151
|
+
return false;
|
|
61
152
|
} finally {
|
|
62
153
|
this.skillsLoading = false;
|
|
63
154
|
}
|
|
64
155
|
},
|
|
65
156
|
|
|
157
|
+
async loadSkillsMarketOverview(options = {}) {
|
|
158
|
+
if (this.skillsMarketLoading) return false;
|
|
159
|
+
const silent = !!(options && options.silent);
|
|
160
|
+
const forceRefresh = !!(options && options.forceRefresh);
|
|
161
|
+
this.skillsMarketLoading = true;
|
|
162
|
+
let localLoaded = this.skillsMarketLocalLoadedOnce === true;
|
|
163
|
+
let importLoaded = this.skillsMarketImportLoadedOnce === true;
|
|
164
|
+
try {
|
|
165
|
+
if (forceRefresh || !localLoaded) {
|
|
166
|
+
localLoaded = await this.refreshSkillsList({ silent });
|
|
167
|
+
this.skillsMarketLocalLoadedOnce = localLoaded;
|
|
168
|
+
}
|
|
169
|
+
if (forceRefresh || !importLoaded) {
|
|
170
|
+
importLoaded = await this.scanImportableSkills({ silent });
|
|
171
|
+
this.skillsMarketImportLoadedOnce = importLoaded;
|
|
172
|
+
}
|
|
173
|
+
return !!(localLoaded && importLoaded);
|
|
174
|
+
} finally {
|
|
175
|
+
this.skillsMarketLoading = false;
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
|
|
66
179
|
resetSkillsFilters() {
|
|
67
180
|
this.skillsKeyword = '';
|
|
68
181
|
this.skillsStatusFilter = 'all';
|
|
@@ -99,18 +212,20 @@
|
|
|
99
212
|
},
|
|
100
213
|
|
|
101
214
|
async scanImportableSkills(options = {}) {
|
|
102
|
-
if (this.skillsScanningImports || this.skillsImporting || this.skillsZipImporting || this.skillsExporting) return;
|
|
215
|
+
if (this.skillsDeleting || this.skillsScanningImports || this.skillsImporting || this.skillsZipImporting || this.skillsExporting) return false;
|
|
103
216
|
const silent = !!(options && options.silent);
|
|
104
217
|
this.skillsScanningImports = true;
|
|
105
218
|
try {
|
|
106
|
-
const res = await api('scan-unmanaged-
|
|
219
|
+
const res = await api('scan-unmanaged-skills', {
|
|
220
|
+
targetApp: this.skillsTargetApp
|
|
221
|
+
});
|
|
107
222
|
if (res.error) {
|
|
108
223
|
this.skillsImportList = [];
|
|
109
224
|
this.skillsImportSelectedKeys = [];
|
|
110
225
|
if (!silent) {
|
|
111
226
|
this.showMessage(res.error, 'error');
|
|
112
227
|
}
|
|
113
|
-
return;
|
|
228
|
+
return false;
|
|
114
229
|
}
|
|
115
230
|
this.skillsImportList = Array.isArray(res.items) ? res.items : [];
|
|
116
231
|
const availableKeys = new Set(this.skillsImportSelectableKeys);
|
|
@@ -121,19 +236,21 @@
|
|
|
121
236
|
} else if (!silent) {
|
|
122
237
|
this.showMessage(`扫描到 ${this.skillsImportList.length} 个可导入 skill`, 'success');
|
|
123
238
|
}
|
|
239
|
+
return true;
|
|
124
240
|
} catch (e) {
|
|
125
241
|
this.skillsImportList = [];
|
|
126
242
|
this.skillsImportSelectedKeys = [];
|
|
127
243
|
if (!silent) {
|
|
128
244
|
this.showMessage('扫描可导入 skill 失败', 'error');
|
|
129
245
|
}
|
|
246
|
+
return false;
|
|
130
247
|
} finally {
|
|
131
248
|
this.skillsScanningImports = false;
|
|
132
249
|
}
|
|
133
250
|
},
|
|
134
251
|
|
|
135
252
|
async importSelectedSkills() {
|
|
136
|
-
if (this.skillsImporting || this.skillsZipImporting || this.skillsExporting) return;
|
|
253
|
+
if (this.skillsDeleting || this.skillsScanningImports || this.skillsImporting || this.skillsZipImporting || this.skillsExporting) return;
|
|
137
254
|
const selectedSet = new Set(Array.isArray(this.skillsImportSelectedKeys) ? this.skillsImportSelectedKeys : []);
|
|
138
255
|
const selectedItems = (Array.isArray(this.skillsImportList) ? this.skillsImportList : [])
|
|
139
256
|
.filter((item) => selectedSet.has(this.buildSkillImportKey(item)))
|
|
@@ -148,7 +265,10 @@
|
|
|
148
265
|
|
|
149
266
|
this.skillsImporting = true;
|
|
150
267
|
try {
|
|
151
|
-
const res = await api('import-
|
|
268
|
+
const res = await api('import-skills', {
|
|
269
|
+
targetApp: this.skillsTargetApp,
|
|
270
|
+
items: selectedItems
|
|
271
|
+
});
|
|
152
272
|
if (res.error) {
|
|
153
273
|
this.showMessage(res.error, 'error');
|
|
154
274
|
return;
|
|
@@ -161,7 +281,7 @@
|
|
|
161
281
|
const first = res.failed[0] && res.failed[0].error ? res.failed[0].error : '导入失败';
|
|
162
282
|
this.showMessage(first, 'error');
|
|
163
283
|
} else {
|
|
164
|
-
this.showMessage(`已导入 ${importedCount} 个 skill`, 'success');
|
|
284
|
+
this.showMessage(`已导入 ${importedCount} 个 skill 到 ${this.skillsTargetLabel}`, 'success');
|
|
165
285
|
}
|
|
166
286
|
await this.refreshSkillsList({ silent: true });
|
|
167
287
|
} catch (e) {
|
|
@@ -197,8 +317,8 @@
|
|
|
197
317
|
async uploadSkillsZipStream(file) {
|
|
198
318
|
const fileName = (file && typeof file.name === 'string' && file.name.trim())
|
|
199
319
|
? file.name.trim()
|
|
200
|
-
:
|
|
201
|
-
const response = await fetch(
|
|
320
|
+
: `${this.skillsTargetApp}-skills.zip`;
|
|
321
|
+
const response = await fetch(`/api/import-skills-zip?targetApp=${encodeURIComponent(this.skillsTargetApp)}`, {
|
|
202
322
|
method: 'POST',
|
|
203
323
|
headers: {
|
|
204
324
|
'x-codexmate-file-name': encodeURIComponent(fileName)
|
|
@@ -222,7 +342,7 @@
|
|
|
222
342
|
},
|
|
223
343
|
|
|
224
344
|
async importSkillsFromZipFile(file) {
|
|
225
|
-
if (this.skillsZipImporting || this.skillsImporting || this.skillsExporting) return;
|
|
345
|
+
if (this.skillsDeleting || this.skillsScanningImports || this.skillsZipImporting || this.skillsImporting || this.skillsExporting) return;
|
|
226
346
|
const maxSize = 20 * 1024 * 1024;
|
|
227
347
|
if (file.size > maxSize) {
|
|
228
348
|
this.showMessage('ZIP 文件过大,限制 20MB', 'error');
|
|
@@ -244,7 +364,7 @@
|
|
|
244
364
|
const first = res.failed[0] && res.failed[0].error ? res.failed[0].error : '导入失败';
|
|
245
365
|
this.showMessage(first, 'error');
|
|
246
366
|
} else {
|
|
247
|
-
this.showMessage(`已导入 ${importedCount} 个 skill`, 'success');
|
|
367
|
+
this.showMessage(`已导入 ${importedCount} 个 skill 到 ${this.skillsTargetLabel}`, 'success');
|
|
248
368
|
}
|
|
249
369
|
await this.refreshSkillsList({ silent: true });
|
|
250
370
|
} catch (e) {
|
|
@@ -257,7 +377,7 @@
|
|
|
257
377
|
},
|
|
258
378
|
|
|
259
379
|
async exportSelectedSkills() {
|
|
260
|
-
if (this.skillsExporting || this.skillsZipImporting || this.skillsImporting) return;
|
|
380
|
+
if (this.skillsDeleting || this.skillsExporting || this.skillsZipImporting || this.skillsImporting) return;
|
|
261
381
|
const selected = Array.isArray(this.skillsSelectedNames)
|
|
262
382
|
? Array.from(new Set(this.skillsSelectedNames.map((item) => String(item || '').trim()).filter(Boolean)))
|
|
263
383
|
: [];
|
|
@@ -267,7 +387,10 @@
|
|
|
267
387
|
}
|
|
268
388
|
this.skillsExporting = true;
|
|
269
389
|
try {
|
|
270
|
-
const res = await api('export-
|
|
390
|
+
const res = await api('export-skills', {
|
|
391
|
+
targetApp: this.skillsTargetApp,
|
|
392
|
+
names: selected
|
|
393
|
+
});
|
|
271
394
|
if (res && res.error) {
|
|
272
395
|
this.showMessage(res.error, 'error');
|
|
273
396
|
return;
|
|
@@ -290,7 +413,7 @@
|
|
|
290
413
|
if (failedCount > 0) {
|
|
291
414
|
this.showMessage(`已导出 ${exportedCount} 个,失败 ${failedCount} 个`, 'error');
|
|
292
415
|
} else {
|
|
293
|
-
this.showMessage(
|
|
416
|
+
this.showMessage(`已从 ${this.skillsTargetLabel} 导出 ${exportedCount} 个 skill`, 'success');
|
|
294
417
|
}
|
|
295
418
|
} catch (e) {
|
|
296
419
|
this.showMessage('导出 skill 失败', 'error');
|
|
@@ -301,6 +424,10 @@
|
|
|
301
424
|
|
|
302
425
|
async deleteSelectedSkills() {
|
|
303
426
|
if (this.skillsDeleting || this.skillsZipImporting || this.skillsExporting || this.skillsImporting) return;
|
|
427
|
+
if (this.skillsScanningImports) {
|
|
428
|
+
this.showMessage('正在扫描导入源,请稍后再试', 'error');
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
304
431
|
const selected = Array.isArray(this.skillsSelectedNames)
|
|
305
432
|
? Array.from(new Set(this.skillsSelectedNames.map((item) => String(item || '').trim()).filter(Boolean)))
|
|
306
433
|
: [];
|
|
@@ -308,14 +435,24 @@
|
|
|
308
435
|
this.showMessage('请先选择要删除的 skill', 'error');
|
|
309
436
|
return;
|
|
310
437
|
}
|
|
311
|
-
const confirmed =
|
|
438
|
+
const confirmed = await this.requestConfirmDialog({
|
|
439
|
+
title: '删除 Skills',
|
|
440
|
+
message: `确认从 ${this.skillsTargetLabel} 删除 ${selected.length} 个 skill 吗?此操作不可撤销。`,
|
|
441
|
+
confirmText: '删除',
|
|
442
|
+
cancelText: '取消',
|
|
443
|
+
confirmDisabled: () => this.skillsDeleting || this.skillsScanningImports,
|
|
444
|
+
danger: true
|
|
445
|
+
});
|
|
312
446
|
if (!confirmed) {
|
|
313
447
|
return;
|
|
314
448
|
}
|
|
315
449
|
|
|
316
450
|
this.skillsDeleting = true;
|
|
317
451
|
try {
|
|
318
|
-
const res = await api('delete-
|
|
452
|
+
const res = await api('delete-skills', {
|
|
453
|
+
targetApp: this.skillsTargetApp,
|
|
454
|
+
names: selected
|
|
455
|
+
});
|
|
319
456
|
if (res.error) {
|
|
320
457
|
this.showMessage(res.error, 'error');
|
|
321
458
|
return;
|
|
@@ -330,7 +467,7 @@
|
|
|
330
467
|
const first = failedList[0] && failedList[0].error ? failedList[0].error : '删除失败';
|
|
331
468
|
this.showMessage(first, 'error');
|
|
332
469
|
} else {
|
|
333
|
-
this.showMessage(
|
|
470
|
+
this.showMessage(`已从 ${this.skillsTargetLabel} 删除 ${deletedCount} 个 skill`, 'success');
|
|
334
471
|
}
|
|
335
472
|
await this.refreshSkillsList({ silent: true });
|
|
336
473
|
} catch (e) {
|