itismyskillmarket 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +294 -0
- package/dist/index.js +452 -14
- package/docs/plans/2026-04-29-weekly-update.md +57 -0
- package/package.json +1 -1
- package/src/cli.ts +67 -11
- package/src/commands/github-install.ts +538 -0
- package/src/commands/uninstall.ts +260 -15
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,365 @@ 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 };
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// src/commands/github-install.ts
|
|
978
|
+
import fs10 from "fs-extra";
|
|
979
|
+
import path9 from "path";
|
|
980
|
+
var GITHUB_URL_PATTERNS = [
|
|
981
|
+
/^https?:\/\/github\.com\/([^/]+)\/([^/]+)(?:\/tree\/([^/]+)(?:\/(.+))?)?$/,
|
|
982
|
+
/^([^/]+)\/([^/]+)(?:#(.+))?$/,
|
|
983
|
+
// owner/repo#branch
|
|
984
|
+
/^([^/]+)\/([^/]+)@(.+)$/
|
|
985
|
+
// owner/repo@commit
|
|
986
|
+
];
|
|
987
|
+
function parseGitHubUrl(input) {
|
|
988
|
+
input = input.replace(/\/$/, "");
|
|
989
|
+
for (const pattern of GITHUB_URL_PATTERNS) {
|
|
990
|
+
const match = input.match(pattern);
|
|
991
|
+
if (match) {
|
|
992
|
+
const owner = match[1];
|
|
993
|
+
const repo = match[2].replace(/\.git$/, "");
|
|
994
|
+
const branch = match[3] || "main";
|
|
995
|
+
const commitOrPath = match[4] || match[3];
|
|
996
|
+
const path10 = match[5] || void 0;
|
|
997
|
+
return {
|
|
998
|
+
owner,
|
|
999
|
+
repo,
|
|
1000
|
+
branch: commitOrPath && !commitOrPath.includes("/") ? commitOrPath : branch,
|
|
1001
|
+
commit: commitOrPath?.match(/^[0-9a-f]{40}$/) ? commitOrPath : void 0,
|
|
1002
|
+
path: path10
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
async function fetchGitHubFile(source, filePath) {
|
|
1009
|
+
const ref = source.commit || source.branch || "main";
|
|
1010
|
+
const url = `https://api.github.com/repos/${source.owner}/${source.repo}/contents/${filePath}?ref=${ref}`;
|
|
1011
|
+
try {
|
|
1012
|
+
const response = await fetch(url, {
|
|
1013
|
+
headers: {
|
|
1014
|
+
"Accept": "application/vnd.github.v3.raw",
|
|
1015
|
+
"User-Agent": "SkillMarket"
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
if (!response.ok) {
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
return await response.text();
|
|
1022
|
+
} catch {
|
|
1023
|
+
return null;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
async function fetchGitHubDir(source, dirPath = "") {
|
|
1027
|
+
const ref = source.commit || source.branch || "main";
|
|
1028
|
+
const url = `https://api.github.com/repos/${source.owner}/${source.repo}/contents/${dirPath}?ref=${ref}`;
|
|
1029
|
+
try {
|
|
1030
|
+
const response = await fetch(url, {
|
|
1031
|
+
headers: {
|
|
1032
|
+
"Accept": "application/vnd.github.v3+json",
|
|
1033
|
+
"User-Agent": "SkillMarket"
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
if (!response.ok) {
|
|
1037
|
+
return [];
|
|
1038
|
+
}
|
|
1039
|
+
const data = await response.json();
|
|
1040
|
+
return Array.isArray(data) ? data : [];
|
|
1041
|
+
} catch {
|
|
1042
|
+
return [];
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
async function detectSkillFromGitHub(source) {
|
|
1046
|
+
const result = {
|
|
1047
|
+
hasSkillMd: false,
|
|
1048
|
+
hasPackageJson: false,
|
|
1049
|
+
hasMetadataJson: false,
|
|
1050
|
+
platforms: [],
|
|
1051
|
+
files: []
|
|
1052
|
+
};
|
|
1053
|
+
const basePath = source.path || "";
|
|
1054
|
+
const skillMdPath = basePath ? `${basePath}/SKILL.md` : "SKILL.md";
|
|
1055
|
+
const skillMdContent = await fetchGitHubFile(source, skillMdPath);
|
|
1056
|
+
if (skillMdContent !== null) {
|
|
1057
|
+
result.hasSkillMd = true;
|
|
1058
|
+
result.files.push(skillMdPath);
|
|
1059
|
+
}
|
|
1060
|
+
const packageJsonPath = basePath ? `${basePath}/package.json` : "package.json";
|
|
1061
|
+
const packageJsonContent = await fetchGitHubFile(source, packageJsonPath);
|
|
1062
|
+
if (packageJsonContent !== null) {
|
|
1063
|
+
result.hasPackageJson = true;
|
|
1064
|
+
result.files.push(packageJsonPath);
|
|
1065
|
+
try {
|
|
1066
|
+
const pkg = JSON.parse(packageJsonContent);
|
|
1067
|
+
result.skillId = pkg.name?.split("/").pop() || pkg.skillmarket?.id;
|
|
1068
|
+
result.displayName = pkg.skillmarket?.displayName;
|
|
1069
|
+
result.description = pkg.description;
|
|
1070
|
+
if (pkg.skillmarket?.platforms) {
|
|
1071
|
+
result.platforms = pkg.skillmarket.platforms;
|
|
1072
|
+
}
|
|
1073
|
+
} catch {
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
const metadataPath = basePath ? `${basePath}/metadata.json` : "metadata.json";
|
|
1077
|
+
const metadataContent = await fetchGitHubFile(source, metadataPath);
|
|
1078
|
+
if (metadataContent !== null) {
|
|
1079
|
+
result.hasMetadataJson = true;
|
|
1080
|
+
result.files.push(metadataPath);
|
|
1081
|
+
}
|
|
1082
|
+
const platformDirs = ["opencode", "cursor", "vscode", "claude", "codex", "antigravity"];
|
|
1083
|
+
for (const dir of platformDirs) {
|
|
1084
|
+
const dirPath = basePath ? `${basePath}/${dir}` : dir;
|
|
1085
|
+
const files = await fetchGitHubDir(source, dirPath);
|
|
1086
|
+
if (files.length > 0) {
|
|
1087
|
+
const platform = dir;
|
|
1088
|
+
if (!result.platforms.includes(platform)) {
|
|
1089
|
+
result.platforms.push(platform);
|
|
1090
|
+
}
|
|
1091
|
+
result.files.push(dirPath);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
if (result.platforms.length === 0) {
|
|
1095
|
+
result.platforms.push("opencode");
|
|
1096
|
+
}
|
|
1097
|
+
return result;
|
|
1098
|
+
}
|
|
1099
|
+
async function generatePlatformAdapters(skillId, existingPlatforms, targetPlatforms, sourceDir) {
|
|
1100
|
+
const skillsDir = getSkillsDir();
|
|
1101
|
+
const skillVersionDir = path9.join(skillsDir, `${skillId}@github`);
|
|
1102
|
+
for (const platform of targetPlatforms) {
|
|
1103
|
+
if (existingPlatforms.includes(platform)) {
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
const platformDir = path9.join(skillVersionDir, platform);
|
|
1107
|
+
await fs10.ensureDir(platformDir);
|
|
1108
|
+
const sourceSkillMd = path9.join(sourceDir, "SKILL.md");
|
|
1109
|
+
const targetSkillMd = path9.join(platformDir, "SKILL.md");
|
|
1110
|
+
if (await fs10.pathExists(sourceSkillMd)) {
|
|
1111
|
+
await fs10.copy(sourceSkillMd, targetSkillMd);
|
|
1112
|
+
}
|
|
1113
|
+
if (platform === "opencode" || platform === "cursor" || platform === "codex" || platform === "antigravity") {
|
|
1114
|
+
} else if (platform === "vscode") {
|
|
1115
|
+
const skillJson = {
|
|
1116
|
+
name: skillId,
|
|
1117
|
+
description: `Skill: ${skillId}`,
|
|
1118
|
+
version: "1.0.0"
|
|
1119
|
+
};
|
|
1120
|
+
await fs10.writeJson(path9.join(platformDir, "skill.json"), skillJson, { spaces: 2 });
|
|
1121
|
+
} else if (platform === "claude") {
|
|
1122
|
+
const skillJson = {
|
|
1123
|
+
name: skillId,
|
|
1124
|
+
description: `Skill: ${skillId}`,
|
|
1125
|
+
version: "1.0.0"
|
|
1126
|
+
};
|
|
1127
|
+
await fs10.writeJson(path9.join(platformDir, "skill.json"), skillJson, { spaces: 2 });
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
async function installFromGitHub(input, options) {
|
|
1132
|
+
let source = parseGitHubUrl(input);
|
|
1133
|
+
if (!source) {
|
|
1134
|
+
throw new Error(`Invalid GitHub URL or format: ${input}`);
|
|
1135
|
+
}
|
|
1136
|
+
if (options?.branch) source.branch = options.branch;
|
|
1137
|
+
if (options?.commit) source.commit = options.commit;
|
|
1138
|
+
console.log(`Installing from GitHub: ${source.owner}/${source.repo}`);
|
|
1139
|
+
if (source.branch) console.log(` Branch: ${source.branch}`);
|
|
1140
|
+
if (source.commit) console.log(` Commit: ${source.commit}`);
|
|
1141
|
+
if (source.path) console.log(` Path: ${source.path}`);
|
|
1142
|
+
console.log("\nDetecting skill...");
|
|
1143
|
+
const detected = await detectSkillFromGitHub(source);
|
|
1144
|
+
if (!detected.hasSkillMd && !detected.hasPackageJson) {
|
|
1145
|
+
throw new Error("No skill found in this repository (missing SKILL.md and package.json)");
|
|
1146
|
+
}
|
|
1147
|
+
console.log(` SKILL.md: ${detected.hasSkillMd ? "\u2705" : "\u274C"}`);
|
|
1148
|
+
console.log(` package.json: ${detected.hasPackageJson ? "\u2705" : "\u274C"}`);
|
|
1149
|
+
console.log(` Detected platforms: ${detected.platforms.join(", ") || "none"}`);
|
|
1150
|
+
const skillId = detected.skillId || source.repo;
|
|
1151
|
+
const version = `github-${source.commit?.substring(0, 7) || source.branch || "main"}`;
|
|
1152
|
+
console.log(`
|
|
1153
|
+
Setting up skill: ${skillId}@${version}`);
|
|
1154
|
+
await ensureMarketDirs();
|
|
1155
|
+
const skillsDir = getSkillsDir();
|
|
1156
|
+
const skillVersionDir = path9.join(skillsDir, `${skillId}@${version}`);
|
|
1157
|
+
await fs10.ensureDir(skillVersionDir);
|
|
1158
|
+
console.log("Downloading files...");
|
|
1159
|
+
const basePath = source.path || "";
|
|
1160
|
+
const skillMdPath = basePath ? `${basePath}/SKILL.md` : "SKILL.md";
|
|
1161
|
+
const skillMdContent = await fetchGitHubFile(source, skillMdPath);
|
|
1162
|
+
if (skillMdContent) {
|
|
1163
|
+
await fs10.writeFile(path9.join(skillVersionDir, "SKILL.md"), skillMdContent);
|
|
1164
|
+
console.log(" \u2705 SKILL.md");
|
|
1165
|
+
}
|
|
1166
|
+
const packageJsonPath = basePath ? `${basePath}/package.json` : "package.json";
|
|
1167
|
+
const packageJsonContent = await fetchGitHubFile(source, packageJsonPath);
|
|
1168
|
+
if (packageJsonContent) {
|
|
1169
|
+
await fs10.writeFile(path9.join(skillVersionDir, "package.json"), packageJsonContent);
|
|
1170
|
+
console.log(" \u2705 package.json");
|
|
1171
|
+
}
|
|
1172
|
+
const metadataPath = basePath ? `${basePath}/metadata.json` : "metadata.json";
|
|
1173
|
+
const metadataContent = await fetchGitHubFile(source, metadataPath);
|
|
1174
|
+
if (metadataContent) {
|
|
1175
|
+
await fs10.writeFile(path9.join(skillVersionDir, "metadata.json"), metadataContent);
|
|
1176
|
+
console.log(" \u2705 metadata.json");
|
|
1177
|
+
}
|
|
1178
|
+
const targetPlatforms = options?.platforms || detected.platforms;
|
|
1179
|
+
const existingPlatforms = detected.platforms;
|
|
1180
|
+
const missingPlatforms = targetPlatforms.filter((p) => !existingPlatforms.includes(p));
|
|
1181
|
+
if (missingPlatforms.length > 0) {
|
|
1182
|
+
console.log(`
|
|
1183
|
+
Generating adapters for missing platforms: ${missingPlatforms.join(", ")}`);
|
|
1184
|
+
await generatePlatformAdapters(skillId, existingPlatforms, targetPlatforms, skillVersionDir);
|
|
1185
|
+
console.log(" \u2705 Platform adapters generated");
|
|
1186
|
+
}
|
|
1187
|
+
const skillDir = path9.join(skillsDir, skillId);
|
|
1188
|
+
await fs10.ensureDir(skillDir);
|
|
1189
|
+
const latestLink = path9.join(skillDir, "latest");
|
|
1190
|
+
try {
|
|
1191
|
+
await fs10.remove(latestLink);
|
|
1192
|
+
await fs10.symlink(skillVersionDir, latestLink, "junction");
|
|
1193
|
+
} catch {
|
|
1194
|
+
await fs10.copy(skillVersionDir, path9.join(skillDir, "latest"), { overwrite: true });
|
|
1195
|
+
}
|
|
1196
|
+
console.log(`
|
|
1197
|
+
Installing to ${targetPlatforms.length} platform(s)...
|
|
1198
|
+
`);
|
|
1199
|
+
const results = [];
|
|
1200
|
+
for (const platform of targetPlatforms) {
|
|
1201
|
+
const adapter = getAdapterByPlatform(platform);
|
|
1202
|
+
if (!adapter) {
|
|
1203
|
+
console.log(`${platform.padEnd(12)} \u274C Unknown platform`);
|
|
1204
|
+
results.push({ name: platform, status: "failed", error: "Unknown platform" });
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
try {
|
|
1208
|
+
const isInstalled = await adapter.isInstalled(skillId);
|
|
1209
|
+
if (isInstalled && !options?.force) {
|
|
1210
|
+
console.log(`${adapter.name.padEnd(12)} \u26A0\uFE0F Already installed (use --force to overwrite)`);
|
|
1211
|
+
results.push({ name: adapter.name, status: "skipped" });
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
await adapter.install(skillId, skillVersionDir);
|
|
1215
|
+
console.log(`${adapter.name.padEnd(12)} \u2705 Installed successfully`);
|
|
1216
|
+
results.push({ name: adapter.name, status: "installed" });
|
|
1217
|
+
} catch (error) {
|
|
1218
|
+
console.log(`${adapter.name.padEnd(12)} \u274C Failed: ${error}`);
|
|
1219
|
+
results.push({ name: adapter.name, status: "failed", error: String(error) });
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
const installed = results.filter((r) => r.status === "installed").length;
|
|
1223
|
+
const skipped = results.filter((r) => r.status === "skipped").length;
|
|
1224
|
+
const failed = results.filter((r) => r.status === "failed").length;
|
|
1225
|
+
console.log(`
|
|
1226
|
+
\u{1F4CA} Summary: ${installed} installed, ${skipped} skipped, ${failed} failed`);
|
|
1227
|
+
console.log(`
|
|
1228
|
+
Installing to platforms: ${targetPlatforms.join(", ")}`);
|
|
1229
|
+
console.log(" (Platform installation logic needs to be completed)");
|
|
1230
|
+
const registry = await loadRegistry();
|
|
1231
|
+
registry.skills[skillId] = {
|
|
1232
|
+
id: skillId,
|
|
1233
|
+
version,
|
|
1234
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1235
|
+
platforms: targetPlatforms
|
|
1236
|
+
};
|
|
1237
|
+
await saveRegistry(registry);
|
|
1238
|
+
console.log(`
|
|
1239
|
+
\u2705 ${skillId}@${version} installed successfully from GitHub!`);
|
|
1240
|
+
console.log(` Source: ${source.owner}/${source.repo}`);
|
|
835
1241
|
}
|
|
836
1242
|
|
|
837
1243
|
// src/cli.ts
|
|
@@ -862,6 +1268,9 @@ Commands:
|
|
|
862
1268
|
--force Overwrite if already installed
|
|
863
1269
|
uninstall <skill> Remove an installed skill
|
|
864
1270
|
--platform Target platforms
|
|
1271
|
+
--all Uninstall ALL installed skills
|
|
1272
|
+
--dry-run Preview without deleting
|
|
1273
|
+
-y, --yes Skip confirmation
|
|
865
1274
|
update [options] Update skills
|
|
866
1275
|
--all Update all skills
|
|
867
1276
|
sync Synchronize platform links
|
|
@@ -881,8 +1290,11 @@ Examples:
|
|
|
881
1290
|
skm install brainstorming --platform opencode Install to OpenCode only
|
|
882
1291
|
skm install brainstorming --platform claude,vscode Install to multiple
|
|
883
1292
|
skm uninstall brainstorming
|
|
1293
|
+
skm uninstall --all Uninstall all skills (with confirmation)
|
|
1294
|
+
skm uninstall --all --yes Force uninstall all without confirmation
|
|
1295
|
+
skm uninstall brainstorming --dry-run Preview uninstall
|
|
884
1296
|
skm platforms Show available platforms
|
|
885
|
-
|
|
1297
|
+
`);
|
|
886
1298
|
process.exit(0);
|
|
887
1299
|
}
|
|
888
1300
|
});
|
|
@@ -905,24 +1317,50 @@ var infoCmd = program.command("info").description("Display skill information");
|
|
|
905
1317
|
infoCmd.argument("<skill-id>", "Skill ID to show info").action((skillId) => {
|
|
906
1318
|
showSkillInfo(skillId);
|
|
907
1319
|
});
|
|
908
|
-
var installCmd = program.command("install").description("Install a skill
|
|
909
|
-
installCmd.argument("<skill>", "Skill ID
|
|
1320
|
+
var installCmd = program.command("install").description("Install a skill from npm or GitHub");
|
|
1321
|
+
installCmd.argument("<skill>", "Skill ID, npm package, or GitHub URL (owner/repo, https://github.com/owner/repo)").option("-p, --platform <platforms>", "Target platforms (comma-separated: opencode,claude,vscode)").option("-f, --force", "Overwrite if already installed").option("-v, --version <version>", "Specific version to install (npm only)").option("-b, --branch <branch>", "GitHub branch to install from").option("-c, --commit <commit>", "GitHub commit hash to install from").action(async (skill, opts) => {
|
|
910
1322
|
try {
|
|
911
1323
|
const platforms = opts.platform ? opts.platform.split(",").map((p) => p.trim()) : void 0;
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1324
|
+
const githubSource = parseGitHubUrl(skill);
|
|
1325
|
+
if (githubSource) {
|
|
1326
|
+
await installFromGitHub(skill, {
|
|
1327
|
+
platforms,
|
|
1328
|
+
force: opts.force,
|
|
1329
|
+
branch: opts.branch,
|
|
1330
|
+
commit: opts.commit
|
|
1331
|
+
});
|
|
1332
|
+
} else {
|
|
1333
|
+
await installSkill(skill, opts.version, {
|
|
1334
|
+
platforms,
|
|
1335
|
+
force: opts.force
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
916
1338
|
} catch (err) {
|
|
917
1339
|
console.error("Installation failed:", err);
|
|
918
1340
|
process.exit(1);
|
|
919
1341
|
}
|
|
920
1342
|
});
|
|
921
1343
|
var uninstallCmd = program.command("uninstall").description("Remove an installed skill from local and platform directories");
|
|
922
|
-
uninstallCmd.argument("
|
|
1344
|
+
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
1345
|
try {
|
|
924
1346
|
const platforms = opts.platform ? opts.platform.split(",").map((p) => p.trim()) : void 0;
|
|
925
|
-
|
|
1347
|
+
if (opts.all) {
|
|
1348
|
+
await uninstallAll({
|
|
1349
|
+
platforms,
|
|
1350
|
+
dryRun: opts.dryRun,
|
|
1351
|
+
yes: opts.yes
|
|
1352
|
+
});
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
if (!skill) {
|
|
1356
|
+
console.error("Error: Skill ID is required (or use --all to uninstall all)");
|
|
1357
|
+
process.exit(1);
|
|
1358
|
+
}
|
|
1359
|
+
await uninstallSkill(skill, {
|
|
1360
|
+
platforms,
|
|
1361
|
+
dryRun: opts.dryRun,
|
|
1362
|
+
yes: opts.yes
|
|
1363
|
+
});
|
|
926
1364
|
} catch (err) {
|
|
927
1365
|
console.error("Uninstall failed:", err);
|
|
928
1366
|
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*
|