sync: SEO canonical URLs, curation improvements, batch scripts, stats fix

- Dynamic canonical URLs and alternates for all pages (SEO fix)
- Shared seo.ts utility for canonical path generation
- Improved duplicate detection: standalone > aggregator priority, forks/created_at tie-breakers
- browseReadyFilter now includes unique aggregator skills
- Stats aligned with browseReadyFilter (was showing 16k instead of 62k+)
- Re-review mechanism: content_hash changes trigger needs-re-review
- review_status field added to skills schema
- Batch security scan and quality score scripts
- Indexer Dockerfile updated for curation deps
- Removed roadmap from README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-02-21 23:40:03 +03:30
parent 77917bf91f
commit 3f599a23fb
30 changed files with 1092 additions and 50 deletions

View File

@@ -6,6 +6,8 @@ import Link from 'next/link';
import { ArrowLeft, ArrowRight } from 'lucide-react';
import { createDb, skills, sql } from '@skillhub/db';
import { formatPromptSkillCount } from '@/lib/format-number';
import { getPageAlternates } from '@/lib/seo';
export const dynamic = 'force-dynamic';
@@ -15,7 +17,7 @@ async function getSkillCount(): Promise<string> {
const result = await db
.select({ count: sql<number>`count(*)::int` })
.from(skills)
.where(sql`${skills.isDuplicate} = false AND (${skills.skillType} IS NULL OR ${skills.skillType} != 'aggregator')`);
.where(sql`${skills.isDuplicate} = false`);
return formatPromptSkillCount(result[0]?.count ?? 16000);
} catch {
return '16,000+';
@@ -56,6 +58,18 @@ Search for unfamiliar tech or complex tasks. Only install "Pass" security status
};
}
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
return {
alternates: getPageAlternates(locale, '/docs/getting-started'),
};
}
export default async function GettingStartedPage({
params,
}: {