sync: AI review scores, reviewed page, search improvements, score filter

- Surface AI review scores across skill pages, browse, and search
- Add dedicated /reviewed page with score filtering
- Add recommended sort using Meilisearch relevance + AI scores
- Fix search sort defaulting to stars instead of recommended
- Fix CLI TypeScript null check for aiScore in search command
- Adjust cache TTL for reviewed content

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-03-14 05:05:37 +03:30
parent 4212b5ba47
commit 3e22d2e238
34 changed files with 929 additions and 194 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "skillhub",
"version": "0.2.5",
"version": "0.2.9",
"description": "CLI tool for managing AI Agent skills - search, install, and update skills for Claude, Codex, Copilot, and more",
"author": "SkillHub Contributors",
"license": "MIT",

View File

@@ -39,7 +39,12 @@ export async function install(skillId: string, options: InstallOptions): Promise
try {
skillInfo = await getSkill(skillId);
if (skillInfo) {
spinner.succeed(`Found in registry: ${skillInfo.name}`);
const reviewBadge = skillInfo.aiScore && skillInfo.reviewStatus === 'ai-reviewed'
? chalk.dim(` | AI: ${skillInfo.aiScore}/100`)
: skillInfo.reviewStatus === 'verified'
? chalk.green(` | AI: ${skillInfo.aiScore}/100 ✓`)
: '';
spinner.succeed(`Found in registry: ${skillInfo.name}${reviewBadge}`);
spinner.start('Preparing installation...');
}
} catch (error) {

View File

@@ -18,7 +18,7 @@ export async function search(query: string, options: SearchOptions): Promise<voi
try {
const limit = parseInt(options.limit || '10');
const page = parseInt(options.page || '1');
const sort = options.sort || 'downloads';
const sort = options.sort || 'recommended';
const result = await searchSkills(query, {
platform: options.platform,
limit,
@@ -34,7 +34,7 @@ export async function search(query: string, options: SearchOptions): Promise<voi
return;
}
const sortLabel = { downloads: 'downloads', stars: 'GitHub stars', rating: 'rating', recent: 'recently updated' }[sort] || sort;
const sortLabel = { recommended: 'recommended', downloads: 'downloads', stars: 'GitHub stars', rating: 'rating', recent: 'recently updated', aiScore: 'AI score' }[sort] || sort;
console.log(chalk.bold(`Found ${result.pagination.total} skills (sorted by ${sortLabel}):\n`));
// Print results as a table
@@ -57,9 +57,14 @@ export async function search(query: string, options: SearchOptions): Promise<voi
`${num} ${verified} ${chalk.cyan(skill.id.padEnd(38))} ${security}`
);
// Second line: downloads, stars, description
// Second line: AI score (if reviewed) + downloads + stars + description
const aiScore = skill.aiScore;
const hasAiScore = aiScore != null && aiScore > 0 && skill.reviewStatus && skill.reviewStatus !== 'unreviewed' && skill.reviewStatus !== 'auto-scored';
const aiPrefix = hasAiScore
? `${chalk.magenta('AI')} ${(aiScore >= 75 ? chalk.green : aiScore >= 50 ? chalk.yellow : chalk.dim)(String(aiScore))} `
: '';
console.log(
`${formatNumber(skill.downloadCount).padStart(6)}${formatNumber(skill.githubStars).padStart(6)} ${chalk.dim(skill.description.slice(0, 55))}${skill.description.length > 55 ? '...' : ''}`
` ${aiPrefix}${formatNumber(skill.downloadCount).padStart(6)}${formatNumber(skill.githubStars).padStart(6)} ${chalk.dim(skill.description.slice(0, hasAiScore ? 45 : 55))}${skill.description.length > (hasAiScore ? 45 : 55) ? '...' : ''}`
);
// Third line: Rating (only if ratingCount >= 3)
@@ -83,8 +88,8 @@ export async function search(query: string, options: SearchOptions): Promise<voi
);
}
if (sort === 'downloads') {
console.log(chalk.dim(`Sort options: ${chalk.white('--sort stars|rating|recent')}`));
if (sort === 'recommended') {
console.log(chalk.dim(`Sort options: ${chalk.white('--sort aiScore|downloads|stars|rating|recent')}`));
}
} catch (error) {
spinner.fail('Search failed');

View File

@@ -46,7 +46,7 @@ program
.command('search <query>')
.description('Search for skills in the registry')
.option('-p, --platform <platform>', 'Filter by platform')
.option('-s, --sort <sort>', 'Sort by: downloads, stars, rating, recent', 'downloads')
.option('-s, --sort <sort>', 'Sort by: recommended, aiScore, downloads, stars, rating, recent', 'recommended')
.option('-l, --limit <number>', 'Number of results', '10')
.option('--page <number>', 'Page number', '1')
.action(async (query: string, options) => {

View File

@@ -126,6 +126,8 @@ export interface SkillInfo {
sourceFormat?: string;
rating?: number | null;
ratingCount?: number | null;
reviewStatus?: string | null;
aiScore?: number | null;
isVerified: boolean;
compatibility: {
platforms: string[];