sync: malware page, security alerts, review diagnostics, skill detail improvements
- Add malware security advisory page and SecurityAlertBanner component - Add review diagnostics API and reviewed stats page - Improve skill detail page with better scoring display and metadata - Update review API endpoints with enhanced filtering and stats - Add skill file serving improvements and cache enhancements - Remove legacy curation scripts (moved to internal tooling) - Update CLI search with score filtering support Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "skillhub",
|
||||
"version": "0.2.9",
|
||||
"version": "0.2.11",
|
||||
"description": "CLI tool for managing AI Agent skills - search, install, and update skills for Claude, Codex, Copilot, and more",
|
||||
"author": "SkillHub Contributors",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -39,6 +39,14 @@ export async function install(skillId: string, options: InstallOptions): Promise
|
||||
try {
|
||||
skillInfo = await getSkill(skillId);
|
||||
if (skillInfo) {
|
||||
// Block installation of malicious skills
|
||||
if (skillInfo.isMalicious) {
|
||||
spinner.fail(chalk.red('This skill has been flagged as malicious (contains malware).'));
|
||||
console.log(chalk.red('Installation blocked for your safety.'));
|
||||
console.log(chalk.dim(`See: https://skills.palebluedot.live/en/skill/${skillId}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const reviewBadge = skillInfo.aiScore && skillInfo.reviewStatus === 'ai-reviewed'
|
||||
? chalk.dim(` | AI: ${skillInfo.aiScore}/100`)
|
||||
: skillInfo.reviewStatus === 'verified'
|
||||
|
||||
@@ -57,14 +57,15 @@ export async function search(query: string, options: SearchOptions): Promise<voi
|
||||
`${num} ${verified} ${chalk.cyan(skill.id.padEnd(38))} ${security}`
|
||||
);
|
||||
|
||||
// Second line: AI score (if reviewed) + downloads + stars + description
|
||||
// Second line: AI score + 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))} `
|
||||
const isAiReviewed = aiScore != null && aiScore > 0 && skill.reviewStatus && skill.reviewStatus !== 'unreviewed' && skill.reviewStatus !== 'auto-scored';
|
||||
const showAiScore = isAiReviewed;
|
||||
const aiPrefix = showAiScore
|
||||
? `${chalk.magenta('AI')} ${(aiScore >= 75 ? chalk.green : aiScore >= 50 ? chalk.yellow : chalk.dim)(String(aiScore).padStart(2))} `
|
||||
: '';
|
||||
console.log(
|
||||
` ${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) ? '...' : ''}`
|
||||
` ${aiPrefix}⬇ ${formatNumber(skill.downloadCount).padStart(6)} ⭐ ${formatNumber(skill.githubStars).padStart(6)} ${chalk.dim(skill.description.slice(0, showAiScore ? 45 : 55))}${skill.description.length > (showAiScore ? 45 : 55) ? '...' : ''}`
|
||||
);
|
||||
|
||||
// Third line: Rating (only if ratingCount >= 3)
|
||||
|
||||
@@ -128,6 +128,7 @@ export interface SkillInfo {
|
||||
ratingCount?: number | null;
|
||||
reviewStatus?: string | null;
|
||||
aiScore?: number | null;
|
||||
isMalicious?: boolean;
|
||||
isVerified: boolean;
|
||||
compatibility: {
|
||||
platforms: string[];
|
||||
|
||||
Reference in New Issue
Block a user