import { a as getGitRoot, i as getGitRemoteUrl, n as cloneOrRefreshRepo, t as parseSource } from "./src-DDJn_-JW.mjs";
import { i as getCatalogPluginPath, o as getCatalogSkillPluginPath, t as getCatalogBlockPluginPath } from "./catalog-BN_wLtPe.mjs";
import { existsSync } from "node:fs";
import path from "node:path";
import process from "node:process";
import { createHash } from "node:crypto";
import { cp, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { xdgConfig } from "xdg-basedir";
//#region src/lib/plugin-manager.ts
const ENTRY_FILES = ["index.ts", "index.js"];
const ENTRY_EXTENSIONS = [".ts", ".js"];
const LEADING_TRAILING_DOT_HYPHEN_REGEX = /^[.\-]+|[.\-]+$/g;
const SANITIZE_NAME_REGEX = /[^a-z0-9._]+/g;
function getProjectPluginsDir(cwd = process.cwd()) {
	return path.join(cwd, ".opencode/plugins");
}
function getGlobalPluginsDir() {
	const configHome = process.env.XDG_CONFIG_HOME?.trim() || xdgConfig || path.join(homedir(), ".config");
	return path.join(configHome, "opencode/plugins");
}
function sanitizePluginName(name) {
	return name.toLowerCase().replace(SANITIZE_NAME_REGEX, "-").replace(LEADING_TRAILING_DOT_HYPHEN_REGEX, "").substring(0, 255) || "unnamed-plugin";
}
async function parsePluginDirectory(pluginDirPath, options = {}) {
	if (!existsSync(pluginDirPath)) return null;
	const extension = path.extname(pluginDirPath);
	if (ENTRY_EXTENSIONS.includes(extension)) return {
		entryPath: pluginDirPath,
		name: path.basename(pluginDirPath, extension),
		path: pluginDirPath,
		skill: options.skill
	};
	for (const entryFile of ENTRY_FILES) {
		const entryPath = path.join(pluginDirPath, entryFile);
		if (!existsSync(entryPath)) continue;
		return {
			entryPath,
			name: path.basename(pluginDirPath),
			path: pluginDirPath,
			skill: options.skill
		};
	}
	return null;
}
async function discoverPlugins(basePath) {
	const rootPlugin = await parsePluginDirectory(basePath);
	if (rootPlugin) return [rootPlugin];
	const results = [];
	try {
		const entries = await readdir(basePath, { withFileTypes: true });
		for (const entry of entries) {
			const entryPath = path.join(basePath, entry.name);
			if (entry.isDirectory()) {
				const plugin = await parsePluginDirectory(entryPath);
				if (plugin) results.push(plugin);
				continue;
			}
			if (entry.isFile() && ENTRY_EXTENSIONS.includes(path.extname(entry.name))) {
				const plugin = await parsePluginDirectory(entryPath);
				if (plugin) results.push(plugin);
			}
		}
	} catch {
		return [];
	}
	return results;
}
async function installPlugin(plugin, options = {}) {
	const baseDir = options.global ? getGlobalPluginsDir() : getProjectPluginsDir(options.cwd);
	const pluginName = sanitizePluginName(plugin.name);
	const targetDir = path.join(baseDir, pluginName);
	const entryExtension = path.extname(plugin.entryPath) || ".ts";
	const shimPath = path.join(baseDir, `${pluginName}${entryExtension}`);
	await rm(targetDir, {
		force: true,
		recursive: true
	}).catch(() => {});
	await rm(shimPath, { force: true }).catch(() => {});
	await mkdir(baseDir, { recursive: true });
	if ((await stat(plugin.path)).isDirectory()) {
		const relativeEntryPath = `./${pluginName}/${path.relative(plugin.path, plugin.entryPath).split(path.sep).join("/")}`;
		await cp(plugin.path, targetDir, {
			force: true,
			recursive: true
		});
		await writeFile(shimPath, `export * from ${JSON.stringify(relativeEntryPath)}\n`, "utf8");
		return {
			canonicalPath: targetDir,
			name: pluginName,
			path: targetDir,
			scope: options.global ? "global" : "project"
		};
	}
	await cp(plugin.path, shimPath, {
		force: true,
		recursive: false
	});
	return {
		canonicalPath: shimPath,
		name: pluginName,
		path: shimPath,
		scope: options.global ? "global" : "project"
	};
}
async function listInstalledPlugins(options = {}) {
	const scopes = options.global === void 0 ? [false, true] : [options.global];
	const results = [];
	for (const isGlobal of scopes) {
		const baseDir = isGlobal ? getGlobalPluginsDir() : getProjectPluginsDir(options.cwd);
		if (!existsSync(baseDir)) continue;
		const entries = await readdir(baseDir, { withFileTypes: true });
		const directoryNames = new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
		for (const entry of entries) {
			if (entry.isDirectory()) {
				const pluginDir = path.join(baseDir, entry.name);
				if (!await parsePluginDirectory(pluginDir)) continue;
				results.push({
					canonicalPath: pluginDir,
					name: entry.name,
					path: pluginDir,
					scope: isGlobal ? "global" : "project"
				});
				continue;
			}
			if (!entry.isFile() || !ENTRY_EXTENSIONS.includes(path.extname(entry.name))) continue;
			const pluginName = path.basename(entry.name, path.extname(entry.name));
			if (directoryNames.has(pluginName)) continue;
			const pluginPath = path.join(baseDir, entry.name);
			results.push({
				canonicalPath: pluginPath,
				name: pluginName,
				path: pluginPath,
				scope: isGlobal ? "global" : "project"
			});
		}
	}
	return results.sort((left, right) => left.name.localeCompare(right.name));
}
async function removeInstalledPlugin(name, options = {}) {
	const baseDir = options.global ? getGlobalPluginsDir() : getProjectPluginsDir(options.cwd);
	const pluginName = sanitizePluginName(name);
	await rm(path.join(baseDir, pluginName), {
		force: true,
		recursive: true
	}).catch(() => {});
	await rm(path.join(baseDir, `${pluginName}.ts`), { force: true }).catch(() => {});
	await rm(path.join(baseDir, `${pluginName}.js`), { force: true }).catch(() => {});
}
//#endregion
//#region src/lib/plugins.ts
async function resolvePluginsFromSource(source, options = {}) {
	const parsed = parseSource(source);
	if (parsed.type === "catalog") {
		if (!parsed.catalogName) throw new Error("Catalog source is missing a catalog name");
		const catalogPath = resolveCatalogPluginPath(parsed.catalogName);
		if (!catalogPath) throw new Error(`Catalog plugin not found: ${parsed.catalogName}`);
		return {
			parsed,
			plugins: applyPluginFilters(await discoverPlugins(catalogPath), options.pluginNames),
			rootPath: catalogPath,
			source
		};
	}
	if (parsed.type === "local") {
		if (!parsed.localPath) throw new Error("Local source is missing a path");
		const rootPath = parsed.subpath ? `${parsed.localPath}/${parsed.subpath}` : parsed.localPath;
		return {
			parsed,
			plugins: applyPluginFilters(await discoverPlugins(rootPath), options.pluginNames),
			rootPath,
			source
		};
	}
	if (parsed.type === "well-known") throw new Error("Well-known plugin sources are not implemented");
	const repoPath = await cloneOrRefreshRepo(parsed.url, parsed.ref);
	const rootPath = parsed.subpath ? `${repoPath}/${parsed.subpath}` : repoPath;
	return {
		parsed,
		plugins: applyPluginFilters(await discoverPlugins(rootPath), options.pluginNames),
		rootPath,
		source
	};
}
async function installResolvedPlugins(resolved, options) {
	const cwd = options.cwd || process.cwd();
	const installed = [];
	for (const plugin of resolved.plugins) installed.push(await installPlugin(plugin, {
		cwd,
		global: options.global
	}));
	return installed;
}
async function getInstalledPluginsForScope(options) {
	return listInstalledPlugins({
		cwd: options.cwd,
		global: options.global
	});
}
async function createPluginLockEntryFromResolved(plugin, resolved, cwd, scope) {
	const repoRoot = await getGitRoot(cwd) ?? cwd;
	const pluginPath = resolved.parsed.type === "catalog" ? path.relative(repoRoot, plugin.path).split(path.sep).join("/") : path.relative(resolved.rootPath, plugin.path).split(path.sep).join("/");
	const local = resolved.parsed.type === "catalog" ? repoRoot : resolved.parsed.localPath;
	const remote = resolved.parsed.type === "catalog" ? await getGitRemoteUrl(cwd) ?? void 0 : resolved.parsed.url || void 0;
	return {
		hash: await computePluginPathHash(plugin.path),
		...local ? { local } : {},
		path: pluginPath,
		...remote ? { remote } : {},
		scope,
		source: resolved.source,
		type: resolved.parsed.type
	};
}
async function removeInstalledPluginByName(name, options) {
	await removeInstalledPlugin(name, {
		cwd: options.cwd,
		global: options.global
	});
}
async function computePluginPathHash(pluginPath) {
	if ((await stat(pluginPath)).isFile()) return createHash("sha256").update(await readFile(pluginPath)).digest("hex");
	const files = [];
	await collectPluginFiles(pluginPath, pluginPath, files);
	files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
	const hash = createHash("sha256");
	for (const file of files) {
		hash.update(file.relativePath);
		hash.update(file.content);
	}
	return hash.digest("hex");
}
async function collectPluginFiles(baseDir, currentDir, results) {
	const entries = await readdir(currentDir, { withFileTypes: true });
	await Promise.all(entries.map(async (entry) => {
		const fullPath = path.join(currentDir, entry.name);
		if (entry.isDirectory()) {
			await collectPluginFiles(baseDir, fullPath, results);
			return;
		}
		if (!entry.isFile()) return;
		results.push({
			content: await readFile(fullPath),
			relativePath: path.relative(baseDir, fullPath).split(path.sep).join("/")
		});
	}));
}
function resolveCatalogPluginPath(catalogName) {
	const [skillName, pluginName] = catalogName.split("/");
	if (!pluginName) {
		const blockPluginPath = getCatalogBlockPluginPath(catalogName);
		if (blockPluginPath) return blockPluginPath;
	}
	if (pluginName) return getCatalogSkillPluginPath(skillName, pluginName);
	return getCatalogPluginPath(catalogName);
}
function applyPluginFilters(plugins, pluginNames) {
	if (!pluginNames || pluginNames.length === 0) {
		if (plugins.length === 0) throw new Error("No plugins found for source");
		return plugins;
	}
	const normalizedNames = pluginNames.map((name) => name.toLowerCase());
	const filtered = plugins.filter((plugin) => normalizedNames.includes(plugin.name.toLowerCase()));
	if (filtered.length === 0) throw new Error(`No plugins matched: ${pluginNames.join(", ")}`);
	return filtered;
}
//#endregion
export { resolvePluginsFromSource as a, removeInstalledPluginByName as i, getInstalledPluginsForScope as n, getGlobalPluginsDir as o, installResolvedPlugins as r, createPluginLockEntryFromResolved as t };

//# sourceMappingURL=plugins-DtsrBxYU.mjs.map