@wang121ye/skillmanager 0.0.1 → 0.0.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/LICENSE +21 -21
- package/README.md +252 -219
- package/manifests/sources.json +38 -38
- package/package.json +50 -50
- package/src/cli.js +178 -178
- package/src/commands/bootstrap.js +153 -153
- package/src/commands/config.js +152 -96
- package/src/commands/select.js +79 -79
- package/src/commands/source.js +103 -103
- package/src/commands/uninstall.js +63 -63
- package/src/commands/update.js +110 -110
- package/src/commands/webui.js +150 -150
- package/src/commands/where.js +23 -23
- package/src/index.js +2 -2
- package/src/lib/concurrency.js +22 -22
- package/src/lib/config.js +75 -75
- package/src/lib/fs.js +33 -33
- package/src/lib/git.js +57 -57
- package/src/lib/http.js +42 -42
- package/src/lib/installed.js +35 -35
- package/src/lib/local-install.js +35 -35
- package/src/lib/manifest.js +84 -84
- package/src/lib/openskills.js +50 -50
- package/src/lib/paths.js +44 -44
- package/src/lib/profiles.js +31 -31
- package/src/lib/scan.js +50 -50
- package/src/lib/source-utils.js +62 -62
- package/src/ui/server.js +321 -321
package/src/commands/select.js
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
const { getAppPaths } = require('../lib/paths');
|
|
2
|
-
const { ensureDir } = require('../lib/fs');
|
|
3
|
-
const { loadSourcesManifest } = require('../lib/manifest');
|
|
4
|
-
const { ensureRepo } = require('../lib/git');
|
|
5
|
-
const { scanSkillsInRepo } = require('../lib/scan');
|
|
6
|
-
const { loadProfile, saveProfile } = require('../lib/profiles');
|
|
7
|
-
const { mapWithConcurrency } = require('../lib/concurrency');
|
|
8
|
-
const { getEffectiveDefaultProfile } = require('../lib/config');
|
|
9
|
-
const { launchSelectionUi } = require('../ui/server');
|
|
10
|
-
|
|
11
|
-
async function selectUi(opts) {
|
|
12
|
-
const paths = getAppPaths();
|
|
13
|
-
await ensureDir(paths.reposDir);
|
|
14
|
-
await ensureDir(paths.profilesDir);
|
|
15
|
-
|
|
16
|
-
const { sources } = await loadSourcesManifest();
|
|
17
|
-
const enabledSources = sources.filter((s) => s && s.enabled !== false);
|
|
18
|
-
|
|
19
|
-
const concurrency = Number(opts?.concurrency || process.env.SKILLMANAGER_CONCURRENCY || 3);
|
|
20
|
-
// eslint-disable-next-line no-console
|
|
21
|
-
console.log(`并发扫描:${Math.max(1, concurrency)}(可用 --concurrency 或环境变量 SKILLMANAGER_CONCURRENCY 调整)`);
|
|
22
|
-
|
|
23
|
-
const perSource = await mapWithConcurrency(enabledSources, concurrency, async (s) => {
|
|
24
|
-
try {
|
|
25
|
-
// eslint-disable-next-line no-console
|
|
26
|
-
console.log(`\n==> 扫描来源:${s.name || s.id}`);
|
|
27
|
-
const repoDir = await ensureRepo({ reposDir: paths.reposDir, source: s });
|
|
28
|
-
const skills = await scanSkillsInRepo({
|
|
29
|
-
sourceId: s.id,
|
|
30
|
-
sourceName: s.name || s.id,
|
|
31
|
-
repoDir
|
|
32
|
-
});
|
|
33
|
-
// eslint-disable-next-line no-console
|
|
34
|
-
console.log(` 找到 ${skills.length} 个 skills`);
|
|
35
|
-
return skills;
|
|
36
|
-
} catch (err) {
|
|
37
|
-
// eslint-disable-next-line no-console
|
|
38
|
-
console.warn(`警告:拉取/扫描来源失败,将跳过:${s.name || s.id}`);
|
|
39
|
-
// eslint-disable-next-line no-console
|
|
40
|
-
console.warn(err?.message || String(err));
|
|
41
|
-
return [];
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const allSkills = perSource.flat();
|
|
46
|
-
|
|
47
|
-
const profileName = opts?.profile || (await getEffectiveDefaultProfile());
|
|
48
|
-
const existing = await loadProfile({ profilesDir: paths.profilesDir, profileName });
|
|
49
|
-
const initialSelected =
|
|
50
|
-
existing?.selectedSkillIds && Array.isArray(existing.selectedSkillIds)
|
|
51
|
-
? existing.selectedSkillIds
|
|
52
|
-
: allSkills.map((s) => s.id);
|
|
53
|
-
|
|
54
|
-
const chosen = await launchSelectionUi({
|
|
55
|
-
title: `skillmanager · profile=${profileName}`,
|
|
56
|
-
skills: allSkills.map((s) => ({
|
|
57
|
-
id: s.id,
|
|
58
|
-
sourceId: s.sourceId,
|
|
59
|
-
sourceName: s.sourceName,
|
|
60
|
-
name: s.name,
|
|
61
|
-
description: s.description
|
|
62
|
-
})),
|
|
63
|
-
selectedSkillIds: initialSelected
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const savedPath = await saveProfile({
|
|
67
|
-
profilesDir: paths.profilesDir,
|
|
68
|
-
profileName,
|
|
69
|
-
selectedSkillIds: chosen
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// eslint-disable-next-line no-console
|
|
73
|
-
console.log(`已保存 profile:${profileName}`);
|
|
74
|
-
// eslint-disable-next-line no-console
|
|
75
|
-
console.log(`路径:${savedPath}`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
module.exports = { selectUi };
|
|
79
|
-
|
|
1
|
+
const { getAppPaths } = require('../lib/paths');
|
|
2
|
+
const { ensureDir } = require('../lib/fs');
|
|
3
|
+
const { loadSourcesManifest } = require('../lib/manifest');
|
|
4
|
+
const { ensureRepo } = require('../lib/git');
|
|
5
|
+
const { scanSkillsInRepo } = require('../lib/scan');
|
|
6
|
+
const { loadProfile, saveProfile } = require('../lib/profiles');
|
|
7
|
+
const { mapWithConcurrency } = require('../lib/concurrency');
|
|
8
|
+
const { getEffectiveDefaultProfile } = require('../lib/config');
|
|
9
|
+
const { launchSelectionUi } = require('../ui/server');
|
|
10
|
+
|
|
11
|
+
async function selectUi(opts) {
|
|
12
|
+
const paths = getAppPaths();
|
|
13
|
+
await ensureDir(paths.reposDir);
|
|
14
|
+
await ensureDir(paths.profilesDir);
|
|
15
|
+
|
|
16
|
+
const { sources } = await loadSourcesManifest();
|
|
17
|
+
const enabledSources = sources.filter((s) => s && s.enabled !== false);
|
|
18
|
+
|
|
19
|
+
const concurrency = Number(opts?.concurrency || process.env.SKILLMANAGER_CONCURRENCY || 3);
|
|
20
|
+
// eslint-disable-next-line no-console
|
|
21
|
+
console.log(`并发扫描:${Math.max(1, concurrency)}(可用 --concurrency 或环境变量 SKILLMANAGER_CONCURRENCY 调整)`);
|
|
22
|
+
|
|
23
|
+
const perSource = await mapWithConcurrency(enabledSources, concurrency, async (s) => {
|
|
24
|
+
try {
|
|
25
|
+
// eslint-disable-next-line no-console
|
|
26
|
+
console.log(`\n==> 扫描来源:${s.name || s.id}`);
|
|
27
|
+
const repoDir = await ensureRepo({ reposDir: paths.reposDir, source: s });
|
|
28
|
+
const skills = await scanSkillsInRepo({
|
|
29
|
+
sourceId: s.id,
|
|
30
|
+
sourceName: s.name || s.id,
|
|
31
|
+
repoDir
|
|
32
|
+
});
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.log(` 找到 ${skills.length} 个 skills`);
|
|
35
|
+
return skills;
|
|
36
|
+
} catch (err) {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
38
|
+
console.warn(`警告:拉取/扫描来源失败,将跳过:${s.name || s.id}`);
|
|
39
|
+
// eslint-disable-next-line no-console
|
|
40
|
+
console.warn(err?.message || String(err));
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const allSkills = perSource.flat();
|
|
46
|
+
|
|
47
|
+
const profileName = opts?.profile || (await getEffectiveDefaultProfile());
|
|
48
|
+
const existing = await loadProfile({ profilesDir: paths.profilesDir, profileName });
|
|
49
|
+
const initialSelected =
|
|
50
|
+
existing?.selectedSkillIds && Array.isArray(existing.selectedSkillIds)
|
|
51
|
+
? existing.selectedSkillIds
|
|
52
|
+
: allSkills.map((s) => s.id);
|
|
53
|
+
|
|
54
|
+
const chosen = await launchSelectionUi({
|
|
55
|
+
title: `skillmanager · profile=${profileName}`,
|
|
56
|
+
skills: allSkills.map((s) => ({
|
|
57
|
+
id: s.id,
|
|
58
|
+
sourceId: s.sourceId,
|
|
59
|
+
sourceName: s.sourceName,
|
|
60
|
+
name: s.name,
|
|
61
|
+
description: s.description
|
|
62
|
+
})),
|
|
63
|
+
selectedSkillIds: initialSelected
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const savedPath = await saveProfile({
|
|
67
|
+
profilesDir: paths.profilesDir,
|
|
68
|
+
profileName,
|
|
69
|
+
selectedSkillIds: chosen
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// eslint-disable-next-line no-console
|
|
73
|
+
console.log(`已保存 profile:${profileName}`);
|
|
74
|
+
// eslint-disable-next-line no-console
|
|
75
|
+
console.log(`路径:${savedPath}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { selectUi };
|
|
79
|
+
|
package/src/commands/source.js
CHANGED
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
const { readUserSourcesManifest, writeUserSourcesManifest, loadSourcesManifest, getUserManifestPath } = require('../lib/manifest');
|
|
2
|
-
const { defaultSourceIdFromInput, parseGitHubRef } = require('../lib/source-utils');
|
|
3
|
-
|
|
4
|
-
function uniqueId(desired, existingIds) {
|
|
5
|
-
if (!existingIds.has(desired)) return desired;
|
|
6
|
-
for (let i = 2; i < 1000; i++) {
|
|
7
|
-
const next = `${desired}-${i}`;
|
|
8
|
-
if (!existingIds.has(next)) return next;
|
|
9
|
-
}
|
|
10
|
-
throw new Error(`无法生成唯一 id:${desired}`);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async function listSources() {
|
|
14
|
-
const { sources, manifestPath } = await loadSourcesManifest();
|
|
15
|
-
// eslint-disable-next-line no-console
|
|
16
|
-
console.log(`sources(来自:${manifestPath})`);
|
|
17
|
-
for (const s of sources) {
|
|
18
|
-
// eslint-disable-next-line no-console
|
|
19
|
-
console.log(
|
|
20
|
-
`- ${s.id} [${s.enabled === false ? 'disabled' : 'enabled'}] ${s.name || ''}\n repo=${s.repo || ''}\n openskillsRef=${s.openskillsRef || ''}`
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
// eslint-disable-next-line no-console
|
|
24
|
-
console.log(`\n用户配置文件路径:${getUserManifestPath()}`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function addSource(repoOrRef, opts) {
|
|
28
|
-
const { manifest, sources, userPath } = await readUserSourcesManifest();
|
|
29
|
-
const existingIds = new Set(sources.map((s) => s && s.id).filter(Boolean));
|
|
30
|
-
|
|
31
|
-
const gh = parseGitHubRef(repoOrRef);
|
|
32
|
-
const desiredId = opts?.id ? String(opts.id) : defaultSourceIdFromInput(repoOrRef);
|
|
33
|
-
const id = uniqueId(desiredId, existingIds);
|
|
34
|
-
|
|
35
|
-
const newSource = {
|
|
36
|
-
id,
|
|
37
|
-
name: opts?.name ? String(opts.name) : gh ? `${gh.owner}/${gh.repo}` : String(repoOrRef),
|
|
38
|
-
kind: 'git',
|
|
39
|
-
enabled: opts?.disabled ? false : true,
|
|
40
|
-
repo: gh?.httpsRepo || String(repoOrRef),
|
|
41
|
-
openskillsRef: opts?.ref ? String(opts.ref) : gh?.openskillsRef || undefined
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const next = {
|
|
45
|
-
...(manifest || {}),
|
|
46
|
-
version: Number(manifest?.version || 1),
|
|
47
|
-
sources: [...sources, newSource]
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
await writeUserSourcesManifest(next);
|
|
51
|
-
|
|
52
|
-
// eslint-disable-next-line no-console
|
|
53
|
-
console.log(`已添加来源:${id}`);
|
|
54
|
-
// eslint-disable-next-line no-console
|
|
55
|
-
console.log(`写入:${userPath}`);
|
|
56
|
-
// eslint-disable-next-line no-console
|
|
57
|
-
console.log(`repo=${newSource.repo}`);
|
|
58
|
-
// eslint-disable-next-line no-console
|
|
59
|
-
console.log(`openskillsRef=${newSource.openskillsRef || '(none)'}`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async function removeSource(id) {
|
|
63
|
-
const { manifest, sources, userPath } = await readUserSourcesManifest();
|
|
64
|
-
const before = sources.length;
|
|
65
|
-
const nextSources = sources.filter((s) => s && s.id !== id);
|
|
66
|
-
if (nextSources.length === before) {
|
|
67
|
-
// eslint-disable-next-line no-console
|
|
68
|
-
console.log(`未找到来源:${id}`);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
const next = { ...(manifest || {}), sources: nextSources };
|
|
72
|
-
await writeUserSourcesManifest(next);
|
|
73
|
-
// eslint-disable-next-line no-console
|
|
74
|
-
console.log(`已移除来源:${id}`);
|
|
75
|
-
// eslint-disable-next-line no-console
|
|
76
|
-
console.log(`写入:${userPath}`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async function setEnabled(id, enabled) {
|
|
80
|
-
const { manifest, sources, userPath } = await readUserSourcesManifest();
|
|
81
|
-
let found = false;
|
|
82
|
-
const nextSources = sources.map((s) => {
|
|
83
|
-
if (s && s.id === id) {
|
|
84
|
-
found = true;
|
|
85
|
-
return { ...s, enabled };
|
|
86
|
-
}
|
|
87
|
-
return s;
|
|
88
|
-
});
|
|
89
|
-
if (!found) {
|
|
90
|
-
// eslint-disable-next-line no-console
|
|
91
|
-
console.log(`未找到来源:${id}`);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const next = { ...(manifest || {}), sources: nextSources };
|
|
95
|
-
await writeUserSourcesManifest(next);
|
|
96
|
-
// eslint-disable-next-line no-console
|
|
97
|
-
console.log(`已${enabled ? '启用' : '禁用'}来源:${id}`);
|
|
98
|
-
// eslint-disable-next-line no-console
|
|
99
|
-
console.log(`写入:${userPath}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
module.exports = { listSources, addSource, removeSource, setEnabled };
|
|
103
|
-
|
|
1
|
+
const { readUserSourcesManifest, writeUserSourcesManifest, loadSourcesManifest, getUserManifestPath } = require('../lib/manifest');
|
|
2
|
+
const { defaultSourceIdFromInput, parseGitHubRef } = require('../lib/source-utils');
|
|
3
|
+
|
|
4
|
+
function uniqueId(desired, existingIds) {
|
|
5
|
+
if (!existingIds.has(desired)) return desired;
|
|
6
|
+
for (let i = 2; i < 1000; i++) {
|
|
7
|
+
const next = `${desired}-${i}`;
|
|
8
|
+
if (!existingIds.has(next)) return next;
|
|
9
|
+
}
|
|
10
|
+
throw new Error(`无法生成唯一 id:${desired}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function listSources() {
|
|
14
|
+
const { sources, manifestPath } = await loadSourcesManifest();
|
|
15
|
+
// eslint-disable-next-line no-console
|
|
16
|
+
console.log(`sources(来自:${manifestPath})`);
|
|
17
|
+
for (const s of sources) {
|
|
18
|
+
// eslint-disable-next-line no-console
|
|
19
|
+
console.log(
|
|
20
|
+
`- ${s.id} [${s.enabled === false ? 'disabled' : 'enabled'}] ${s.name || ''}\n repo=${s.repo || ''}\n openskillsRef=${s.openskillsRef || ''}`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
// eslint-disable-next-line no-console
|
|
24
|
+
console.log(`\n用户配置文件路径:${getUserManifestPath()}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function addSource(repoOrRef, opts) {
|
|
28
|
+
const { manifest, sources, userPath } = await readUserSourcesManifest();
|
|
29
|
+
const existingIds = new Set(sources.map((s) => s && s.id).filter(Boolean));
|
|
30
|
+
|
|
31
|
+
const gh = parseGitHubRef(repoOrRef);
|
|
32
|
+
const desiredId = opts?.id ? String(opts.id) : defaultSourceIdFromInput(repoOrRef);
|
|
33
|
+
const id = uniqueId(desiredId, existingIds);
|
|
34
|
+
|
|
35
|
+
const newSource = {
|
|
36
|
+
id,
|
|
37
|
+
name: opts?.name ? String(opts.name) : gh ? `${gh.owner}/${gh.repo}` : String(repoOrRef),
|
|
38
|
+
kind: 'git',
|
|
39
|
+
enabled: opts?.disabled ? false : true,
|
|
40
|
+
repo: gh?.httpsRepo || String(repoOrRef),
|
|
41
|
+
openskillsRef: opts?.ref ? String(opts.ref) : gh?.openskillsRef || undefined
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const next = {
|
|
45
|
+
...(manifest || {}),
|
|
46
|
+
version: Number(manifest?.version || 1),
|
|
47
|
+
sources: [...sources, newSource]
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
await writeUserSourcesManifest(next);
|
|
51
|
+
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
console.log(`已添加来源:${id}`);
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
console.log(`写入:${userPath}`);
|
|
56
|
+
// eslint-disable-next-line no-console
|
|
57
|
+
console.log(`repo=${newSource.repo}`);
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.log(`openskillsRef=${newSource.openskillsRef || '(none)'}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function removeSource(id) {
|
|
63
|
+
const { manifest, sources, userPath } = await readUserSourcesManifest();
|
|
64
|
+
const before = sources.length;
|
|
65
|
+
const nextSources = sources.filter((s) => s && s.id !== id);
|
|
66
|
+
if (nextSources.length === before) {
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
68
|
+
console.log(`未找到来源:${id}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const next = { ...(manifest || {}), sources: nextSources };
|
|
72
|
+
await writeUserSourcesManifest(next);
|
|
73
|
+
// eslint-disable-next-line no-console
|
|
74
|
+
console.log(`已移除来源:${id}`);
|
|
75
|
+
// eslint-disable-next-line no-console
|
|
76
|
+
console.log(`写入:${userPath}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function setEnabled(id, enabled) {
|
|
80
|
+
const { manifest, sources, userPath } = await readUserSourcesManifest();
|
|
81
|
+
let found = false;
|
|
82
|
+
const nextSources = sources.map((s) => {
|
|
83
|
+
if (s && s.id === id) {
|
|
84
|
+
found = true;
|
|
85
|
+
return { ...s, enabled };
|
|
86
|
+
}
|
|
87
|
+
return s;
|
|
88
|
+
});
|
|
89
|
+
if (!found) {
|
|
90
|
+
// eslint-disable-next-line no-console
|
|
91
|
+
console.log(`未找到来源:${id}`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const next = { ...(manifest || {}), sources: nextSources };
|
|
95
|
+
await writeUserSourcesManifest(next);
|
|
96
|
+
// eslint-disable-next-line no-console
|
|
97
|
+
console.log(`已${enabled ? '启用' : '禁用'}来源:${id}`);
|
|
98
|
+
// eslint-disable-next-line no-console
|
|
99
|
+
console.log(`写入:${userPath}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = { listSources, addSource, removeSource, setEnabled };
|
|
103
|
+
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const os = require('os');
|
|
3
|
-
const fsp = require('fs/promises');
|
|
4
|
-
|
|
5
|
-
const { listInstalledSkills } = require('../lib/installed');
|
|
6
|
-
const { syncAgents } = require('../lib/openskills');
|
|
7
|
-
|
|
8
|
-
function resolveTargetDir({ globalInstall, universal }) {
|
|
9
|
-
const folder = universal ? '.agent/skills' : '.claude/skills';
|
|
10
|
-
return globalInstall ? path.join(os.homedir(), folder) : path.join(process.cwd(), folder);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async function uninstall(opts, skillNames) {
|
|
14
|
-
const globalInstall = !!opts?.global;
|
|
15
|
-
const universal = !!opts?.universal;
|
|
16
|
-
const targetDir = resolveTargetDir({ globalInstall, universal });
|
|
17
|
-
|
|
18
|
-
const installed = await listInstalledSkills(targetDir);
|
|
19
|
-
const installedByName = new Map(installed.map((s) => [s.name, s]));
|
|
20
|
-
|
|
21
|
-
let toRemove = Array.isArray(skillNames) ? skillNames.filter(Boolean) : [];
|
|
22
|
-
|
|
23
|
-
if (opts?.all) {
|
|
24
|
-
toRemove = installed.map((s) => s.name);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
toRemove = Array.from(new Set(toRemove)).filter((n) => installedByName.has(n));
|
|
28
|
-
|
|
29
|
-
if (toRemove.length === 0) {
|
|
30
|
-
// eslint-disable-next-line no-console
|
|
31
|
-
console.log(`未选择任何可卸载的 skill。目标目录:${targetDir}`);
|
|
32
|
-
// eslint-disable-next-line no-console
|
|
33
|
-
console.log('用法:');
|
|
34
|
-
// eslint-disable-next-line no-console
|
|
35
|
-
console.log(' - skillmanager webui --mode uninstall');
|
|
36
|
-
// eslint-disable-next-line no-console
|
|
37
|
-
console.log(' - skillmanager uninstall <skill1> <skill2>');
|
|
38
|
-
// eslint-disable-next-line no-console
|
|
39
|
-
console.log(' - skillmanager uninstall --all');
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// eslint-disable-next-line no-console
|
|
44
|
-
console.log(`将卸载 ${toRemove.length} 个 skills(目标:${targetDir})…`);
|
|
45
|
-
|
|
46
|
-
for (const name of toRemove) {
|
|
47
|
-
const entry = installedByName.get(name);
|
|
48
|
-
if (!entry) continue;
|
|
49
|
-
// eslint-disable-next-line no-console
|
|
50
|
-
console.log(`- remove ${name}`);
|
|
51
|
-
await fsp.rm(entry.skillDir, { recursive: true, force: true });
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (opts?.sync !== false) {
|
|
55
|
-
await syncAgents({ output: opts?.output, cwd: process.cwd() });
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// eslint-disable-next-line no-console
|
|
59
|
-
console.log('完成。');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
module.exports = { uninstall };
|
|
63
|
-
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const fsp = require('fs/promises');
|
|
4
|
+
|
|
5
|
+
const { listInstalledSkills } = require('../lib/installed');
|
|
6
|
+
const { syncAgents } = require('../lib/openskills');
|
|
7
|
+
|
|
8
|
+
function resolveTargetDir({ globalInstall, universal }) {
|
|
9
|
+
const folder = universal ? '.agent/skills' : '.claude/skills';
|
|
10
|
+
return globalInstall ? path.join(os.homedir(), folder) : path.join(process.cwd(), folder);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function uninstall(opts, skillNames) {
|
|
14
|
+
const globalInstall = !!opts?.global;
|
|
15
|
+
const universal = !!opts?.universal;
|
|
16
|
+
const targetDir = resolveTargetDir({ globalInstall, universal });
|
|
17
|
+
|
|
18
|
+
const installed = await listInstalledSkills(targetDir);
|
|
19
|
+
const installedByName = new Map(installed.map((s) => [s.name, s]));
|
|
20
|
+
|
|
21
|
+
let toRemove = Array.isArray(skillNames) ? skillNames.filter(Boolean) : [];
|
|
22
|
+
|
|
23
|
+
if (opts?.all) {
|
|
24
|
+
toRemove = installed.map((s) => s.name);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
toRemove = Array.from(new Set(toRemove)).filter((n) => installedByName.has(n));
|
|
28
|
+
|
|
29
|
+
if (toRemove.length === 0) {
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
31
|
+
console.log(`未选择任何可卸载的 skill。目标目录:${targetDir}`);
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.log('用法:');
|
|
34
|
+
// eslint-disable-next-line no-console
|
|
35
|
+
console.log(' - skillmanager webui --mode uninstall');
|
|
36
|
+
// eslint-disable-next-line no-console
|
|
37
|
+
console.log(' - skillmanager uninstall <skill1> <skill2>');
|
|
38
|
+
// eslint-disable-next-line no-console
|
|
39
|
+
console.log(' - skillmanager uninstall --all');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// eslint-disable-next-line no-console
|
|
44
|
+
console.log(`将卸载 ${toRemove.length} 个 skills(目标:${targetDir})…`);
|
|
45
|
+
|
|
46
|
+
for (const name of toRemove) {
|
|
47
|
+
const entry = installedByName.get(name);
|
|
48
|
+
if (!entry) continue;
|
|
49
|
+
// eslint-disable-next-line no-console
|
|
50
|
+
console.log(`- remove ${name}`);
|
|
51
|
+
await fsp.rm(entry.skillDir, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (opts?.sync !== false) {
|
|
55
|
+
await syncAgents({ output: opts?.output, cwd: process.cwd() });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.log('完成。');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { uninstall };
|
|
63
|
+
|