claude-flow
Version:
Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration
428 lines • 20.8 kB
JavaScript
/**
* V3 CLI Transfer Store Commands
* Pattern marketplace - list, search, download, publish
*/
import { output } from '../output.js';
import { createDiscoveryService, createDownloader, createPublisher, searchPatterns, } from '../transfer/index.js';
// Store list subcommand
export const storeListCommand = {
name: 'list',
aliases: ['ls'],
description: 'List patterns from decentralized registry',
options: [
{ name: 'registry', short: 'r', type: 'string', description: 'Registry name (default: claude-flow-official)' },
{ name: 'category', short: 'c', type: 'string', description: 'Filter by category' },
{ name: 'featured', short: 'f', type: 'boolean', description: 'Show featured patterns' },
{ name: 'trending', short: 't', type: 'boolean', description: 'Show trending patterns' },
{ name: 'newest', short: 'n', type: 'boolean', description: 'Show newest patterns' },
{ name: 'limit', short: 'l', type: 'number', description: 'Maximum results', default: 20 },
],
examples: [
{ command: 'claude-flow hooks transfer store list', description: 'List all patterns' },
{ command: 'claude-flow hooks transfer store list --category routing', description: 'List routing patterns' },
{ command: 'claude-flow hooks transfer store list --featured', description: 'List featured patterns' },
],
action: async (ctx) => {
const registryName = ctx.flags.registry;
const category = ctx.flags.category;
const featured = ctx.flags.featured;
const trending = ctx.flags.trending;
const newest = ctx.flags.newest;
const limit = ctx.flags.limit || 20;
const spinner = output.createSpinner({ text: 'Discovering registry...', spinner: 'dots' });
spinner.start();
try {
const discovery = createDiscoveryService();
const result = await discovery.discoverRegistry(registryName);
if (!result.success || !result.registry) {
spinner.fail('Failed to discover registry');
output.printError(result.error || 'Unknown error');
return { success: false, exitCode: 1 };
}
spinner.succeed(`Connected to ${result.source}`);
// Get patterns based on flags
let patterns = result.registry.patterns;
let title = 'Available Patterns';
if (featured) {
patterns = result.registry.featured
.map(id => patterns.find(p => p.id === id))
.filter((p) => p !== undefined);
title = 'Featured Patterns';
}
else if (trending) {
patterns = result.registry.trending
.map(id => patterns.find(p => p.id === id))
.filter((p) => p !== undefined);
title = 'Trending Patterns';
}
else if (newest) {
patterns = result.registry.newest
.map(id => patterns.find(p => p.id === id))
.filter((p) => p !== undefined);
title = 'Newest Patterns';
}
if (category) {
patterns = patterns.filter(p => p.categories.includes(category));
title = `Patterns in "${category}"`;
}
patterns = patterns.slice(0, limit);
output.writeln();
output.writeln(output.bold(title));
output.writeln(output.dim('─'.repeat(70)));
if (patterns.length === 0) {
output.writeln(output.dim('No patterns found'));
}
else {
output.printTable({
columns: [
{ key: 'name', header: 'Name', width: 25 },
{ key: 'version', header: 'Version', width: 10 },
{ key: 'downloads', header: 'Downloads', width: 12 },
{ key: 'rating', header: 'Rating', width: 10 },
{ key: 'verified', header: 'Status', width: 12 },
],
data: patterns.map(p => ({
name: p.displayName,
version: p.version,
downloads: p.downloads.toLocaleString(),
rating: `${p.rating.toFixed(1)}/5`,
verified: p.verified ? output.success('Verified') : output.dim('Community'),
})),
});
}
output.writeln();
output.writeln(output.dim(`Registry: ${result.source} | Total: ${result.registry.totalPatterns} patterns`));
return { success: true };
}
catch (error) {
spinner.fail('Error listing patterns');
output.printError(String(error));
return { success: false, exitCode: 1 };
}
},
};
// Store search subcommand
export const storeSearchCommand = {
name: 'search',
description: 'Search patterns in the decentralized registry',
options: [
{ name: 'query', short: 'q', type: 'string', description: 'Search query', required: true },
{ name: 'category', short: 'c', type: 'string', description: 'Filter by category' },
{ name: 'language', short: 'l', type: 'string', description: 'Filter by language' },
{ name: 'framework', short: 'f', type: 'string', description: 'Filter by framework' },
{ name: 'tags', short: 't', type: 'string', description: 'Filter by tags (comma-separated)' },
{ name: 'min-rating', type: 'number', description: 'Minimum rating (0-5)' },
{ name: 'verified', short: 'v', type: 'boolean', description: 'Only verified patterns' },
{ name: 'limit', type: 'number', description: 'Maximum results', default: 20 },
],
examples: [
{ command: 'claude-flow hooks transfer store search -q "routing"', description: 'Search for routing patterns' },
{ command: 'claude-flow hooks transfer store search -q "react" --language typescript', description: 'Search with filters' },
],
action: async (ctx) => {
const query = (ctx.args[0] || ctx.flags.query);
if (!query) {
output.printError('Search query is required. Use --query or -q flag.');
return { success: false, exitCode: 1 };
}
const spinner = output.createSpinner({ text: 'Searching patterns...', spinner: 'dots' });
spinner.start();
try {
const discovery = createDiscoveryService();
const result = await discovery.discoverRegistry();
if (!result.success || !result.registry) {
spinner.fail('Failed to discover registry');
return { success: false, exitCode: 1 };
}
const searchOptions = {
query,
category: ctx.flags.category,
language: ctx.flags.language,
framework: ctx.flags.framework,
tags: ctx.flags.tags ? ctx.flags.tags.split(',') : undefined,
minRating: ctx.flags.minRating,
verified: ctx.flags.verified,
limit: ctx.flags.limit || 20,
};
const searchResult = searchPatterns(result.registry, searchOptions);
spinner.succeed(`Found ${searchResult.total} patterns`);
output.writeln();
output.writeln(output.bold(`Search Results for "${query}"`));
output.writeln(output.dim('─'.repeat(70)));
if (searchResult.patterns.length === 0) {
output.writeln(output.dim('No patterns match your search'));
}
else {
for (const pattern of searchResult.patterns) {
output.writeln();
output.writeln(` ${output.bold(pattern.displayName)} ${output.dim(`v${pattern.version}`)}`);
output.writeln(` ${output.dim(pattern.description.slice(0, 70))}...`);
output.writeln(` ${output.dim('Tags:')} ${pattern.tags.slice(0, 5).join(', ')}`);
output.writeln(` ${output.dim('Rating:')} ${pattern.rating.toFixed(1)}/5 | ${output.dim('Downloads:')} ${pattern.downloads}`);
}
}
output.writeln();
output.writeln(output.dim(`Showing ${searchResult.patterns.length} of ${searchResult.total} results`));
return { success: true, data: searchResult };
}
catch (error) {
spinner.fail('Search failed');
output.printError(String(error));
return { success: false, exitCode: 1 };
}
},
};
// Store download subcommand
export const storeDownloadCommand = {
name: 'download',
aliases: ['get', 'install'],
description: 'Download a pattern from the registry',
options: [
{ name: 'name', short: 'n', type: 'string', description: 'Pattern name or ID', required: true },
{ name: 'output', short: 'o', type: 'string', description: 'Output path' },
{ name: 'verify', short: 'v', type: 'boolean', description: 'Verify checksum', default: true },
{ name: 'import', short: 'i', type: 'boolean', description: 'Import after download' },
],
examples: [
{ command: 'claude-flow hooks transfer store download -n seraphine-genesis', description: 'Download pattern' },
{ command: 'claude-flow hooks transfer store download -n seraphine-genesis --import', description: 'Download and import' },
],
action: async (ctx) => {
const patternName = (ctx.args[0] || ctx.flags.name);
if (!patternName) {
output.printError('Pattern name is required. Use --name or -n flag.');
return { success: false, exitCode: 1 };
}
const spinner = output.createSpinner({ text: 'Finding pattern...', spinner: 'dots' });
spinner.start();
try {
const discovery = createDiscoveryService();
const result = await discovery.discoverRegistry();
if (!result.success || !result.registry) {
spinner.fail('Failed to discover registry');
return { success: false, exitCode: 1 };
}
// Find pattern by name or ID
const pattern = result.registry.patterns.find(p => p.name === patternName || p.id === patternName || p.displayName === patternName);
if (!pattern) {
spinner.fail(`Pattern not found: ${patternName}`);
return { success: false, exitCode: 1 };
}
spinner.setText(`Downloading ${pattern.displayName}...`);
const downloader = createDownloader();
const downloadResult = await downloader.downloadPattern(pattern, {
output: ctx.flags.output,
verify: ctx.flags.verify,
import: ctx.flags.import,
}, (progress) => {
spinner.setText(`Downloading... ${progress.percentage}%`);
});
if (!downloadResult.success) {
spinner.fail('Download failed');
return { success: false, exitCode: 1 };
}
spinner.succeed(`Downloaded ${pattern.displayName}`);
output.writeln();
output.printBox([
`Pattern: ${pattern.displayName}`,
`Version: ${pattern.version}`,
`Size: ${downloadResult.size.toLocaleString()} bytes`,
`Verified: ${downloadResult.verified ? 'Yes' : 'No'}`,
downloadResult.outputPath ? `Path: ${downloadResult.outputPath}` : '',
downloadResult.imported ? 'Status: Imported' : '',
].filter(Boolean).join('\n'), 'Download Complete');
return { success: true, data: downloadResult };
}
catch (error) {
spinner.fail('Download failed');
output.printError(String(error));
return { success: false, exitCode: 1 };
}
},
};
// Store publish subcommand
export const storePublishCommand = {
name: 'publish',
aliases: ['contribute'],
description: 'Publish a pattern to the decentralized registry',
options: [
{ name: 'input', short: 'i', type: 'string', description: 'Input CFP file path', required: true },
{ name: 'name', short: 'n', type: 'string', description: 'Pattern name', required: true },
{ name: 'description', short: 'd', type: 'string', description: 'Pattern description', required: true },
{ name: 'categories', short: 'c', type: 'string', description: 'Categories (comma-separated)', required: true },
{ name: 'tags', short: 't', type: 'string', description: 'Tags (comma-separated)', required: true },
{ name: 'license', short: 'l', type: 'string', description: 'SPDX license', default: 'MIT' },
{ name: 'anonymize', short: 'a', type: 'string', description: 'Anonymization level', default: 'strict' },
{ name: 'language', type: 'string', description: 'Primary language' },
{ name: 'framework', type: 'string', description: 'Primary framework' },
],
examples: [
{ command: 'claude-flow hooks transfer store publish -i patterns.cfp -n my-patterns -d "My patterns" -c routing -t custom', description: 'Publish pattern' },
],
action: async (ctx) => {
const inputPath = ctx.flags.input;
const name = ctx.flags.name;
const description = ctx.flags.description;
if (!inputPath || !name || !description) {
output.printError('Input path, name, and description are required.');
return { success: false, exitCode: 1 };
}
const spinner = output.createSpinner({ text: 'Preparing pattern...', spinner: 'dots' });
spinner.start();
try {
// Read and parse CFP file
const fs = await import('fs');
if (!fs.existsSync(inputPath)) {
spinner.fail(`File not found: ${inputPath}`);
return { success: false, exitCode: 1 };
}
const content = fs.readFileSync(inputPath, 'utf-8');
const cfp = JSON.parse(content);
spinner.setText('Publishing to IPFS...');
const publisher = createPublisher();
const result = await publisher.publishPattern(cfp, {
name,
displayName: name.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase()),
description,
categories: ctx.flags.categories.split(',').map(s => s.trim()),
tags: ctx.flags.tags.split(',').map(s => s.trim()),
license: ctx.flags.license || 'MIT',
anonymize: ctx.flags.anonymize || 'strict',
language: ctx.flags.language,
framework: ctx.flags.framework,
});
if (!result.success) {
spinner.fail('Publish failed');
output.printError(result.message);
return { success: false, exitCode: 1 };
}
spinner.succeed('Pattern published!');
output.writeln();
output.printBox([
`Pattern ID: ${result.patternId}`,
`CID: ${result.cid}`,
`Gateway URL: ${result.gatewayUrl}`,
``,
`Your pattern has been uploaded to IPFS.`,
`Submit a contribution request to add it to the official registry.`,
].join('\n'), 'Publish Complete');
return { success: true, data: result };
}
catch (error) {
spinner.fail('Publish failed');
output.printError(String(error));
return { success: false, exitCode: 1 };
}
},
};
// Store info subcommand
export const storeInfoCommand = {
name: 'info',
description: 'Show detailed information about a pattern',
options: [
{ name: 'name', short: 'n', type: 'string', description: 'Pattern name or ID', required: true },
],
examples: [
{ command: 'claude-flow hooks transfer store info -n seraphine-genesis', description: 'Show pattern info' },
],
action: async (ctx) => {
const patternName = (ctx.args[0] || ctx.flags.name);
if (!patternName) {
output.printError('Pattern name is required. Use --name or -n flag.');
return { success: false, exitCode: 1 };
}
const spinner = output.createSpinner({ text: 'Fetching pattern info...', spinner: 'dots' });
spinner.start();
try {
const discovery = createDiscoveryService();
const result = await discovery.discoverRegistry();
if (!result.success || !result.registry) {
spinner.fail('Failed to discover registry');
return { success: false, exitCode: 1 };
}
const pattern = result.registry.patterns.find(p => p.name === patternName || p.id === patternName || p.displayName === patternName);
if (!pattern) {
spinner.fail(`Pattern not found: ${patternName}`);
return { success: false, exitCode: 1 };
}
spinner.succeed('Pattern found');
output.writeln();
output.printBox([
`Name: ${pattern.displayName}`,
`ID: ${pattern.id}`,
`Version: ${pattern.version}`,
``,
`Description:`,
` ${pattern.description}`,
``,
`Author: ${pattern.author.displayName || pattern.author.id}`,
`License: ${pattern.license}`,
``,
`Categories: ${pattern.categories.join(', ')}`,
`Tags: ${pattern.tags.join(', ')}`,
``,
`Stats:`,
` Downloads: ${pattern.downloads.toLocaleString()}`,
` Rating: ${pattern.rating.toFixed(1)}/5 (${pattern.ratingCount} reviews)`,
``,
`IPFS:`,
` CID: ${pattern.cid}`,
` Size: ${pattern.size.toLocaleString()} bytes`,
` Checksum: ${pattern.checksum.slice(0, 16)}...`,
``,
`Trust: ${pattern.trustLevel}`,
`Verified: ${pattern.verified ? 'Yes' : 'No'}`,
`Min Version: ${pattern.minClaudeFlowVersion}`,
``,
`Created: ${pattern.createdAt}`,
`Updated: ${pattern.lastUpdated}`,
].join('\n'), 'Pattern Details');
return { success: true, data: pattern };
}
catch (error) {
spinner.fail('Error fetching pattern info');
output.printError(String(error));
return { success: false, exitCode: 1 };
}
},
};
// Main store command
export const storeCommand = {
name: 'store',
description: 'Pattern marketplace - list, search, download, publish',
subcommands: [
storeListCommand,
storeSearchCommand,
storeDownloadCommand,
storePublishCommand,
storeInfoCommand,
],
examples: [
{ command: 'claude-flow hooks transfer store list', description: 'List patterns' },
{ command: 'claude-flow hooks transfer store search -q "routing"', description: 'Search patterns' },
{ command: 'claude-flow hooks transfer store download -n seraphine-genesis', description: 'Download pattern' },
{ command: 'claude-flow hooks transfer store publish -i patterns.cfp ...', description: 'Publish pattern' },
],
action: async () => {
output.writeln();
output.writeln(output.bold('Claude Flow Pattern Store'));
output.writeln(output.dim('Decentralized pattern marketplace via IPFS'));
output.writeln();
output.writeln('Subcommands:');
output.printList([
'list - List patterns from registry',
'search - Search patterns',
'download - Download a pattern',
'publish - Publish a pattern',
'info - Show pattern details',
]);
output.writeln();
output.writeln('Example:');
output.writeln(output.dim(' claude-flow hooks transfer store list --featured'));
output.writeln(output.dim(' claude-flow hooks transfer store search -q "routing"'));
output.writeln(output.dim(' claude-flow hooks transfer store download -n seraphine-genesis'));
return { success: true };
},
};
export default storeCommand;
//# sourceMappingURL=transfer-store.js.map