Claude-skill-registry Extension System
Paperback-compatible extension runtime and source API
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/extensions" ~/.claude/skills/majiayu000-claude-skill-registry-extension-system && rm -rf "$T"
manifest:
skills/data/extensions/SKILL.mdsource content
Extension System
Architecture
Extensions run in an isolated WebView (
ExtensionRunner.tsx) that:
- Loads Paperback-compatible JavaScript bundles
- Executes source methods via message passing
- Returns manga/chapter data to the app
Source Service API
import { sourceService } from '../services/sourceService'; // Get discover sections (homepage) const sections = await sourceService.getDiscoverSections(sourceId); // Get manga from a section const manga = await sourceService.getDiscoverSection(sourceId, sectionId); // Search manga const results = await sourceService.searchManga(sourceId, query); // Get manga details const details = await sourceService.getMangaDetails(sourceId, mangaId); // Get chapter list const chapters = await sourceService.getChapters(sourceId, mangaId); // Get chapter pages (image URLs) const pages = await sourceService.getChapterPages(sourceId, chapterId);
Extension Service
import { extensionService } from '../services/extensionService'; // Get installed extensions const extensions = await extensionService.getInstalledExtensions(); // Install extension await extensionService.installExtension(repositoryUrl, extensionId); // Uninstall extension await extensionService.uninstallExtension(extensionId); // Get extension info const info = await extensionService.getExtensionInfo(extensionId);
Data Types
interface Manga { id: string; title: string; image: string; author?: string; artist?: string; desc?: string; status?: string; genres?: string[]; } interface Chapter { id: string; mangaId: string; title: string; chapNum?: number; volume?: number; time?: Date; langCode?: string; } interface Extension { id: string; name: string; version: string; icon: string; sourceId: string; repositoryUrl: string; }
Extension Runner Component
// ExtensionRunner.tsx handles WebView communication <ExtensionRunner sourceId={sourceId} onReady={() => setLoaded(true)} onError={(error) => console.error(error)} />
WebView Message Protocol
// App -> WebView webViewRef.current.postMessage(JSON.stringify({ type: 'getMangaDetails', mangaId: 'manga-123', })); // WebView -> App (via onMessage) const handleMessage = (event) => { const { type, data, error } = JSON.parse(event.nativeEvent.data); if (type === 'mangaDetails') { setManga(data); } };
Repository Format
Extensions are hosted in repositories with this structure:
repository/ ├── versioning.json # Repository metadata ├── sources/ │ ├── source1/ │ │ ├── source.js # Extension bundle │ │ └── info.json # Extension metadata │ └── source2/ │ └── ...
Adding New Source Support
- Extension must implement Paperback source interface
- Bundle is loaded via WebView
- Methods are called via message passing
- Results are parsed and returned to app
Error Handling
try { const manga = await sourceService.getMangaDetails(sourceId, mangaId); } catch (error) { if (error.message.includes('timeout')) { // Extension took too long } else if (error.message.includes('not found')) { // Manga doesn't exist } else { // Generic error } }