export type ExtensionCompatibilityKind = 'pi' | 'opencode' | 'unknown' export type ExtensionCompatibilityStatus = 'supported' | 'partial' | 'unsupported' export type ExtensionCompatibilityCapability = 'extensions' | 'skills' | 'prompts' | 'themes' | 'commands' export interface ExtensionCompatibilityDescriptor { kind: ExtensionCompatibilityKind status: ExtensionCompatibilityStatus reason: string capabilities: ExtensionCompatibilityCapability[] } interface ExtensionPackageManifest { name?: unknown pi?: unknown opencode?: unknown } const PI_CAPABILITY_KEYS: ExtensionCompatibilityCapability[] = ['extensions', 'skills', 'prompts', 'themes'] const OPENCODE_CAPABILITY_KEYS: ExtensionCompatibilityCapability[] = ['extensions', 'commands'] export function describeExtensionCompatibility(manifest: ExtensionPackageManifest): ExtensionCompatibilityDescriptor { const piCapabilities = collectCapabilities(manifest.pi, PI_CAPABILITY_KEYS) if (piCapabilities.length > 0) { return { kind: 'pi', status: 'supported', reason: 'Package declares Pi extension entrypoints.', capabilities: piCapabilities, } } const opencodeCapabilities = collectCapabilities(manifest.opencode, OPENCODE_CAPABILITY_KEYS) if (opencodeCapabilities.length > 0) { return { kind: 'opencode', status: 'partial', reason: 'OpenCode manifest detected; execution is disabled until sandbox permissions are implemented.', capabilities: opencodeCapabilities, } } return { kind: 'unknown', status: 'unsupported', reason: 'No Pi or OpenCode extension manifest was detected.', capabilities: [], } } function collectCapabilities( value: unknown, capabilityKeys: ExtensionCompatibilityCapability[], ): ExtensionCompatibilityCapability[] { if (!isRecord(value)) return [] return capabilityKeys.filter(key => hasManifestEntries(value[key])) } function hasManifestEntries(value: unknown): boolean { if (typeof value === 'string') return value.trim().length > 0 if (Array.isArray(value)) return value.some(entry => typeof entry === 'string' && entry.trim().length > 0) return false } function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value) }