skillverse 0.1.5 → 0.1.8

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/bin.js CHANGED
@@ -785,6 +785,32 @@ var init_skillService = __esm({
785
785
  });
786
786
  }
787
787
  }
788
+ /**
789
+ * Sync skills between filesystem and database:
790
+ * 1. Remove orphaned database records (skills in DB but not on disk)
791
+ * 2. Import new skills from disk (skills on disk but not in DB)
792
+ */
793
+ async syncSkillsWithFileSystem() {
794
+ const { skills: skillsDir } = getPaths();
795
+ const dbSkills = await prisma.skill.findMany();
796
+ let removedCount = 0;
797
+ for (const skill of dbSkills) {
798
+ if (!existsSync3(skill.storagePath)) {
799
+ console.log(`\u{1F5D1}\uFE0F Removing orphaned skill from database: ${skill.name} (path: ${skill.storagePath})`);
800
+ try {
801
+ await prisma.skill.delete({ where: { id: skill.id } });
802
+ removedCount++;
803
+ } catch (error) {
804
+ console.error(`Failed to remove orphaned skill ${skill.name}:`, error);
805
+ }
806
+ }
807
+ }
808
+ if (removedCount > 0) {
809
+ console.log(`\u2705 Removed ${removedCount} orphaned skill(s) from database.`);
810
+ }
811
+ const importedCount = await this.scanForSkills();
812
+ return { removedCount, importedCount: importedCount || 0 };
813
+ }
788
814
  async scanForSkills() {
789
815
  const { skills: skillsDir } = getPaths();
790
816
  console.log(`Scanning for skills in ${skillsDir}...`);
@@ -1151,7 +1177,7 @@ ${relativeSkillsPath}/
1151
1177
  if (!existsSync4(skillsPath)) {
1152
1178
  return [];
1153
1179
  }
1154
- const { readdir: readdir2, stat: stat2, readFile: readFile3 } = await import("fs/promises");
1180
+ const { readdir: readdir2, stat: stat2, readFile: readFile4 } = await import("fs/promises");
1155
1181
  const items = await readdir2(skillsPath);
1156
1182
  const existingSkills = [];
1157
1183
  for (const item of items) {
@@ -1192,7 +1218,7 @@ ${relativeSkillsPath}/
1192
1218
  if (!existsSync4(SKILLS_DIR)) {
1193
1219
  await mkdir2(SKILLS_DIR, { recursive: true });
1194
1220
  }
1195
- const { rename, readFile: readFile3, cp: cp2 } = await import("fs/promises");
1221
+ const { rename, readFile: readFile4, cp: cp2 } = await import("fs/promises");
1196
1222
  const migrated = [];
1197
1223
  const errors = [];
1198
1224
  for (const skillName of skillNames) {
@@ -1225,7 +1251,7 @@ ${relativeSkillsPath}/
1225
1251
  if (existsSync4(skillMdPath)) {
1226
1252
  try {
1227
1253
  const matter2 = await import("gray-matter");
1228
- const fileContent = await readFile3(skillMdPath, "utf-8");
1254
+ const fileContent = await readFile4(skillMdPath, "utf-8");
1229
1255
  const parsed = matter2.default(fileContent);
1230
1256
  description = parsed.data.description || "";
1231
1257
  metadata = parsed.data;
@@ -1320,6 +1346,18 @@ var init_skills = __esm({
1320
1346
  next(error);
1321
1347
  }
1322
1348
  });
1349
+ router.post("/sync", async (req, res, next) => {
1350
+ try {
1351
+ const result = await skillService.syncSkillsWithFileSystem();
1352
+ res.json({
1353
+ success: true,
1354
+ data: result,
1355
+ message: `Synced: removed ${result.removedCount} orphan(s), imported ${result.importedCount} new skill(s)`
1356
+ });
1357
+ } catch (error) {
1358
+ next(error);
1359
+ }
1360
+ });
1323
1361
  router.get("/:id", async (req, res, next) => {
1324
1362
  try {
1325
1363
  const skill = await skillService.getSkillById(req.params.id);
@@ -1496,7 +1534,7 @@ var init_skills = __esm({
1496
1534
  });
1497
1535
  router.get("/:id/skill-md", async (req, res, next) => {
1498
1536
  try {
1499
- const { readFile: readFile3 } = await import("fs/promises");
1537
+ const { readFile: readFile4 } = await import("fs/promises");
1500
1538
  const skill = await skillService.getSkillById(req.params.id);
1501
1539
  const skillMdPath = join5(skill.storagePath, "SKILL.md");
1502
1540
  if (!existsSync5(skillMdPath)) {
@@ -1508,7 +1546,7 @@ var init_skills = __esm({
1508
1546
  }
1509
1547
  });
1510
1548
  }
1511
- const content = await readFile3(skillMdPath, "utf-8");
1549
+ const content = await readFile4(skillMdPath, "utf-8");
1512
1550
  res.json({
1513
1551
  success: true,
1514
1552
  data: {
@@ -1766,52 +1804,300 @@ var init_bundleService = __esm({
1766
1804
  }
1767
1805
  });
1768
1806
 
1807
+ // src/services/githubMarketplaceRegistry.ts
1808
+ import { Buffer as Buffer2 } from "buffer";
1809
+ import { createHash } from "crypto";
1810
+ import { readFile as readFile3 } from "fs/promises";
1811
+ function sha256Hex(buf) {
1812
+ return createHash("sha256").update(buf).digest("hex");
1813
+ }
1814
+ function nowIso() {
1815
+ return (/* @__PURE__ */ new Date()).toISOString();
1816
+ }
1817
+ function normalizeIndex(index) {
1818
+ const base = {
1819
+ version: typeof index?.version === "number" ? index.version : 1,
1820
+ updatedAt: typeof index?.updatedAt === "string" ? index.updatedAt : nowIso(),
1821
+ skills: Array.isArray(index?.skills) ? index.skills : []
1822
+ };
1823
+ return base;
1824
+ }
1825
+ async function requireOctokit() {
1826
+ const mod = await import("octokit");
1827
+ return mod.Octokit;
1828
+ }
1829
+ var GitHubMarketplaceRegistry;
1830
+ var init_githubMarketplaceRegistry = __esm({
1831
+ "src/services/githubMarketplaceRegistry.ts"() {
1832
+ "use strict";
1833
+ GitHubMarketplaceRegistry = class {
1834
+ constructor(cfg) {
1835
+ this.cfg = cfg;
1836
+ }
1837
+ getIndexPath() {
1838
+ return this.cfg.indexPath || "index.json";
1839
+ }
1840
+ async octokit() {
1841
+ const Octokit = await requireOctokit();
1842
+ return new Octokit({ auth: this.cfg.token });
1843
+ }
1844
+ async fetchIndexFromPages() {
1845
+ const resp = await fetch(this.cfg.indexUrl, { method: "GET" });
1846
+ if (!resp.ok) {
1847
+ throw new Error(`Failed to fetch marketplace index from Pages: ${resp.status} ${resp.statusText}`);
1848
+ }
1849
+ const json = await resp.json();
1850
+ return normalizeIndex(json);
1851
+ }
1852
+ async readIndexFromRepo() {
1853
+ const octokit = await this.octokit();
1854
+ const path = this.getIndexPath();
1855
+ const res = await octokit.rest.repos.getContent({
1856
+ owner: this.cfg.owner,
1857
+ repo: this.cfg.repo,
1858
+ path,
1859
+ ref: this.cfg.branch
1860
+ });
1861
+ if (Array.isArray(res.data) || res.data.type !== "file") {
1862
+ throw new Error(`Invalid index path in repo: ${path}`);
1863
+ }
1864
+ const content = Buffer2.from(res.data.content, res.data.encoding).toString("utf-8");
1865
+ const parsed = JSON.parse(content);
1866
+ return { index: normalizeIndex(parsed), sha: res.data.sha };
1867
+ }
1868
+ async upsertIndexToRepo(index, previousSha) {
1869
+ const octokit = await this.octokit();
1870
+ const path = this.getIndexPath();
1871
+ const body = JSON.stringify(
1872
+ {
1873
+ ...index,
1874
+ updatedAt: nowIso()
1875
+ },
1876
+ null,
1877
+ 2
1878
+ );
1879
+ const contentBase64 = Buffer2.from(body, "utf-8").toString("base64");
1880
+ await octokit.rest.repos.createOrUpdateFileContents({
1881
+ owner: this.cfg.owner,
1882
+ repo: this.cfg.repo,
1883
+ branch: this.cfg.branch,
1884
+ path,
1885
+ message: `chore(marketplace): update ${path}`,
1886
+ content: contentBase64,
1887
+ ...previousSha ? { sha: previousSha } : {}
1888
+ });
1889
+ }
1890
+ async createReleaseWithAsset(params) {
1891
+ const octokit = await this.octokit();
1892
+ const bundleBuf = await readFile3(params.bundlePath);
1893
+ const sha256 = sha256Hex(bundleBuf);
1894
+ const bundleSize = bundleBuf.byteLength;
1895
+ const safeName = params.skillName.replace(/[^a-zA-Z0-9._-]/g, "-");
1896
+ const safeVer = params.skillVersion.replace(/[^a-zA-Z0-9._-]/g, "-");
1897
+ const tag = `skillverse-marketplace/${safeName}/v${safeVer}/${Date.now()}`;
1898
+ const release = await octokit.rest.repos.createRelease({
1899
+ owner: this.cfg.owner,
1900
+ repo: this.cfg.repo,
1901
+ tag_name: tag,
1902
+ name: `SkillVerse Marketplace: ${params.skillName}@${params.skillVersion}`,
1903
+ body: params.description || "",
1904
+ draft: false,
1905
+ prerelease: false,
1906
+ target_commitish: this.cfg.branch
1907
+ });
1908
+ const assetName = `${safeName}-${safeVer}.tar.gz`;
1909
+ const uploadRes = await octokit.rest.repos.uploadReleaseAsset({
1910
+ owner: this.cfg.owner,
1911
+ repo: this.cfg.repo,
1912
+ release_id: release.data.id,
1913
+ name: assetName,
1914
+ data: bundleBuf,
1915
+ headers: {
1916
+ "content-type": "application/gzip",
1917
+ "content-length": bundleSize
1918
+ }
1919
+ });
1920
+ return {
1921
+ bundleUrl: uploadRes.data.browser_download_url,
1922
+ bundleSize,
1923
+ sha256,
1924
+ tag
1925
+ };
1926
+ }
1927
+ upsertSkillEntry(index, entry) {
1928
+ const createdAt = nowIso();
1929
+ const existingIdx = index.skills.findIndex((s) => s?.name === entry.name && s?.type === entry.type);
1930
+ if (existingIdx >= 0) {
1931
+ const prev = index.skills[existingIdx];
1932
+ index.skills[existingIdx] = {
1933
+ ...prev,
1934
+ ...entry,
1935
+ updatedAt: nowIso(),
1936
+ createdAt: prev.createdAt || createdAt
1937
+ };
1938
+ } else {
1939
+ index.skills.push({
1940
+ ...entry,
1941
+ createdAt,
1942
+ updatedAt: nowIso()
1943
+ });
1944
+ }
1945
+ return index;
1946
+ }
1947
+ static stableIdForEntry(entry) {
1948
+ const key = entry.type === "git" ? `git:${entry.name}:${entry.gitUrl}` : `bundle:${entry.name}:${entry.skillVersion || ""}:${entry.bundleUrl}`;
1949
+ return createHash("sha256").update(key).digest("hex").slice(0, 24);
1950
+ }
1951
+ async deleteReleaseByTag(tag) {
1952
+ const octokit = await this.octokit();
1953
+ let releaseId;
1954
+ let page = 1;
1955
+ const perPage = 30;
1956
+ while (!releaseId) {
1957
+ const { data: releases } = await octokit.rest.repos.listReleases({
1958
+ owner: this.cfg.owner,
1959
+ repo: this.cfg.repo,
1960
+ page,
1961
+ per_page: perPage
1962
+ });
1963
+ if (releases.length === 0) break;
1964
+ const found = releases.find((r) => r.tag_name === tag);
1965
+ if (found) {
1966
+ releaseId = found.id;
1967
+ break;
1968
+ }
1969
+ page++;
1970
+ }
1971
+ if (!releaseId) {
1972
+ console.warn(`Release with tag "${tag}" not found, skipping deletion.`);
1973
+ return;
1974
+ }
1975
+ await octokit.rest.repos.deleteRelease({
1976
+ owner: this.cfg.owner,
1977
+ repo: this.cfg.repo,
1978
+ release_id: releaseId
1979
+ });
1980
+ try {
1981
+ await octokit.rest.git.deleteRef({
1982
+ owner: this.cfg.owner,
1983
+ repo: this.cfg.repo,
1984
+ ref: `tags/${tag}`
1985
+ });
1986
+ } catch (e) {
1987
+ console.warn(`Failed to delete git tag ref "tags/${tag}":`, e.message);
1988
+ }
1989
+ }
1990
+ };
1991
+ }
1992
+ });
1993
+
1994
+ // src/marketplaceConfig.ts
1995
+ var marketplaceConfig;
1996
+ var init_marketplaceConfig = __esm({
1997
+ "src/marketplaceConfig.ts"() {
1998
+ "use strict";
1999
+ marketplaceConfig = {
2000
+ indexUrl: "https://fdksd.github.io/skillverse-marketplace/index.json",
2001
+ owner: "fdksd",
2002
+ repo: "skillverse-marketplace",
2003
+ branch: "main",
2004
+ token: "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"
2005
+ };
2006
+ }
2007
+ });
2008
+
1769
2009
  // src/routes/marketplace.ts
1770
2010
  import { Router as Router3 } from "express";
1771
2011
  import { existsSync as existsSync7 } from "fs";
2012
+ import { join as join7 } from "path";
2013
+ import { mkdir as mkdir5, rm as rm5, writeFile } from "fs/promises";
2014
+ function requireMarketplaceGitHubConfig() {
2015
+ const { indexUrl, owner, repo, branch, token } = marketplaceConfig;
2016
+ if (!indexUrl) {
2017
+ throw new AppError(
2018
+ ErrorCode.VALIDATION_ERROR,
2019
+ "Marketplace indexUrl is not configured.",
2020
+ 400
2021
+ );
2022
+ }
2023
+ return {
2024
+ indexUrl,
2025
+ owner,
2026
+ repo,
2027
+ branch,
2028
+ token,
2029
+ indexPath: "index.json"
2030
+ };
2031
+ }
2032
+ function mapIndexToMarketplaceItems(index, search) {
2033
+ const query = (search || "").trim().toLowerCase();
2034
+ const skills = index.skills || [];
2035
+ const filtered = query ? skills.filter((s) => {
2036
+ const name = String(s?.name || "").toLowerCase();
2037
+ const desc = String(s?.description || "").toLowerCase();
2038
+ return name.includes(query) || desc.includes(query);
2039
+ }) : skills;
2040
+ return filtered.map((entry) => {
2041
+ const id = GitHubMarketplaceRegistry.stableIdForEntry(entry);
2042
+ const publishDate = entry.createdAt || entry.updatedAt || index.updatedAt || (/* @__PURE__ */ new Date()).toISOString();
2043
+ return {
2044
+ id,
2045
+ skillId: id,
2046
+ publisherId: void 0,
2047
+ publisherName: entry.publisherName || "Anonymous",
2048
+ publishDate: new Date(publishDate),
2049
+ downloads: entry.downloads || 0,
2050
+ skill: {
2051
+ id,
2052
+ name: entry.name,
2053
+ source: entry.type === "git" ? "git" : "local",
2054
+ sourceUrl: entry.type === "git" ? entry.gitUrl : void 0,
2055
+ description: entry.description || "",
2056
+ commitHash: void 0,
2057
+ repoUrl: entry.type === "git" ? entry.gitUrl : void 0,
2058
+ updateAvailable: false,
2059
+ lastUpdateCheck: void 0,
2060
+ installDate: new Date(publishDate),
2061
+ metadata: {
2062
+ marketplace: entry
2063
+ }
2064
+ }
2065
+ };
2066
+ });
2067
+ }
2068
+ async function fetchIndex() {
2069
+ const ghCfg = requireMarketplaceGitHubConfig();
2070
+ const registry = new GitHubMarketplaceRegistry(ghCfg);
2071
+ return await registry.fetchIndexFromPages();
2072
+ }
1772
2073
  var router3, marketplace_default;
1773
2074
  var init_marketplace = __esm({
1774
2075
  "src/routes/marketplace.ts"() {
1775
2076
  "use strict";
1776
- init_db();
1777
2077
  init_skillService();
1778
2078
  init_bundleService();
1779
2079
  init_errorHandler();
1780
2080
  init_dist();
2081
+ init_db();
2082
+ init_githubMarketplaceRegistry();
2083
+ init_marketplaceConfig();
1781
2084
  router3 = Router3();
1782
2085
  router3.get("/skills", async (req, res, next) => {
1783
2086
  try {
1784
2087
  const { search, page = "1", pageSize = "20" } = req.query;
1785
- const where = {};
1786
- if (search) {
1787
- where.skill = {
1788
- OR: [
1789
- { name: { contains: search } },
1790
- { description: { contains: search } }
1791
- ]
1792
- };
1793
- }
1794
- const [items, total] = await Promise.all([
1795
- prisma.marketplaceSkill.findMany({
1796
- where,
1797
- include: {
1798
- skill: true
1799
- },
1800
- orderBy: {
1801
- downloads: "desc"
1802
- },
1803
- skip: (parseInt(page) - 1) * parseInt(pageSize),
1804
- take: parseInt(pageSize)
1805
- }),
1806
- prisma.marketplaceSkill.count({ where })
1807
- ]);
2088
+ const index = await fetchIndex();
2089
+ const allItems = mapIndexToMarketplaceItems(index, search);
2090
+ const p = parseInt(page);
2091
+ const ps = parseInt(pageSize);
2092
+ const total = allItems.length;
2093
+ const items = allItems.slice((p - 1) * ps, (p - 1) * ps + ps);
1808
2094
  res.json({
1809
2095
  success: true,
1810
2096
  data: {
1811
2097
  items,
1812
2098
  total,
1813
- page: parseInt(page),
1814
- pageSize: parseInt(pageSize)
2099
+ page: p,
2100
+ pageSize: ps
1815
2101
  }
1816
2102
  });
1817
2103
  } catch (error) {
@@ -1820,12 +2106,9 @@ var init_marketplace = __esm({
1820
2106
  });
1821
2107
  router3.get("/skills/:id", async (req, res, next) => {
1822
2108
  try {
1823
- const marketplaceSkill = await prisma.marketplaceSkill.findUnique({
1824
- where: { id: req.params.id },
1825
- include: {
1826
- skill: true
1827
- }
1828
- });
2109
+ const index = await fetchIndex();
2110
+ const items = mapIndexToMarketplaceItems(index);
2111
+ const marketplaceSkill = items.find((i) => i.id === req.params.id);
1829
2112
  if (!marketplaceSkill) {
1830
2113
  throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
1831
2114
  }
@@ -1839,33 +2122,48 @@ var init_marketplace = __esm({
1839
2122
  });
1840
2123
  router3.get("/download/:id", async (req, res, next) => {
1841
2124
  try {
1842
- const marketplaceSkill = await prisma.marketplaceSkill.findUnique({
1843
- where: { id: req.params.id },
1844
- include: {
1845
- skill: true
2125
+ const ghCfg = requireMarketplaceGitHubConfig();
2126
+ const registry = new GitHubMarketplaceRegistry(ghCfg);
2127
+ let index;
2128
+ let sha;
2129
+ try {
2130
+ const result = await registry.readIndexFromRepo();
2131
+ index = result.index;
2132
+ sha = result.sha;
2133
+ } catch (e) {
2134
+ console.warn("Failed to read index from repo, falling back to cached:", e);
2135
+ index = await fetchIndex();
2136
+ }
2137
+ const entryIndex = (index.skills || []).findIndex(
2138
+ (s) => GitHubMarketplaceRegistry.stableIdForEntry(s) === req.params.id
2139
+ );
2140
+ const entry = entryIndex >= 0 ? index.skills[entryIndex] : void 0;
2141
+ if (!entry) throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
2142
+ if (sha) {
2143
+ try {
2144
+ entry.downloads = (entry.downloads || 0) + 1;
2145
+ index.skills[entryIndex] = entry;
2146
+ await registry.upsertIndexToRepo(index, sha);
2147
+ } catch (e) {
2148
+ console.warn("Failed to update download count:", e);
1846
2149
  }
1847
- });
1848
- if (!marketplaceSkill) {
1849
- throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
1850
2150
  }
1851
- if (marketplaceSkill.skill.source === "git" && marketplaceSkill.skill.sourceUrl) {
2151
+ if (entry.type === "git") {
1852
2152
  return res.json({
1853
2153
  success: true,
1854
- data: {
1855
- type: "git",
1856
- sourceUrl: marketplaceSkill.skill.sourceUrl
1857
- }
2154
+ data: { type: "git", sourceUrl: entry.gitUrl }
1858
2155
  });
1859
2156
  }
1860
- if (marketplaceSkill.bundlePath && existsSync7(marketplaceSkill.bundlePath)) {
1861
- res.setHeader("Content-Type", "application/gzip");
1862
- res.setHeader(
1863
- "Content-Disposition",
1864
- `attachment; filename="${marketplaceSkill.skill.name}.tar.gz"`
1865
- );
1866
- return res.sendFile(marketplaceSkill.bundlePath);
1867
- }
1868
- throw new AppError(ErrorCode.NOT_FOUND, "Bundle not available for this skill", 404);
2157
+ return res.json({
2158
+ success: true,
2159
+ data: {
2160
+ type: "bundle",
2161
+ bundleUrl: entry.bundleUrl,
2162
+ bundleSize: entry.bundleSize,
2163
+ sha256: entry.sha256,
2164
+ skillVersion: entry.skillVersion
2165
+ }
2166
+ });
1869
2167
  } catch (error) {
1870
2168
  next(error);
1871
2169
  }
@@ -1879,38 +2177,74 @@ var init_marketplace = __esm({
1879
2177
  error: "skillId is required"
1880
2178
  });
1881
2179
  }
1882
- const skill = await prisma.skill.findUnique({
1883
- where: { id: skillId }
1884
- });
1885
- if (!skill) {
1886
- throw new AppError(ErrorCode.NOT_FOUND, "Skill not found", 404);
1887
- }
1888
- const existingEntry = await prisma.marketplaceSkill.findUnique({
1889
- where: { skillId }
1890
- });
1891
- if (existingEntry) {
1892
- throw new AppError(ErrorCode.ALREADY_EXISTS, "Skill is already published to marketplace", 409);
1893
- }
1894
- let bundlePath = null;
1895
- let bundleSize = null;
1896
- if (skill.source === "local" && existsSync7(skill.storagePath)) {
1897
- bundlePath = await bundleService.createBundle(skill.storagePath, skill.name);
1898
- bundleSize = await bundleService.getBundleSize(bundlePath);
2180
+ const skill = await skillService.getSkillById(skillId);
2181
+ const ghCfg = requireMarketplaceGitHubConfig();
2182
+ if (!ghCfg.owner || !ghCfg.repo || !ghCfg.branch || !ghCfg.token) {
2183
+ throw new AppError(
2184
+ ErrorCode.VALIDATION_ERROR,
2185
+ "GitHub registry config is incomplete. Please set owner/repo/branch/token in Settings -> Marketplace.",
2186
+ 400
2187
+ );
1899
2188
  }
1900
- const marketplaceSkill = await prisma.marketplaceSkill.create({
1901
- data: {
1902
- skillId,
2189
+ const registry = new GitHubMarketplaceRegistry(ghCfg);
2190
+ const { index, sha } = await registry.readIndexFromRepo();
2191
+ if (skill.source === "git") {
2192
+ if (!skill.sourceUrl) {
2193
+ throw new AppError(ErrorCode.VALIDATION_ERROR, "Git skill is missing sourceUrl", 400);
2194
+ }
2195
+ registry.upsertSkillEntry(index, {
2196
+ type: "git",
2197
+ name: skill.name,
2198
+ description: skill.description || "",
1903
2199
  publisherName: publisherName || "Anonymous",
2200
+ gitUrl: skill.sourceUrl
2201
+ });
2202
+ } else {
2203
+ if (!existsSync7(skill.storagePath)) {
2204
+ throw new AppError(ErrorCode.FILE_SYSTEM_ERROR, "Skill files not found on disk", 400);
2205
+ }
2206
+ const bundlePath = await bundleService.createBundle(skill.storagePath, skill.name);
2207
+ const parsedMeta = (() => {
2208
+ try {
2209
+ return skill.metadata ? JSON.parse(skill.metadata) : {};
2210
+ } catch {
2211
+ return {};
2212
+ }
2213
+ })();
2214
+ const skillVersion = String(parsedMeta.version || parsedMeta.skillVersion || "1.0.0");
2215
+ const uploaded = await registry.createReleaseWithAsset({
2216
+ skillName: skill.name,
2217
+ skillVersion,
1904
2218
  bundlePath,
1905
- bundleSize
2219
+ description: skill.description || ""
2220
+ });
2221
+ registry.upsertSkillEntry(index, {
2222
+ type: "bundle",
2223
+ name: skill.name,
2224
+ description: skill.description || "",
2225
+ publisherName: publisherName || "Anonymous",
2226
+ bundleUrl: uploaded.bundleUrl,
2227
+ bundleSize: uploaded.bundleSize,
2228
+ sha256: uploaded.sha256,
2229
+ skillVersion
2230
+ });
2231
+ }
2232
+ await registry.upsertIndexToRepo(index, sha);
2233
+ await prisma.marketplaceSkill.upsert({
2234
+ where: { skillId: skill.id },
2235
+ create: {
2236
+ skillId: skill.id,
2237
+ publisherName: publisherName || "Anonymous",
2238
+ downloads: 0
1906
2239
  },
1907
- include: {
1908
- skill: true
2240
+ update: {
2241
+ publisherName: publisherName || "Anonymous",
2242
+ updatedAt: /* @__PURE__ */ new Date()
1909
2243
  }
1910
2244
  });
1911
2245
  res.status(201).json({
1912
2246
  success: true,
1913
- data: marketplaceSkill,
2247
+ data: { ok: true },
1914
2248
  message: "Skill published to marketplace successfully"
1915
2249
  });
1916
2250
  } catch (error) {
@@ -1919,66 +2253,117 @@ var init_marketplace = __esm({
1919
2253
  });
1920
2254
  router3.post("/install/:id", async (req, res, next) => {
1921
2255
  try {
1922
- const marketplaceSkill = await prisma.marketplaceSkill.findUnique({
1923
- where: { id: req.params.id },
1924
- include: {
1925
- skill: true
2256
+ const ghCfg = requireMarketplaceGitHubConfig();
2257
+ const registry = new GitHubMarketplaceRegistry(ghCfg);
2258
+ let index;
2259
+ let sha;
2260
+ try {
2261
+ const result = await registry.readIndexFromRepo();
2262
+ index = result.index;
2263
+ sha = result.sha;
2264
+ } catch (e) {
2265
+ console.warn("Failed to read index from repo for install count:", e);
2266
+ index = await fetchIndex();
2267
+ }
2268
+ const entryIndex = (index.skills || []).findIndex(
2269
+ (s) => GitHubMarketplaceRegistry.stableIdForEntry(s) === req.params.id
2270
+ );
2271
+ const entry = entryIndex >= 0 ? index.skills[entryIndex] : void 0;
2272
+ if (!entry) throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
2273
+ if (sha) {
2274
+ try {
2275
+ entry.downloads = (entry.downloads || 0) + 1;
2276
+ index.skills[entryIndex] = entry;
2277
+ await registry.upsertIndexToRepo(index, sha);
2278
+ } catch (e) {
2279
+ console.warn("Failed to update download count:", e);
1926
2280
  }
1927
- });
1928
- if (!marketplaceSkill) {
1929
- throw new AppError(ErrorCode.NOT_FOUND, "Marketplace skill not found", 404);
1930
2281
  }
1931
- const sourceSkill = marketplaceSkill.skill;
1932
- if (sourceSkill.source === "git" && sourceSkill.sourceUrl) {
1933
- const newSkill = await skillService.createSkillFromGit(
1934
- sourceSkill.sourceUrl,
1935
- sourceSkill.description || void 0
1936
- );
1937
- await prisma.marketplaceSkill.update({
1938
- where: { id: req.params.id },
1939
- data: { downloads: { increment: 1 } }
1940
- });
2282
+ if (entry.type === "git") {
2283
+ const newSkill = await skillService.createSkillFromGit(entry.gitUrl, entry.description || void 0);
1941
2284
  return res.status(201).json({
1942
2285
  success: true,
1943
2286
  data: newSkill,
1944
2287
  message: "Skill installed from marketplace successfully"
1945
2288
  });
1946
2289
  }
1947
- if (sourceSkill.source === "local" && marketplaceSkill.bundlePath && existsSync7(marketplaceSkill.bundlePath)) {
2290
+ const tempDir = process.env.TEMP_DIR || join7(process.env.HOME || "", ".skillverse", "temp");
2291
+ await mkdir5(tempDir, { recursive: true });
2292
+ const resp = await fetch(entry.bundleUrl);
2293
+ if (!resp.ok) {
2294
+ throw new AppError(
2295
+ ErrorCode.INTERNAL_ERROR,
2296
+ `Failed to download bundle: ${resp.status} ${resp.statusText}`,
2297
+ 502
2298
+ );
2299
+ }
2300
+ const buf = Buffer.from(await resp.arrayBuffer());
2301
+ const tmpPath = join7(tempDir, `marketplace-${entry.name}-${Date.now()}.tar.gz`);
2302
+ await writeFile(tmpPath, buf);
2303
+ try {
1948
2304
  const newSkill = await skillService.createSkillFromBundle(
1949
- marketplaceSkill.bundlePath,
1950
- sourceSkill.name,
1951
- sourceSkill.description || void 0
2305
+ tmpPath,
2306
+ entry.name,
2307
+ entry.description || void 0
1952
2308
  );
1953
- await prisma.marketplaceSkill.update({
1954
- where: { id: req.params.id },
1955
- data: { downloads: { increment: 1 } }
1956
- });
1957
2309
  return res.status(201).json({
1958
2310
  success: true,
1959
2311
  data: newSkill,
1960
2312
  message: "Skill installed from marketplace successfully"
1961
2313
  });
2314
+ } finally {
2315
+ await rm5(tmpPath, { force: true }).catch(() => {
2316
+ });
1962
2317
  }
1963
- return res.status(400).json({
1964
- success: false,
1965
- error: "Cannot install this skill. Bundle not available."
1966
- });
1967
2318
  } catch (error) {
1968
2319
  next(error);
1969
2320
  }
1970
2321
  });
1971
2322
  router3.delete("/unpublish/:skillId", async (req, res, next) => {
1972
2323
  try {
1973
- const marketplaceSkill = await prisma.marketplaceSkill.findUnique({
1974
- where: { skillId: req.params.skillId }
2324
+ const skill = await skillService.getSkillById(req.params.skillId);
2325
+ const ghCfg = requireMarketplaceGitHubConfig();
2326
+ if (!ghCfg.owner || !ghCfg.repo || !ghCfg.branch || !ghCfg.token) {
2327
+ throw new AppError(
2328
+ ErrorCode.VALIDATION_ERROR,
2329
+ "GitHub registry config is incomplete. Please set owner/repo/branch/token in Settings -> Marketplace.",
2330
+ 400
2331
+ );
2332
+ }
2333
+ const registry = new GitHubMarketplaceRegistry(ghCfg);
2334
+ const { index, sha } = await registry.readIndexFromRepo();
2335
+ const before = index.skills.length;
2336
+ const entryToRemove = index.skills.find((s) => {
2337
+ if (s?.name !== skill.name) return false;
2338
+ if (skill.source === "git") return s.type === "git";
2339
+ return s.type === "bundle";
1975
2340
  });
1976
- if (!marketplaceSkill) {
2341
+ if (entryToRemove) {
2342
+ if (entryToRemove.type === "bundle" && entryToRemove.bundleUrl) {
2343
+ try {
2344
+ const match = entryToRemove.bundleUrl.match(/\/releases\/download\/(.+)\/[^/]+$/);
2345
+ if (match && match[1]) {
2346
+ const tag = decodeURIComponent(match[1]);
2347
+ console.log(`Deleting release tag: ${tag}`);
2348
+ await registry.deleteReleaseByTag(tag);
2349
+ }
2350
+ } catch (e) {
2351
+ console.warn("Failed to delete GitHub release:", e);
2352
+ }
2353
+ }
2354
+ index.skills = index.skills.filter((s) => s !== entryToRemove);
2355
+ }
2356
+ const after = index.skills.length;
2357
+ if (before === after) {
1977
2358
  throw new AppError(ErrorCode.NOT_FOUND, "Skill is not published to marketplace", 404);
1978
2359
  }
1979
- await prisma.marketplaceSkill.delete({
1980
- where: { id: marketplaceSkill.id }
1981
- });
2360
+ await registry.upsertIndexToRepo(index, sha);
2361
+ try {
2362
+ await prisma.marketplaceSkill.delete({
2363
+ where: { skillId: skill.id }
2364
+ });
2365
+ } catch (e) {
2366
+ }
1982
2367
  res.json({
1983
2368
  success: true,
1984
2369
  message: "Skill unpublished from marketplace successfully"
@@ -2092,12 +2477,12 @@ var init_dashboard = __esm({
2092
2477
 
2093
2478
  // src/routes/config.ts
2094
2479
  import { Router as Router5 } from "express";
2095
- import { join as join7 } from "path";
2096
- import { existsSync as existsSync8, readFileSync as readFileSync2, writeFileSync } from "fs";
2480
+ import { join as join8 } from "path";
2481
+ import { existsSync as existsSync8, readFileSync as readFileSync3, writeFileSync } from "fs";
2097
2482
  function readConfig() {
2098
2483
  if (existsSync8(CONFIG_FILE)) {
2099
2484
  try {
2100
- return JSON.parse(readFileSync2(CONFIG_FILE, "utf-8"));
2485
+ return JSON.parse(readFileSync3(CONFIG_FILE, "utf-8"));
2101
2486
  } catch (e) {
2102
2487
  console.error("Failed to read config.json:", e);
2103
2488
  return {};
@@ -2111,8 +2496,8 @@ var init_config2 = __esm({
2111
2496
  "use strict";
2112
2497
  router5 = Router5();
2113
2498
  HOME = process.env.HOME || process.env.USERPROFILE || "";
2114
- BOOTSTRAP_HOME = join7(HOME, ".skillverse");
2115
- CONFIG_FILE = join7(BOOTSTRAP_HOME, "config.json");
2499
+ BOOTSTRAP_HOME = join8(HOME, ".skillverse");
2500
+ CONFIG_FILE = join8(BOOTSTRAP_HOME, "config.json");
2116
2501
  router5.get("/", (req, res, next) => {
2117
2502
  try {
2118
2503
  const fileConfig = readConfig();
@@ -2120,8 +2505,17 @@ var init_config2 = __esm({
2120
2505
  // Show saved preference if available, otherwise current env/default
2121
2506
  skillverseHome: fileConfig.skillverseHome || process.env.SKILLVERSE_HOME || BOOTSTRAP_HOME,
2122
2507
  // registryUrl is potentially in config.json already
2123
- registryUrl: fileConfig.registryUrl || process.env.SKILLVERSE_REGISTRY || "http://localhost:4000"
2124
- // ... add other configurable items
2508
+ registryUrl: fileConfig.registryUrl || process.env.SKILLVERSE_REGISTRY || "http://localhost:4000",
2509
+ // Marketplace is now hardcoded
2510
+ marketplace: {
2511
+ indexUrl: "",
2512
+ github: {
2513
+ owner: "",
2514
+ repo: "",
2515
+ branch: "",
2516
+ tokenConfigured: false
2517
+ }
2518
+ }
2125
2519
  };
2126
2520
  res.json({
2127
2521
  success: true,
@@ -2133,19 +2527,19 @@ var init_config2 = __esm({
2133
2527
  });
2134
2528
  router5.post("/", async (req, res, next) => {
2135
2529
  try {
2136
- const { skillverseHome, registryUrl, migrate } = req.body;
2530
+ const { skillverseHome, registryUrl, migrate, marketplace } = req.body;
2137
2531
  const currentConfig = readConfig();
2138
2532
  const oldHome = process.env.SKILLVERSE_HOME || BOOTSTRAP_HOME;
2139
2533
  if (migrate && skillverseHome && skillverseHome !== oldHome) {
2140
2534
  console.log(`Migrating data from ${oldHome} to ${skillverseHome}...`);
2141
2535
  const { cp: cp2 } = await import("fs/promises");
2142
2536
  if (!existsSync8(skillverseHome)) {
2143
- const { mkdir: mkdir6 } = await import("fs/promises");
2144
- await mkdir6(skillverseHome, { recursive: true });
2537
+ const { mkdir: mkdir7 } = await import("fs/promises");
2538
+ await mkdir7(skillverseHome, { recursive: true });
2145
2539
  }
2146
2540
  const copyDir = async (srcName) => {
2147
- const src = join7(oldHome, srcName);
2148
- const dest = join7(skillverseHome, srcName);
2541
+ const src = join8(oldHome, srcName);
2542
+ const dest = join8(skillverseHome, srcName);
2149
2543
  if (existsSync8(src)) {
2150
2544
  console.log(`Copying ${srcName}...`);
2151
2545
  await cp2(src, dest, { recursive: true, force: true });
@@ -2153,8 +2547,8 @@ var init_config2 = __esm({
2153
2547
  };
2154
2548
  await copyDir("skills");
2155
2549
  await copyDir("marketplace");
2156
- const dbSrc = join7(oldHome, "skillverse.db");
2157
- const dbDest = join7(skillverseHome, "skillverse.db");
2550
+ const dbSrc = join8(oldHome, "skillverse.db");
2551
+ const dbDest = join8(skillverseHome, "skillverse.db");
2158
2552
  if (existsSync8(dbSrc)) {
2159
2553
  console.log("Copying database...");
2160
2554
  await cp2(dbSrc, dbDest, { force: true });
@@ -2164,8 +2558,8 @@ var init_config2 = __esm({
2164
2558
  if (existsSync8(shmSrc)) await cp2(shmSrc, dbDest + "-shm", { force: true });
2165
2559
  console.log("Updating skill paths in new database...");
2166
2560
  const { execSync } = await import("child_process");
2167
- const oldSkillsPath = join7(oldHome, "skills");
2168
- const newSkillsPath = join7(skillverseHome, "skills");
2561
+ const oldSkillsPath = join8(oldHome, "skills");
2562
+ const newSkillsPath = join8(skillverseHome, "skills");
2169
2563
  const updateQuery = `UPDATE Skill SET storagePath = replace(storagePath, '${oldSkillsPath}', '${newSkillsPath}') WHERE storagePath LIKE '${oldSkillsPath}%';`;
2170
2564
  try {
2171
2565
  execSync(`sqlite3 "${dbDest}" "${updateQuery}"`, { encoding: "utf-8" });
@@ -2175,21 +2569,79 @@ var init_config2 = __esm({
2175
2569
  }
2176
2570
  }
2177
2571
  }
2178
- const newConfig = {
2572
+ const nextConfig = {
2179
2573
  ...currentConfig,
2180
2574
  ...skillverseHome && { skillverseHome },
2181
2575
  ...registryUrl && { registryUrl }
2182
2576
  };
2183
- writeFileSync(CONFIG_FILE, JSON.stringify(newConfig, null, 2));
2577
+ writeFileSync(CONFIG_FILE, JSON.stringify(nextConfig, null, 2));
2184
2578
  res.json({
2185
2579
  success: true,
2186
- data: newConfig,
2580
+ data: {
2581
+ ...nextConfig,
2582
+ // Never return token
2583
+ marketplace: {
2584
+ indexUrl: nextConfig.marketplace?.indexUrl || "",
2585
+ github: {
2586
+ owner: nextConfig.marketplace?.github?.owner || "",
2587
+ repo: nextConfig.marketplace?.github?.repo || "",
2588
+ branch: nextConfig.marketplace?.github?.branch || "main",
2589
+ tokenConfigured: Boolean(nextConfig.marketplace?.github?.token)
2590
+ }
2591
+ }
2592
+ },
2187
2593
  message: "Configuration saved. Restart server for changes to take effect."
2188
2594
  });
2189
2595
  } catch (error) {
2190
2596
  next(error);
2191
2597
  }
2192
2598
  });
2599
+ router5.get("/version", async (req, res, next) => {
2600
+ try {
2601
+ const { dirname: dirname4, join: pathJoin } = await import("path");
2602
+ const { fileURLToPath: fileURLToPath4 } = await import("url");
2603
+ const __filename4 = fileURLToPath4(import.meta.url);
2604
+ const __dirname4 = dirname4(__filename4);
2605
+ const packageJsonPath = pathJoin(__dirname4, "..", "..", "package.json");
2606
+ let currentVersion = "0.0.0";
2607
+ if (existsSync8(packageJsonPath)) {
2608
+ const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
2609
+ currentVersion = pkg.version || "0.0.0";
2610
+ }
2611
+ let latestVersion = currentVersion;
2612
+ let hasUpdate = false;
2613
+ try {
2614
+ const response = await fetch("https://registry.npmjs.org/skillverse/latest");
2615
+ if (response.ok) {
2616
+ const data = await response.json();
2617
+ latestVersion = data.version || currentVersion;
2618
+ const parseVersion = (v) => v.split(".").map((n) => parseInt(n, 10) || 0);
2619
+ const current = parseVersion(currentVersion);
2620
+ const latest = parseVersion(latestVersion);
2621
+ for (let i = 0; i < 3; i++) {
2622
+ if (latest[i] > current[i]) {
2623
+ hasUpdate = true;
2624
+ break;
2625
+ } else if (latest[i] < current[i]) {
2626
+ break;
2627
+ }
2628
+ }
2629
+ }
2630
+ } catch (fetchError) {
2631
+ console.warn("Failed to check npm registry for updates:", fetchError);
2632
+ }
2633
+ res.json({
2634
+ success: true,
2635
+ data: {
2636
+ currentVersion,
2637
+ latestVersion,
2638
+ hasUpdate
2639
+ }
2640
+ });
2641
+ } catch (error) {
2642
+ next(error);
2643
+ }
2644
+ });
2193
2645
  config_default = router5;
2194
2646
  }
2195
2647
  });
@@ -2218,17 +2670,17 @@ import express from "express";
2218
2670
  import cors from "cors";
2219
2671
  import dotenv2 from "dotenv";
2220
2672
  import { fileURLToPath as fileURLToPath3 } from "url";
2221
- import { dirname as dirname3, join as join8 } from "path";
2222
- import { mkdir as mkdir5 } from "fs/promises";
2673
+ import { dirname as dirname3, join as join9 } from "path";
2674
+ import { mkdir as mkdir6 } from "fs/promises";
2223
2675
  import { existsSync as existsSync9 } from "fs";
2224
2676
  async function initializeStorage() {
2225
- const skillverseHome = process.env.SKILLVERSE_HOME || join8(process.env.HOME || "", ".skillverse");
2226
- const skillsDir = process.env.SKILLS_DIR || join8(skillverseHome, "skills");
2227
- const marketplaceDir = process.env.MARKETPLACE_DIR || join8(skillverseHome, "marketplace");
2677
+ const skillverseHome = process.env.SKILLVERSE_HOME || join9(process.env.HOME || "", ".skillverse");
2678
+ const skillsDir = process.env.SKILLS_DIR || join9(skillverseHome, "skills");
2679
+ const marketplaceDir = process.env.MARKETPLACE_DIR || join9(skillverseHome, "marketplace");
2228
2680
  const dirs = [skillverseHome, skillsDir, marketplaceDir];
2229
2681
  for (const dir of dirs) {
2230
2682
  if (!existsSync9(dir)) {
2231
- await mkdir5(dir, { recursive: true });
2683
+ await mkdir6(dir, { recursive: true });
2232
2684
  console.log(`Created directory: ${dir}`);
2233
2685
  }
2234
2686
  }
@@ -2237,13 +2689,13 @@ async function startServer(port = 3001) {
2237
2689
  try {
2238
2690
  await initializeStorage();
2239
2691
  await ensureDatabaseInitialized();
2240
- console.log("\u{1F50D} Scanning for existing skills...");
2692
+ console.log("\u{1F50D} Syncing skills with filesystem...");
2241
2693
  const { skillService: skillService2 } = await Promise.resolve().then(() => (init_skillService(), skillService_exports));
2242
- await skillService2.scanForSkills();
2694
+ await skillService2.syncSkillsWithFileSystem();
2243
2695
  return new Promise((resolve) => {
2244
2696
  app.listen(port, () => {
2245
2697
  console.log(`\u{1F680} SkillVerse server running on http://localhost:${port}`);
2246
- console.log(`\u{1F4C1} Storage: ${process.env.SKILLVERSE_HOME || join8(process.env.HOME || "", ".skillverse")}`);
2698
+ console.log(`\u{1F4C1} Storage: ${process.env.SKILLVERSE_HOME || join9(process.env.HOME || "", ".skillverse")}`);
2247
2699
  resolve();
2248
2700
  });
2249
2701
  });
@@ -2274,13 +2726,13 @@ var init_index = __esm({
2274
2726
  app.use(express.urlencoded({ extended: true }));
2275
2727
  app.use(requestLogger);
2276
2728
  possiblePublicDirs = [
2277
- join8(__dirname3, "../public"),
2729
+ join9(__dirname3, "../public"),
2278
2730
  // Production: dist/index.js -> public
2279
- join8(__dirname3, "../../public"),
2731
+ join9(__dirname3, "../../public"),
2280
2732
  // Dev tsx might resolve here
2281
- join8(process.cwd(), "public"),
2733
+ join9(process.cwd(), "public"),
2282
2734
  // Fallback: relative to cwd
2283
- join8(process.cwd(), "server/public")
2735
+ join9(process.cwd(), "server/public")
2284
2736
  // Fallback: from root
2285
2737
  ];
2286
2738
  publicDir = possiblePublicDirs.find((dir) => existsSync9(dir));
@@ -2300,7 +2752,7 @@ var init_index = __esm({
2300
2752
  });
2301
2753
  if (publicDir && existsSync9(publicDir)) {
2302
2754
  app.get("*", (req, res) => {
2303
- res.sendFile(join8(publicDir, "index.html"));
2755
+ res.sendFile(join9(publicDir, "index.html"));
2304
2756
  });
2305
2757
  }
2306
2758
  app.use(errorHandler);
@@ -2317,12 +2769,12 @@ init_skillService();
2317
2769
  init_workspaceService();
2318
2770
  import { program } from "commander";
2319
2771
  import open from "open";
2320
- import { existsSync as existsSync10, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
2321
- import { join as join9 } from "path";
2772
+ import { existsSync as existsSync10, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
2773
+ import { join as join10 } from "path";
2322
2774
  import readline from "readline";
2323
2775
  var { skillverseHome: SKILLVERSE_HOME } = config;
2324
- var AUTH_FILE = join9(SKILLVERSE_HOME, "auth.json");
2325
- var CONFIG_FILE2 = join9(SKILLVERSE_HOME, "config.json");
2776
+ var AUTH_FILE = join10(SKILLVERSE_HOME, "auth.json");
2777
+ var CONFIG_FILE2 = join10(SKILLVERSE_HOME, "config.json");
2326
2778
  program.hook("preAction", async (thisCommand, actionCommand) => {
2327
2779
  if (["list", "start", "install", "add", "remove", "import", "search"].includes(actionCommand.name())) {
2328
2780
  await ensureDatabaseInitialized();
@@ -2330,14 +2782,14 @@ program.hook("preAction", async (thisCommand, actionCommand) => {
2330
2782
  });
2331
2783
  function getRegistryUrl() {
2332
2784
  if (existsSync10(CONFIG_FILE2)) {
2333
- const config3 = JSON.parse(readFileSync3(CONFIG_FILE2, "utf-8"));
2785
+ const config3 = JSON.parse(readFileSync4(CONFIG_FILE2, "utf-8"));
2334
2786
  return config3.registryUrl || "http://localhost:4000";
2335
2787
  }
2336
2788
  return process.env.SKILLVERSE_REGISTRY || "http://localhost:4000";
2337
2789
  }
2338
2790
  function getAuthToken() {
2339
2791
  if (existsSync10(AUTH_FILE)) {
2340
- const auth = JSON.parse(readFileSync3(AUTH_FILE, "utf-8"));
2792
+ const auth = JSON.parse(readFileSync4(AUTH_FILE, "utf-8"));
2341
2793
  return auth.token || null;
2342
2794
  }
2343
2795
  return null;
@@ -2449,7 +2901,7 @@ program.command("publish [path]").description("Publish a skill to the Registry")
2449
2901
  const form = new FormData();
2450
2902
  form.append("name", skillName);
2451
2903
  if (options.description) form.append("description", options.description);
2452
- form.append("bundle", readFileSync3(bundlePath), {
2904
+ form.append("bundle", readFileSync4(bundlePath), {
2453
2905
  filename: `${skillName}.tar.gz`,
2454
2906
  contentType: "application/gzip"
2455
2907
  });
@@ -2508,7 +2960,7 @@ program.command("search <query>").description("Search for skills in the Registry
2508
2960
  }
2509
2961
  });
2510
2962
  program.command("config").description("Configure SkillVerse settings").option("-r, --registry <url>", "Set default registry URL").action((options) => {
2511
- const config3 = existsSync10(CONFIG_FILE2) ? JSON.parse(readFileSync3(CONFIG_FILE2, "utf-8")) : {};
2963
+ const config3 = existsSync10(CONFIG_FILE2) ? JSON.parse(readFileSync4(CONFIG_FILE2, "utf-8")) : {};
2512
2964
  if (options.registry) {
2513
2965
  config3.registryUrl = options.registry;
2514
2966
  writeFileSync2(CONFIG_FILE2, JSON.stringify(config3, null, 2));
@@ -2561,7 +3013,7 @@ program.command("install [gitUrl]").description("Install a skill from Git URL").
2561
3013
  }
2562
3014
  });
2563
3015
  program.command("add").description("Add a local skill").requiredOption("-p, --path <path>", "Path to skill directory").option("-a, --agent <agent>", "Link to agent workspace").action(async (options) => {
2564
- const sourcePath = options.path.startsWith("/") ? options.path : join9(process.cwd(), options.path);
3016
+ const sourcePath = options.path.startsWith("/") ? options.path : join10(process.cwd(), options.path);
2565
3017
  if (!existsSync10(sourcePath)) {
2566
3018
  console.error(`\u274C Source path not found: ${sourcePath}`);
2567
3019
  process.exit(1);