itismyskillmarket 1.3.10 → 1.3.12

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/dist/index.js CHANGED
@@ -103,6 +103,22 @@ async function isSkillInstalled(skillId) {
103
103
  // src/commands/npm.ts
104
104
  import https from "https";
105
105
  import { URL as URL2 } from "url";
106
+
107
+ // src/config.ts
108
+ var NPM_SCOPE = process.env.SKM_NPM_SCOPE || "@itismyskillmarket";
109
+ var NPM_SCOPE_FALLBACK = process.env.SKM_NPM_SCOPE_FALLBACK || "@wanxuchen";
110
+ var NPM_REGISTRY = process.env.SKM_NPM_REGISTRY || "https://registry.npmjs.org";
111
+ var DEFAULT_SCOPES = [
112
+ "@itismyskillmarket",
113
+ "@wanxuchen",
114
+ "@thisisskillmarket",
115
+ "@this-is-skillmarket",
116
+ "@skillmarket"
117
+ ];
118
+ var SKILL_SCOPES = process.env.SKM_NPM_SCOPES ? process.env.SKM_NPM_SCOPES.split(",").map((s) => s.trim()).filter(Boolean) : DEFAULT_SCOPES;
119
+ var SKM_URL = process.env.SKM_URL || `https://www.npmjs.com/package/${NPM_SCOPE}`;
120
+
121
+ // src/commands/npm.ts
106
122
  async function fetchNpmPackage(packageName, retries = 1) {
107
123
  for (let attempt = 0; attempt <= retries; attempt++) {
108
124
  try {
@@ -165,18 +181,6 @@ async function fetchNpmPackageOnce(packageName) {
165
181
  });
166
182
  });
167
183
  }
168
- var SKILL_SCOPES = [
169
- "@wanxuchen",
170
- // 原作者 scope
171
- "@itismyskillmarket",
172
- // 当前包名 scope
173
- "@thisisskillmarket",
174
- // 曾用 scope
175
- "@this-is-skillmarket",
176
- // 曾用 scope (带横线)
177
- "@skillmarket"
178
- // 通用 scope
179
- ];
180
184
  function getPossiblePackageNames(skillId) {
181
185
  if (skillId.startsWith("@")) {
182
186
  return [skillId];
@@ -903,7 +907,7 @@ async function syncSkill(skillId) {
903
907
  // src/commands/update.ts
904
908
  async function updateSkill(skillId) {
905
909
  if (skillId) {
906
- const pkgInfo = await fetchNpmPackage(`@itismyskillmarket/${skillId}`);
910
+ const pkgInfo = await fetchNpmPackage(`${NPM_SCOPE}/${skillId}`);
907
911
  if (pkgInfo) {
908
912
  const latestVersion = pkgInfo["dist-tags"]?.latest;
909
913
  console.log(`Updating ${skillId} to ${latestVersion}...`);
@@ -920,7 +924,7 @@ async function updateSkill(skillId) {
920
924
  `);
921
925
  let hasUpdates = false;
922
926
  for (const skill of installed) {
923
- const pkgInfo = await fetchNpmPackage(`@wanxuchen/${skill.id}`);
927
+ const pkgInfo = await fetchNpmPackage(`${NPM_SCOPE_FALLBACK}/${skill.id}`);
924
928
  if (pkgInfo) {
925
929
  const latestVersion = pkgInfo["dist-tags"]?.latest;
926
930
  if (latestVersion && latestVersion !== skill.version) {
@@ -1443,7 +1447,7 @@ async function publishSkill(skillName, options) {
1443
1447
  }
1444
1448
  console.log(`
1445
1449
  \u2705 ${skillName} published successfully!`);
1446
- console.log(` View at: https://www.npmjs.com/package/@itismyskillmarket/${skillName}`);
1450
+ console.log(` View at: ${SKM_URL}/${skillName}`);
1447
1451
  }
1448
1452
 
1449
1453
  // src/commands/verify.ts
@@ -1746,6 +1750,15 @@ API_ROUTES.GET["/api/skill-info"] = async (_req, res, url) => {
1746
1750
  jsonResponse(res, 500, { error: String(err) });
1747
1751
  }
1748
1752
  };
1753
+ API_ROUTES.GET["/api/config"] = async (_req, res, _url) => {
1754
+ jsonResponse(res, 200, {
1755
+ npmScope: NPM_SCOPE,
1756
+ npmScopeFallback: NPM_SCOPE_FALLBACK,
1757
+ npmRegistry: NPM_REGISTRY,
1758
+ skmUrl: SKM_URL,
1759
+ skillScopes: SKILL_SCOPES
1760
+ });
1761
+ };
1749
1762
  API_ROUTES.POST["/api/install"] = async (req, res, _url) => {
1750
1763
  try {
1751
1764
  const body = await parseBody(req);
@@ -1856,6 +1869,336 @@ Press Ctrl+C to stop
1856
1869
  });
1857
1870
  }
1858
1871
 
1872
+ // src/commands/admin.ts
1873
+ async function fetchScopePackages() {
1874
+ const all = /* @__PURE__ */ new Set();
1875
+ for (const scope of SKILL_SCOPES) {
1876
+ try {
1877
+ const { packages } = await searchSkillmarketPackages({ from: 0, size: 100, keyword: scope });
1878
+ for (const p of packages) {
1879
+ if (p.startsWith(scope)) all.add(p);
1880
+ }
1881
+ } catch {
1882
+ }
1883
+ }
1884
+ return [...all].sort();
1885
+ }
1886
+ async function adminList() {
1887
+ console.log("\n\u{1F50D} Fetching all published skills...\n");
1888
+ const packages = await fetchScopePackages();
1889
+ if (packages.length === 0) {
1890
+ console.log("No published skills found.");
1891
+ return;
1892
+ }
1893
+ const details = await Promise.all(
1894
+ packages.map(async (pkg) => {
1895
+ try {
1896
+ const info = await fetchNpmPackage(pkg);
1897
+ if (!info) return null;
1898
+ const ver = info["dist-tags"]?.latest || "?";
1899
+ const p = info.versions?.[ver];
1900
+ return {
1901
+ name: info.name,
1902
+ version: ver,
1903
+ description: p?.description || "",
1904
+ hasSkillmarket: !!p?.skillmarket,
1905
+ platforms: (p?.skillmarket?.platforms || []).join(", "),
1906
+ updated: info.time?.[ver] || ""
1907
+ };
1908
+ } catch {
1909
+ return null;
1910
+ }
1911
+ })
1912
+ );
1913
+ const valid = details.filter(Boolean);
1914
+ console.log(`\u{1F4E6} ${valid.length} published skill(s):
1915
+ `);
1916
+ valid.sort((a, b) => a.name.localeCompare(b.name));
1917
+ for (const d of valid) {
1918
+ const flag = d.hasSkillmarket ? "\u2705" : "\u{1F4E6}";
1919
+ console.log(` ${flag} ${d.name}@${d.version}`);
1920
+ if (d.description) console.log(` ${d.description.slice(0, 80)}`);
1921
+ if (d.platforms) console.log(` Platforms: ${d.platforms}`);
1922
+ console.log();
1923
+ }
1924
+ }
1925
+ async function adminInfo(skillId) {
1926
+ console.log(`
1927
+ \u{1F50D} Fetching detailed info for "${skillId}"...
1928
+ `);
1929
+ const info = await fetchSkillPackage(skillId);
1930
+ if (!info) {
1931
+ console.error(`\u274C Skill "${skillId}" not found in any configured scope.`);
1932
+ console.log(` Scopes checked: ${SKILL_SCOPES.join(", ")}`);
1933
+ process.exit(1);
1934
+ }
1935
+ const latestVer = info["dist-tags"]?.latest || "unknown";
1936
+ const latestPkg = info.versions?.[latestVer];
1937
+ const meta = latestPkg?.skillmarket;
1938
+ const allVersions = Object.keys(info.versions || {});
1939
+ console.log(`\u{1F4E6} ${info.name}`);
1940
+ console.log(` Description: ${info.description || "N/A"}`);
1941
+ console.log(` Latest: ${latestVer}`);
1942
+ console.log(` Total versions: ${allVersions.length}`);
1943
+ console.log(` Modified: ${info.time?.modified || "N/A"}`);
1944
+ console.log(` Author: ${info.author?.name || latestPkg?.author?.name || "N/A"}`);
1945
+ console.log(` License: ${info.license || latestPkg?.license || "N/A"}`);
1946
+ if (meta) {
1947
+ console.log(`
1948
+ \u{1F4CB} SkillMarket Metadata:`);
1949
+ if (meta.id) console.log(` ID: ${meta.id}`);
1950
+ if (meta.displayName) console.log(` Display Name: ${meta.displayName}`);
1951
+ if (meta.description) console.log(` Description: ${meta.description}`);
1952
+ if (meta.platforms && meta.platforms.length > 0) {
1953
+ console.log(` Platforms: ${meta.platforms.join(", ")}`);
1954
+ const unknown = meta.platforms.filter((p) => !PLATFORMS.includes(p));
1955
+ if (unknown.length > 0) {
1956
+ console.log(` \u26A0\uFE0F Unknown platforms: ${unknown.join(", ")}`);
1957
+ }
1958
+ }
1959
+ } else {
1960
+ console.log(`
1961
+ \u26A0\uFE0F No SkillMarket metadata (not a skillmarket skill)`);
1962
+ }
1963
+ console.log(`
1964
+ \u{1F4C5} Version History:`);
1965
+ for (const ver of allVersions.slice().reverse()) {
1966
+ const v = info.versions?.[ver];
1967
+ const time = info.time?.[ver] ? new Date(info.time[ver]).toLocaleDateString() : "?";
1968
+ const size = v?.dist?.unpackedSize ? ` (${(v.dist.unpackedSize / 1024).toFixed(1)} KB)` : "";
1969
+ const tag = ver === latestVer ? " \u2190 latest" : "";
1970
+ console.log(` ${ver}${tag} \u2014 ${time}${size}`);
1971
+ }
1972
+ const tags = info["dist-tags"] || {};
1973
+ const otherTags = Object.entries(tags).filter(([k]) => k !== "latest");
1974
+ if (otherTags.length > 0) {
1975
+ console.log(`
1976
+ \u{1F3F7}\uFE0F dist-tags:`);
1977
+ for (const [tag, ver] of otherTags) {
1978
+ console.log(` ${tag}: ${ver}`);
1979
+ }
1980
+ }
1981
+ console.log(`
1982
+ \u{1F517} Registry:`);
1983
+ console.log(` ${NPM_REGISTRY}/${info.name}`);
1984
+ console.log();
1985
+ }
1986
+ async function adminSearch(keyword, limit = 20) {
1987
+ console.log(`
1988
+ \u{1F50D} Searching published skills for "${keyword}"...
1989
+ `);
1990
+ const scopePackages = await fetchScopePackages();
1991
+ const matched = scopePackages.filter(
1992
+ (p) => p.toLowerCase().includes(keyword.toLowerCase())
1993
+ );
1994
+ if (matched.length === 0) {
1995
+ const { packages } = await searchSkillmarketPackages({ keyword });
1996
+ const filtered = packages.filter((p) => p.toLowerCase().includes(keyword.toLowerCase())).slice(0, limit);
1997
+ if (filtered.length === 0) {
1998
+ console.log(`No skills found matching "${keyword}".`);
1999
+ return;
2000
+ }
2001
+ console.log(`Found ${filtered.length} skill(s) (from npm keyword search):
2002
+ `);
2003
+ for (const pkg of filtered) {
2004
+ const info = await fetchNpmPackage(pkg);
2005
+ const ver = info?.["dist-tags"]?.latest || "?";
2006
+ const p = info?.versions?.[ver];
2007
+ console.log(` ${pkg}@${ver}`);
2008
+ if (p?.description) console.log(` ${p.description.slice(0, 80)}`);
2009
+ console.log();
2010
+ }
2011
+ return;
2012
+ }
2013
+ console.log(`Found ${matched.length} skill(s) in configured scopes:
2014
+ `);
2015
+ const show = matched.slice(0, limit);
2016
+ for (const pkg of show) {
2017
+ const info = await fetchNpmPackage(pkg);
2018
+ const ver = info?.["dist-tags"]?.latest || "?";
2019
+ const p = info?.versions?.[ver];
2020
+ console.log(` ${pkg}@${ver}`);
2021
+ if (p?.skillmarket) {
2022
+ console.log(` \u{1F4CB} ${p.skillmarket.displayName || ""} \u2014 Platforms: ${(p.skillmarket.platforms || []).join(", ") || "none"}`);
2023
+ }
2024
+ if (p?.description) console.log(` ${p.description.slice(0, 80)}`);
2025
+ console.log();
2026
+ }
2027
+ if (matched.length > limit) {
2028
+ console.log(` ... and ${matched.length - limit} more (use --limit to show more)`);
2029
+ }
2030
+ }
2031
+ async function adminStats() {
2032
+ console.log("\n\u{1F4CA} SkillMarket Publishing Statistics\n");
2033
+ const packages = await fetchScopePackages();
2034
+ if (packages.length === 0) {
2035
+ console.log("No published skills found.");
2036
+ return;
2037
+ }
2038
+ const infos = (await Promise.all(
2039
+ packages.map(async (pkg) => {
2040
+ try {
2041
+ const info = await fetchNpmPackage(pkg);
2042
+ return info ? { name: pkg, info } : null;
2043
+ } catch {
2044
+ return null;
2045
+ }
2046
+ })
2047
+ )).filter(Boolean);
2048
+ const totalSkills = infos.length;
2049
+ let totalVersions = 0;
2050
+ let totalSize = 0;
2051
+ const platformSet = /* @__PURE__ */ new Set();
2052
+ let withMetadata = 0;
2053
+ let mostVersions = { name: "", count: 0 };
2054
+ let mostRecent = { name: "", date: "" };
2055
+ for (const { name, info } of infos) {
2056
+ const versions = Object.keys(info.versions || {});
2057
+ totalVersions += versions.length;
2058
+ if (versions.length > mostVersions.count) {
2059
+ mostVersions = { name, count: versions.length };
2060
+ }
2061
+ const latestVer = info["dist-tags"]?.latest;
2062
+ const latestPkg = latestVer ? info.versions?.[latestVer] : void 0;
2063
+ const meta = latestPkg?.skillmarket;
2064
+ if (meta) {
2065
+ withMetadata++;
2066
+ if (meta.platforms) {
2067
+ for (const p of meta.platforms) platformSet.add(p);
2068
+ }
2069
+ }
2070
+ if (latestPkg?.dist?.unpackedSize) {
2071
+ totalSize += latestPkg.dist.unpackedSize;
2072
+ }
2073
+ const modTime = info.time?.modified || "";
2074
+ if (modTime && modTime > mostRecent.date) {
2075
+ mostRecent = { name, date: modTime };
2076
+ }
2077
+ }
2078
+ console.log(`\u{1F4E6} Total published skills: ${totalSkills}`);
2079
+ console.log(`\u{1F4DD} Total versions: ${totalVersions}`);
2080
+ console.log(` Avg versions/skill: ${(totalVersions / totalSkills).toFixed(1)}`);
2081
+ console.log(`\u{1F4CB} Skills with skillmarket metadata: ${withMetadata}/${totalSkills}`);
2082
+ console.log(`\u{1F4BE} Total unpacked size: ${(totalSize / 1024 / 1024).toFixed(2)} MB`);
2083
+ console.log(`\u{1F527} Platforms covered: ${platformSet.size} (${[...platformSet].join(", ")})`);
2084
+ console.log(`\u{1F3C6} Most versions: ${mostVersions.name} (${mostVersions.count})`);
2085
+ if (mostRecent.date) {
2086
+ console.log(`\u{1F550} Most recent update: ${mostRecent.name} (${new Date(mostRecent.date).toLocaleDateString()})`);
2087
+ }
2088
+ console.log(`\u{1F517} Registry: ${NPM_REGISTRY}`);
2089
+ console.log(`
2090
+ Configured scopes: ${SKILL_SCOPES.join(", ")}`);
2091
+ console.log();
2092
+ }
2093
+ async function adminVerify(skillId) {
2094
+ console.log(`
2095
+ \u{1F50D} Verifying published skill "${skillId}"...
2096
+ `);
2097
+ const info = await fetchSkillPackage(skillId);
2098
+ if (!info) {
2099
+ console.error(`\u274C Skill "${skillId}" not found.`);
2100
+ process.exit(1);
2101
+ }
2102
+ let passed = 0;
2103
+ let failed = 0;
2104
+ let warnings = 0;
2105
+ const nameValid = /^@[^/]+\/[^/]+$/.test(info.name);
2106
+ if (nameValid) {
2107
+ console.log(`\u2705 Package name format: ${info.name}`);
2108
+ passed++;
2109
+ } else {
2110
+ console.log(`\u26A0\uFE0F Package name format unusual: ${info.name}`);
2111
+ warnings++;
2112
+ }
2113
+ const tags = info["dist-tags"] || {};
2114
+ if (tags.latest) {
2115
+ console.log(`\u2705 dist-tags.latest: ${tags.latest}`);
2116
+ passed++;
2117
+ } else {
2118
+ console.log(`\u274C dist-tags.latest missing`);
2119
+ failed++;
2120
+ }
2121
+ const latestVer = tags.latest;
2122
+ const latestPkg = latestVer ? info.versions?.[latestVer] : void 0;
2123
+ if (latestPkg) {
2124
+ console.log(`\u2705 Latest version ${latestVer} exists in versions`);
2125
+ passed++;
2126
+ } else {
2127
+ console.log(`\u274C Latest version ${latestVer} not found in versions object`);
2128
+ failed++;
2129
+ }
2130
+ const meta = latestPkg?.skillmarket;
2131
+ if (meta) {
2132
+ console.log(`\u2705 Has skillmarket metadata`);
2133
+ const checks = [
2134
+ { name: "id", ok: !!meta.id },
2135
+ { name: "displayName", ok: !!meta.displayName },
2136
+ { name: "platforms (array)", ok: Array.isArray(meta.platforms) && meta.platforms.length > 0 }
2137
+ ];
2138
+ for (const c of checks) {
2139
+ if (c.ok) {
2140
+ console.log(` \u2705 skillmarket.${c.name}`);
2141
+ passed++;
2142
+ } else {
2143
+ console.log(` \u26A0\uFE0F skillmarket.${c.name} missing or empty`);
2144
+ warnings++;
2145
+ }
2146
+ }
2147
+ if (meta.platforms) {
2148
+ const unknown = meta.platforms.filter(
2149
+ (p) => !PLATFORMS.includes(p)
2150
+ );
2151
+ if (unknown.length > 0) {
2152
+ console.log(` \u26A0\uFE0F Unknown platforms: ${unknown.join(", ")}`);
2153
+ warnings++;
2154
+ } else {
2155
+ console.log(` \u2705 All platforms recognized`);
2156
+ passed++;
2157
+ }
2158
+ }
2159
+ } else {
2160
+ console.log(`\u26A0\uFE0F No skillmarket metadata (not a skillmarket-formatted skill)`);
2161
+ warnings++;
2162
+ }
2163
+ if (latestPkg?.description) {
2164
+ console.log(`\u2705 Has description (${latestPkg.description.length} chars)`);
2165
+ passed++;
2166
+ } else {
2167
+ console.log(`\u26A0\uFE0F No description`);
2168
+ warnings++;
2169
+ }
2170
+ if (info.license || latestPkg?.license) {
2171
+ console.log(`\u2705 License: ${info.license || latestPkg?.license}`);
2172
+ passed++;
2173
+ } else {
2174
+ console.log(`\u26A0\uFE0F No license specified`);
2175
+ warnings++;
2176
+ }
2177
+ if (latestPkg?.dist?.unpackedSize) {
2178
+ const sizeKB = (latestPkg.dist.unpackedSize / 1024).toFixed(1);
2179
+ console.log(`\u2705 Package size: ${sizeKB} KB (unpacked)`);
2180
+ passed++;
2181
+ }
2182
+ const versionCount = Object.keys(info.versions || {}).length;
2183
+ console.log(`\u2139\uFE0F Total versions: ${versionCount}`);
2184
+ const total = passed + failed;
2185
+ console.log(`
2186
+ \u{1F4CA} Verification Result:`);
2187
+ console.log(` \u2705 Passed: ${passed}`);
2188
+ console.log(` \u26A0\uFE0F Warnings: ${warnings}`);
2189
+ console.log(` \u274C Failed: ${failed}`);
2190
+ if (failed === 0) {
2191
+ console.log(`
2192
+ \u2705 Skill "${skillId}" is valid!
2193
+ `);
2194
+ } else {
2195
+ console.log(`
2196
+ \u26A0\uFE0F Skill "${skillId}" has issues that need attention.
2197
+ `);
2198
+ process.exitCode = 1;
2199
+ }
2200
+ }
2201
+
1859
2202
  // src/cli.ts
1860
2203
  var __filename3 = fileURLToPath4(import.meta.url);
1861
2204
  var __dirname3 = dirname2(__filename3);
@@ -2048,4 +2391,45 @@ program.command("verify <skill>").description("Verify skill integrity and format
2048
2391
  process.exit(1);
2049
2392
  }
2050
2393
  });
2394
+ var admin = program.command("admin").description("Admin: manage published skills (cloud)");
2395
+ admin.command("ls").description("List all published skills").action(async () => {
2396
+ try {
2397
+ await adminList();
2398
+ } catch (err) {
2399
+ console.error("Admin ls failed:", err);
2400
+ process.exit(1);
2401
+ }
2402
+ });
2403
+ admin.command("info <skill>").description("Show detailed info for a published skill").action(async (skill) => {
2404
+ try {
2405
+ await adminInfo(skill);
2406
+ } catch (err) {
2407
+ console.error("Admin info failed:", err);
2408
+ process.exit(1);
2409
+ }
2410
+ });
2411
+ admin.command("search <keyword>").description("Search across published skills").option("-l, --limit <number>", "Max results (default: 20)", parseInt).action(async (keyword, opts) => {
2412
+ try {
2413
+ await adminSearch(keyword, opts.limit ?? 20);
2414
+ } catch (err) {
2415
+ console.error("Admin search failed:", err);
2416
+ process.exit(1);
2417
+ }
2418
+ });
2419
+ admin.command("stats").description("Show publishing statistics").action(async () => {
2420
+ try {
2421
+ await adminStats();
2422
+ } catch (err) {
2423
+ console.error("Admin stats failed:", err);
2424
+ process.exit(1);
2425
+ }
2426
+ });
2427
+ admin.command("verify <skill>").description("Verify a published skill structure and metadata").action(async (skill) => {
2428
+ try {
2429
+ await adminVerify(skill);
2430
+ } catch (err) {
2431
+ console.error("Admin verify failed:", err);
2432
+ process.exit(1);
2433
+ }
2434
+ });
2051
2435
  program.parse();
package/gui/app.js CHANGED
@@ -68,6 +68,9 @@ function switchView(view) {
68
68
  case 'platforms':
69
69
  loadPlatforms();
70
70
  break;
71
+ case 'help':
72
+ loadHelp();
73
+ break;
71
74
  }
72
75
  }
73
76
 
@@ -284,9 +287,101 @@ function renderPlatforms(platforms, container) {
284
287
  }
285
288
 
286
289
  // -----------------------------------------------------------------------------
287
- // 操作函数
290
+ // Help 视图
288
291
  // -----------------------------------------------------------------------------
289
292
 
293
+ async function loadHelp() {
294
+ const container = document.getElementById('help-content');
295
+ container.innerHTML = '<div class="loading">Loading...</div>';
296
+
297
+ try {
298
+ const response = await fetch('/api/config');
299
+ const config = await response.json();
300
+ renderHelp(config, container);
301
+ } catch (err) {
302
+ container.innerHTML = `<div class="loading">Error: ${err.message}</div>`;
303
+ }
304
+ }
305
+
306
+ function renderHelp(config, container) {
307
+ const envVars = [
308
+ { var: 'SKM_NPM_SCOPE', default: config.npmScope, desc: '主要 npm scope,用于发布/查找 skill' },
309
+ { var: 'SKM_NPM_SCOPE_FALLBACK', default: config.npmScopeFallback, desc: '回退 scope(兼容旧安装)' },
310
+ { var: 'SKM_NPM_SCOPES', default: config.skillScopes.join(', '), desc: '搜索时尝试的 scope 列表(逗号分隔)' },
311
+ { var: 'SKM_NPM_REGISTRY', default: config.npmRegistry, desc: 'npm registry 地址' },
312
+ { var: 'SKM_URL', default: config.skmUrl, desc: '个人链接前缀(publish 输出用)' },
313
+ ];
314
+
315
+ container.innerHTML = `
316
+ <div class="help-section">
317
+ <h3>📖 GitHub Actions — Publish Skill</h3>
318
+ <p>在 GitHub 仓库页面操作:</p>
319
+ <div class="help-steps">
320
+ <div class="help-step">
321
+ <strong>发布主包(SkillMarket CLI)</strong>
322
+ <ol>
323
+ <li>GitHub → <strong>Releases</strong> → Create a new release</li>
324
+ <li>输入 Tag (如 v1.3.11),点击 Publish release</li>
325
+ <li>Action <code>Publish to npm</code> 自动触发 → 构建 → npm publish</li>
326
+ </ol>
327
+ </div>
328
+ <div class="help-step">
329
+ <strong>发布独立 Skill</strong>
330
+ <ol>
331
+ <li>GitHub → <strong>Actions</strong> → Publish Skill → Run workflow</li>
332
+ <li>输入 <code>skill_name</code>(skills/ 下的目录名)和可选 <code>version</code></li>
333
+ <li>自动执行:npm install → npm version → npm publish</li>
334
+ </ol>
335
+ <p class="help-note">⚠ 需要 GitHub Secrets 中配置 <code>NPM_TOKEN</code></p>
336
+ </div>
337
+ <div class="help-step">
338
+ <strong>本地发布</strong>
339
+ <pre>skm publish &lt;skill-name&gt;
340
+ skm publish &lt;skill-name&gt; --version 1.0.1</pre>
341
+ </div>
342
+ </div>
343
+ </div>
344
+
345
+ <div class="help-section">
346
+ <h3>⚙️ 环境变量配置</h3>
347
+ <p>设置以下环境变量可覆盖默认配置:</p>
348
+ <table class="help-table">
349
+ <thead>
350
+ <tr><th>变量</th><th>当前值</th><th>说明</th></tr>
351
+ </thead>
352
+ <tbody>
353
+ ${envVars.map(v => `
354
+ <tr>
355
+ <td><code>${v.var}</code></td>
356
+ <td><code>${v.default}</code></td>
357
+ <td>${v.desc}</td>
358
+ </tr>
359
+ `).join('')}
360
+ </tbody>
361
+ </table>
362
+ </div>
363
+
364
+ <div class="help-section">
365
+ <h3>🔧 常用命令</h3>
366
+ <table class="help-table">
367
+ <thead><tr><th>命令</th><th>说明</th></tr></thead>
368
+ <tbody>
369
+ <tr><td><code>skm ls</code></td><td>列出可用 skills</td></tr>
370
+ <tr><td><code>skm ls --installed</code></td><td>列出已安装 skills</td></tr>
371
+ <tr><td><code>skm install &lt;skill&gt;</code></td><td>安装 skill 到所有平台</td></tr>
372
+ <tr><td><code>skm install &lt;skill&gt; --platform opencode</code></td><td>安装到指定平台</td></tr>
373
+ <tr><td><code>skm uninstall &lt;skill&gt;</code></td><td>卸载 skill</td></tr>
374
+ <tr><td><code>skm update &lt;skill&gt;</code></td><td>更新 skill</td></tr>
375
+ <tr><td><code>skm update --all</code></td><td>更新所有 skills</td></tr>
376
+ <tr><td><code>skm platforms</code></td><td>查看可用平台</td></tr>
377
+ <tr><td><code>skm gui</code></td><td>启动图形界面</td></tr>
378
+ <tr><td><code>skm gui 18790</code></td><td>指定端口启动 GUI</td></tr>
379
+ </tbody>
380
+ </table>
381
+ </div>
382
+ `;
383
+ }
384
+
290
385
  async function installSkill(skillId) {
291
386
  try {
292
387
  showToast(`Installing ${skillId}...`, 'info');
package/gui/index.html CHANGED
@@ -23,9 +23,12 @@
23
23
  <button class="nav-btn" data-view="platforms">
24
24
  <span class="icon">💻</span> Platforms
25
25
  </button>
26
+ <button class="nav-btn" data-view="help">
27
+ <span class="icon">📖</span> Help
28
+ </button>
26
29
  </nav>
27
30
  <div class="sidebar-footer">
28
- <span class="version" id="gui-version">v1.3.10</span>
31
+ <span class="version" id="gui-version">v1.3.12</span>
29
32
  </div>
30
33
  </aside>
31
34
 
@@ -65,6 +68,14 @@
65
68
  </div>
66
69
  <div id="platforms-list" class="platforms-list"></div>
67
70
  </div>
71
+
72
+ <!-- Help 视图 -->
73
+ <div id="view-help" class="view">
74
+ <div class="view-header">
75
+ <h2>Help &amp; Configuration</h2>
76
+ </div>
77
+ <div id="help-content"></div>
78
+ </div>
68
79
  </main>
69
80
 
70
81
  <!-- Skill 详情模态框 -->
package/gui/style.css CHANGED
@@ -435,6 +435,105 @@ nav {
435
435
  color: var(--text-muted);
436
436
  }
437
437
 
438
+ /* -----------------------------------------------------------------------------
439
+ Help 视图
440
+ ----------------------------------------------------------------------------- */
441
+
442
+ .help-section {
443
+ background: var(--bg-secondary);
444
+ border: 1px solid var(--border-color);
445
+ border-radius: 10px;
446
+ padding: 24px;
447
+ margin-bottom: 20px;
448
+ }
449
+
450
+ .help-section h3 {
451
+ color: var(--accent);
452
+ font-size: 1.15rem;
453
+ margin-bottom: 12px;
454
+ }
455
+
456
+ .help-section > p {
457
+ color: var(--text-secondary);
458
+ font-size: 0.9rem;
459
+ margin-bottom: 16px;
460
+ }
461
+
462
+ .help-steps {
463
+ display: flex;
464
+ flex-direction: column;
465
+ gap: 16px;
466
+ }
467
+
468
+ .help-step {
469
+ background: var(--bg-card);
470
+ border-radius: 8px;
471
+ padding: 16px;
472
+ }
473
+
474
+ .help-step > strong {
475
+ color: var(--text-secondary);
476
+ font-size: 0.95rem;
477
+ display: block;
478
+ margin-bottom: 8px;
479
+ }
480
+
481
+ .help-step ol {
482
+ margin: 0;
483
+ padding-left: 20px;
484
+ color: var(--text-secondary);
485
+ font-size: 0.9rem;
486
+ line-height: 1.8;
487
+ }
488
+
489
+ .help-step pre {
490
+ background: var(--bg-primary);
491
+ color: var(--text-secondary);
492
+ padding: 12px;
493
+ border-radius: 6px;
494
+ font-size: 0.85rem;
495
+ overflow-x: auto;
496
+ margin: 8px 0 0;
497
+ }
498
+
499
+ .help-note {
500
+ color: #ffcc00;
501
+ font-size: 0.85rem;
502
+ margin-top: 8px;
503
+ }
504
+
505
+ .help-table {
506
+ width: 100%;
507
+ border-collapse: collapse;
508
+ font-size: 0.9rem;
509
+ }
510
+
511
+ .help-table th {
512
+ text-align: left;
513
+ padding: 10px 12px;
514
+ background: var(--bg-card);
515
+ color: var(--text-secondary);
516
+ border-bottom: 2px solid var(--border-color);
517
+ }
518
+
519
+ .help-table td {
520
+ padding: 10px 12px;
521
+ color: var(--text-secondary);
522
+ border-bottom: 1px solid var(--border-color);
523
+ vertical-align: top;
524
+ }
525
+
526
+ .help-table code {
527
+ background: var(--bg-card);
528
+ padding: 2px 6px;
529
+ border-radius: 3px;
530
+ font-size: 0.85rem;
531
+ }
532
+
533
+ .help-table tr:hover td {
534
+ background: var(--bg-hover);
535
+ }
536
+
438
537
  /* -----------------------------------------------------------------------------
439
538
  滚动条
440
539
  ----------------------------------------------------------------------------- */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itismyskillmarket",
3
- "version": "1.3.10",
3
+ "version": "1.3.12",
4
4
  "description": "Cross-platform skill manager for AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {