import "./tool-display-DMBmPBI-.js";
import { v as resolveStateDir } from "./paths-WcYpcMAY.js";
import { Dt as theme, _ as defaultRuntime, it as resolveConfigDir, lt as shortenHomeInString, st as resolveUserPath, ut as shortenHomePath } from "./entry.js";
import "./auth-profiles-CaNK0cUT.js";
import "./exec-CmvuUufz.js";
import "./agent-scope-E2aR3Ci_.js";
import "./github-copilot-token-CKOQDjEZ.js";
import "./emitter-BKo8D7Oj.js";
import "./sqlite-CVrhPija.js";
import "./reliability-BnL75OlZ.js";
import "./pi-model-discovery-Cd63DV2B.js";
import "./model-nrbMzlbz.js";
import "./frontmatter-C3D7gE4Z.js";
import "./skills-UwTwDeDw.js";
import { f as applyExclusiveSlotSelection, o as resolveBundledPluginsDir, p as defaultSlotIdForKey, t as clearPluginManifestRegistryCache } from "./manifest-registry-AgGLF4V7.js";
import "./validation-B00oOkJ3.js";
import { d as writeConfigFile, o as loadConfig } from "./config-CoCeK5B2.js";
import "./client-DtQEs2_8.js";
import "./call-CkgY6Bq7.js";
import "./message-channel-DKXbq8GH.js";
import "./subagent-registry-BSfrC7gZ.js";
import "./sessions-CFJLtXRH.js";
import "./tokens-D-4TlWcT.js";
import "./heartbeat-XNOTz34R.js";
import "./accounts-CNwod9BY.js";
import "./accounts-C3i8BBiA.js";
import "./normalize-CRf-uoB2.js";
import "./bindings-B1hNZin3.js";
import "./helpers-C1I2eYI4.js";
import "./allow-list-C1QL6XM_.js";
import "./logging-BiuZwq5D.js";
import "./client-BrCP9C3o.js";
import "./plugins-DMpYx7kb.js";
import "./targets-DvKzWjKi.js";
import "./with-timeout-xvreNRIp.js";
import "./diagnostic-CvuUUG_n.js";
import "./diagnostic-session-state-Dom6CWV9.js";
import "./lanes-6YZC1uc5.js";
import "./reply-payloads-DHkZZC3s.js";
import "./route-reply-0Dr2Q7BS.js";
import "./tts-CLI3TzKv.js";
import "./image-ops-D0pBQqKh.js";
import "./audio-xR21p4sl.js";
import "./pi-embedded-helpers-D-q9ylFX.js";
import "./sandbox-C79rEJ_w.js";
import "./chrome-BTLLz_O2.js";
import { d as promptYesNo } from "./tailscale-V4nwZFBO.js";
import "./auth-Beo6eR0R.js";
import "./server-context-T5wFkzWi.js";
import "./routes-Ch0tqbTH.js";
import "./redact-2qrQ_uJt.js";
import "./errors-BlUw40-o.js";
import "./paths-Cb76_eC_.js";
import "./ssrf-BEPhIfOk.js";
import "./store-Bz34pm8z.js";
import "./ports-BsKQWWi9.js";
import "./trash-Cn3CsF86.js";
import "./dock-BvRrtCWj.js";
import "./accounts-CdYdWklO.js";
import "./paths-CDTtU20P.js";
import "./tool-images-B4eVOjD0.js";
import "./thinking-C03RqJxq.js";
import "./models-config-By95gdZa.js";
import "./chat-trace-C0wGIaWO.js";
import "./response-prefix-template-C1xl3U-G.js";
import "./tool-tracking-CtNygH0q.js";
import "./memory-cli-85mo64bb.js";
import "./manager-CFXUPO5v.js";
import "./gemini-auth-dH8SvNUd.js";
import "./session-files-0TqN4M7r.js";
import "./retry-Bdf0L_dZ.js";
import "./hybrid-DSp5ZIQe.js";
import "./common-CxpUJZLb.js";
import "./send-BozJfiUK.js";
import "./chunk-CNObemR3.js";
import "./markdown-tables-Cex18ib0.js";
import "./fetch-guard-BLrk92iq.js";
import "./fetch-DFo2YRBT.js";
import "./media-PPft1pQs.js";
import "./ir-CRxad2DW.js";
import "./render-JkHfM4BD.js";
import "./commands-registry-Ckq8wOdS.js";
import "./sessions-helpers-NrlH9x7w.js";
import "./pi-embedded-utils-BCSBp2bL.js";
import "./external-content-pnoCpvzd.js";
import "./runner-Dbz36vkv.js";
import "./image-tool.helpers-Bb25eyJc.js";
import "./image-DnnIE6E3.js";
import "./model-catalog-c_sp3sxR.js";
import "./session-utils-CwXLwy_N.js";
import "./workspace-dirs-Dm1vImLQ.js";
import "./skill-commands-B6qVCgej.js";
import "./fetch-CbnJhXfl.js";
import "./pairing-store-BHSQgDEs.js";
import "./exec-approvals-DGBfDg6X.js";
import "./gateway-CWnGRD4E.js";
import "./nodes-utils-DKSV6jwI.js";
import "./session-cost-usage-COXOEQzN.js";
import "./typed-schema-NhH7oBv2.js";
import "./knowledge-graph-njh3pt-F.js";
import "./model-fallback-N2p6k-x8.js";
import "./send-DpN8GxWR.js";
import "./channel-activity-Renq827a.js";
import "./tables-BD7LbCl1.js";
import "./server-startup-users-Dzui320y.js";
import "./resolve-route-CjJJ3gIl.js";
import "./replies-f6OSxuwk.js";
import "./reply-prefix-CjtRvYlq.js";
import "./pi-tools.policy-CW2flz_o.js";
import "./send-CdF3YtrB.js";
import "./onboard-helpers-HmtMUhNQ.js";
import "./prompt-style-BNQCf62G.js";
import "./onboard-helpers-CMBWpQU-.js";
import "./outbound-attachment-BhyLC0RY.js";
import "./targets-Df3HJYGb.js";
import "./pairing-labels-B7JDegCR.js";
import "./send-oTQYKgod.js";
import "./send-BxB9sGGU.js";
import "./proxy-BWmn1C2E.js";
import { t as formatDocsLink } from "./links-BcL73qkF.js";
import "./cli-utils-CpDxBFOW.js";
import "./help-format-CSTLKmFP.js";
import "./progress-Dh4aWkCA.js";
import "./active-listener-Bqb34bVB.js";
import { c as resolveArchiveKind } from "./install-safe-path-D9nN_mdy.js";
import "./npm-registry-spec-CPq_jPVk.js";
import "./skill-scanner-BJtREUzt.js";
import { i as resolvePluginInstallDir, n as installPluginFromNpmSpec, r as installPluginFromPath, t as recordPluginInstall } from "./installs-UNHq0xKX.js";
import { t as renderTable } from "./table-_G8aohdO.js";
import { t as buildPluginStatusReport } from "./status-DJk3y3bG.js";
import { n as updateNpmInstalledPlugins } from "./update-BBbD12SP.js";
import os from "node:os";
import path from "node:path";
import fs from "node:fs";
import fs$1 from "node:fs/promises";

//#region src/plugins/source-display.ts
function tryRelative(root, filePath) {
	const rel = path.relative(root, filePath);
	if (!rel || rel === ".") return null;
	if (rel === "..") return null;
	if (rel.startsWith(`..${path.sep}`) || rel.startsWith("../") || rel.startsWith("..\\")) return null;
	if (path.isAbsolute(rel)) return null;
	return rel.replaceAll("\\", "/");
}
function resolvePluginSourceRoots(params) {
	return {
		stock: resolveBundledPluginsDir(),
		global: path.join(resolveConfigDir(), "extensions"),
		workspace: params.workspaceDir ? path.join(params.workspaceDir, ".minion", "extensions") : void 0
	};
}
function formatPluginSourceForTable(plugin, roots) {
	const raw = plugin.source;
	if (plugin.origin === "bundled" && roots.stock) {
		const rel = tryRelative(roots.stock, raw);
		if (rel) return {
			value: `stock:${rel}`,
			rootKey: "stock"
		};
	}
	if (plugin.origin === "workspace" && roots.workspace) {
		const rel = tryRelative(roots.workspace, raw);
		if (rel) return {
			value: `workspace:${rel}`,
			rootKey: "workspace"
		};
	}
	if (plugin.origin === "global" && roots.global) {
		const rel = tryRelative(roots.global, raw);
		if (rel) return {
			value: `global:${rel}`,
			rootKey: "global"
		};
	}
	return { value: shortenHomeInString(raw) };
}

//#endregion
//#region src/plugins/uninstall.ts
function resolveUninstallDirectoryTarget(params) {
	if (!params.hasInstall) return null;
	if (params.installRecord?.source === "path") return null;
	let defaultPath;
	try {
		defaultPath = resolvePluginInstallDir(params.pluginId, params.extensionsDir);
	} catch {
		return null;
	}
	const configuredPath = params.installRecord?.installPath;
	if (!configuredPath) return defaultPath;
	if (path.resolve(configuredPath) === path.resolve(defaultPath)) return configuredPath;
	return defaultPath;
}
/**
* Remove plugin references from config (pure config mutation).
* Returns a new config with the plugin removed from entries, installs, allow, load.paths, and slots.
*/
function removePluginFromConfig(cfg, pluginId) {
	const actions = {
		entry: false,
		install: false,
		allowlist: false,
		loadPath: false,
		memorySlot: false
	};
	const pluginsConfig = cfg.plugins ?? {};
	let entries = pluginsConfig.entries;
	if (entries && pluginId in entries) {
		const { [pluginId]: _, ...rest } = entries;
		entries = Object.keys(rest).length > 0 ? rest : void 0;
		actions.entry = true;
	}
	let installs = pluginsConfig.installs;
	const installRecord = installs?.[pluginId];
	if (installs && pluginId in installs) {
		const { [pluginId]: _, ...rest } = installs;
		installs = Object.keys(rest).length > 0 ? rest : void 0;
		actions.install = true;
	}
	let allow = pluginsConfig.allow;
	if (Array.isArray(allow) && allow.includes(pluginId)) {
		allow = allow.filter((id) => id !== pluginId);
		if (allow.length === 0) allow = void 0;
		actions.allowlist = true;
	}
	let load = pluginsConfig.load;
	if (installRecord?.source === "path" && installRecord.sourcePath) {
		const sourcePath = installRecord.sourcePath;
		const loadPaths = load?.paths;
		if (Array.isArray(loadPaths) && loadPaths.includes(sourcePath)) {
			const nextLoadPaths = loadPaths.filter((p) => p !== sourcePath);
			load = nextLoadPaths.length > 0 ? {
				...load,
				paths: nextLoadPaths
			} : void 0;
			actions.loadPath = true;
		}
	}
	let slots = pluginsConfig.slots;
	if (slots?.memory === pluginId) {
		slots = {
			...slots,
			memory: defaultSlotIdForKey("memory")
		};
		actions.memorySlot = true;
	}
	if (slots && Object.keys(slots).length === 0) slots = void 0;
	const cleanedPlugins = {
		...pluginsConfig,
		entries,
		installs,
		allow,
		load,
		slots
	};
	if (cleanedPlugins.entries === void 0) delete cleanedPlugins.entries;
	if (cleanedPlugins.installs === void 0) delete cleanedPlugins.installs;
	if (cleanedPlugins.allow === void 0) delete cleanedPlugins.allow;
	if (cleanedPlugins.load === void 0) delete cleanedPlugins.load;
	if (cleanedPlugins.slots === void 0) delete cleanedPlugins.slots;
	return {
		config: {
			...cfg,
			plugins: Object.keys(cleanedPlugins).length > 0 ? cleanedPlugins : void 0
		},
		actions
	};
}
/**
* Uninstall a plugin by removing it from config and optionally deleting installed files.
* Linked plugins (source === "path") never have their source directory deleted.
*/
async function uninstallPlugin(params) {
	const { config, pluginId, deleteFiles = true, extensionsDir } = params;
	const hasEntry = pluginId in (config.plugins?.entries ?? {});
	const hasInstall = pluginId in (config.plugins?.installs ?? {});
	if (!hasEntry && !hasInstall) return {
		ok: false,
		error: `Plugin not found: ${pluginId}`
	};
	const installRecord = config.plugins?.installs?.[pluginId];
	const isLinked = installRecord?.source === "path";
	const { config: newConfig, actions: configActions } = removePluginFromConfig(config, pluginId);
	const actions = {
		...configActions,
		directory: false
	};
	const warnings = [];
	const deleteTarget = deleteFiles && !isLinked ? resolveUninstallDirectoryTarget({
		pluginId,
		hasInstall,
		installRecord,
		extensionsDir
	}) : null;
	if (deleteTarget) {
		const existed = await fs$1.access(deleteTarget).then(() => true).catch(() => false) ?? false;
		try {
			await fs$1.rm(deleteTarget, {
				recursive: true,
				force: true
			});
			actions.directory = existed;
		} catch (error) {
			warnings.push(`Failed to remove plugin directory ${deleteTarget}: ${error instanceof Error ? error.message : String(error)}`);
		}
	}
	return {
		ok: true,
		config: newConfig,
		pluginId,
		actions,
		warnings
	};
}

//#endregion
//#region src/cli/plugins-cli.ts
function resolveFileNpmSpecToLocalPath(raw) {
	const trimmed = raw.trim();
	if (!trimmed.toLowerCase().startsWith("file:")) return null;
	const rest = trimmed.slice(5);
	if (!rest) return {
		ok: false,
		error: "unsupported file: spec: missing path"
	};
	if (rest.startsWith("///")) return {
		ok: true,
		path: rest.slice(2)
	};
	if (rest.startsWith("//localhost/")) return {
		ok: true,
		path: rest.slice(11)
	};
	if (rest.startsWith("//")) return {
		ok: false,
		error: "unsupported file: URL host (expected \"file:<path>\" or \"file:///abs/path\")"
	};
	return {
		ok: true,
		path: rest
	};
}
function formatPluginLine(plugin, verbose = false) {
	const status = plugin.status === "loaded" ? theme.success("loaded") : plugin.status === "disabled" ? theme.warn("disabled") : theme.error("error");
	const name = theme.command(plugin.name || plugin.id);
	const idSuffix = plugin.name && plugin.name !== plugin.id ? theme.muted(` (${plugin.id})`) : "";
	const desc = plugin.description ? theme.muted(plugin.description.length > 60 ? `${plugin.description.slice(0, 57)}...` : plugin.description) : theme.muted("(no description)");
	if (!verbose) return `${name}${idSuffix} ${status} - ${desc}`;
	const parts = [
		`${name}${idSuffix} ${status}`,
		`  source: ${theme.muted(shortenHomeInString(plugin.source))}`,
		`  origin: ${plugin.origin}`
	];
	if (plugin.version) parts.push(`  version: ${plugin.version}`);
	if (plugin.providerIds.length > 0) parts.push(`  providers: ${plugin.providerIds.join(", ")}`);
	if (plugin.error) parts.push(theme.error(`  error: ${plugin.error}`));
	return parts.join("\n");
}
function applySlotSelectionForPlugin(config, pluginId) {
	const report = buildPluginStatusReport({ config });
	const plugin = report.plugins.find((entry) => entry.id === pluginId);
	if (!plugin) return {
		config,
		warnings: []
	};
	const result = applyExclusiveSlotSelection({
		config,
		selectedId: plugin.id,
		selectedKind: plugin.kind,
		registry: report
	});
	return {
		config: result.config,
		warnings: result.warnings
	};
}
function createPluginInstallLogger() {
	return {
		info: (msg) => defaultRuntime.log(msg),
		warn: (msg) => defaultRuntime.log(theme.warn(msg))
	};
}
function enablePluginInConfig(config, pluginId) {
	return {
		...config,
		plugins: {
			...config.plugins,
			entries: {
				...config.plugins?.entries,
				[pluginId]: {
					...config.plugins?.entries?.[pluginId],
					enabled: true
				}
			}
		}
	};
}
function logSlotWarnings(warnings) {
	if (warnings.length === 0) return;
	for (const warning of warnings) defaultRuntime.log(theme.warn(warning));
}
function registerPluginsCli(program) {
	const plugins = program.command("plugins").description("Manage OpenClaw plugins and extensions").addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/plugins", "docs.openclaw.ai/cli/plugins")}\n`);
	plugins.command("list").description("List discovered plugins").option("--json", "Print JSON").option("--enabled", "Only show enabled plugins", false).option("--verbose", "Show detailed entries", false).action((opts) => {
		const report = buildPluginStatusReport();
		const list = opts.enabled ? report.plugins.filter((p) => p.status === "loaded") : report.plugins;
		if (opts.json) {
			const payload = {
				workspaceDir: report.workspaceDir,
				plugins: list,
				diagnostics: report.diagnostics
			};
			defaultRuntime.log(JSON.stringify(payload, null, 2));
			return;
		}
		if (list.length === 0) {
			defaultRuntime.log(theme.muted("No plugins found."));
			return;
		}
		const loaded = list.filter((p) => p.status === "loaded").length;
		defaultRuntime.log(`${theme.heading("Plugins")} ${theme.muted(`(${loaded}/${list.length} loaded)`)}`);
		if (!opts.verbose) {
			const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1);
			const sourceRoots = resolvePluginSourceRoots({ workspaceDir: report.workspaceDir });
			const usedRoots = /* @__PURE__ */ new Set();
			const rows = list.map((plugin) => {
				const desc = plugin.description ? theme.muted(plugin.description) : "";
				const formattedSource = formatPluginSourceForTable(plugin, sourceRoots);
				if (formattedSource.rootKey) usedRoots.add(formattedSource.rootKey);
				const sourceLine = desc ? `${formattedSource.value}\n${desc}` : formattedSource.value;
				return {
					Name: plugin.name || plugin.id,
					ID: plugin.name && plugin.name !== plugin.id ? plugin.id : "",
					Status: plugin.status === "loaded" ? theme.success("loaded") : plugin.status === "disabled" ? theme.warn("disabled") : theme.error("error"),
					Source: sourceLine,
					Version: plugin.version ?? ""
				};
			});
			if (usedRoots.size > 0) {
				defaultRuntime.log(theme.muted("Source roots:"));
				for (const key of [
					"stock",
					"workspace",
					"global"
				]) {
					if (!usedRoots.has(key)) continue;
					const dir = sourceRoots[key];
					if (!dir) continue;
					defaultRuntime.log(`  ${theme.command(`${key}:`)} ${theme.muted(dir)}`);
				}
				defaultRuntime.log("");
			}
			defaultRuntime.log(renderTable({
				width: tableWidth,
				columns: [
					{
						key: "Name",
						header: "Name",
						minWidth: 14,
						flex: true
					},
					{
						key: "ID",
						header: "ID",
						minWidth: 10,
						flex: true
					},
					{
						key: "Status",
						header: "Status",
						minWidth: 10
					},
					{
						key: "Source",
						header: "Source",
						minWidth: 26,
						flex: true
					},
					{
						key: "Version",
						header: "Version",
						minWidth: 8
					}
				],
				rows
			}).trimEnd());
			return;
		}
		const lines = [];
		for (const plugin of list) {
			lines.push(formatPluginLine(plugin, true));
			lines.push("");
		}
		defaultRuntime.log(lines.join("\n").trim());
	});
	plugins.command("info").description("Show plugin details").argument("<id>", "Plugin id").option("--json", "Print JSON").action((id, opts) => {
		const plugin = buildPluginStatusReport().plugins.find((p) => p.id === id || p.name === id);
		if (!plugin) {
			defaultRuntime.error(`Plugin not found: ${id}`);
			process.exit(1);
		}
		const install = loadConfig().plugins?.installs?.[plugin.id];
		if (opts.json) {
			defaultRuntime.log(JSON.stringify(plugin, null, 2));
			return;
		}
		const lines = [];
		lines.push(theme.heading(plugin.name || plugin.id));
		if (plugin.name && plugin.name !== plugin.id) lines.push(theme.muted(`id: ${plugin.id}`));
		if (plugin.description) lines.push(plugin.description);
		lines.push("");
		lines.push(`${theme.muted("Status:")} ${plugin.status}`);
		lines.push(`${theme.muted("Source:")} ${shortenHomeInString(plugin.source)}`);
		lines.push(`${theme.muted("Origin:")} ${plugin.origin}`);
		if (plugin.version) lines.push(`${theme.muted("Version:")} ${plugin.version}`);
		if (plugin.toolNames.length > 0) lines.push(`${theme.muted("Tools:")} ${plugin.toolNames.join(", ")}`);
		if (plugin.hookNames.length > 0) lines.push(`${theme.muted("Hooks:")} ${plugin.hookNames.join(", ")}`);
		if (plugin.gatewayMethods.length > 0) lines.push(`${theme.muted("Gateway methods:")} ${plugin.gatewayMethods.join(", ")}`);
		if (plugin.providerIds.length > 0) lines.push(`${theme.muted("Providers:")} ${plugin.providerIds.join(", ")}`);
		if (plugin.cliCommands.length > 0) lines.push(`${theme.muted("CLI commands:")} ${plugin.cliCommands.join(", ")}`);
		if (plugin.services.length > 0) lines.push(`${theme.muted("Services:")} ${plugin.services.join(", ")}`);
		if (plugin.error) lines.push(`${theme.error("Error:")} ${plugin.error}`);
		if (install) {
			lines.push("");
			lines.push(`${theme.muted("Install:")} ${install.source}`);
			if (install.spec) lines.push(`${theme.muted("Spec:")} ${install.spec}`);
			if (install.sourcePath) lines.push(`${theme.muted("Source path:")} ${shortenHomePath(install.sourcePath)}`);
			if (install.installPath) lines.push(`${theme.muted("Install path:")} ${shortenHomePath(install.installPath)}`);
			if (install.version) lines.push(`${theme.muted("Recorded version:")} ${install.version}`);
			if (install.installedAt) lines.push(`${theme.muted("Installed at:")} ${install.installedAt}`);
		}
		defaultRuntime.log(lines.join("\n"));
	});
	plugins.command("enable").description("Enable a plugin in config").argument("<id>", "Plugin id").action(async (id) => {
		const cfg = loadConfig();
		let next = {
			...cfg,
			plugins: {
				...cfg.plugins,
				entries: {
					...cfg.plugins?.entries,
					[id]: {
						...(cfg.plugins?.entries)?.[id],
						enabled: true
					}
				}
			}
		};
		const slotResult = applySlotSelectionForPlugin(next, id);
		next = slotResult.config;
		await writeConfigFile(next);
		logSlotWarnings(slotResult.warnings);
		defaultRuntime.log(`Enabled plugin "${id}". Restart the gateway to apply.`);
	});
	plugins.command("disable").description("Disable a plugin in config").argument("<id>", "Plugin id").action(async (id) => {
		const cfg = loadConfig();
		await writeConfigFile({
			...cfg,
			plugins: {
				...cfg.plugins,
				entries: {
					...cfg.plugins?.entries,
					[id]: {
						...(cfg.plugins?.entries)?.[id],
						enabled: false
					}
				}
			}
		});
		defaultRuntime.log(`Disabled plugin "${id}". Restart the gateway to apply.`);
	});
	plugins.command("uninstall").description("Uninstall a plugin").argument("<id>", "Plugin id").option("--keep-files", "Keep installed files on disk", false).option("--keep-config", "Deprecated alias for --keep-files", false).option("--force", "Skip confirmation prompt", false).option("--dry-run", "Show what would be removed without making changes", false).action(async (id, opts) => {
		const cfg = loadConfig();
		const report = buildPluginStatusReport({ config: cfg });
		const extensionsDir = path.join(resolveStateDir(process.env, os.homedir), "extensions");
		const keepFiles = Boolean(opts.keepFiles || opts.keepConfig);
		if (opts.keepConfig) defaultRuntime.log(theme.warn("`--keep-config` is deprecated, use `--keep-files`."));
		const plugin = report.plugins.find((p) => p.id === id || p.name === id);
		const pluginId = plugin?.id ?? id;
		const hasEntry = pluginId in (cfg.plugins?.entries ?? {});
		const hasInstall = pluginId in (cfg.plugins?.installs ?? {});
		if (!hasEntry && !hasInstall) {
			if (plugin) defaultRuntime.error(`Plugin "${pluginId}" is not managed by plugins config/install records and cannot be uninstalled.`);
			else defaultRuntime.error(`Plugin not found: ${id}`);
			process.exit(1);
		}
		const install = cfg.plugins?.installs?.[pluginId];
		const isLinked = install?.source === "path";
		const preview = [];
		if (hasEntry) preview.push("config entry");
		if (hasInstall) preview.push("install record");
		if (cfg.plugins?.allow?.includes(pluginId)) preview.push("allowlist entry");
		if (isLinked && install?.sourcePath && cfg.plugins?.load?.paths?.includes(install.sourcePath)) preview.push("load path");
		if (cfg.plugins?.slots?.memory === pluginId) preview.push(`memory slot (will reset to "memory-core")`);
		const deleteTarget = !keepFiles ? resolveUninstallDirectoryTarget({
			pluginId,
			hasInstall,
			installRecord: install,
			extensionsDir
		}) : null;
		if (deleteTarget) preview.push(`directory: ${shortenHomePath(deleteTarget)}`);
		const pluginName = plugin?.name || pluginId;
		defaultRuntime.log(`Plugin: ${theme.command(pluginName)}${pluginName !== pluginId ? theme.muted(` (${pluginId})`) : ""}`);
		defaultRuntime.log(`Will remove: ${preview.length > 0 ? preview.join(", ") : "(nothing)"}`);
		if (opts.dryRun) {
			defaultRuntime.log(theme.muted("Dry run, no changes made."));
			return;
		}
		if (!opts.force) {
			if (!await promptYesNo(`Uninstall plugin "${pluginId}"?`)) {
				defaultRuntime.log("Cancelled.");
				return;
			}
		}
		const result = await uninstallPlugin({
			config: cfg,
			pluginId,
			deleteFiles: !keepFiles,
			extensionsDir
		});
		if (!result.ok) {
			defaultRuntime.error(result.error);
			process.exit(1);
		}
		for (const warning of result.warnings) defaultRuntime.log(theme.warn(warning));
		await writeConfigFile(result.config);
		const removed = [];
		if (result.actions.entry) removed.push("config entry");
		if (result.actions.install) removed.push("install record");
		if (result.actions.allowlist) removed.push("allowlist");
		if (result.actions.loadPath) removed.push("load path");
		if (result.actions.memorySlot) removed.push("memory slot");
		if (result.actions.directory) removed.push("directory");
		defaultRuntime.log(`Uninstalled plugin "${pluginId}". Removed: ${removed.length > 0 ? removed.join(", ") : "nothing"}.`);
		defaultRuntime.log("Restart the gateway to apply changes.");
	});
	plugins.command("install").description("Install a plugin (path, archive, or npm spec)").argument("<path-or-spec>", "Path (.ts/.js/.zip/.tgz/.tar.gz) or an npm package spec").option("-l, --link", "Link a local path instead of copying", false).action(async (raw, opts) => {
		const fileSpec = resolveFileNpmSpecToLocalPath(raw);
		if (fileSpec && !fileSpec.ok) {
			defaultRuntime.error(fileSpec.error);
			process.exit(1);
		}
		const resolved = resolveUserPath(fileSpec && fileSpec.ok ? fileSpec.path : raw);
		const cfg = loadConfig();
		if (fs.existsSync(resolved)) {
			if (opts.link) {
				const existing = cfg.plugins?.load?.paths ?? [];
				const merged = Array.from(new Set([...existing, resolved]));
				const probe = await installPluginFromPath({
					path: resolved,
					dryRun: true
				});
				if (!probe.ok) {
					defaultRuntime.error(probe.error);
					process.exit(1);
				}
				let next = enablePluginInConfig({
					...cfg,
					plugins: {
						...cfg.plugins,
						load: {
							...cfg.plugins?.load,
							paths: merged
						}
					}
				}, probe.pluginId);
				next = recordPluginInstall(next, {
					pluginId: probe.pluginId,
					source: "path",
					sourcePath: resolved,
					installPath: resolved,
					version: probe.version
				});
				const slotResult = applySlotSelectionForPlugin(next, probe.pluginId);
				next = slotResult.config;
				await writeConfigFile(next);
				logSlotWarnings(slotResult.warnings);
				defaultRuntime.log(`Linked plugin path: ${shortenHomePath(resolved)}`);
				defaultRuntime.log(`Restart the gateway to load plugins.`);
				return;
			}
			const result = await installPluginFromPath({
				path: resolved,
				logger: createPluginInstallLogger()
			});
			if (!result.ok) {
				defaultRuntime.error(result.error);
				process.exit(1);
			}
			clearPluginManifestRegistryCache();
			let next = enablePluginInConfig(cfg, result.pluginId);
			const source = resolveArchiveKind(resolved) ? "archive" : "path";
			next = recordPluginInstall(next, {
				pluginId: result.pluginId,
				source,
				sourcePath: resolved,
				installPath: result.targetDir,
				version: result.version
			});
			const slotResult = applySlotSelectionForPlugin(next, result.pluginId);
			next = slotResult.config;
			await writeConfigFile(next);
			logSlotWarnings(slotResult.warnings);
			defaultRuntime.log(`Installed plugin: ${result.pluginId}`);
			defaultRuntime.log(`Restart the gateway to load plugins.`);
			return;
		}
		if (opts.link) {
			defaultRuntime.error("`--link` requires a local path.");
			process.exit(1);
		}
		if (raw.startsWith(".") || raw.startsWith("~") || path.isAbsolute(raw) || raw.endsWith(".ts") || raw.endsWith(".js") || raw.endsWith(".mjs") || raw.endsWith(".cjs") || raw.endsWith(".tgz") || raw.endsWith(".tar.gz") || raw.endsWith(".tar") || raw.endsWith(".zip")) {
			defaultRuntime.error(`Path not found: ${resolved}`);
			process.exit(1);
		}
		const result = await installPluginFromNpmSpec({
			spec: raw,
			logger: createPluginInstallLogger()
		});
		if (!result.ok) {
			defaultRuntime.error(result.error);
			process.exit(1);
		}
		clearPluginManifestRegistryCache();
		let next = enablePluginInConfig(cfg, result.pluginId);
		next = recordPluginInstall(next, {
			pluginId: result.pluginId,
			source: "npm",
			spec: raw,
			installPath: result.targetDir,
			version: result.version
		});
		const slotResult = applySlotSelectionForPlugin(next, result.pluginId);
		next = slotResult.config;
		await writeConfigFile(next);
		logSlotWarnings(slotResult.warnings);
		defaultRuntime.log(`Installed plugin: ${result.pluginId}`);
		defaultRuntime.log(`Restart the gateway to load plugins.`);
	});
	plugins.command("update").description("Update installed plugins (npm installs only)").argument("[id]", "Plugin id (omit with --all)").option("--all", "Update all tracked plugins", false).option("--dry-run", "Show what would change without writing", false).action(async (id, opts) => {
		const cfg = loadConfig();
		const installs = cfg.plugins?.installs ?? {};
		const targets = opts.all ? Object.keys(installs) : id ? [id] : [];
		if (targets.length === 0) {
			if (opts.all) {
				defaultRuntime.log("No npm-installed plugins to update.");
				return;
			}
			defaultRuntime.error("Provide a plugin id or use --all.");
			process.exit(1);
		}
		const result = await updateNpmInstalledPlugins({
			config: cfg,
			pluginIds: targets,
			dryRun: opts.dryRun,
			logger: {
				info: (msg) => defaultRuntime.log(msg),
				warn: (msg) => defaultRuntime.log(theme.warn(msg))
			}
		});
		for (const outcome of result.outcomes) {
			if (outcome.status === "error") {
				defaultRuntime.log(theme.error(outcome.message));
				continue;
			}
			if (outcome.status === "skipped") {
				defaultRuntime.log(theme.warn(outcome.message));
				continue;
			}
			defaultRuntime.log(outcome.message);
		}
		if (!opts.dryRun && result.changed) {
			await writeConfigFile(result.config);
			defaultRuntime.log("Restart the gateway to load plugins.");
		}
	});
	plugins.command("doctor").description("Report plugin load issues").action(() => {
		const report = buildPluginStatusReport();
		const errors = report.plugins.filter((p) => p.status === "error");
		const diags = report.diagnostics.filter((d) => d.level === "error");
		if (errors.length === 0 && diags.length === 0) {
			defaultRuntime.log("No plugin issues detected.");
			return;
		}
		const lines = [];
		if (errors.length > 0) {
			lines.push(theme.error("Plugin errors:"));
			for (const entry of errors) lines.push(`- ${entry.id}: ${entry.error ?? "failed to load"} (${entry.source})`);
		}
		if (diags.length > 0) {
			if (lines.length > 0) lines.push("");
			lines.push(theme.warn("Diagnostics:"));
			for (const diag of diags) {
				const target = diag.pluginId ? `${diag.pluginId}: ` : "";
				lines.push(`- ${target}${diag.message}`);
			}
		}
		const docs = formatDocsLink("/plugin", "docs.openclaw.ai/plugin");
		lines.push("");
		lines.push(`${theme.muted("Docs:")} ${docs}`);
		defaultRuntime.log(lines.join("\n"));
	});
}

//#endregion
export { registerPluginsCli };