sync: stale skill detection, sentry filtering, claim removal, review improvements

Stale Detection:
- Detect skills removed/moved from GitHub (3 consecutive 404s threshold)
- Hide stale skills from browse/search, show warning banner on detail page
- Serve cached files with isStale flag when GitHub returns 404
- Add stale-check crawler command for batch verification
- CLI shows warning when installing stale skills from cache

Sentry & Error Handling:
- Filter browser extension errors and add denyUrls
- Anti-inflation measures and sentinel recalibration for curation

Claim & Removal:
- Enhanced ClaimForm with repo-level removal support
- Add repo-removal-request API endpoint with tests
- Improved owner page with bilingual content

Review Pipeline:
- Review version and reviewer tracking in submit API
- Source format filter for pending reviews
- Updated review tests

Other:
- Updated i18n strings (en/fa)
- BrowseFilters improvements
- Dockerfile updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-03-12 05:59:26 +03:30
parent 447f9eff59
commit 4212b5ba47
31 changed files with 1590 additions and 58 deletions

View File

@@ -42,6 +42,7 @@ export default async function ClaimPage({
tabs: {
remove: t('tabs.remove'),
add: t('tabs.add'),
removeRepo: t('tabs.removeRepo'),
},
form: {
skillId: t('form.skillId'),
@@ -78,6 +79,27 @@ export default async function ClaimPage({
foundSkillsIn: t('addSuccess.foundSkillsIn'),
root: t('addSuccess.root'),
andMore: t.raw('addSuccess.andMore') as string,
reEnabledTitle: t('addSuccess.reEnabledTitle'),
reEnabledDescription: t('addSuccess.reEnabledDescription'),
},
removeRepoForm: {
repoUrl: t('removeRepoForm.repoUrl'),
repoUrlPlaceholder: t('removeRepoForm.repoUrlPlaceholder'),
repoUrlHelp: t('removeRepoForm.repoUrlHelp'),
reason: t('removeRepoForm.reason'),
reasonPlaceholder: t('removeRepoForm.reasonPlaceholder'),
submit: t('removeRepoForm.submit'),
submitting: t('removeRepoForm.submitting'),
},
removeRepoSuccess: {
title: t('removeRepoSuccess.title'),
description: t.raw('removeRepoSuccess.description') as string,
descriptionZero: t('removeRepoSuccess.descriptionZero'),
},
removeRepoError: {
notOwner: t('removeRepoError.notOwner'),
invalidRepo: t('removeRepoError.invalidRepo'),
parseError: t('removeRepoError.parseError'),
},
error: {
notOwner: t('error.notOwner'),
@@ -90,6 +112,7 @@ export default async function ClaimPage({
rateLimitExceeded: t('error.rateLimitExceeded'),
networkTimeout: t('error.networkTimeout'),
generic: t('error.generic'),
repoBlockedByOwner: t('error.repoBlockedByOwner'),
},
myRequests: {
title: t('myRequests.title'),