itismyskillmarket 1.3.1 → 1.3.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/CHANGELOG.md +155 -0
- package/dist/index.js +170 -8
- package/docs/plans/2026-04-29-weekly-update.md +57 -0
- package/package.json +1 -1
- package/src/cli.ts +43 -4
- package/src/commands/uninstall.ts +260 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,158 @@
|
|
|
1
|
+
# SkillMarket v1.3.2 更新日志
|
|
2
|
+
|
|
3
|
+
**日期**: 2026-04-30
|
|
4
|
+
**版本**: 1.3.2
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 🎉 新功能:增强卸载命令
|
|
9
|
+
|
|
10
|
+
### 1. 卸载所有 Skills (`--all`)
|
|
11
|
+
|
|
12
|
+
现在可以一键卸载所有已安装的 skills:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# 卸载所有 skills(需要确认)
|
|
16
|
+
skm uninstall --all
|
|
17
|
+
|
|
18
|
+
# 强制卸载所有,跳过确认
|
|
19
|
+
skm uninstall --all --yes
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**确认提示示例**:
|
|
23
|
+
```
|
|
24
|
+
Found 5 installed skill(s):
|
|
25
|
+
- brainstorming@1.2.0
|
|
26
|
+
- test-skill-1@1.1.0
|
|
27
|
+
- test-skill-2@1.0.0
|
|
28
|
+
- weather-time@1.0.0
|
|
29
|
+
- chinese-almanac@1.0.0
|
|
30
|
+
|
|
31
|
+
⚠️ Are you sure you want to uninstall ALL 5 skill(s)? This action cannot be undone. (y/N): _
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. 预览模式 (`--dry-run`)
|
|
35
|
+
|
|
36
|
+
新增 `--dry-run` 标志,可以预览将要删除的内容,而不实际执行删除:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 预览卸载单个 skill
|
|
40
|
+
skm uninstall brainstorming --dry-run
|
|
41
|
+
|
|
42
|
+
# 预览卸载所有 skills
|
|
43
|
+
skm uninstall --all --dry-run
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**预览输出示例**:
|
|
47
|
+
```
|
|
48
|
+
📋 Uninstall Preview for "brainstorming":
|
|
49
|
+
|
|
50
|
+
Version: 1.2.0
|
|
51
|
+
Installed: 2026-04-15T10:30:00Z
|
|
52
|
+
Platforms (from registry): OpenCode, Claude Code, VSCode
|
|
53
|
+
|
|
54
|
+
Local files to remove:
|
|
55
|
+
- ~/.skillmarket/skills/brainstorming
|
|
56
|
+
|
|
57
|
+
Platform links to remove:
|
|
58
|
+
- ~/.skillmarket/platform-links/opencode/skills/brainstorming
|
|
59
|
+
- ~/.skillmarket/platform-links/claude/skills/brainstorming
|
|
60
|
+
- ~/.skillmarket/platform-links/vscode/skills/brainstorming
|
|
61
|
+
|
|
62
|
+
⚠️ This was a dry-run. No files were actually deleted.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 3. 跳过确认 (`-y, --yes`)
|
|
66
|
+
|
|
67
|
+
新增 `-y` 或 `--yes` 选项,跳过所有确认提示:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# 强制卸载,不提示确认
|
|
71
|
+
skm uninstall brainstorming --yes
|
|
72
|
+
|
|
73
|
+
# 强制卸载所有,不提示确认
|
|
74
|
+
skm uninstall --all --yes
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 4. 改进错误处理
|
|
78
|
+
|
|
79
|
+
- 当平台卸载失败时,会询问用户是否继续清理本地文件
|
|
80
|
+
- 避免误删本地文件导致无法恢复
|
|
81
|
+
|
|
82
|
+
**错误处理示例**:
|
|
83
|
+
```
|
|
84
|
+
Uninstalling from 3 platform(s)...
|
|
85
|
+
|
|
86
|
+
OpenCode ✅ Uninstalled
|
|
87
|
+
Claude Code ❌ Failed: EPERM: operation not permitted
|
|
88
|
+
VSCode ✅ Uninstalled
|
|
89
|
+
|
|
90
|
+
⚠️ 1 platform(s) failed to uninstall. Continue with local cleanup? (y/N): _
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 5. 更新帮助文档
|
|
94
|
+
|
|
95
|
+
`skm --help` 现在包含卸载命令的完整说明:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
skm uninstall --help
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 🔧 技术实现
|
|
104
|
+
|
|
105
|
+
### 新增函数
|
|
106
|
+
|
|
107
|
+
| 函数名 | 功能 |
|
|
108
|
+
|--------|------|
|
|
109
|
+
| `uninstallAll()` | 卸载所有已安装的 skills |
|
|
110
|
+
| `askConfirmation()` | 请求用户确认(内部工具函数) |
|
|
111
|
+
| `getUninstallPreview()` | 收集卸载预览信息(内部工具函数) |
|
|
112
|
+
|
|
113
|
+
### 更新接口
|
|
114
|
+
|
|
115
|
+
**UninstallOptions** 新增字段:
|
|
116
|
+
```typescript
|
|
117
|
+
export interface UninstallOptions {
|
|
118
|
+
platforms?: string[]; // 目标平台列表
|
|
119
|
+
all?: boolean; // 卸载所有 skills
|
|
120
|
+
dryRun?: boolean; // 预览模式
|
|
121
|
+
yes?: boolean; // 跳过确认
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### CLI 参数更新
|
|
126
|
+
|
|
127
|
+
| 参数 | 说明 |
|
|
128
|
+
|------|------|
|
|
129
|
+
| `-a, --all` | 卸载所有已安装的 skills |
|
|
130
|
+
| `-d, --dry-run` | 预览模式,不实际删除 |
|
|
131
|
+
| `-y, --yes` | 跳过确认提示 |
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 📦 完整版本历史
|
|
136
|
+
|
|
137
|
+
| 版本 | 日期 | 描述 |
|
|
138
|
+
|------|------|------|
|
|
139
|
+
| 1.3.2 | 2026-04-30 | 增强卸载命令:--all, --dry-run, --yes |
|
|
140
|
+
| 1.3.1 | 2026-04-29 | Bug 修复,workflow 改进 |
|
|
141
|
+
| 1.3.0 | 2026-04-23 | 独立搜索命令,改进分页逻辑 |
|
|
142
|
+
| 1.2.6 | 2026-04-22 | 添加搜索功能(--search) |
|
|
143
|
+
| 1.2.5 | 2026-04-16 | 文档更新 |
|
|
144
|
+
| 1.2.4 | 2026-04-16 | 修复版本号硬编码问题 |
|
|
145
|
+
| 1.2.3 | 2026-04-15 | 跨平台 Skill 安装支持 |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 贡献者
|
|
150
|
+
|
|
151
|
+
- wxc2004 (wanxuchen)
|
|
152
|
+
- Sisyphus Agent
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
1
156
|
# SkillMarket v1.2.6 更新日志
|
|
2
157
|
|
|
3
158
|
**日期**: 2026-04-22
|
package/dist/index.js
CHANGED
|
@@ -787,15 +787,80 @@ async function updateSkill(skillId) {
|
|
|
787
787
|
// src/commands/uninstall.ts
|
|
788
788
|
import fs9 from "fs-extra";
|
|
789
789
|
import path8 from "path";
|
|
790
|
+
import readline from "readline";
|
|
791
|
+
async function askConfirmation(message) {
|
|
792
|
+
const rl = readline.createInterface({
|
|
793
|
+
input: process.stdin,
|
|
794
|
+
output: process.stdout
|
|
795
|
+
});
|
|
796
|
+
return new Promise((resolve2) => {
|
|
797
|
+
rl.question(`${message} (y/N): `, (answer) => {
|
|
798
|
+
rl.close();
|
|
799
|
+
resolve2(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
async function getUninstallPreview(skillId, options) {
|
|
804
|
+
const registry = await loadRegistry();
|
|
805
|
+
const skillInfo = registry.skills[skillId];
|
|
806
|
+
let platformNames = [];
|
|
807
|
+
if (options?.platforms && options.platforms.length > 0) {
|
|
808
|
+
platformNames = options.platforms;
|
|
809
|
+
} else {
|
|
810
|
+
const adapters2 = await detectPlatforms();
|
|
811
|
+
platformNames = adapters2.map((a) => a.name);
|
|
812
|
+
}
|
|
813
|
+
const skillsDir = getSkillsDir();
|
|
814
|
+
const localPath = path8.join(skillsDir, skillId);
|
|
815
|
+
const platformLinksDir = getPlatformLinksDir();
|
|
816
|
+
const platformLinks = [];
|
|
817
|
+
for (const platform of PLATFORMS) {
|
|
818
|
+
const linkPath = path8.join(platformLinksDir, platform, "skills", skillId);
|
|
819
|
+
if (await fs9.pathExists(linkPath)) {
|
|
820
|
+
platformLinks.push(linkPath);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return { skillInfo, platforms: platformNames, localPath, platformLinks };
|
|
824
|
+
}
|
|
790
825
|
async function uninstallSkill(skillId, options) {
|
|
791
826
|
const registry = await loadRegistry();
|
|
792
827
|
if (!(skillId in registry.skills)) {
|
|
793
|
-
console.log(
|
|
794
|
-
return;
|
|
828
|
+
console.log(`\u274C Skill "${skillId}" is not installed.`);
|
|
829
|
+
return false;
|
|
795
830
|
}
|
|
796
831
|
const skillInfo = registry.skills[skillId];
|
|
797
|
-
|
|
832
|
+
if (options?.dryRun) {
|
|
833
|
+
const preview = await getUninstallPreview(skillId, options);
|
|
834
|
+
console.log(`
|
|
835
|
+
\u{1F4CB} Uninstall Preview for "${skillId}":`);
|
|
836
|
+
console.log(` Version: ${skillInfo.version}`);
|
|
837
|
+
console.log(` Installed: ${skillInfo.installedAt}`);
|
|
838
|
+
console.log(` Platforms (from registry): ${preview.platforms.join(", ") || "none"}`);
|
|
839
|
+
console.log(`
|
|
840
|
+
Local files to remove:`);
|
|
841
|
+
console.log(` - ${preview.localPath}`);
|
|
842
|
+
if (preview.platformLinks.length > 0) {
|
|
843
|
+
console.log(`
|
|
844
|
+
Platform links to remove:`);
|
|
845
|
+
for (const link of preview.platformLinks) {
|
|
846
|
+
console.log(` - ${link}`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
console.log(`
|
|
850
|
+
\u26A0\uFE0F This was a dry-run. No files were actually deleted.`);
|
|
851
|
+
return true;
|
|
852
|
+
}
|
|
853
|
+
if (!options?.yes) {
|
|
854
|
+
const confirmed = await askConfirmation(`Are you sure you want to uninstall "${skillId}"?`);
|
|
855
|
+
if (!confirmed) {
|
|
856
|
+
console.log("Uninstall cancelled.");
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
console.log(`
|
|
861
|
+
Uninstalling ${skillId}@${skillInfo.version}...`);
|
|
798
862
|
let targetAdapters = [];
|
|
863
|
+
let platformUninstallErrors = [];
|
|
799
864
|
if (options?.platforms && options.platforms.length > 0) {
|
|
800
865
|
for (const platformStr of options.platforms) {
|
|
801
866
|
const platform = platformStr;
|
|
@@ -814,24 +879,99 @@ Uninstalling from ${validAdapters.length} platform(s)...
|
|
|
814
879
|
await adapter.uninstall(skillId);
|
|
815
880
|
console.log(`${adapter.name.padEnd(12)} \u2705 Uninstalled`);
|
|
816
881
|
} catch (error) {
|
|
817
|
-
|
|
882
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
883
|
+
console.log(`${adapter.name.padEnd(12)} \u274C Failed: ${errorMsg}`);
|
|
884
|
+
platformUninstallErrors.push({ name: adapter.name, error: errorMsg });
|
|
818
885
|
}
|
|
819
886
|
}
|
|
820
887
|
}
|
|
888
|
+
if (platformUninstallErrors.length > 0 && !options?.yes) {
|
|
889
|
+
const continueAnyway = await askConfirmation(
|
|
890
|
+
`\u26A0\uFE0F ${platformUninstallErrors.length} platform(s) failed to uninstall. Continue with local cleanup?`
|
|
891
|
+
);
|
|
892
|
+
if (!continueAnyway) {
|
|
893
|
+
console.log("Uninstall aborted. Platform files may still exist.");
|
|
894
|
+
return false;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
821
897
|
const skillsDir = getSkillsDir();
|
|
822
898
|
const skillDir = path8.join(skillsDir, skillId);
|
|
823
|
-
await fs9.
|
|
899
|
+
if (await fs9.pathExists(skillDir)) {
|
|
900
|
+
await fs9.remove(skillDir);
|
|
901
|
+
console.log(`\u2705 Removed local files: ${skillDir}`);
|
|
902
|
+
}
|
|
824
903
|
const platformLinksDir = getPlatformLinksDir();
|
|
904
|
+
let removedLinks = 0;
|
|
825
905
|
for (const platform of PLATFORMS) {
|
|
826
906
|
const linkPath = path8.join(platformLinksDir, platform, "skills", skillId);
|
|
827
907
|
if (await fs9.pathExists(linkPath)) {
|
|
828
908
|
await fs9.remove(linkPath);
|
|
909
|
+
removedLinks++;
|
|
829
910
|
}
|
|
830
911
|
}
|
|
912
|
+
if (removedLinks > 0) {
|
|
913
|
+
console.log(`\u2705 Removed ${removedLinks} platform link(s)`);
|
|
914
|
+
}
|
|
831
915
|
delete registry.skills[skillId];
|
|
832
916
|
await saveRegistry(registry);
|
|
917
|
+
console.log(`\u2705 Registry updated`);
|
|
833
918
|
console.log(`
|
|
834
919
|
\u2705 ${skillId} uninstalled successfully!`);
|
|
920
|
+
return true;
|
|
921
|
+
}
|
|
922
|
+
async function uninstallAll(options) {
|
|
923
|
+
const registry = await loadRegistry();
|
|
924
|
+
const installedSkills = Object.keys(registry.skills);
|
|
925
|
+
if (installedSkills.length === 0) {
|
|
926
|
+
console.log("No skills installed.");
|
|
927
|
+
return { success: 0, failed: 0 };
|
|
928
|
+
}
|
|
929
|
+
console.log(`
|
|
930
|
+
Found ${installedSkills.length} installed skill(s):`);
|
|
931
|
+
for (const skillId of installedSkills) {
|
|
932
|
+
const info = registry.skills[skillId];
|
|
933
|
+
console.log(` - ${skillId}@${info.version}`);
|
|
934
|
+
}
|
|
935
|
+
if (options?.dryRun) {
|
|
936
|
+
console.log(`
|
|
937
|
+
\u{1F4CB} Dry-run: Would uninstall ${installedSkills.length} skill(s).`);
|
|
938
|
+
console.log(`\u26A0\uFE0F No files were actually deleted.`);
|
|
939
|
+
return { success: installedSkills.length, failed: 0 };
|
|
940
|
+
}
|
|
941
|
+
if (!options?.yes) {
|
|
942
|
+
const confirmed = await askConfirmation(
|
|
943
|
+
`
|
|
944
|
+
\u26A0\uFE0F Are you sure you want to uninstall ALL ${installedSkills.length} skill(s)? This action cannot be undone.`
|
|
945
|
+
);
|
|
946
|
+
if (!confirmed) {
|
|
947
|
+
console.log("Uninstall cancelled.");
|
|
948
|
+
return { success: 0, failed: 0 };
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
console.log(`
|
|
952
|
+
Uninstalling all skills...
|
|
953
|
+
`);
|
|
954
|
+
let successCount = 0;
|
|
955
|
+
let failedCount = 0;
|
|
956
|
+
for (const skillId of installedSkills) {
|
|
957
|
+
try {
|
|
958
|
+
const success = await uninstallSkill(skillId, { ...options, yes: true });
|
|
959
|
+
if (success) {
|
|
960
|
+
successCount++;
|
|
961
|
+
} else {
|
|
962
|
+
failedCount++;
|
|
963
|
+
}
|
|
964
|
+
} catch (error) {
|
|
965
|
+
console.log(`\u274C Failed to uninstall ${skillId}: ${error}`);
|
|
966
|
+
failedCount++;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
console.log(`
|
|
970
|
+
\u{1F4CA} Summary:`);
|
|
971
|
+
console.log(` \u2705 Success: ${successCount}`);
|
|
972
|
+
console.log(` \u274C Failed: ${failedCount}`);
|
|
973
|
+
console.log(` \u{1F4E6} Total: ${installedSkills.length}`);
|
|
974
|
+
return { success: successCount, failed: failedCount };
|
|
835
975
|
}
|
|
836
976
|
|
|
837
977
|
// src/cli.ts
|
|
@@ -862,6 +1002,9 @@ Commands:
|
|
|
862
1002
|
--force Overwrite if already installed
|
|
863
1003
|
uninstall <skill> Remove an installed skill
|
|
864
1004
|
--platform Target platforms
|
|
1005
|
+
--all Uninstall ALL installed skills
|
|
1006
|
+
--dry-run Preview without deleting
|
|
1007
|
+
-y, --yes Skip confirmation
|
|
865
1008
|
update [options] Update skills
|
|
866
1009
|
--all Update all skills
|
|
867
1010
|
sync Synchronize platform links
|
|
@@ -881,8 +1024,11 @@ Examples:
|
|
|
881
1024
|
skm install brainstorming --platform opencode Install to OpenCode only
|
|
882
1025
|
skm install brainstorming --platform claude,vscode Install to multiple
|
|
883
1026
|
skm uninstall brainstorming
|
|
1027
|
+
skm uninstall --all Uninstall all skills (with confirmation)
|
|
1028
|
+
skm uninstall --all --yes Force uninstall all without confirmation
|
|
1029
|
+
skm uninstall brainstorming --dry-run Preview uninstall
|
|
884
1030
|
skm platforms Show available platforms
|
|
885
|
-
|
|
1031
|
+
`);
|
|
886
1032
|
process.exit(0);
|
|
887
1033
|
}
|
|
888
1034
|
});
|
|
@@ -919,10 +1065,26 @@ installCmd.argument("<skill>", "Skill ID to install (e.g., brainstorming or @sco
|
|
|
919
1065
|
}
|
|
920
1066
|
});
|
|
921
1067
|
var uninstallCmd = program.command("uninstall").description("Remove an installed skill from local and platform directories");
|
|
922
|
-
uninstallCmd.argument("
|
|
1068
|
+
uninstallCmd.argument("[skill]", "Skill ID to uninstall (required unless using --all)").option("-p, --platform <platforms>", "Target platforms (comma-separated)").option("-a, --all", "Uninstall ALL installed skills (requires confirmation)").option("-d, --dry-run", "Preview what would be uninstalled without actually deleting").option("-y, --yes", "Skip confirmation prompts").action(async (skill, opts) => {
|
|
923
1069
|
try {
|
|
924
1070
|
const platforms = opts.platform ? opts.platform.split(",").map((p) => p.trim()) : void 0;
|
|
925
|
-
|
|
1071
|
+
if (opts.all) {
|
|
1072
|
+
await uninstallAll({
|
|
1073
|
+
platforms,
|
|
1074
|
+
dryRun: opts.dryRun,
|
|
1075
|
+
yes: opts.yes
|
|
1076
|
+
});
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
if (!skill) {
|
|
1080
|
+
console.error("Error: Skill ID is required (or use --all to uninstall all)");
|
|
1081
|
+
process.exit(1);
|
|
1082
|
+
}
|
|
1083
|
+
await uninstallSkill(skill, {
|
|
1084
|
+
platforms,
|
|
1085
|
+
dryRun: opts.dryRun,
|
|
1086
|
+
yes: opts.yes
|
|
1087
|
+
});
|
|
926
1088
|
} catch (err) {
|
|
927
1089
|
console.error("Uninstall failed:", err);
|
|
928
1090
|
process.exit(1);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# SkillMarket 本周更新日志 (2026-04-23 ~ 2026-04-29)
|
|
2
|
+
|
|
3
|
+
## 📦 版本发布
|
|
4
|
+
|
|
5
|
+
### v1.3.1 - 2026-04-29
|
|
6
|
+
|
|
7
|
+
**新功能:**
|
|
8
|
+
- ✨ 新增独立 `skm search` 命令,支持关键词匹配搜索技能
|
|
9
|
+
- 🔍 优化搜索精度,增加本地过滤支持
|
|
10
|
+
|
|
11
|
+
**修复:**
|
|
12
|
+
- 🐛 修复 GitHub Actions workflow 中版本重复更新的问题
|
|
13
|
+
- 🔧 改进 `publish-npm.yml` 的版本判断逻辑
|
|
14
|
+
|
|
15
|
+
**发布详情:**
|
|
16
|
+
- npm: https://www.npmjs.com/package/itismyskillmarket/v/1.3.1
|
|
17
|
+
- GitHub Release: https://github.com/wxc2004/market/releases/tag/v1.3.1
|
|
18
|
+
- 对比: https://github.com/wxc2004/market/compare/v1.2.9...v1.3.1
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 📊 本周统计
|
|
23
|
+
|
|
24
|
+
| 指标 | 数据 |
|
|
25
|
+
|------|------|
|
|
26
|
+
| 新增功能 | 1 个 |
|
|
27
|
+
| Bug 修复 | 1 个 |
|
|
28
|
+
| 版本发布 | 1 个 (v1.3.1) |
|
|
29
|
+
| Commits | 2 个 |
|
|
30
|
+
| 代码行变更 | ~150 行 |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🔜 下周计划
|
|
35
|
+
|
|
36
|
+
- [ ] 增加技能评分/评论功能
|
|
37
|
+
- [ ] 支持更多 AI 编码工具平台
|
|
38
|
+
- [ ] 优化安装速度(增量更新)
|
|
39
|
+
- [ ] 增加技能依赖检查
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 📝 详细变更
|
|
44
|
+
|
|
45
|
+
### feat: add independent search command with keyword matching (1e91352)
|
|
46
|
+
- 新增 `skm search <keyword>` 命令
|
|
47
|
+
- 支持按名称、描述关键词搜索已安装和远程技能
|
|
48
|
+
- 本地缓存优先,减少网络请求
|
|
49
|
+
|
|
50
|
+
### fix: skip npm version when version unchanged in workflow (2213d4a)
|
|
51
|
+
- 修复 GitHub Actions 发布时 `npm version` 报错问题
|
|
52
|
+
- 增加版本号判断逻辑,避免重复设置
|
|
53
|
+
- 优化 CI/CD 流程稳定性
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
*生成时间: 2026-04-29*
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -47,7 +47,7 @@ import { showSkillInfo } from './commands/info.js'; // 信息命令
|
|
|
47
47
|
import { installSkill } from './commands/install.js'; // 安装命令
|
|
48
48
|
import { syncPlatformLinks } from './commands/sync.js'; // 同步命令
|
|
49
49
|
import { updateSkill } from './commands/update.js'; // 更新命令
|
|
50
|
-
import { uninstallSkill } from './commands/uninstall.js'; // 卸载命令
|
|
50
|
+
import { uninstallSkill, uninstallAll } from './commands/uninstall.js'; // 卸载命令
|
|
51
51
|
import { detectPlatforms, getAllAdapters, OpenCodeAdapter, ClaudeAdapter, VSCodeAdapter } from './adapters/index.js'; // 平台适配器
|
|
52
52
|
|
|
53
53
|
// -----------------------------------------------------------------------------
|
|
@@ -108,6 +108,9 @@ Commands:
|
|
|
108
108
|
--force Overwrite if already installed
|
|
109
109
|
uninstall <skill> Remove an installed skill
|
|
110
110
|
--platform Target platforms
|
|
111
|
+
--all Uninstall ALL installed skills
|
|
112
|
+
--dry-run Preview without deleting
|
|
113
|
+
-y, --yes Skip confirmation
|
|
111
114
|
update [options] Update skills
|
|
112
115
|
--all Update all skills
|
|
113
116
|
sync Synchronize platform links
|
|
@@ -127,8 +130,11 @@ Examples:
|
|
|
127
130
|
skm install brainstorming --platform opencode Install to OpenCode only
|
|
128
131
|
skm install brainstorming --platform claude,vscode Install to multiple
|
|
129
132
|
skm uninstall brainstorming
|
|
133
|
+
skm uninstall --all Uninstall all skills (with confirmation)
|
|
134
|
+
skm uninstall --all --yes Force uninstall all without confirmation
|
|
135
|
+
skm uninstall brainstorming --dry-run Preview uninstall
|
|
130
136
|
skm platforms Show available platforms
|
|
131
|
-
|
|
137
|
+
`);
|
|
132
138
|
process.exit(0);
|
|
133
139
|
}
|
|
134
140
|
});
|
|
@@ -264,22 +270,55 @@ installCmd
|
|
|
264
270
|
* 用法:
|
|
265
271
|
* - skm uninstall <skill> 卸载所有平台
|
|
266
272
|
* - skm uninstall <skill> --platform opencode 卸载特定平台
|
|
273
|
+
* - skm uninstall --all 卸载所有已安装的 skills
|
|
274
|
+
* - skm uninstall --dry-run 预览删除内容
|
|
275
|
+
*
|
|
276
|
+
* 新增 (v1.4.0):
|
|
277
|
+
* --all: 卸载所有已安装的 skills(需要确认)
|
|
278
|
+
* --dry-run: 预览模式,不实际删除
|
|
279
|
+
* -y, --yes: 跳过确认提示
|
|
267
280
|
*
|
|
268
281
|
* @example
|
|
269
282
|
* skm uninstall brainstorming
|
|
270
283
|
* skm uninstall brainstorming --platform claude
|
|
284
|
+
* skm uninstall --all
|
|
285
|
+
* skm uninstall --all --yes
|
|
286
|
+
* skm uninstall brainstorming --dry-run
|
|
271
287
|
*/
|
|
272
288
|
const uninstallCmd = program.command('uninstall').description('Remove an installed skill from local and platform directories');
|
|
273
289
|
uninstallCmd
|
|
274
|
-
.argument('
|
|
290
|
+
.argument('[skill]', 'Skill ID to uninstall (required unless using --all)')
|
|
275
291
|
.option('-p, --platform <platforms>', 'Target platforms (comma-separated)')
|
|
292
|
+
.option('-a, --all', 'Uninstall ALL installed skills (requires confirmation)')
|
|
293
|
+
.option('-d, --dry-run', 'Preview what would be uninstalled without actually deleting')
|
|
294
|
+
.option('-y, --yes', 'Skip confirmation prompts')
|
|
276
295
|
.action(async (skill, opts) => {
|
|
277
296
|
try {
|
|
278
297
|
const platforms = opts.platform
|
|
279
298
|
? opts.platform.split(',').map((p: string) => p.trim())
|
|
280
299
|
: undefined;
|
|
281
300
|
|
|
282
|
-
|
|
301
|
+
// 处理 --all 选项
|
|
302
|
+
if (opts.all) {
|
|
303
|
+
await uninstallAll({
|
|
304
|
+
platforms,
|
|
305
|
+
dryRun: opts.dryRun,
|
|
306
|
+
yes: opts.yes
|
|
307
|
+
});
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// skill 参数是必需的(除非使用 --all)
|
|
312
|
+
if (!skill) {
|
|
313
|
+
console.error('Error: Skill ID is required (or use --all to uninstall all)');
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
await uninstallSkill(skill, {
|
|
318
|
+
platforms,
|
|
319
|
+
dryRun: opts.dryRun,
|
|
320
|
+
yes: opts.yes
|
|
321
|
+
});
|
|
283
322
|
} catch (err) {
|
|
284
323
|
console.error('Uninstall failed:', err);
|
|
285
324
|
process.exit(1);
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
* 4. 删除各平台的软链接
|
|
13
13
|
* 5. 从注册表中移除记录
|
|
14
14
|
*
|
|
15
|
+
* 新增功能 (v1.4.0):
|
|
16
|
+
* - 支持 --all 卸载所有 skills
|
|
17
|
+
* - 支持 --dry-run 预览删除内容
|
|
18
|
+
* - 添加确认提示(--all 时强制确认)
|
|
19
|
+
* - 改进错误处理:平台卸载失败时暂停本地删除
|
|
20
|
+
*
|
|
15
21
|
* @module commands/uninstall
|
|
16
22
|
*/
|
|
17
23
|
|
|
@@ -21,12 +27,13 @@
|
|
|
21
27
|
|
|
22
28
|
import fs from 'fs-extra'; // 文件系统操作
|
|
23
29
|
import path from 'path'; // 路径处理
|
|
24
|
-
import
|
|
30
|
+
import readline from 'readline'; // 用户交互确认
|
|
31
|
+
import { loadRegistry, saveRegistry, getInstalledSkills } from './registry.js'; // 注册表操作
|
|
25
32
|
import { getSkillsDir, getPlatformLinksDir } from '../utils/dirs.js'; // 目录工具
|
|
26
33
|
import { PLATFORMS } from '../constants.js'; // 平台常量
|
|
27
34
|
import { detectPlatforms, getAdapterByPlatform } from '../adapters/index.js'; // 平台适配器
|
|
28
35
|
import type { Platform } from '../constants.js';
|
|
29
|
-
import type { PlatformAdapter } from '../types.js';
|
|
36
|
+
import type { PlatformAdapter, InstalledSkill } from '../types.js';
|
|
30
37
|
|
|
31
38
|
// -----------------------------------------------------------------------------
|
|
32
39
|
// 卸载选项接口
|
|
@@ -35,10 +42,91 @@ import type { PlatformAdapter } from '../types.js';
|
|
|
35
42
|
export interface UninstallOptions {
|
|
36
43
|
/** 目标平台列表(留空则卸载所有平台) */
|
|
37
44
|
platforms?: string[];
|
|
45
|
+
/** 卸载所有已安装的 skills */
|
|
46
|
+
all?: boolean;
|
|
47
|
+
/** 预览模式:不实际删除,只显示将要删除的内容 */
|
|
48
|
+
dryRun?: boolean;
|
|
49
|
+
/** 跳过确认提示 */
|
|
50
|
+
yes?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// -----------------------------------------------------------------------------
|
|
54
|
+
// 工具函数:用户确认提示
|
|
55
|
+
// -----------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 请求用户确认
|
|
59
|
+
*
|
|
60
|
+
* @param {string} message - 确认提示信息
|
|
61
|
+
* @returns {Promise<boolean>} 用户是否确认
|
|
62
|
+
*/
|
|
63
|
+
async function askConfirmation(message: string): Promise<boolean> {
|
|
64
|
+
const rl = readline.createInterface({
|
|
65
|
+
input: process.stdin,
|
|
66
|
+
output: process.stdout
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
rl.question(`${message} (y/N): `, (answer) => {
|
|
71
|
+
rl.close();
|
|
72
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// -----------------------------------------------------------------------------
|
|
78
|
+
// 工具函数:收集 skill 删除预览信息
|
|
79
|
+
// -----------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 收集指定 skill 的删除预览信息
|
|
83
|
+
*
|
|
84
|
+
* @param {string} skillId - Skill 标识符
|
|
85
|
+
* @param {UninstallOptions} options - 卸载选项
|
|
86
|
+
* @returns {Promise<{skillInfo: InstalledSkill, platforms: string[], localFiles: string[]}>} 预览信息
|
|
87
|
+
*/
|
|
88
|
+
async function getUninstallPreview(
|
|
89
|
+
skillId: string,
|
|
90
|
+
options?: UninstallOptions
|
|
91
|
+
): Promise<{
|
|
92
|
+
skillInfo: InstalledSkill;
|
|
93
|
+
platforms: string[];
|
|
94
|
+
localPath: string;
|
|
95
|
+
platformLinks: string[];
|
|
96
|
+
}> {
|
|
97
|
+
const registry = await loadRegistry();
|
|
98
|
+
const skillInfo = registry.skills[skillId];
|
|
99
|
+
|
|
100
|
+
// 收集目标平台
|
|
101
|
+
let platformNames: string[] = [];
|
|
102
|
+
|
|
103
|
+
if (options?.platforms && options.platforms.length > 0) {
|
|
104
|
+
platformNames = options.platforms;
|
|
105
|
+
} else {
|
|
106
|
+
const adapters = await detectPlatforms();
|
|
107
|
+
platformNames = adapters.map(a => a.name);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 本地文件路径
|
|
111
|
+
const skillsDir = getSkillsDir();
|
|
112
|
+
const localPath = path.join(skillsDir, skillId);
|
|
113
|
+
|
|
114
|
+
// 平台软链接路径
|
|
115
|
+
const platformLinksDir = getPlatformLinksDir();
|
|
116
|
+
const platformLinks: string[] = [];
|
|
117
|
+
|
|
118
|
+
for (const platform of PLATFORMS) {
|
|
119
|
+
const linkPath = path.join(platformLinksDir, platform, 'skills', skillId);
|
|
120
|
+
if (await fs.pathExists(linkPath)) {
|
|
121
|
+
platformLinks.push(linkPath);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { skillInfo, platforms: platformNames, localPath, platformLinks };
|
|
38
126
|
}
|
|
39
127
|
|
|
40
128
|
// -----------------------------------------------------------------------------
|
|
41
|
-
//
|
|
129
|
+
// 卸载单个 skill
|
|
42
130
|
// -----------------------------------------------------------------------------
|
|
43
131
|
|
|
44
132
|
/**
|
|
@@ -46,7 +134,7 @@ export interface UninstallOptions {
|
|
|
46
134
|
*
|
|
47
135
|
* @param {string} skillId - Skill 标识符
|
|
48
136
|
* @param {UninstallOptions} [options] - 卸载选项
|
|
49
|
-
* @returns {Promise<
|
|
137
|
+
* @returns {Promise<boolean>} 是否成功卸载
|
|
50
138
|
*
|
|
51
139
|
* @example
|
|
52
140
|
* // 卸载 brainstorming
|
|
@@ -58,7 +146,7 @@ export interface UninstallOptions {
|
|
|
58
146
|
export async function uninstallSkill(
|
|
59
147
|
skillId: string,
|
|
60
148
|
options?: UninstallOptions
|
|
61
|
-
): Promise<
|
|
149
|
+
): Promise<boolean> {
|
|
62
150
|
// ==========================================================================
|
|
63
151
|
// 步骤 1: 检查是否已安装
|
|
64
152
|
// ==========================================================================
|
|
@@ -67,20 +155,58 @@ export async function uninstallSkill(
|
|
|
67
155
|
|
|
68
156
|
// 检查注册表中是否存在该 skill
|
|
69
157
|
if (!(skillId in registry.skills)) {
|
|
70
|
-
console.log(
|
|
71
|
-
return;
|
|
158
|
+
console.log(`❌ Skill "${skillId}" is not installed.`);
|
|
159
|
+
return false;
|
|
72
160
|
}
|
|
73
161
|
|
|
74
162
|
// 获取 skill 信息(用于显示)
|
|
75
163
|
const skillInfo = registry.skills[skillId];
|
|
76
164
|
|
|
77
|
-
|
|
165
|
+
// ==========================================================================
|
|
166
|
+
// 步骤 2: Dry-run 模式 - 只显示预览
|
|
167
|
+
// ==========================================================================
|
|
168
|
+
|
|
169
|
+
if (options?.dryRun) {
|
|
170
|
+
const preview = await getUninstallPreview(skillId, options);
|
|
171
|
+
|
|
172
|
+
console.log(`\n📋 Uninstall Preview for "${skillId}":`);
|
|
173
|
+
console.log(` Version: ${skillInfo.version}`);
|
|
174
|
+
console.log(` Installed: ${skillInfo.installedAt}`);
|
|
175
|
+
console.log(` Platforms (from registry): ${preview.platforms.join(', ') || 'none'}`);
|
|
176
|
+
console.log(`\n Local files to remove:`);
|
|
177
|
+
console.log(` - ${preview.localPath}`);
|
|
178
|
+
|
|
179
|
+
if (preview.platformLinks.length > 0) {
|
|
180
|
+
console.log(`\n Platform links to remove:`);
|
|
181
|
+
for (const link of preview.platformLinks) {
|
|
182
|
+
console.log(` - ${link}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
console.log(`\n⚠️ This was a dry-run. No files were actually deleted.`);
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
78
189
|
|
|
79
190
|
// ==========================================================================
|
|
80
|
-
// 步骤
|
|
191
|
+
// 步骤 3: 确认提示(除非使用 --yes)
|
|
192
|
+
// ==========================================================================
|
|
193
|
+
|
|
194
|
+
if (!options?.yes) {
|
|
195
|
+
const confirmed = await askConfirmation(`Are you sure you want to uninstall "${skillId}"?`);
|
|
196
|
+
if (!confirmed) {
|
|
197
|
+
console.log('Uninstall cancelled.');
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
console.log(`\nUninstalling ${skillId}@${skillInfo.version}...`);
|
|
203
|
+
|
|
204
|
+
// ==========================================================================
|
|
205
|
+
// 步骤 4: 从平台卸载
|
|
81
206
|
// ==========================================================================
|
|
82
207
|
|
|
83
208
|
let targetAdapters: (PlatformAdapter | undefined)[] = [];
|
|
209
|
+
let platformUninstallErrors: { name: string; error: string }[] = [];
|
|
84
210
|
|
|
85
211
|
if (options?.platforms && options.platforms.length > 0) {
|
|
86
212
|
// 用户指定了平台
|
|
@@ -104,27 +230,46 @@ export async function uninstallSkill(
|
|
|
104
230
|
await adapter.uninstall(skillId);
|
|
105
231
|
console.log(`${adapter.name.padEnd(12)} ✅ Uninstalled`);
|
|
106
232
|
} catch (error) {
|
|
107
|
-
|
|
233
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
234
|
+
console.log(`${adapter.name.padEnd(12)} ❌ Failed: ${errorMsg}`);
|
|
235
|
+
platformUninstallErrors.push({ name: adapter.name, error: errorMsg });
|
|
108
236
|
}
|
|
109
237
|
}
|
|
110
238
|
}
|
|
111
239
|
|
|
112
240
|
// ==========================================================================
|
|
113
|
-
// 步骤
|
|
241
|
+
// 步骤 5: 如果有平台卸载失败,询问是否继续
|
|
242
|
+
// ==========================================================================
|
|
243
|
+
|
|
244
|
+
if (platformUninstallErrors.length > 0 && !options?.yes) {
|
|
245
|
+
const continueAnyway = await askConfirmation(
|
|
246
|
+
`⚠️ ${platformUninstallErrors.length} platform(s) failed to uninstall. Continue with local cleanup?`
|
|
247
|
+
);
|
|
248
|
+
if (!continueAnyway) {
|
|
249
|
+
console.log('Uninstall aborted. Platform files may still exist.');
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ==========================================================================
|
|
255
|
+
// 步骤 6: 删除 skill 主目录
|
|
114
256
|
// ==========================================================================
|
|
115
257
|
|
|
116
258
|
// skill 主目录包含所有版本和 latest 软链接
|
|
117
259
|
const skillsDir = getSkillsDir();
|
|
118
260
|
const skillDir = path.join(skillsDir, skillId);
|
|
119
261
|
|
|
120
|
-
|
|
121
|
-
|
|
262
|
+
if (await fs.pathExists(skillDir)) {
|
|
263
|
+
await fs.remove(skillDir);
|
|
264
|
+
console.log(`✅ Removed local files: ${skillDir}`);
|
|
265
|
+
}
|
|
122
266
|
|
|
123
267
|
// ==========================================================================
|
|
124
|
-
// 步骤
|
|
268
|
+
// 步骤 7: 删除平台软链接
|
|
125
269
|
// ==========================================================================
|
|
126
270
|
|
|
127
271
|
const platformLinksDir = getPlatformLinksDir();
|
|
272
|
+
let removedLinks = 0;
|
|
128
273
|
|
|
129
274
|
// 遍历所有平台,删除对应的软链接
|
|
130
275
|
for (const platform of PLATFORMS) {
|
|
@@ -134,11 +279,16 @@ export async function uninstallSkill(
|
|
|
134
279
|
// 如果软链接存在则删除
|
|
135
280
|
if (await fs.pathExists(linkPath)) {
|
|
136
281
|
await fs.remove(linkPath);
|
|
282
|
+
removedLinks++;
|
|
137
283
|
}
|
|
138
284
|
}
|
|
139
285
|
|
|
286
|
+
if (removedLinks > 0) {
|
|
287
|
+
console.log(`✅ Removed ${removedLinks} platform link(s)`);
|
|
288
|
+
}
|
|
289
|
+
|
|
140
290
|
// ==========================================================================
|
|
141
|
-
// 步骤
|
|
291
|
+
// 步骤 8: 更新注册表
|
|
142
292
|
// ==========================================================================
|
|
143
293
|
|
|
144
294
|
// 从注册表中删除该 skill 的记录
|
|
@@ -146,10 +296,105 @@ export async function uninstallSkill(
|
|
|
146
296
|
|
|
147
297
|
// 保存更新后的注册表
|
|
148
298
|
await saveRegistry(registry);
|
|
299
|
+
console.log(`✅ Registry updated`);
|
|
149
300
|
|
|
150
301
|
// ==========================================================================
|
|
151
302
|
// 完成
|
|
152
303
|
// ==========================================================================
|
|
153
304
|
|
|
154
305
|
console.log(`\n✅ ${skillId} uninstalled successfully!`);
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// -----------------------------------------------------------------------------
|
|
310
|
+
// 卸载所有已安装的 skills
|
|
311
|
+
// -----------------------------------------------------------------------------
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* 卸载所有已安装的 skills
|
|
315
|
+
*
|
|
316
|
+
* @param {UninstallOptions} [options] - 卸载选项
|
|
317
|
+
* @returns {Promise<{success: number, failed: number}>} 卸载结果统计
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* // 卸载所有,带确认提示
|
|
321
|
+
* await uninstallAll({ yes: false });
|
|
322
|
+
*
|
|
323
|
+
* // 强制卸载所有,跳过确认
|
|
324
|
+
* await uninstallAll({ yes: true });
|
|
325
|
+
*/
|
|
326
|
+
export async function uninstallAll(
|
|
327
|
+
options?: UninstallOptions
|
|
328
|
+
): Promise<{ success: number; failed: number }> {
|
|
329
|
+
const registry = await loadRegistry();
|
|
330
|
+
const installedSkills = Object.keys(registry.skills);
|
|
331
|
+
|
|
332
|
+
if (installedSkills.length === 0) {
|
|
333
|
+
console.log('No skills installed.');
|
|
334
|
+
return { success: 0, failed: 0 };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
console.log(`\nFound ${installedSkills.length} installed skill(s):`);
|
|
338
|
+
for (const skillId of installedSkills) {
|
|
339
|
+
const info = registry.skills[skillId];
|
|
340
|
+
console.log(` - ${skillId}@${info.version}`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ==========================================================================
|
|
344
|
+
// Dry-run 模式
|
|
345
|
+
// ==========================================================================
|
|
346
|
+
|
|
347
|
+
if (options?.dryRun) {
|
|
348
|
+
console.log(`\n📋 Dry-run: Would uninstall ${installedSkills.length} skill(s).`);
|
|
349
|
+
console.log(`⚠️ No files were actually deleted.`);
|
|
350
|
+
return { success: installedSkills.length, failed: 0 };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ==========================================================================
|
|
354
|
+
// 确认提示(--all 时强制要求确认,除非使用 --yes)
|
|
355
|
+
// ==========================================================================
|
|
356
|
+
|
|
357
|
+
if (!options?.yes) {
|
|
358
|
+
const confirmed = await askConfirmation(
|
|
359
|
+
`\n⚠️ Are you sure you want to uninstall ALL ${installedSkills.length} skill(s)? This action cannot be undone.`
|
|
360
|
+
);
|
|
361
|
+
if (!confirmed) {
|
|
362
|
+
console.log('Uninstall cancelled.');
|
|
363
|
+
return { success: 0, failed: 0 };
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// ==========================================================================
|
|
368
|
+
// 逐个卸载
|
|
369
|
+
// ==========================================================================
|
|
370
|
+
|
|
371
|
+
console.log(`\nUninstalling all skills...\n`);
|
|
372
|
+
|
|
373
|
+
let successCount = 0;
|
|
374
|
+
let failedCount = 0;
|
|
375
|
+
|
|
376
|
+
for (const skillId of installedSkills) {
|
|
377
|
+
try {
|
|
378
|
+
const success = await uninstallSkill(skillId, { ...options, yes: true }); // 已经确认过,跳过子确认
|
|
379
|
+
if (success) {
|
|
380
|
+
successCount++;
|
|
381
|
+
} else {
|
|
382
|
+
failedCount++;
|
|
383
|
+
}
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.log(`❌ Failed to uninstall ${skillId}: ${error}`);
|
|
386
|
+
failedCount++;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// ==========================================================================
|
|
391
|
+
// 完成统计
|
|
392
|
+
// ==========================================================================
|
|
393
|
+
|
|
394
|
+
console.log(`\n📊 Summary:`);
|
|
395
|
+
console.log(` ✅ Success: ${successCount}`);
|
|
396
|
+
console.log(` ❌ Failed: ${failedCount}`);
|
|
397
|
+
console.log(` 📦 Total: ${installedSkills.length}`);
|
|
398
|
+
|
|
399
|
+
return { success: successCount, failed: failedCount };
|
|
155
400
|
}
|