import { join } from "node:path";
import { getAutoSession } from "../../auto/session.js";
import { handleCmux } from "../../commands-cmux.js";
import {
	ensurePreferencesFile,
	handlePrefs,
	handlePrefsMode,
	handlePrefsWizard,
} from "../../commands-prefs-wizard.js";
import { runEnvironmentChecks } from "../../doctor-environment.js";
import {
	getGlobalSFPreferencesPath,
	getProjectSFPreferencesPath,
} from "../../preferences.js";
import {
	computeProgressScore,
	formatProgressLine,
} from "../../progress-score.js";
import { setSessionModelOverride } from "../../session-model-override.js";
import { formattedShortcutPair } from "../../shortcut-defs.js";
import { deriveState } from "../../state.js";
import { writeUokDiagnostics } from "../../uok/diagnostic-synthesis.js";
import { projectRoot } from "../context.js";
export function showHelp(ctx, args = "") {
	const summaryLines = [
		"SF — Singularity Forge\n",
		"QUICK START",
		"  /start <tpl>   Start a workflow template",
		"  /next          Run one assisted unit",
		"  /autonomous    Run all queued product units continuously",
		"  /pause         Pause autonomous mode",
		"  /autonomous stop Stop autonomous mode gracefully",
		"",
		"VISIBILITY",
		`  /status         Dashboard  (${formattedShortcutPair("dashboard")})`,
		`  /parallel watch Parallel monitor  (${formattedShortcutPair("parallel")})`,
		`  /notifications  Notification history  (${formattedShortcutPair("notifications")})`,
		"  /tasks          Background work surface — units, workers, budget",
		"  /visualize      Interactive 10-tab TUI",
		"  /queue          Show queued/dispatched units",
		"",
		"COURSE CORRECTION",
		"  /steer <desc>   Apply user override to active work",
		"  /steer mode <m> [scope]  Change work mode (now|after-current-unit|next-milestone)",
		"  /steer permission-profile <p> [scope] Change permission profile",
		"  /steer model-mode <m>    Change model mode for next unit",
		"  /capture <text> Quick-capture a thought to CAPTURES.md",
		"  /triage         Classify and route pending captures",
		"  /undo           Revert last completed unit  [--force]",
		"  /rethink        Conversational project reorganization",
		"",
		"SETUP",
		"  /init           Project init wizard",
		"  /setup          Global setup status  [llm|search|remote|keys|prefs]",
		"  /reload         Snapshot and reload agent with fresh extension code",
		"  /model          Switch active session model",
		"  /prefs          Manage preferences",
		"  /doctor         Diagnose and repair .sf/ state",
		"  /repair         Switch to repair work mode and run diagnostics",
		"  /tasks          Background work surface",
		"  /skills         List discovered skills",
		"",
		"Use /help all for the complete command reference.",
	];
	const allLines = [
		"SF — Singularity Forge\n",
		"WORKFLOW",
		"  /start <tpl>   Start a workflow template (bugfix, spike, feature, hotfix, etc.)",
		"  /templates     List available workflow templates  [info <name>]",
		"  /next           Run one assisted unit",
		"  /next           Assisted mode: execute next task, then pause  [--dry-run] [--verbose]",
		"  /autonomous     Run all queued product units continuously  [--verbose]",
		"  /autonomous stop Stop autonomous mode gracefully",
		"  /pause          Pause autonomous mode (preserves state, /autonomous to resume)",
		"  /discuss        Start guided milestone/slice discussion",
		"  /new-milestone  Create milestone from headless context (used by sf headless)",
		"",
		"VISIBILITY",
		`  /status         Show progress dashboard  (${formattedShortcutPair("dashboard")})`,
		`  /parallel watch Open parallel worker monitor  (${formattedShortcutPair("parallel")})`,
		"  /visualize      Interactive 10-tab TUI (progress, timeline, deps, metrics, health, agent, changes, knowledge, captures, export)",
		"  /queue          Show queued/dispatched units and execution order",
		"  /tasks          Background work surface — units, workers, budget, checkpoints",
		"  /history        View execution history  [--cost] [--phase] [--model] [N]",
		"  /changelog      Show categorized release notes  [version]",
		`  /notifications  View persistent notification history  [clear|tail|filter]  (${formattedShortcutPair("notifications")})`,
		"",
		"COURSE CORRECTION",
		"  /steer <desc>   Apply user override to active work",
		"  /capture <text> Quick-capture a thought to CAPTURES.md",
		"  /triage         Classify and route pending captures",
		"  /skip <unit>    Prevent a unit from autonomous mode dispatch",
		"  /undo           Revert last completed unit  [--force]",
		"  /rethink        Conversational project reorganization — reorder, park, discard, add milestones",
		"  /park [id]      Park a milestone — skip without deleting  [reason]",
		"  /unpark [id]    Reactivate a parked milestone",
		"",
		"PROJECT KNOWLEDGE",
		"  /knowledge <type> <text>   Add rule, pattern, or lesson to KNOWLEDGE.md",
		"  /codebase [generate|update|stats|indexer]   Manage CODEBASE.md and Sift code search",
		"",
		"SCHEDULE",
		"  /schedule add --in <dur> <title>   Schedule a follow-up item",
		"  /schedule list                     Show pending scheduled items",
		"  /schedule done <id>                Mark an item complete",
		"",
		"SETUP & CONFIGURATION",
		"  /init           Project init wizard — detect, configure, bootstrap .sf/",
		"  /setup          Global setup status  [llm|search|remote|keys|prefs]",
		"  /model          Switch active session model  [provider/model|model-id]",
		"  /mode           Switch work mode (chat/plan/build/review/repair/research)",
		"  /control        Switch run control (manual/assisted/autonomous)",
		"  /permission-profile Switch permission profile (restricted/normal/trusted/unrestricted)",
		"  /model-mode     Switch model mode (fast/smart/deep)",
		"  /prefs          Manage preferences  [global|project|status|wizard|setup|import-claude]",
		"  /cmux           Manage cmux integration  [status|on|off|notifications|sidebar|splits|browser]",
		"  /config         Set API keys for external tools",
		"  /keys           API key manager  [list|add|remove|test|rotate|doctor]",
		"  /show-config    Show effective configuration (models, routing, toggles)",
		"  /hooks          Show post-unit hook configuration",
		"  /extensions     Manage extensions  [list|enable|disable|info]",
		"  /fast           Toggle OpenAI service tier  [on|off|flex|status]",
		"  /mcp            External MCP server status  [status|check <server>]",
		"",
		"MAINTENANCE",
		"  /doctor         Diagnose and repair .sf/ state  [audit|fix|heal] [scope]",
		"  /repair         Switch to repair work mode and run diagnostics  [--autonomous]",
		"  /tasks          Background work surface  [--refresh|--failed|--cancelled|--all]",
		"  /skills         List discovered skills from .agents/skills/",
		"  /skills --eval <name>  Run eval cases for a skill",
		"  /reload         Snapshot & reload agent, resume same session",
		"  /export         Export milestone/slice results  [--json|--markdown|--html] [--all]",
		"  /cleanup        Remove merged branches or snapshots  [branches|snapshots]",
		"  /worktree       Manage worktrees from the TUI  [list|merge|clean|remove]",
		"  /migrate        Migrate .planning/ (v1) to .sf/ (v2) format",
		"  /remote         Configure remote question delivery  [slack|discord|status|disconnect]",
		"  /inspect        Show SQLite DB diagnostics (schema, row counts, recent entries)",
		"  /update         Update SF to the latest version via npm",
	];
	const showAll = args.trim().toLowerCase() === "all";
	ctx.ui.notify((showAll ? allLines : summaryLines).join("\n"), "info");
}
export async function handleStatus(ctx) {
	const basePath = projectRoot();
	// Open DB in cold sessions so status uses DB-backed state, not filesystem fallback (#3385)
	const { ensureDbOpen } = await import("../../bootstrap/dynamic-tools.js");
	await ensureDbOpen();
	const state = await deriveState(basePath);
	if (state.registry.length === 0) {
		ctx.ui.notify("No SF milestones found. Run /next to start.", "info");
		return;
	}
	const { SFDashboardOverlay } = await import("../../dashboard-overlay.js");
	const result = await ctx.ui.custom(
		(tui, theme, _kb, done) =>
			new SFDashboardOverlay(tui, theme, () => done(true)),
		{
			overlay: true,
			overlayOptions: {
				width: "90%",
				minWidth: 80,
				maxHeight: "92%",
				anchor: "center",
			},
		},
	);
	if (result === undefined) {
		ctx.ui.notify(formatTextStatus(state), "info");
	}
}
export async function fireStatusViaCommand(ctx) {
	await handleStatus(ctx);
}
export async function handleVisualize(ctx) {
	if (!ctx.hasUI) {
		ctx.ui.notify("Visualizer requires an interactive terminal.", "warning");
		return;
	}
	const { SFVisualizerOverlay } = await import("../../visualizer-overlay.js");
	const result = await ctx.ui.custom(
		(tui, theme, _kb, done) =>
			new SFVisualizerOverlay(tui, theme, () => done(true)),
		{
			overlay: true,
			overlayOptions: {
				width: "80%",
				minWidth: 80,
				maxHeight: "90%",
				anchor: "center",
			},
		},
	);
	if (result === undefined) {
		ctx.ui.notify(
			"Visualizer requires an interactive terminal. Use /status for a text-based overview.",
			"warning",
		);
	}
}
export async function handleSetup(args, ctx) {
	const { detectProjectState, hasGlobalSetup } = await import(
		"../../detection.js"
	);
	const globalConfigured = hasGlobalSetup();
	const detection = detectProjectState(projectRoot());
	const statusLines = ["SF Setup Status\n"];
	statusLines.push(
		`  Global preferences: ${globalConfigured ? "configured" : "not set"}`,
	);
	statusLines.push(`  Project state: ${detection.state}`);
	if (detection.projectSignals.primaryLanguage) {
		statusLines.push(`  Detected: ${detection.projectSignals.primaryLanguage}`);
	}
	if (args === "llm" || args === "auth") {
		ctx.ui.notify("Use /login to configure LLM authentication.", "info");
		return;
	}
	if (args === "search") {
		ctx.ui.notify("Use /search-provider to configure web search.", "info");
		return;
	}
	if (args === "remote") {
		ctx.ui.notify("Use /remote to configure remote questions.", "info");
		return;
	}
	if (args === "keys") {
		const { handleKeys } = await import("../../key-manager.js");
		await handleKeys("", ctx);
		return;
	}
	if (args === "prefs") {
		await ensurePreferencesFile(getGlobalSFPreferencesPath(), ctx, "global");
		await handlePrefsWizard(ctx, "global");
		return;
	}
	ctx.ui.notify(statusLines.join("\n"), "info");
	ctx.ui.notify(
		"Available setup commands:\n" +
			"  /setup llm     — LLM authentication\n" +
			"  /setup search  — Web search provider\n" +
			"  /setup remote  — Remote questions (Discord/Slack/Telegram)\n" +
			"  /setup keys    — Tool API keys\n" +
			"  /setup prefs   — Global preferences wizard",
		"info",
	);
}
function sortModelsForSelection(models, currentModel) {
	return [...models].sort((a, b) => {
		const aCurrent =
			currentModel &&
			a.provider === currentModel.provider &&
			a.id === currentModel.id;
		const bCurrent =
			currentModel &&
			b.provider === currentModel.provider &&
			b.id === currentModel.id;
		if (aCurrent && !bCurrent) return -1;
		if (!aCurrent && bCurrent) return 1;
		const providerCmp = a.provider.localeCompare(b.provider);
		if (providerCmp !== 0) return providerCmp;
		return a.id.localeCompare(b.id);
	});
}
function buildProviderModelGroups(models, currentModel) {
	const byProvider = new Map();
	for (const model of sortModelsForSelection(models, currentModel)) {
		let group = byProvider.get(model.provider);
		if (!group) {
			group = [];
			byProvider.set(model.provider, group);
		}
		group.push(model);
	}
	return byProvider;
}
async function selectModelByProvider(title, models, ctx, currentModel) {
	const byProvider = buildProviderModelGroups(models, currentModel);
	const providerOptions = Array.from(byProvider.entries()).map(
		([provider, group]) =>
			`${provider} (${group.length} model${group.length === 1 ? "" : "s"})`,
	);
	providerOptions.push("(cancel)");
	const providerChoice = await ctx.ui.select(
		`${title} — choose provider:`,
		providerOptions,
	);
	if (
		!providerChoice ||
		typeof providerChoice !== "string" ||
		providerChoice === "(cancel)"
	)
		return undefined;
	const providerName = providerChoice.replace(/ \(\d+ models?\)$/, "");
	const providerModels = byProvider.get(providerName);
	if (!providerModels || providerModels.length === 0) return undefined;
	const optionToModel = new Map();
	const modelOptions = providerModels.map((model) => {
		const isCurrent =
			currentModel &&
			model.provider === currentModel.provider &&
			model.id === currentModel.id;
		const label = `${isCurrent ? "* " : ""}${model.id}`;
		optionToModel.set(label, model);
		return label;
	});
	modelOptions.push("(cancel)");
	const modelChoice = await ctx.ui.select(
		`${title} — ${providerName}:`,
		modelOptions,
	);
	if (
		!modelChoice ||
		typeof modelChoice !== "string" ||
		modelChoice === "(cancel)"
	)
		return undefined;
	return optionToModel.get(modelChoice);
}
async function resolveRequestedModel(query, ctx) {
	const { resolveModelId } = await import("../../auto-model-selection.js");
	const models = ctx.modelRegistry.getAvailable();
	const exact = resolveModelId(query, models, ctx.model?.provider);
	if (exact) return exact;
	const lowerQuery = query.toLowerCase();
	const partialMatches = models.filter(
		(model) =>
			model.id.toLowerCase().includes(lowerQuery) ||
			`${model.provider}/${model.id}`.toLowerCase().includes(lowerQuery),
	);
	if (partialMatches.length === 1) return partialMatches[0];
	if (partialMatches.length === 0 || !ctx.hasUI) return undefined;
	return selectModelByProvider(
		`Multiple models match "${query}"`,
		partialMatches,
		ctx,
		ctx.model,
	);
}
async function handleModel(trimmedArgs, ctx, pi) {
	const availableModels = ctx.modelRegistry.getAvailable();
	if (availableModels.length === 0) {
		ctx.ui.notify(
			"No available models found. Check provider auth and model discovery.",
			"warning",
		);
		return;
	}
	if (!pi) {
		ctx.ui.notify("Model switching is unavailable in this context.", "warning");
		return;
	}
	const trimmed = trimmedArgs.trim();
	let targetModel;
	if (!trimmed) {
		if (!ctx.hasUI) {
			const current = ctx.model
				? `${ctx.model.provider}/${ctx.model.id}`
				: "(none)";
			ctx.ui.notify(
				`Current model: ${current}\nUsage: /model <provider/model|model-id>`,
				"info",
			);
			return;
		}
		targetModel = await selectModelByProvider(
			"Select session model:",
			availableModels,
			ctx,
			ctx.model,
		);
	} else {
		targetModel = await resolveRequestedModel(trimmed, ctx);
	}
	if (!targetModel) {
		ctx.ui.notify(
			`Model "${trimmed}" not found. Use /model with an exact provider/model or a unique model ID.`,
			"warning",
		);
		return;
	}
	const ok = await pi.setModel(targetModel);
	if (!ok) {
		ctx.ui.notify(
			`No API key for ${targetModel.provider}/${targetModel.id}`,
			"warning",
		);
		return;
	}
	// /model is an explicit per-session pin for SF dispatches.
	// This is captured at auto bootstrap so it survives internal session
	// switches during /autonomous and /next runs.
	const sessionId = ctx.sessionManager?.getSessionId?.();
	if (sessionId) {
		setSessionModelOverride(sessionId, {
			provider: targetModel.provider,
			id: targetModel.id,
		});
	}
	ctx.ui.notify(`Model: ${targetModel.provider}/${targetModel.id}`, "info");
}
function formatModeState(mode) {
	if (!mode) return "SF — mode unknown";
	return `SF — ${mode.workMode} · ${mode.runControl} · ${mode.permissionProfile} · ${mode.modelMode}`;
}
function handleModeCommand(args, ctx) {
	const s = getAutoSession();
	const parts = args.trim().split(/\s+/).filter(Boolean);
	if (parts.length === 0) {
		const mode = s.getMode();
		ctx.ui.notify(formatModeState(mode), "info");
		return true;
	}
	const workMode = parts[0];
	const transition = s.setMode({ workMode });
	ctx.ui.notify(
		`Mode: ${transition.from.workMode} → ${transition.to.workMode}`,
		"info",
	);
	return true;
}
function handleControlCommand(args, ctx) {
	const s = getAutoSession();
	const runControl = args.trim();
	if (!runControl) {
		const mode = s.getMode();
		ctx.ui.notify(`Run control: ${mode.runControl}`, "info");
		return true;
	}
	const transition = s.setMode({ runControl });
	ctx.ui.notify(
		`Run control: ${transition.from.runControl} → ${transition.to.runControl}`,
		"info",
	);
	return true;
}
function handlePermissionProfileCommand(args, ctx) {
	const s = getAutoSession();
	const permissionProfile = args.trim();
	if (!permissionProfile) {
		const mode = s.getMode();
		ctx.ui.notify(`Permission profile: ${mode.permissionProfile}`, "info");
		return true;
	}
	const transition = s.setMode({ permissionProfile });
	ctx.ui.notify(
		`Permission profile: ${transition.from.permissionProfile} → ${transition.to.permissionProfile}`,
		"info",
	);
	return true;
}
function handleModelModeCommand(args, ctx) {
	const s = getAutoSession();
	const modelMode = args.trim();
	if (!modelMode) {
		const mode = s.getMode();
		ctx.ui.notify(`Model mode: ${mode.modelMode}`, "info");
		return true;
	}
	const transition = s.setMode({ modelMode });
	ctx.ui.notify(
		`Model mode: ${transition.from.modelMode} → ${transition.to.modelMode}`,
		"info",
	);
	return true;
}
export async function handleCoreCommand(trimmed, ctx, pi) {
	if (
		trimmed === "help" ||
		trimmed === "h" ||
		trimmed === "?" ||
		trimmed.startsWith("help ")
	) {
		showHelp(ctx, trimmed.startsWith("help ") ? trimmed.slice(5).trim() : "");
		return true;
	}
	if (trimmed === "status") {
		await handleStatus(ctx);
		return true;
	}
	if (trimmed === "visualize") {
		await handleVisualize(ctx);
		return true;
	}
	if (trimmed === "widget" || trimmed.startsWith("widget ")) {
		const { cycleWidgetMode, setWidgetMode, getWidgetMode } = await import(
			"../../auto-dashboard.js"
		);
		const arg = trimmed.replace(/^widget\s*/, "").trim();
		if (arg === "full" || arg === "small" || arg === "min" || arg === "off") {
			setWidgetMode(arg);
		} else {
			cycleWidgetMode();
		}
		ctx.ui.notify(`Widget: ${getWidgetMode()}`, "info");
		return true;
	}
	if (trimmed === "model" || trimmed.startsWith("model ")) {
		await handleModel(trimmed.replace(/^model\s*/, "").trim(), ctx, pi);
		return true;
	}
	if (trimmed === "mode" || trimmed.startsWith("mode ")) {
		const modeArgs = trimmed.replace(/^mode\s*/, "").trim();
		// If arg is a work mode (chat/plan/build/review/repair/research), use new mode system
		const workModes = ["chat", "plan", "build", "review", "repair", "research"];
		if (workModes.includes(modeArgs)) {
			handleModeCommand(modeArgs, ctx);
			return true;
		}
		// Otherwise fall back to old prefs mode (solo/team)
		const scope = modeArgs === "project" ? "project" : "global";
		const path =
			scope === "project"
				? getProjectSFPreferencesPath()
				: getGlobalSFPreferencesPath();
		await ensurePreferencesFile(path, ctx, scope);
		await handlePrefsMode(ctx, scope);
		return true;
	}
	if (trimmed === "control" || trimmed.startsWith("control ")) {
		handleControlCommand(trimmed.replace(/^control\s*/, "").trim(), ctx);
		return true;
	}
	if (
		trimmed === "permission-profile" ||
		trimmed.startsWith("permission-profile ")
	) {
		handlePermissionProfileCommand(
			trimmed.replace(/^permission-profile\s*/, "").trim(),
			ctx,
		);
		return true;
	}
	if (trimmed === "model-mode" || trimmed.startsWith("model-mode ")) {
		handleModelModeCommand(trimmed.replace(/^model-mode\s*/, "").trim(), ctx);
		return true;
	}
	if (trimmed === "prefs" || trimmed.startsWith("prefs ")) {
		await handlePrefs(trimmed.replace(/^prefs\s*/, "").trim(), ctx);
		return true;
	}
	if (trimmed === "tasks" || trimmed.startsWith("tasks ")) {
		const { handleTasks } = await import("./tasks.js");
		await handleTasks(trimmed.replace(/^tasks\s*/, "").trim(), ctx);
		return true;
	}
	if (trimmed === "cmux" || trimmed.startsWith("cmux ")) {
		await handleCmux(trimmed.replace(/^cmux\s*/, "").trim(), ctx);
		return true;
	}
	if (trimmed === "show-config") {
		const { SFConfigOverlay, formatConfigText } = await import(
			"../../config-overlay.js"
		);
		const result = await ctx.ui.custom(
			(tui, theme, _kb, done) =>
				new SFConfigOverlay(tui, theme, () => done(true)),
			{
				overlay: true,
				overlayOptions: {
					width: "65%",
					minWidth: 55,
					maxHeight: "85%",
					anchor: "center",
				},
			},
		);
		if (result === undefined) {
			ctx.ui.notify(formatConfigText(), "info");
		}
		return true;
	}
	if (trimmed === "setup" || trimmed.startsWith("setup ")) {
		await handleSetup(trimmed.replace(/^setup\s*/, "").trim(), ctx);
		return true;
	}
	if (trimmed === "reload") {
		if (process.env.SF_HEADLESS !== "1") {
			ctx.ui.notify(
				"Reloading extensions, skills, prompts, and themes...",
				"info",
			);
			await ctx.reload();
			ctx.ui.notify(
				"Reloaded extensions, skills, prompts, and themes.",
				"info",
			);
			return true;
		}
		ctx.ui.notify(
			"Reloading agent with fresh extension code — session will be resumed...",
			"info",
		);
		const tmpDir = process.env.TEMP ?? "/tmp";
		const sessionIdFile = join(tmpDir, "sf-current-session");
		const sentinelFile = join(tmpDir, "sf-reload-sentinel");
		const { existsSync, readFileSync, unlinkSync, writeFileSync } =
			await import("node:fs");
		if (existsSync(sessionIdFile)) {
			try {
				const sessionId = readFileSync(sessionIdFile, "utf-8").trim();
				if (sessionId) {
					writeFileSync(sentinelFile, sessionId, "utf-8");
				}
			} catch {
				/* non-fatal */
			}
			try {
				unlinkSync(sessionIdFile);
			} catch {
				/* non-fatal */
			}
		}
		// EXIT_RELOAD = 12 — same as kill_agent
		const EXIT_RELOAD = 12; // must match EXIT_RELOAD in src/headless-events.ts
		process.exit(EXIT_RELOAD);
		return true;
	}
	if (trimmed === "skills" || trimmed.startsWith("skills ")) {
		const args = trimmed.replace(/^skills\s*/, "").trim();
		// Auto-create mode: detect patterns and generate skills
		if (args === "--auto-create" || args === "-a") {
			const {
				detectSkillCandidates,
				isAutoSkillCreationAllowed,
				generateSkill,
			} = await import("../../skills/auto-create.js");
			const basePath = projectRoot();
			if (!isAutoSkillCreationAllowed(basePath)) {
				ctx.ui.notify(
					"Auto-skill creation is on cooldown. Try again tomorrow.",
					"warning",
				);
				return true;
			}
			const candidates = detectSkillCandidates(basePath);
			if (candidates.length === 0) {
				ctx.ui.notify(
					"No skill candidates detected. Need ≥3 occurrences of a pattern.",
					"info",
				);
				return true;
			}
			const lines = ["SF Skill Auto-Creation\n"];
			lines.push(`Detected ${candidates.length} candidate(s):\n`);
			for (const c of candidates) {
				lines.push(`  ${c.name} — ${c.description}`);
			}
			// Generate the first candidate
			const result = generateSkill(candidates[0], basePath);
			if (result.created) {
				lines.push(`\n✓ Created: ${result.path}`);
				ctx.ui.notify(lines.join("\n"), "info");
			} else {
				lines.push(`\n✗ Skipped: ${result.reason}`);
				ctx.ui.notify(lines.join("\n"), "warning");
			}
			return true;
		}
		// Eval mode: run eval cases for a skill
		if (args.startsWith("--eval ") || args.startsWith("-e ")) {
			const skillName = args
				.replace(/^--eval\s+/, "")
				.replace(/^-e\s+/, "")
				.trim();
			const { loadSkills, runSkillEvals } = await import(
				"../../skills/loader.js"
			);
			const { createEvalCase, generateDefaultEvalCase } = await import(
				"../../skills/eval-harness.js"
			);
			const skills = loadSkills(projectRoot());
			const skill = skills.find((s) => s.name === skillName);
			if (!skill) {
				ctx.ui.notify(`Skill "${skillName}" not found.`, "warning");
				return true;
			}
			// Auto-create default eval case if none exist
			const evalDir = join(
				skill.basePath ?? projectRoot(),
				".agents",
				"skills",
				skillName,
				"evals",
			);
			const { existsSync } = await import("node:fs");
			if (!existsSync(evalDir)) {
				const caseDef = generateDefaultEvalCase(skill);
				createEvalCase(
					join(skill.basePath ?? projectRoot(), ".agents", "skills", skillName),
					"default",
					caseDef,
				);
				ctx.ui.notify(
					`Created default eval case for ${skillName}. Run /skills --eval ${skillName} again.`,
					"info",
				);
				return true;
			}
			const result = await runSkillEvals(
				join(skill.basePath ?? projectRoot(), ".agents", "skills", skillName),
				ctx,
			);
			const lines = [`SF Skill Eval: ${skillName}\n`];
			lines.push(
				`Cases: ${result.passedCases}/${result.totalCases} passed · Score: ${(result.totalScore * 100).toFixed(0)}%\n`,
			);
			for (const c of result.cases) {
				const icon = c.passed ? "✓" : "✗";
				lines.push(`${icon} ${c.caseName}: ${c.score.toFixed(2)}`);
				for (const d of c.details.slice(0, 3)) {
					lines.push(`    ${d}`);
				}
			}
			ctx.ui.notify(
				lines.join("\n"),
				result.passedCases === result.totalCases ? "success" : "warning",
			);
			return true;
		}
		// Normal list mode
		const { loadSkills, getPermittedSkills, getModelInvocableSkills } =
			await import("../../skills/loader.js");
		const skills = loadSkills(projectRoot());
		const mode = getAutoSession().getMode();
		const permitted = getPermittedSkills(skills, mode.permissionProfile);
		const modelInvocable = getModelInvocableSkills(skills, mode.workMode);

		const lines = ["SF Skills\n"];
		lines.push(
			`Found ${skills.length} skill(s) · ${permitted.length} permitted · ${modelInvocable.length} model-invocable\n`,
		);

		for (const skill of skills) {
			const icon = skill.valid ? "✓" : "✗";
			const user = skill.userInvocable ? "U" : " ";
			const model = skill.modelInvocable ? "M" : " ";
			lines.push(
				`${icon} [${user}${model}] ${skill.name} · ${skill.permissionProfile} · ${skill.sideEffects}`,
			);
			if (!skill.valid && skill.errors?.length > 0) {
				lines.push(`    ⚠ ${skill.errors[0]}`);
			}
		}

		lines.push(
			"\nLegend: [U]ser-invocable [M]odel-invocable · R=restricted N=normal T=trusted",
		);
		lines.push(
			"Use /skills --auto-create to detect and generate skills from activity patterns.",
		);
		lines.push("Use /skills --eval <name> to run eval cases for a skill.");
		ctx.ui.notify(lines.join("\n"), "info");
		return true;
	}
	return false;
}
export function formatTextStatus(state) {
	const lines = ["SF Status\n"];
	lines.push(formatProgressLine(computeProgressScore()));
	lines.push("");
	lines.push(`Phase: ${state.phase}`);
	if (state.activeMilestone) {
		lines.push(
			`Active milestone: ${state.activeMilestone.id} — ${state.activeMilestone.title}`,
		);
	}
	if (state.activeSlice) {
		lines.push(
			`Active slice: ${state.activeSlice.id} — ${state.activeSlice.title}`,
		);
	}
	if (state.activeTask) {
		lines.push(
			`Active task: ${state.activeTask.id} — ${state.activeTask.title}`,
		);
	}
	if (state.progress) {
		const { milestones, slices, tasks } = state.progress;
		const parts = [`milestones ${milestones.done}/${milestones.total}`];
		if (slices) parts.push(`slices ${slices.done}/${slices.total}`);
		if (tasks) parts.push(`tasks ${tasks.done}/${tasks.total}`);
		lines.push(`Progress: ${parts.join(", ")}`);
	}
	if (state.nextAction) {
		lines.push(`Next: ${state.nextAction}`);
	}
	if (state.blockers.length > 0) {
		lines.push(`Blockers: ${state.blockers.join("; ")}`);
	}
	try {
		const diagnostics = writeUokDiagnostics(projectRoot());
		lines.push(
			`UOK diagnostics: ${diagnostics.verdict} (${diagnostics.classification})`,
		);
		const firstIssue = diagnostics.issues?.[0];
		if (firstIssue) lines.push(`UOK issue: ${firstIssue.code}`);
	} catch {
		// Status text must stay available even when diagnostics cannot be written.
	}
	if (state.registry.length > 0) {
		lines.push("");
		lines.push("Milestones:");
		for (const milestone of state.registry) {
			const icon =
				milestone.status === "complete"
					? "✓"
					: milestone.status === "active"
						? "▶"
						: milestone.status === "parked"
							? "⏸"
							: "○";
			lines.push(
				`  ${icon} ${milestone.id}: ${milestone.title} (${milestone.status})`,
			);
		}
	}
	const envResults = runEnvironmentChecks(projectRoot());
	const envIssues = envResults.filter((result) => result.status !== "ok");
	if (envIssues.length > 0) {
		lines.push("");
		lines.push("Environment:");
		for (const issue of envIssues) {
			lines.push(`  ${issue.status === "error" ? "✗" : "⚠"} ${issue.message}`);
		}
	}
	return lines.join("\n");
}
