// skill registry — load, discover, and match skills import * as fs from 'fs' import * as path from 'path' import { skill as a11yReviewSkill } from './builtin/a11y-review' import { skill as compatCheckSkill } from './builtin/compat-check' import { skill as maestroTestSkill } from './builtin/maestro-test' import { skill as perfProfileSkill } from './builtin/perf-profile' import { skill as screenshotAllSkill } from './builtin/screenshot-all' import { skill as visualDiffSkill } from './builtin/visual-diff' import type { RNXSkill, SkillContext } from './types' const _skills: Map = new Map() const BUILTIN_SKILLS = [ a11yReviewSkill, compatCheckSkill, perfProfileSkill, screenshotAllSkill, maestroTestSkill, visualDiffSkill, ] export const skillRegistry = { register(skill: RNXSkill) { _skills.set(skill.name, skill) }, get(name: string): RNXSkill | undefined { return _skills.get(name) }, listAll(): RNXSkill[] { return Array.from(_skills.values()) }, // match skills by natural language query against triggers match(query: string): RNXSkill[] { const q = query.toLowerCase() return this.listAll().filter((skill) => skill.triggers.some((trigger) => q.includes(trigger.toLowerCase())), ) }, // load built-in skills async loadBuiltins() { for (const skill of BUILTIN_SKILLS) { this.register(skill) } }, // load skills from a project manifest (rnx-skills.json) async loadFromManifest(projectDir: string) { const manifestPath = path.join(projectDir, 'rnx-skills.json') if (!fs.existsSync(manifestPath)) return try { const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) if (Array.isArray(manifest.skills)) { for (const entry of manifest.skills) { if (typeof entry === 'string') { // npm package name try { const mod = await import(entry) if (mod.skill) this.register(mod.skill) } catch {} } else if (entry.file) { // local file try { const mod = await import(path.resolve(projectDir, entry.file)) if (mod.skill) this.register(mod.skill) } catch {} } } } } catch {} }, // auto-discover skills based on project dependencies suggestSkills(projectDir: string): string[] { const suggestions: string[] = [] const pkgPath = path.join(projectDir, 'package.json') if (!fs.existsSync(pkgPath)) return suggestions try { const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) const deps = { ...pkg.dependencies, ...pkg.devDependencies } // suggest testing skill if detox or maestro is in deps if (deps['detox'] || deps['maestro']) { suggestions.push('maestro-test') } // suggest a11y review if no a11y testing library if (!deps['jest-axe'] && !deps['@testing-library/jest-dom']) { suggestions.push('a11y-review') } // always suggest visual-diff and screenshot suggestions.push('visual-diff', 'screenshot-all') // suggest perf if app has many screens suggestions.push('perf-profile') } catch {} return suggestions }, }