/** * Reads plugin manifest from plugin.yaml or in-memory registration. * * defineScope() registers a manifest in-memory, skipping plugin.yaml. * Raw createPlugin() still loads from plugin.yaml. */ import { readFileSync, existsSync } from "fs"; import { resolve } from "path"; import YAML from "yaml"; import { cfg } from "../config"; import type { PluginManifest } from "../types"; let registeredManifest: PluginManifest | null = null; /** Register a manifest in-memory (used by defineScope to skip plugin.yaml). */ export function registerManifest(manifest: PluginManifest): void { registeredManifest = manifest; } export function loadManifest(): PluginManifest { // Two ways a plugin declares its manifest (name/version/command_types): // 1. defineScope() registers it in-memory at import time — the common path; // scope plugins never need a plugin.yaml. // 2. Raw createPlugin() plugins (no defineScope) fall back to a plugin.yaml // file on disk. Override its path with NP_PLUGIN_MANIFEST. if (registeredManifest) { return registeredManifest; } const manifestOverride = cfg.pluginManifest(); const manifestPath = resolve( manifestOverride || process.cwd(), manifestOverride ? "" : "plugin.yaml" ); if (!existsSync(manifestPath)) { console.error( `[plugin] plugin.yaml not found at ${manifestPath}. ` + `Set NP_PLUGIN_MANIFEST env var or run from the plugin directory.` ); process.exit(1); } const raw = readFileSync(manifestPath, "utf-8"); const manifest = YAML.parse(raw) as PluginManifest; if (!manifest.name || !manifest.version || !manifest.command_types?.length) { console.error( "[plugin] plugin.yaml must have name, version, and command_types." ); process.exit(1); } return manifest; }