import "./pi-embedded-helpers-Df7l-4vq.js";
import "./reply-CZUaueaY.js";
import { s as defaultRuntime } from "./subsystem-hATE68va.js";
import "./paths-VdmjGGyc.js";
import { r as theme } from "./theme-C5EwWDWE.js";
import "./ansi-By7x1TI7.js";
import { h as resolveUserPath, t as CONFIG_DIR, y as shortenHomePath } from "./utils-D019Z2NU.js";
import "./logger-BKidfKRU.js";
import { t as runCommandWithTimeout } from "./exec-BBWSxSu-.js";
import { l as resolveKoiWorkspaceDir, r as resolveDefaultKoiId } from "./koi-scope-BeICsBhH.js";
import "./model-selection-DvQd6xjM.js";
import "./dist-Czg7dzzf.js";
import "./dist-cjs-CtVce_lP.js";
import { t as formatCliCommand } from "./command-format-DOYW_19_.js";
import "./boolean-D-3joTkO.js";
import "./env-ChCKTuXK.js";
import "./github-copilot-token-BqhLBQWA.js";
import { o as loadConfig, u as writeConfigFile } from "./config-BOTdzMIB.js";
import { a as MANIFEST_KEY } from "./manifest-registry-D7MfBQxU.js";
import "./plugins-dW6cc0qG.js";
import "./sessions-DyqGkYWU.js";
import "./image-CUVl1Hhm.js";
import "./dist-iFsGHQA2.js";
import "./lib-C-WEldjv.js";
import "./src-CiVOi-Ma.js";
import "./core-DrwlWTgS.js";
import "./pi-model-discovery-E8vIYAbb.js";
import "./sandbox-CDOYKOkn.js";
import "./chrome-CTicuLaA.js";
import "./skills-DHpPesfZ.js";
import "./routes-C952rvWJ.js";
import "./server-context-Cs8Yx5qf.js";
import "./image-ops-B6MN1Uli.js";
import "./message-channel-B-LM-U45.js";
import "./logging-Dn5qdhsZ.js";
import "./accounts-B8F2xBS8.js";
import "./paths-DSzdyYR5.js";
import "./tool-images-DOYdkgEG.js";
import "./redact-Db6nXe_o.js";
import "./tool-display-CGLMse-l.js";
import "./deliver-BU-ss2iZ.js";
import "./dispatcher-Da_eDTB8.js";
import "./dist-JRNJAZWM.js";
import "./pi-tool-definition-adapter-DZBstpDD.js";
import "./manager-CvGvPIqL.js";
import "./sqlite-CbC9FUO2.js";
import "./warnings-D1qUihoO.js";
import "./proxy-from-env-BBdtl5-Y.js";
import "./login-qr-BDJgj3j2.js";
import "./channel-summary-DZinsO88.js";
import "./device-identity-CBC9Fa-4.js";
import "./client-CnWPlz1L.js";
import "./call-By5olg7I.js";
import "./koi-events-CVGX2xkt.js";
import "./pairing-store-DnxZNJ-U.js";
import { t as formatDocsLink } from "./links-DlFnXfJ_.js";
import "./progress-X-YSIEVf.js";
import "./node-pairing-ClP_ps1x.js";
import "./pi-tools.policy-CA2hGIde.js";
import "./onboard-helpers-DXmuVr9a.js";
import "./prompt-style-ko1k1LE0.js";
import "./pairing-labels-CNooTiAc.js";
import "./satisfies-D4f4jwpc.js";
import "./session-cost-usage-BiwFgQ3E.js";
import "./control-service-CamrxDfq.js";
import "./channel-selection-BTYECKhQ.js";
import "./event-log-DMRSRkMD.js";
import { a as resolvePackedRootDir, i as resolveArchiveKind, n as fileExists, r as readJsonFile, t as extractArchive } from "./archive-BrDQDedR.js";
import { t as renderTable } from "./table-Bu1gPkD7.js";
import { a as parseFrontmatter, n as loadWorkspaceHookEntries, t as buildWorkspaceHookStatus } from "./hooks-status-CV-O6Xro.js";
import { t as buildPluginStatusReport } from "./status-DdawtCgi.js";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import fs$1 from "node:fs/promises";

//#region src/hooks/install.ts
const defaultLogger = {};
function unscopedPackageName(name) {
	const trimmed = name.trim();
	if (!trimmed) return trimmed;
	return trimmed.includes("/") ? trimmed.split("/").pop() ?? trimmed : trimmed;
}
function safeDirName(input) {
	const trimmed = input.trim();
	if (!trimmed) return trimmed;
	return trimmed.replaceAll("/", "__").replaceAll("\\", "__");
}
function validateHookId(hookId) {
	if (!hookId) return "invalid hook name: missing";
	if (hookId === "." || hookId === "..") return "invalid hook name: reserved path segment";
	if (hookId.includes("/") || hookId.includes("\\")) return "invalid hook name: path separators not allowed";
	return null;
}
function resolveHookInstallDir(hookId, hooksDir) {
	const hooksBase = hooksDir ? resolveUserPath(hooksDir) : path.join(CONFIG_DIR, "hooks");
	const hookIdError = validateHookId(hookId);
	if (hookIdError) throw new Error(hookIdError);
	const targetDirResult = resolveSafeInstallDir(hooksBase, hookId);
	if (!targetDirResult.ok) throw new Error(targetDirResult.error);
	return targetDirResult.path;
}
function resolveSafeInstallDir(hooksDir, hookId) {
	const targetDir = path.join(hooksDir, safeDirName(hookId));
	const resolvedBase = path.resolve(hooksDir);
	const resolvedTarget = path.resolve(targetDir);
	const relative = path.relative(resolvedBase, resolvedTarget);
	if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) return {
		ok: false,
		error: "invalid hook name: path traversal detected"
	};
	return {
		ok: true,
		path: targetDir
	};
}
async function ensureSKYKOIHooks(manifest) {
	const hooks = manifest[MANIFEST_KEY]?.hooks;
	if (!Array.isArray(hooks)) throw new Error("package.json missing SKYKOI.hooks");
	const list = hooks.map((e) => typeof e === "string" ? e.trim() : "").filter(Boolean);
	if (list.length === 0) throw new Error("package.json SKYKOI.hooks is empty");
	return list;
}
async function resolveHookNameFromDir(hookDir) {
	const hookMdPath = path.join(hookDir, "HOOK.md");
	if (!await fileExists(hookMdPath)) throw new Error(`HOOK.md missing in ${hookDir}`);
	return parseFrontmatter(await fs$1.readFile(hookMdPath, "utf-8")).name || path.basename(hookDir);
}
async function validateHookDir(hookDir) {
	if (!await fileExists(path.join(hookDir, "HOOK.md"))) throw new Error(`HOOK.md missing in ${hookDir}`);
	if (!await Promise.all([
		"handler.ts",
		"handler.js",
		"index.ts",
		"index.js"
	].map(async (candidate) => fileExists(path.join(hookDir, candidate)))).then((results) => results.some(Boolean))) throw new Error(`handler.ts/handler.js/index.ts/index.js missing in ${hookDir}`);
}
async function installHookPackageFromDir(params) {
	const logger = params.logger ?? defaultLogger;
	const timeoutMs = params.timeoutMs ?? 12e4;
	const mode = params.mode ?? "install";
	const dryRun = params.dryRun ?? false;
	const manifestPath = path.join(params.packageDir, "package.json");
	if (!await fileExists(manifestPath)) return {
		ok: false,
		error: "package.json missing"
	};
	let manifest;
	try {
		manifest = await readJsonFile(manifestPath);
	} catch (err) {
		return {
			ok: false,
			error: `invalid package.json: ${String(err)}`
		};
	}
	let hookEntries;
	try {
		hookEntries = await ensureSKYKOIHooks(manifest);
	} catch (err) {
		return {
			ok: false,
			error: String(err)
		};
	}
	const pkgName = typeof manifest.name === "string" ? manifest.name : "";
	const hookPackId = pkgName ? unscopedPackageName(pkgName) : path.basename(params.packageDir);
	const hookIdError = validateHookId(hookPackId);
	if (hookIdError) return {
		ok: false,
		error: hookIdError
	};
	if (params.expectedHookPackId && params.expectedHookPackId !== hookPackId) return {
		ok: false,
		error: `hook pack id mismatch: expected ${params.expectedHookPackId}, got ${hookPackId}`
	};
	const hooksDir = params.hooksDir ? resolveUserPath(params.hooksDir) : path.join(CONFIG_DIR, "hooks");
	await fs$1.mkdir(hooksDir, { recursive: true });
	const targetDirResult = resolveSafeInstallDir(hooksDir, hookPackId);
	if (!targetDirResult.ok) return {
		ok: false,
		error: targetDirResult.error
	};
	const targetDir = targetDirResult.path;
	if (mode === "install" && await fileExists(targetDir)) return {
		ok: false,
		error: `hook pack already exists: ${targetDir} (delete it first)`
	};
	const resolvedHooks = [];
	for (const entry of hookEntries) {
		const hookDir = path.resolve(params.packageDir, entry);
		await validateHookDir(hookDir);
		const hookName = await resolveHookNameFromDir(hookDir);
		resolvedHooks.push(hookName);
	}
	if (dryRun) return {
		ok: true,
		hookPackId,
		hooks: resolvedHooks,
		targetDir,
		version: typeof manifest.version === "string" ? manifest.version : void 0
	};
	logger.info?.(`Installing to ${targetDir}…`);
	let backupDir = null;
	if (mode === "update" && await fileExists(targetDir)) {
		backupDir = `${targetDir}.backup-${Date.now()}`;
		await fs$1.rename(targetDir, backupDir);
	}
	try {
		await fs$1.cp(params.packageDir, targetDir, { recursive: true });
	} catch (err) {
		if (backupDir) {
			await fs$1.rm(targetDir, {
				recursive: true,
				force: true
			}).catch(() => void 0);
			await fs$1.rename(backupDir, targetDir).catch(() => void 0);
		}
		return {
			ok: false,
			error: `failed to copy hook pack: ${String(err)}`
		};
	}
	const deps = manifest.dependencies ?? {};
	if (Object.keys(deps).length > 0) {
		logger.info?.("Installing hook pack dependencies…");
		const npmRes = await runCommandWithTimeout([
			"npm",
			"install",
			"--omit=dev",
			"--silent"
		], {
			timeoutMs: Math.max(timeoutMs, 3e5),
			cwd: targetDir
		});
		if (npmRes.code !== 0) {
			if (backupDir) {
				await fs$1.rm(targetDir, {
					recursive: true,
					force: true
				}).catch(() => void 0);
				await fs$1.rename(backupDir, targetDir).catch(() => void 0);
			}
			return {
				ok: false,
				error: `npm install failed: ${npmRes.stderr.trim() || npmRes.stdout.trim()}`
			};
		}
	}
	if (backupDir) await fs$1.rm(backupDir, {
		recursive: true,
		force: true
	}).catch(() => void 0);
	return {
		ok: true,
		hookPackId,
		hooks: resolvedHooks,
		targetDir,
		version: typeof manifest.version === "string" ? manifest.version : void 0
	};
}
async function installHookFromDir(params) {
	const logger = params.logger ?? defaultLogger;
	const mode = params.mode ?? "install";
	const dryRun = params.dryRun ?? false;
	await validateHookDir(params.hookDir);
	const hookName = await resolveHookNameFromDir(params.hookDir);
	const hookIdError = validateHookId(hookName);
	if (hookIdError) return {
		ok: false,
		error: hookIdError
	};
	if (params.expectedHookPackId && params.expectedHookPackId !== hookName) return {
		ok: false,
		error: `hook id mismatch: expected ${params.expectedHookPackId}, got ${hookName}`
	};
	const hooksDir = params.hooksDir ? resolveUserPath(params.hooksDir) : path.join(CONFIG_DIR, "hooks");
	await fs$1.mkdir(hooksDir, { recursive: true });
	const targetDirResult = resolveSafeInstallDir(hooksDir, hookName);
	if (!targetDirResult.ok) return {
		ok: false,
		error: targetDirResult.error
	};
	const targetDir = targetDirResult.path;
	if (mode === "install" && await fileExists(targetDir)) return {
		ok: false,
		error: `hook already exists: ${targetDir} (delete it first)`
	};
	if (dryRun) return {
		ok: true,
		hookPackId: hookName,
		hooks: [hookName],
		targetDir
	};
	logger.info?.(`Installing to ${targetDir}…`);
	let backupDir = null;
	if (mode === "update" && await fileExists(targetDir)) {
		backupDir = `${targetDir}.backup-${Date.now()}`;
		await fs$1.rename(targetDir, backupDir);
	}
	try {
		await fs$1.cp(params.hookDir, targetDir, { recursive: true });
	} catch (err) {
		if (backupDir) {
			await fs$1.rm(targetDir, {
				recursive: true,
				force: true
			}).catch(() => void 0);
			await fs$1.rename(backupDir, targetDir).catch(() => void 0);
		}
		return {
			ok: false,
			error: `failed to copy hook: ${String(err)}`
		};
	}
	if (backupDir) await fs$1.rm(backupDir, {
		recursive: true,
		force: true
	}).catch(() => void 0);
	return {
		ok: true,
		hookPackId: hookName,
		hooks: [hookName],
		targetDir
	};
}
async function installHooksFromArchive(params) {
	const logger = params.logger ?? defaultLogger;
	const timeoutMs = params.timeoutMs ?? 12e4;
	const archivePath = resolveUserPath(params.archivePath);
	if (!await fileExists(archivePath)) return {
		ok: false,
		error: `archive not found: ${archivePath}`
	};
	if (!resolveArchiveKind(archivePath)) return {
		ok: false,
		error: `unsupported archive: ${archivePath}`
	};
	const tmpDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "SKYKOI-hook-"));
	const extractDir = path.join(tmpDir, "extract");
	await fs$1.mkdir(extractDir, { recursive: true });
	logger.info?.(`Extracting ${archivePath}…`);
	try {
		await extractArchive({
			archivePath,
			destDir: extractDir,
			timeoutMs,
			logger
		});
	} catch (err) {
		return {
			ok: false,
			error: `failed to extract archive: ${String(err)}`
		};
	}
	let rootDir = "";
	try {
		rootDir = await resolvePackedRootDir(extractDir);
	} catch (err) {
		return {
			ok: false,
			error: String(err)
		};
	}
	if (await fileExists(path.join(rootDir, "package.json"))) return await installHookPackageFromDir({
		packageDir: rootDir,
		hooksDir: params.hooksDir,
		timeoutMs,
		logger,
		mode: params.mode,
		dryRun: params.dryRun,
		expectedHookPackId: params.expectedHookPackId
	});
	return await installHookFromDir({
		hookDir: rootDir,
		hooksDir: params.hooksDir,
		logger,
		mode: params.mode,
		dryRun: params.dryRun,
		expectedHookPackId: params.expectedHookPackId
	});
}
async function installHooksFromNpmSpec(params) {
	const logger = params.logger ?? defaultLogger;
	const timeoutMs = params.timeoutMs ?? 12e4;
	const mode = params.mode ?? "install";
	const dryRun = params.dryRun ?? false;
	const expectedHookPackId = params.expectedHookPackId;
	const spec = params.spec.trim();
	if (!spec) return {
		ok: false,
		error: "missing npm spec"
	};
	const tmpDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "SKYKOI-hook-pack-"));
	logger.info?.(`Downloading ${spec}…`);
	const res = await runCommandWithTimeout([
		"npm",
		"pack",
		spec
	], {
		timeoutMs: Math.max(timeoutMs, 3e5),
		cwd: tmpDir,
		env: { COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" }
	});
	if (res.code !== 0) return {
		ok: false,
		error: `npm pack failed: ${res.stderr.trim() || res.stdout.trim()}`
	};
	const packed = (res.stdout || "").split("\n").map((l) => l.trim()).filter(Boolean).pop();
	if (!packed) return {
		ok: false,
		error: "npm pack produced no archive"
	};
	return await installHooksFromArchive({
		archivePath: path.join(tmpDir, packed),
		hooksDir: params.hooksDir,
		timeoutMs,
		logger,
		mode,
		dryRun,
		expectedHookPackId
	});
}
async function installHooksFromPath(params) {
	const resolved = resolveUserPath(params.path);
	if (!await fileExists(resolved)) return {
		ok: false,
		error: `path not found: ${resolved}`
	};
	if ((await fs$1.stat(resolved)).isDirectory()) {
		if (await fileExists(path.join(resolved, "package.json"))) return await installHookPackageFromDir({
			packageDir: resolved,
			hooksDir: params.hooksDir,
			timeoutMs: params.timeoutMs,
			logger: params.logger,
			mode: params.mode,
			dryRun: params.dryRun,
			expectedHookPackId: params.expectedHookPackId
		});
		return await installHookFromDir({
			hookDir: resolved,
			hooksDir: params.hooksDir,
			logger: params.logger,
			mode: params.mode,
			dryRun: params.dryRun,
			expectedHookPackId: params.expectedHookPackId
		});
	}
	if (!resolveArchiveKind(resolved)) return {
		ok: false,
		error: `unsupported hook file: ${resolved}`
	};
	return await installHooksFromArchive({
		archivePath: resolved,
		hooksDir: params.hooksDir,
		timeoutMs: params.timeoutMs,
		logger: params.logger,
		mode: params.mode,
		dryRun: params.dryRun,
		expectedHookPackId: params.expectedHookPackId
	});
}

//#endregion
//#region src/hooks/installs.ts
function recordHookInstall(cfg, update) {
	const { hookId, ...record } = update;
	const installs = {
		...cfg.hooks?.internal?.installs,
		[hookId]: {
			...cfg.hooks?.internal?.installs?.[hookId],
			...record,
			installedAt: record.installedAt ?? (/* @__PURE__ */ new Date()).toISOString()
		}
	};
	return {
		...cfg,
		hooks: {
			...cfg.hooks,
			internal: {
				...cfg.hooks?.internal,
				installs: {
					...installs,
					[hookId]: installs[hookId]
				}
			}
		}
	};
}

//#endregion
//#region src/cli/hooks-cli.ts
function mergeHookEntries(pluginEntries, workspaceEntries) {
	const merged = /* @__PURE__ */ new Map();
	for (const entry of pluginEntries) merged.set(entry.hook.name, entry);
	for (const entry of workspaceEntries) merged.set(entry.hook.name, entry);
	return Array.from(merged.values());
}
function buildHooksReport(config) {
	const workspaceDir = resolveKoiWorkspaceDir(config, resolveDefaultKoiId(config));
	const workspaceEntries = loadWorkspaceHookEntries(workspaceDir, { config });
	return buildWorkspaceHookStatus(workspaceDir, {
		config,
		entries: mergeHookEntries(buildPluginStatusReport({
			config,
			workspaceDir
		}).hooks.map((hook) => hook.entry), workspaceEntries)
	});
}
function formatHookStatus(hook) {
	if (hook.eligible) return theme.success("âœ“ ready");
	if (hook.disabled) return theme.warn("â¸ disabled");
	return theme.error("âœ— missing");
}
function formatHookName(hook) {
	return `${hook.emoji ?? "ðŸ”—"} ${theme.command(hook.name)}`;
}
function formatHookSource(hook) {
	if (!hook.managedByPlugin) return hook.source;
	return `plugin:${hook.pluginId ?? "unknown"}`;
}
function formatHookMissingSummary(hook) {
	const missing = [];
	if (hook.missing.bins.length > 0) missing.push(`bins: ${hook.missing.bins.join(", ")}`);
	if (hook.missing.anyBins.length > 0) missing.push(`anyBins: ${hook.missing.anyBins.join(", ")}`);
	if (hook.missing.env.length > 0) missing.push(`env: ${hook.missing.env.join(", ")}`);
	if (hook.missing.config.length > 0) missing.push(`config: ${hook.missing.config.join(", ")}`);
	if (hook.missing.os.length > 0) missing.push(`os: ${hook.missing.os.join(", ")}`);
	return missing.join("; ");
}
async function readInstalledPackageVersion(dir) {
	try {
		const raw = await fs$1.readFile(path.join(dir, "package.json"), "utf-8");
		const parsed = JSON.parse(raw);
		return typeof parsed.version === "string" ? parsed.version : void 0;
	} catch {
		return;
	}
}
/**
* Format the hooks list output
*/
function formatHooksList(report, opts) {
	const hooks = opts.eligible ? report.hooks.filter((h) => h.eligible) : report.hooks;
	if (opts.json) {
		const jsonReport = {
			workspaceDir: report.workspaceDir,
			managedHooksDir: report.managedHooksDir,
			hooks: hooks.map((h) => ({
				name: h.name,
				description: h.description,
				emoji: h.emoji,
				eligible: h.eligible,
				disabled: h.disabled,
				source: h.source,
				pluginId: h.pluginId,
				events: h.events,
				homepage: h.homepage,
				missing: h.missing,
				managedByPlugin: h.managedByPlugin
			}))
		};
		return JSON.stringify(jsonReport, null, 2);
	}
	if (hooks.length === 0) return opts.eligible ? `No eligible hooks found. Run \`${formatCliCommand("skykoi hooks list")}\` to see all hooks.` : "No hooks found.";
	const eligible = hooks.filter((h) => h.eligible);
	const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1);
	const rows = hooks.map((hook) => {
		const missing = formatHookMissingSummary(hook);
		return {
			Status: formatHookStatus(hook),
			Hook: formatHookName(hook),
			Description: theme.muted(hook.description),
			Source: formatHookSource(hook),
			Missing: missing ? theme.warn(missing) : ""
		};
	});
	const columns = [
		{
			key: "Status",
			header: "Status",
			minWidth: 10
		},
		{
			key: "Hook",
			header: "Hook",
			minWidth: 18,
			flex: true
		},
		{
			key: "Description",
			header: "Description",
			minWidth: 24,
			flex: true
		},
		{
			key: "Source",
			header: "Source",
			minWidth: 12,
			flex: true
		}
	];
	if (opts.verbose) columns.push({
		key: "Missing",
		header: "Missing",
		minWidth: 18,
		flex: true
	});
	const lines = [];
	lines.push(`${theme.heading("Hooks")} ${theme.muted(`(${eligible.length}/${hooks.length} ready)`)}`);
	lines.push(renderTable({
		width: tableWidth,
		columns,
		rows
	}).trimEnd());
	return lines.join("\n");
}
/**
* Format detailed info for a single hook
*/
function formatHookInfo(report, hookName, opts) {
	const hook = report.hooks.find((h) => h.name === hookName || h.hookKey === hookName);
	if (!hook) {
		if (opts.json) return JSON.stringify({
			error: "not found",
			hook: hookName
		}, null, 2);
		return `Hook "${hookName}" not found. Run \`${formatCliCommand("skykoi hooks list")}\` to see available hooks.`;
	}
	if (opts.json) return JSON.stringify(hook, null, 2);
	const lines = [];
	const emoji = hook.emoji ?? "ðŸ”—";
	const status = hook.eligible ? theme.success("âœ“ Ready") : hook.disabled ? theme.warn("â¸ Disabled") : theme.error("âœ— Missing requirements");
	lines.push(`${emoji} ${theme.heading(hook.name)} ${status}`);
	lines.push("");
	lines.push(hook.description);
	lines.push("");
	lines.push(theme.heading("Details:"));
	if (hook.managedByPlugin) lines.push(`${theme.muted("  Source:")} ${hook.source} (${hook.pluginId ?? "unknown"})`);
	else lines.push(`${theme.muted("  Source:")} ${hook.source}`);
	lines.push(`${theme.muted("  Path:")} ${shortenHomePath(hook.filePath)}`);
	lines.push(`${theme.muted("  Handler:")} ${shortenHomePath(hook.handlerPath)}`);
	if (hook.homepage) lines.push(`${theme.muted("  Homepage:")} ${hook.homepage}`);
	if (hook.events.length > 0) lines.push(`${theme.muted("  Events:")} ${hook.events.join(", ")}`);
	if (hook.managedByPlugin) lines.push(theme.muted("  Managed by plugin; enable/disable via hooks CLI not available."));
	if (hook.requirements.bins.length > 0 || hook.requirements.anyBins.length > 0 || hook.requirements.env.length > 0 || hook.requirements.config.length > 0 || hook.requirements.os.length > 0) {
		lines.push("");
		lines.push(theme.heading("Requirements:"));
		if (hook.requirements.bins.length > 0) {
			const binsStatus = hook.requirements.bins.map((bin) => {
				return hook.missing.bins.includes(bin) ? theme.error(`âœ— ${bin}`) : theme.success(`âœ“ ${bin}`);
			});
			lines.push(`${theme.muted("  Binaries:")} ${binsStatus.join(", ")}`);
		}
		if (hook.requirements.anyBins.length > 0) {
			const anyBinsStatus = hook.missing.anyBins.length > 0 ? theme.error(`âœ— (any of: ${hook.requirements.anyBins.join(", ")})`) : theme.success(`âœ“ (any of: ${hook.requirements.anyBins.join(", ")})`);
			lines.push(`${theme.muted("  Any binary:")} ${anyBinsStatus}`);
		}
		if (hook.requirements.env.length > 0) {
			const envStatus = hook.requirements.env.map((env) => {
				return hook.missing.env.includes(env) ? theme.error(`âœ— ${env}`) : theme.success(`âœ“ ${env}`);
			});
			lines.push(`${theme.muted("  Environment:")} ${envStatus.join(", ")}`);
		}
		if (hook.requirements.config.length > 0) {
			const configStatus = hook.configChecks.map((check) => {
				return check.satisfied ? theme.success(`âœ“ ${check.path}`) : theme.error(`âœ— ${check.path}`);
			});
			lines.push(`${theme.muted("  Config:")} ${configStatus.join(", ")}`);
		}
		if (hook.requirements.os.length > 0) {
			const osStatus = hook.missing.os.length > 0 ? theme.error(`âœ— (${hook.requirements.os.join(", ")})`) : theme.success(`âœ“ (${hook.requirements.os.join(", ")})`);
			lines.push(`${theme.muted("  OS:")} ${osStatus}`);
		}
	}
	return lines.join("\n");
}
/**
* Format check output
*/
function formatHooksCheck(report, opts) {
	if (opts.json) {
		const eligible = report.hooks.filter((h) => h.eligible);
		const notEligible = report.hooks.filter((h) => !h.eligible);
		return JSON.stringify({
			total: report.hooks.length,
			eligible: eligible.length,
			notEligible: notEligible.length,
			hooks: {
				eligible: eligible.map((h) => h.name),
				notEligible: notEligible.map((h) => ({
					name: h.name,
					missing: h.missing
				}))
			}
		}, null, 2);
	}
	const eligible = report.hooks.filter((h) => h.eligible);
	const notEligible = report.hooks.filter((h) => !h.eligible);
	const lines = [];
	lines.push(theme.heading("Hooks Status"));
	lines.push("");
	lines.push(`${theme.muted("Total hooks:")} ${report.hooks.length}`);
	lines.push(`${theme.success("Ready:")} ${eligible.length}`);
	lines.push(`${theme.warn("Not ready:")} ${notEligible.length}`);
	if (notEligible.length > 0) {
		lines.push("");
		lines.push(theme.heading("Hooks not ready:"));
		for (const hook of notEligible) {
			const reasons = [];
			if (hook.disabled) reasons.push("disabled");
			if (hook.missing.bins.length > 0) reasons.push(`bins: ${hook.missing.bins.join(", ")}`);
			if (hook.missing.anyBins.length > 0) reasons.push(`anyBins: ${hook.missing.anyBins.join(", ")}`);
			if (hook.missing.env.length > 0) reasons.push(`env: ${hook.missing.env.join(", ")}`);
			if (hook.missing.config.length > 0) reasons.push(`config: ${hook.missing.config.join(", ")}`);
			if (hook.missing.os.length > 0) reasons.push(`os: ${hook.missing.os.join(", ")}`);
			lines.push(`  ${hook.emoji ?? "ðŸ”—"} ${hook.name} - ${reasons.join("; ")}`);
		}
	}
	return lines.join("\n");
}
async function enableHook(hookName) {
	const config = loadConfig();
	const hook = buildHooksReport(config).hooks.find((h) => h.name === hookName);
	if (!hook) throw new Error(`Hook "${hookName}" not found`);
	if (hook.managedByPlugin) throw new Error(`Hook "${hookName}" is managed by plugin "${hook.pluginId ?? "unknown"}" and cannot be enabled/disabled.`);
	if (!hook.eligible) throw new Error(`Hook "${hookName}" is not eligible (missing requirements)`);
	const entries = { ...config.hooks?.internal?.entries };
	entries[hookName] = {
		...entries[hookName],
		enabled: true
	};
	await writeConfigFile({
		...config,
		hooks: {
			...config.hooks,
			internal: {
				...config.hooks?.internal,
				enabled: true,
				entries
			}
		}
	});
	defaultRuntime.log(`${theme.success("âœ“")} Enabled hook: ${hook.emoji ?? "ðŸ”—"} ${theme.command(hookName)}`);
}
async function disableHook(hookName) {
	const config = loadConfig();
	const hook = buildHooksReport(config).hooks.find((h) => h.name === hookName);
	if (!hook) throw new Error(`Hook "${hookName}" not found`);
	if (hook.managedByPlugin) throw new Error(`Hook "${hookName}" is managed by plugin "${hook.pluginId ?? "unknown"}" and cannot be enabled/disabled.`);
	const entries = { ...config.hooks?.internal?.entries };
	entries[hookName] = {
		...entries[hookName],
		enabled: false
	};
	await writeConfigFile({
		...config,
		hooks: {
			...config.hooks,
			internal: {
				...config.hooks?.internal,
				entries
			}
		}
	});
	defaultRuntime.log(`${theme.warn("â¸")} Disabled hook: ${hook.emoji ?? "ðŸ”—"} ${theme.command(hookName)}`);
}
function registerHooksCli(program) {
	const hooks = program.command("hooks").description("Manage internal koi hooks").addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/hooks", "docs.skykoi.com/cli/hooks")}\n`);
	hooks.command("list").description("List all hooks").option("--eligible", "Show only eligible hooks", false).option("--json", "Output as JSON", false).option("-v, --verbose", "Show more details including missing requirements", false).action(async (opts) => {
		try {
			const report = buildHooksReport(loadConfig());
			defaultRuntime.log(formatHooksList(report, opts));
		} catch (err) {
			defaultRuntime.error(`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`);
			process.exit(1);
		}
	});
	hooks.command("info <name>").description("Show detailed information about a hook").option("--json", "Output as JSON", false).action(async (name, opts) => {
		try {
			const report = buildHooksReport(loadConfig());
			defaultRuntime.log(formatHookInfo(report, name, opts));
		} catch (err) {
			defaultRuntime.error(`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`);
			process.exit(1);
		}
	});
	hooks.command("check").description("Check hooks eligibility status").option("--json", "Output as JSON", false).action(async (opts) => {
		try {
			const report = buildHooksReport(loadConfig());
			defaultRuntime.log(formatHooksCheck(report, opts));
		} catch (err) {
			defaultRuntime.error(`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`);
			process.exit(1);
		}
	});
	hooks.command("enable <name>").description("Enable a hook").action(async (name) => {
		try {
			await enableHook(name);
		} catch (err) {
			defaultRuntime.error(`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`);
			process.exit(1);
		}
	});
	hooks.command("disable <name>").description("Disable a hook").action(async (name) => {
		try {
			await disableHook(name);
		} catch (err) {
			defaultRuntime.error(`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`);
			process.exit(1);
		}
	});
	hooks.command("install").description("Install a hook pack (path, archive, or npm spec)").argument("<path-or-spec>", "Path to a hook pack or npm package spec").option("-l, --link", "Link a local path instead of copying", false).action(async (raw, opts) => {
		const resolved = resolveUserPath(raw);
		const cfg = loadConfig();
		if (fs.existsSync(resolved)) {
			if (opts.link) {
				if (!fs.statSync(resolved).isDirectory()) {
					defaultRuntime.error("Linked hook paths must be directories.");
					process.exit(1);
				}
				const existing = cfg.hooks?.internal?.load?.extraDirs ?? [];
				const merged = Array.from(new Set([...existing, resolved]));
				const probe = await installHooksFromPath({
					path: resolved,
					dryRun: true
				});
				if (!probe.ok) {
					defaultRuntime.error(probe.error);
					process.exit(1);
				}
				let next = {
					...cfg,
					hooks: {
						...cfg.hooks,
						internal: {
							...cfg.hooks?.internal,
							enabled: true,
							load: {
								...cfg.hooks?.internal?.load,
								extraDirs: merged
							}
						}
					}
				};
				for (const hookName of probe.hooks) next = {
					...next,
					hooks: {
						...next.hooks,
						internal: {
							...next.hooks?.internal,
							entries: {
								...next.hooks?.internal?.entries,
								[hookName]: {
									...next.hooks?.internal?.entries?.[hookName],
									enabled: true
								}
							}
						}
					}
				};
				next = recordHookInstall(next, {
					hookId: probe.hookPackId,
					source: "path",
					sourcePath: resolved,
					installPath: resolved,
					version: probe.version,
					hooks: probe.hooks
				});
				await writeConfigFile(next);
				defaultRuntime.log(`Linked hook path: ${shortenHomePath(resolved)}`);
				defaultRuntime.log(`Restart the gateway to load hooks.`);
				return;
			}
			const result = await installHooksFromPath({
				path: resolved,
				logger: {
					info: (msg) => defaultRuntime.log(msg),
					warn: (msg) => defaultRuntime.log(theme.warn(msg))
				}
			});
			if (!result.ok) {
				defaultRuntime.error(result.error);
				process.exit(1);
			}
			let next = {
				...cfg,
				hooks: {
					...cfg.hooks,
					internal: {
						...cfg.hooks?.internal,
						enabled: true,
						entries: { ...cfg.hooks?.internal?.entries }
					}
				}
			};
			for (const hookName of result.hooks) next = {
				...next,
				hooks: {
					...next.hooks,
					internal: {
						...next.hooks?.internal,
						entries: {
							...next.hooks?.internal?.entries,
							[hookName]: {
								...next.hooks?.internal?.entries?.[hookName],
								enabled: true
							}
						}
					}
				}
			};
			const source = resolveArchiveKind(resolved) ? "archive" : "path";
			next = recordHookInstall(next, {
				hookId: result.hookPackId,
				source,
				sourcePath: resolved,
				installPath: result.targetDir,
				version: result.version,
				hooks: result.hooks
			});
			await writeConfigFile(next);
			defaultRuntime.log(`Installed hooks: ${result.hooks.join(", ")}`);
			defaultRuntime.log(`Restart the gateway to load hooks.`);
			return;
		}
		if (opts.link) {
			defaultRuntime.error("`--link` requires a local path.");
			process.exit(1);
		}
		if (raw.startsWith(".") || raw.startsWith("~") || path.isAbsolute(raw) || raw.endsWith(".zip") || raw.endsWith(".tgz") || raw.endsWith(".tar.gz") || raw.endsWith(".tar")) {
			defaultRuntime.error(`Path not found: ${resolved}`);
			process.exit(1);
		}
		const result = await installHooksFromNpmSpec({
			spec: raw,
			logger: {
				info: (msg) => defaultRuntime.log(msg),
				warn: (msg) => defaultRuntime.log(theme.warn(msg))
			}
		});
		if (!result.ok) {
			defaultRuntime.error(result.error);
			process.exit(1);
		}
		let next = {
			...cfg,
			hooks: {
				...cfg.hooks,
				internal: {
					...cfg.hooks?.internal,
					enabled: true,
					entries: { ...cfg.hooks?.internal?.entries }
				}
			}
		};
		for (const hookName of result.hooks) next = {
			...next,
			hooks: {
				...next.hooks,
				internal: {
					...next.hooks?.internal,
					entries: {
						...next.hooks?.internal?.entries,
						[hookName]: {
							...next.hooks?.internal?.entries?.[hookName],
							enabled: true
						}
					}
				}
			}
		};
		next = recordHookInstall(next, {
			hookId: result.hookPackId,
			source: "npm",
			spec: raw,
			installPath: result.targetDir,
			version: result.version,
			hooks: result.hooks
		});
		await writeConfigFile(next);
		defaultRuntime.log(`Installed hooks: ${result.hooks.join(", ")}`);
		defaultRuntime.log(`Restart the gateway to load hooks.`);
	});
	hooks.command("update").description("Update installed hooks (npm installs only)").argument("[id]", "Hook pack id (omit with --all)").option("--all", "Update all tracked hooks", false).option("--dry-run", "Show what would change without writing", false).action(async (id, opts) => {
		const cfg = loadConfig();
		const installs = cfg.hooks?.internal?.installs ?? {};
		const targets = opts.all ? Object.keys(installs) : id ? [id] : [];
		if (targets.length === 0) {
			defaultRuntime.error("Provide a hook id or use --all.");
			process.exit(1);
		}
		let nextCfg = cfg;
		let updatedCount = 0;
		for (const hookId of targets) {
			const record = installs[hookId];
			if (!record) {
				defaultRuntime.log(theme.warn(`No install record for "${hookId}".`));
				continue;
			}
			if (record.source !== "npm") {
				defaultRuntime.log(theme.warn(`Skipping "${hookId}" (source: ${record.source}).`));
				continue;
			}
			if (!record.spec) {
				defaultRuntime.log(theme.warn(`Skipping "${hookId}" (missing npm spec).`));
				continue;
			}
			let installPath;
			try {
				installPath = record.installPath ?? resolveHookInstallDir(hookId);
			} catch (err) {
				defaultRuntime.log(theme.error(`Invalid install path for "${hookId}": ${String(err)}`));
				continue;
			}
			const currentVersion = await readInstalledPackageVersion(installPath);
			if (opts.dryRun) {
				const probe = await installHooksFromNpmSpec({
					spec: record.spec,
					mode: "update",
					dryRun: true,
					expectedHookPackId: hookId,
					logger: {
						info: (msg) => defaultRuntime.log(msg),
						warn: (msg) => defaultRuntime.log(theme.warn(msg))
					}
				});
				if (!probe.ok) {
					defaultRuntime.log(theme.error(`Failed to check ${hookId}: ${probe.error}`));
					continue;
				}
				const nextVersion = probe.version ?? "unknown";
				const currentLabel = currentVersion ?? "unknown";
				if (currentVersion && probe.version && currentVersion === probe.version) defaultRuntime.log(`${hookId} is up to date (${currentLabel}).`);
				else defaultRuntime.log(`Would update ${hookId}: ${currentLabel} â†’ ${nextVersion}.`);
				continue;
			}
			const result = await installHooksFromNpmSpec({
				spec: record.spec,
				mode: "update",
				expectedHookPackId: hookId,
				logger: {
					info: (msg) => defaultRuntime.log(msg),
					warn: (msg) => defaultRuntime.log(theme.warn(msg))
				}
			});
			if (!result.ok) {
				defaultRuntime.log(theme.error(`Failed to update ${hookId}: ${result.error}`));
				continue;
			}
			const nextVersion = result.version ?? await readInstalledPackageVersion(result.targetDir);
			nextCfg = recordHookInstall(nextCfg, {
				hookId,
				source: "npm",
				spec: record.spec,
				installPath: result.targetDir,
				version: nextVersion,
				hooks: result.hooks
			});
			updatedCount += 1;
			const currentLabel = currentVersion ?? "unknown";
			const nextLabel = nextVersion ?? "unknown";
			if (currentVersion && nextVersion && currentVersion === nextVersion) defaultRuntime.log(`${hookId} already at ${currentLabel}.`);
			else defaultRuntime.log(`Updated ${hookId}: ${currentLabel} â†’ ${nextLabel}.`);
		}
		if (updatedCount > 0) {
			await writeConfigFile(nextCfg);
			defaultRuntime.log("Restart the gateway to load hooks.");
		}
	});
	hooks.action(async () => {
		try {
			const report = buildHooksReport(loadConfig());
			defaultRuntime.log(formatHooksList(report, {}));
		} catch (err) {
			defaultRuntime.error(`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`);
			process.exit(1);
		}
	});
}

//#endregion
export { registerHooksCli };