aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
168 lines • 6.6 kB
JavaScript
/**
* Marketplace Command Handler
*
* Implements `aiwg marketplace <subcommand>` for discovering and listing
* packages across all configured marketplace sources (local, ClawHub,
* OpenClaw, etc.).
*
* Subcommands:
* search <query> Fan-out search to all adapters, display with source attribution
* list List installed marketplace packages
*
* Flags:
* --source <id> Limit to a single adapter (e.g. clawhub, openclaw, local)
* --json Emit structured JSON for programmatic use
*
* @implements #805
*/
import { searchSkills } from '../../skills/registry.js';
import { listInstalledPackages } from '../../packages/registry.js';
import * as ui from '../ui.js';
// ── Usage string ───────────────────────────────────────────────────────────
const USAGE = [
'Usage:',
' aiwg marketplace search <query> Search across all marketplace sources',
' aiwg marketplace list List installed marketplace packages',
'',
'Flags:',
' --source <id> Limit search to a specific source (clawhub, openclaw, local)',
' --json Output structured JSON',
'',
'Examples:',
' aiwg marketplace search parallel-dispatch',
' aiwg marketplace search auth --source clawhub',
' aiwg marketplace search auth --json',
' aiwg marketplace list',
' aiwg marketplace list --json',
].join('\n');
// ── Flag helpers ───────────────────────────────────────────────────────────
function parseFlag(args, flag) {
const idx = args.indexOf(flag);
if (idx === -1 || idx + 1 >= args.length)
return undefined;
return args[idx + 1];
}
function hasFlag(args, flag) {
return args.includes(flag);
}
// ── Handler ────────────────────────────────────────────────────────────────
export const marketplaceHandler = {
id: 'marketplace',
name: 'Marketplace',
description: 'Search and manage marketplace packages (search, list)',
category: 'framework',
aliases: ['market'],
async execute(ctx) {
const subcommand = ctx.args[0];
if (!subcommand) {
return { exitCode: 0, message: USAGE };
}
if (subcommand === 'search') {
return handleSearch(ctx);
}
if (subcommand === 'list') {
return handleList(ctx);
}
return {
exitCode: 1,
message: `Error: Unknown subcommand '${subcommand}'\n\n${USAGE}`,
};
},
};
// ── Search ─────────────────────────────────────────────────────────────────
async function handleSearch(ctx) {
// First positional arg after 'search' that isn't a flag value
const query = ctx.args[1];
if (!query || query.startsWith('--')) {
return {
exitCode: 1,
message: 'Error: Query required\n\nUsage: aiwg marketplace search <query>',
};
}
const source = parseFlag(ctx.args, '--source');
const jsonMode = hasFlag(ctx.args, '--json');
const results = await searchSkills(query, source);
if (jsonMode) {
console.log(JSON.stringify(results, null, 2));
return { exitCode: 0 };
}
ui.blank();
console.log(` ${ui.brandMark()} ${ui.bold(`Marketplace Search: "${query}"`)}`);
if (source) {
ui.dim(` Source: ${source}`);
}
ui.rule();
if (results.length === 0) {
ui.dim(' No results found.');
ui.blank();
ui.dim(' Try a broader query or a different source.');
ui.blank();
return { exitCode: 0 };
}
// Column widths
const nameWidth = Math.max(12, ...results.map((r) => r.name.length));
const sourceWidth = Math.max(8, ...results.map((r) => r.source.length));
const pkgWidth = Math.max(10, ...results.map((r) => (r.package ?? '').length));
const header = [
'Name'.padEnd(nameWidth),
'Source'.padEnd(sourceWidth),
'Package'.padEnd(pkgWidth),
'Description',
].join(' ');
ui.dim(` ${header}`);
ui.dim(` ${'─'.repeat(header.length)}`);
for (const r of results) {
const row = [
r.name.padEnd(nameWidth),
r.source.padEnd(sourceWidth),
(r.package ?? '').padEnd(pkgWidth),
r.description.length > 60 ? r.description.slice(0, 57) + '...' : r.description,
].join(' ');
console.log(` ${row}`);
}
ui.blank();
ui.dim(` ${results.length} result${results.length !== 1 ? 's' : ''}`);
ui.blank();
return { exitCode: 0 };
}
// ── List ───────────────────────────────────────────────────────────────────
async function handleList(ctx) {
const jsonMode = hasFlag(ctx.args, '--json');
const packages = await listInstalledPackages();
if (jsonMode) {
console.log(JSON.stringify(packages, null, 2));
return { exitCode: 0 };
}
ui.blank();
console.log(` ${ui.brandMark()} ${ui.bold('Installed Marketplace Packages')}`);
ui.rule();
if (packages.length === 0) {
ui.dim(' No packages installed.');
ui.blank();
ui.dim(' Install a package: aiwg install owner/name');
ui.blank();
return { exitCode: 0 };
}
const keyWidth = Math.max(12, ...packages.map((p) => p.key.length));
const versionWidth = Math.max(7, ...packages.map((p) => p.version.length));
const header = [
'Package'.padEnd(keyWidth),
'Version'.padEnd(versionWidth),
'Type',
].join(' ');
ui.dim(` ${header}`);
ui.dim(` ${'─'.repeat(header.length)}`);
for (const pkg of packages) {
const row = [
pkg.key.padEnd(keyWidth),
pkg.version.padEnd(versionWidth),
pkg.type,
].join(' ');
console.log(` ${row}`);
}
ui.blank();
ui.dim(` ${packages.length} package${packages.length !== 1 ? 's' : ''} installed`);
ui.blank();
return { exitCode: 0 };
}
//# sourceMappingURL=marketplace.js.map