import fs from 'fs';
import path from 'path';
import { parseSkill } from './parser';
import { validateBoundary } from './validator';
import { ParseResult } from './types';
export interface LoadedSkill {
path: string;
result: ParseResult;
isValid: boolean;
errors?: string[];
}
export async function loadSkill(skillPath: string): Promise<LoadedSkill> {
const readmePath = path.join(skillPath, 'SKILL.md');
try {
if (!fs.existsSync(readmePath)) {
return {
path: skillPath,
result: null as any,
isValid: false,
errors: ['SKILL.md not found']
};
}
const content = await fs.promises.readFile(readmePath, 'utf-8');
const parseResult = parseSkill(content);
const validation = validateBoundary(parseResult.boundary);
if (!validation.success) {
return {
path: skillPath,
result: parseResult,
isValid: false,
errors: [validation.error || 'Validation failed']
};
}
return {
path: skillPath,
result: parseResult,
isValid: true
};
} catch (err: any) {
return {
path: skillPath,
result: null as any,
isValid: false,
errors: [err.message]
};
}
}
export async function scanSkillsRegistry(registryRoot: string): Promise<LoadedSkill[]> {
const results: LoadedSkill[] = [];
if (!fs.existsSync(registryRoot)) {
return [];
}
// Recursive scan could be added here, currently assuming flat or domain-grouped
// A simple robust approach: use `find` or recursive readdir.
// For now, let's implement a simple 2-level scan (domain/skill) as per spec standard.
const entries = await fs.promises.readdir(registryRoot, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
const fullPath = path.join(registryRoot, entry.name);
// Check if this directory itself is a skill
if (fs.existsSync(path.join(fullPath, 'SKILL.md'))) {
results.push(await loadSkill(fullPath));
continue;
}
// Check children (domain grouping)
const subEntries = await fs.promises.readdir(fullPath, { withFileTypes: true });
for (const sub of subEntries) {
if (sub.isDirectory()) {
const subPath = path.join(fullPath, sub.name);
if (fs.existsSync(path.join(subPath, 'SKILL.md'))) {
results.push(await loadSkill(subPath));
}
}
}
}
}
return results;
}