/**
 * Plugin Discovery for Commands and Skills from Installed Packages
 *
 * Story 93-3: Discover and register commands, skills, and API routers
 * from installed @pennyfarthing/* packages via their package.json
 * "pennyfarthing" field.
 */
import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
import { join, resolve } from 'node:path';
/** Packages that are part of the framework, not plugins */
const EXCLUDED_PACKAGES = ['core', 'shared', 'benchmark'];
/**
 * Parse the "pennyfarthing" field from a package's package.json.
 *
 * @param packageDir - Absolute path to the package directory
 * @returns Parsed manifest, or null if not a plugin
 */
export function parsePluginManifest(packageDir) {
    const pkgJsonPath = join(packageDir, 'package.json');
    if (!existsSync(pkgJsonPath)) {
        return null;
    }
    try {
        const raw = readFileSync(pkgJsonPath, 'utf-8');
        const pkg = JSON.parse(raw);
        if (!pkg.pennyfarthing || typeof pkg.pennyfarthing !== 'object') {
            return null;
        }
        return pkg.pennyfarthing;
    }
    catch {
        return null;
    }
}
/**
 * Discover installed @pennyfarthing/* plugin packages.
 *
 * Scans node_modules/@pennyfarthing/ for packages that have a "pennyfarthing"
 * field in their package.json. Skips core and shared packages.
 *
 * @param projectRoot - Absolute path to project root
 * @returns Array of discovered plugins
 */
export function discoverPlugins(projectRoot) {
    const scopeDir = join(projectRoot, 'node_modules/@pennyfarthing');
    if (!existsSync(scopeDir)) {
        return [];
    }
    let entries;
    try {
        entries = readdirSync(scopeDir);
    }
    catch {
        return [];
    }
    const plugins = [];
    for (const entry of entries) {
        if (EXCLUDED_PACKAGES.includes(entry)) {
            continue;
        }
        const pkgDir = resolve(join(scopeDir, entry));
        const manifest = parsePluginManifest(pkgDir);
        if (manifest) {
            plugins.push({
                name: `@pennyfarthing/${entry}`,
                path: pkgDir,
                manifest,
            });
        }
    }
    return plugins;
}
/**
 * Get all command file paths from discovered plugins.
 *
 * Scans each plugin's declared commands directory for .md files.
 *
 * @param plugins - Array of discovered plugins
 * @returns Array of command metadata with paths
 */
export function getPluginCommands(plugins) {
    const commands = [];
    for (const plugin of plugins) {
        if (!plugin.manifest.commands) {
            continue;
        }
        const commandsDir = join(plugin.path, plugin.manifest.commands);
        if (!existsSync(commandsDir)) {
            continue;
        }
        const files = readdirSync(commandsDir).filter(f => f.endsWith('.md'));
        for (const file of files) {
            commands.push({
                name: file.replace(/\.md$/, ''),
                path: join(commandsDir, file),
                plugin: plugin.name,
            });
        }
    }
    return commands;
}
/**
 * Get all skill directory paths from discovered plugins.
 *
 * Scans each plugin's declared skills directory for subdirectories.
 *
 * @param plugins - Array of discovered plugins
 * @returns Array of skill metadata with paths
 */
export function getPluginSkills(plugins) {
    const skills = [];
    for (const plugin of plugins) {
        if (!plugin.manifest.skills) {
            continue;
        }
        const skillsDir = join(plugin.path, plugin.manifest.skills);
        if (!existsSync(skillsDir)) {
            continue;
        }
        const entries = readdirSync(skillsDir).filter(f => {
            if (f.startsWith('.'))
                return false;
            const fullPath = join(skillsDir, f);
            try {
                return statSync(fullPath).isDirectory();
            }
            catch {
                return false;
            }
        });
        for (const entry of entries) {
            skills.push({
                name: entry,
                path: join(skillsDir, entry),
                plugin: plugin.name,
            });
        }
    }
    return skills;
}
/**
 * Get all API router entry points from discovered plugins.
 *
 * Reads API router definitions from each plugin's manifest.
 *
 * @param plugins - Array of discovered plugins
 * @returns Array of router metadata
 */
export function getPluginRouters(plugins) {
    const routers = [];
    for (const plugin of plugins) {
        if (!plugin.manifest.api) {
            continue;
        }
        const api = plugin.manifest.api;
        routers.push({
            mountPath: api.path,
            modulePath: join(plugin.path, api.module),
            exportName: api.export,
            plugin: plugin.name,
        });
    }
    return routers;
}
//# sourceMappingURL=plugin-discovery.js.map