@lenne.tech/cli
Version:
lenne.Tech CLI: lt
160 lines (159 loc) • 7.3 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_EXTERNAL_PLUGINS = exports.MARKETPLACES = void 0;
exports.fetchAvailablePlugins = fetchAvailablePlugins;
exports.fetchPluginsFromMarketplace = fetchPluginsFromMarketplace;
exports.printAvailablePlugins = printAvailablePlugins;
/**
* Plugin marketplace utilities
* Handles fetching and managing plugins from GitHub-based marketplaces
*/
const json_utils_1 = require("./json-utils");
/**
* Available marketplaces for plugin discovery
*/
exports.MARKETPLACES = [
{
apiBase: 'https://api.github.com/repos/lenneTech/claude-code/contents',
name: 'lenne-tech',
rawBase: 'https://raw.githubusercontent.com/lenneTech/claude-code/main',
repo: 'lenneTech/claude-code',
},
{
apiBase: 'https://api.github.com/repos/anthropics/claude-plugins-official/contents',
name: 'claude-plugins-official',
rawBase: 'https://raw.githubusercontent.com/anthropics/claude-plugins-official/main',
repo: 'anthropics/claude-plugins-official',
},
];
/**
* Default plugins to install when no specific plugins are requested
* These are installed in addition to all plugins from the primary marketplace (lenne-tech)
*/
exports.DEFAULT_EXTERNAL_PLUGINS = [
{ marketplaceName: 'claude-plugins-official', pluginName: 'typescript-lsp' },
];
/**
* Fetch available plugins from all configured marketplaces
* @param spin - Spinner factory function from toolbox
* @returns Array of plugin configurations
* @throws Error if no plugins are found
*/
function fetchAvailablePlugins(spin) {
return __awaiter(this, void 0, void 0, function* () {
const spinner = spin('Fetching available plugins from marketplaces');
try {
// Fetch plugins from all marketplaces in parallel
const results = yield Promise.all(exports.MARKETPLACES.map((marketplace) => fetchPluginsFromMarketplace(marketplace)));
// Flatten results
const plugins = results.flat();
if (plugins.length === 0) {
spinner.fail('No plugins found in any marketplace');
throw new Error('No plugins found');
}
// Group by marketplace for display
const byMarketplace = exports.MARKETPLACES.map((m) => ({
count: plugins.filter((p) => p.marketplaceName === m.name).length,
name: m.name,
})).filter((m) => m.count > 0);
const summary = byMarketplace.map((m) => `${m.name}: ${m.count}`).join(', ');
spinner.succeed(`Found ${plugins.length} plugins (${summary})`);
return plugins;
}
catch (err) {
spinner.fail('Failed to fetch plugins from marketplaces');
throw err;
}
});
}
/**
* Fetch available plugins from a single marketplace
* First tries central marketplace.json, then falls back to directory scan
* @param marketplace - Marketplace configuration
* @returns Array of plugin configurations
*/
function fetchPluginsFromMarketplace(marketplace) {
return __awaiter(this, void 0, void 0, function* () {
const plugins = [];
try {
// First try to read central marketplace.json (used by official marketplace)
const marketplaceJsonUrl = `${marketplace.rawBase}/.claude-plugin/marketplace.json`;
const marketplaceJsonResponse = yield fetch(marketplaceJsonUrl);
if (marketplaceJsonResponse.ok) {
const text = yield marketplaceJsonResponse.text();
const marketplaceManifest = (0, json_utils_1.safeJsonParse)(text);
if ((marketplaceManifest === null || marketplaceManifest === void 0 ? void 0 : marketplaceManifest.plugins) && marketplaceManifest.plugins.length > 0) {
for (const plugin of marketplaceManifest.plugins) {
plugins.push({
description: plugin.description || '',
marketplaceName: marketplace.name,
marketplaceRepo: marketplace.repo,
pluginName: plugin.name,
});
}
return plugins;
}
}
// Fallback: Get list of plugin directories and read individual plugin.json files
const dirResponse = yield fetch(`${marketplace.apiBase}/plugins`);
if (!dirResponse.ok) {
return plugins;
}
const text = yield dirResponse.text();
const directories = (0, json_utils_1.safeJsonParse)(text);
if (!directories) {
return plugins;
}
const pluginDirs = directories.filter((d) => d.type === 'dir');
// Fetch plugin.json for each plugin in parallel
const manifestPromises = pluginDirs.map((dir) => __awaiter(this, void 0, void 0, function* () {
try {
const manifestUrl = `${marketplace.rawBase}/plugins/${dir.name}/.claude-plugin/plugin.json`;
const manifestResponse = yield fetch(manifestUrl);
if (manifestResponse.ok) {
const manifestText = yield manifestResponse.text();
const manifest = (0, json_utils_1.safeJsonParse)(manifestText);
if (manifest) {
return {
description: manifest.description,
marketplaceName: marketplace.name,
marketplaceRepo: marketplace.repo,
pluginName: manifest.name,
};
}
}
}
catch (_a) {
// Skip plugins without valid manifest
}
return null;
}));
const results = yield Promise.all(manifestPromises);
plugins.push(...results.filter((p) => p !== null));
}
catch (_a) {
// Marketplace fetch failed, return empty array
}
return plugins;
});
}
/**
* Print available plugins list
* @param plugins - Array of plugin configurations
* @param info - Info print function from toolbox
*/
function printAvailablePlugins(plugins, info) {
info('Available plugins:');
for (const plugin of plugins) {
info(` ${plugin.pluginName} - ${plugin.description}`);
}
}