sync: multi-branch scanning, discovery strategies, UTF-8 fix, curation improvements

- Add multi-branch scanning to discover skills on non-default branches
- Add popular repos and commits search discovery strategies
- Fix UTF-8 encoding sanitization in skill parser
- Add repo_created_at for threshold-based duplicate tie-breaking
- Add indexer tests and vitest config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-02-22 04:16:35 +03:30
parent 3f599a23fb
commit b05cb21a65
20 changed files with 1314 additions and 64 deletions

View File

@@ -16,6 +16,21 @@ const NAME_PATTERN = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
const MAX_DESCRIPTION_LENGTH = 1024;
const MAX_NAME_LENGTH = 64;
/**
* Sanitize a string to be valid UTF-8 for JSONB storage.
* Removes incomplete multi-byte sequences and control characters
* that PostgreSQL's jsonb_build_object rejects.
*/
function sanitizeUtf8(input: string): string {
// Remove null bytes and C0 control characters (except \t \n \r)
let result = input.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
// Encode to buffer and decode back to strip invalid sequences
result = Buffer.from(result, 'utf8').toString('utf8');
// Replace lone surrogates (U+D800-U+DFFF) which are invalid in UTF-8
result = result.replace(/[\uD800-\uDFFF]/g, '');
return result;
}
/**
* File patterns for discovering instruction files on GitHub
*/
@@ -117,8 +132,8 @@ export function parseSkillMd(content: string): ParsedSkill {
// Build metadata
const metadata: SkillMetadata = {
name: String(frontmatter.name || ''),
description: String(frontmatter.description || ''),
name: sanitizeUtf8(String(frontmatter.name || '')),
description: sanitizeUtf8(String(frontmatter.description || '')),
version: frontmatter.version ? String(frontmatter.version) : undefined,
license: frontmatter.license ? String(frontmatter.license) : undefined,
author: frontmatter.author ? String(frontmatter.author) : undefined,
@@ -387,8 +402,8 @@ export function parseGenericInstructionFile(
const inferredPlatform = pattern?.inferredPlatform || 'claude';
const metadata: SkillMetadata = {
name: derivedName,
description: derivedDescription.slice(0, MAX_DESCRIPTION_LENGTH),
name: sanitizeUtf8(derivedName),
description: sanitizeUtf8(derivedDescription.slice(0, MAX_DESCRIPTION_LENGTH)),
version: frontmatter.version ? String(frontmatter.version) : undefined,
license: frontmatter.license ? String(frontmatter.license) : undefined,
author: frontmatter.author ? String(frontmatter.author) : repoMeta.owner,