import { t as resolveWorkspaceTemplateDir } from "./workspace-templates-BBJakJ5Y.js";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import fs$1 from "node:fs/promises";
import crypto from "node:crypto";
//#region src/infra/openclaw-migrator/agent-enumeration.ts
async function enumerateSourceAgents(source) {
	const agents = [];
	let config = null;
	try {
		const raw = await fs$1.readFile(source.configPath, "utf-8");
		config = JSON.parse(raw);
	} catch {}
	const agentList = (config?.agents)?.list;
	if (agentList && Array.isArray(agentList)) for (const entry of agentList) {
		const id = typeof entry.id === "string" ? entry.id : "";
		if (!id) continue;
		const isDefault = Boolean(entry.default);
		const workspaceOverride = typeof entry.workspace === "string" ? entry.workspace : void 0;
		const workspaceDir = workspaceOverride ? path.resolve(source.dir, workspaceOverride) : isDefault ? path.join(source.dir, "workspace") : path.join(source.dir, `workspace-${id}`);
		agents.push({
			id,
			workspaceDir,
			isDefault
		});
	}
	if (agents.length === 0) agents.push({
		id: "main",
		workspaceDir: path.join(source.dir, "workspace"),
		isDefault: true
	});
	return agents;
}
async function walkDir(dir) {
	let fileCount = 0;
	let totalSize = 0;
	const entries = await fs$1.readdir(dir, { withFileTypes: true });
	for (const entry of entries) {
		if (entry.name.startsWith(".")) continue;
		const full = path.join(dir, entry.name);
		if (entry.isDirectory()) {
			const sub = await walkDir(full);
			fileCount += sub.fileCount;
			totalSize += sub.totalSize;
		} else if (entry.isFile()) {
			const stat = await fs$1.stat(full);
			fileCount += 1;
			totalSize += stat.size;
		}
	}
	return {
		fileCount,
		totalSize
	};
}
async function computeWorkspaceStats(workspaceDir) {
	const result = {
		fileCount: 0,
		totalSize: 0,
		hasMemory: false,
		hasSessions: false
	};
	let dirExists = false;
	try {
		dirExists = (await fs$1.stat(workspaceDir)).isDirectory();
	} catch {
		return result;
	}
	if (!dirExists) return result;
	try {
		const walked = await walkDir(workspaceDir);
		result.fileCount = walked.fileCount;
		result.totalSize = walked.totalSize;
	} catch {}
	try {
		result.hasMemory = (await fs$1.stat(path.join(workspaceDir, "memory"))).isDirectory();
	} catch {}
	const sessionCandidates = [path.join(workspaceDir, "..", "state", "sessions"), path.join(workspaceDir, "..", "sessions")];
	for (const candidate of sessionCandidates) try {
		if ((await fs$1.stat(candidate)).isDirectory()) {
			result.hasSessions = true;
			break;
		}
	} catch {}
	return result;
}
async function enumerateSourceSkills(source) {
	const skillsDir = path.join(source.dir, "skills");
	const names = [];
	try {
		const entries = await fs$1.readdir(skillsDir, { withFileTypes: true });
		for (const entry of entries) {
			if (!entry.isDirectory()) continue;
			const skillMd = path.join(skillsDir, entry.name, "SKILL.md");
			try {
				await fs$1.access(skillMd);
				names.push(entry.name);
			} catch {}
		}
	} catch {}
	return names.toSorted();
}
//#endregion
//#region src/infra/openclaw-migrator/detect.ts
const SOURCE_CANDIDATES = [
	{
		dirname: ".openclaw",
		configFilename: "openclaw.json",
		brand: "openclaw"
	},
	{
		dirname: ".clawdbot",
		configFilename: "clawdbot.json",
		brand: "clawdbot"
	},
	{
		dirname: ".moltbot",
		configFilename: "moltbot.json",
		brand: "moltbot"
	}
];
function detectMigrationSource(homedir = os.homedir) {
	const home = homedir();
	for (const candidate of SOURCE_CANDIDATES) {
		const dir = path.join(home, candidate.dirname);
		const configPath = path.join(dir, candidate.configFilename);
		try {
			if (fs.existsSync(dir) && fs.statSync(dir).isDirectory() && fs.existsSync(configPath)) return {
				dir,
				brand: candidate.brand,
				configPath,
				configFilename: candidate.configFilename
			};
		} catch {}
	}
	return null;
}
function detectScenario(targetDir) {
	const configPath = path.join(targetDir, "kaijibot.json");
	try {
		if (fs.existsSync(configPath)) return "import";
	} catch {}
	return "fresh";
}
//#endregion
//#region src/infra/openclaw-migrator/brand-rewrite.ts
/** Regex matching brand references that need rewriting. */
const BRAND_REFERENCES = /\b(openclaw|OpenClaw|clawdbot|ClawdBot|moltbot|MoltBot)\b/g;
/** Banner prepended to skill files that had brand references rewritten. */
const MIGRATION_BANNER = "<!-- Migrated from OpenClaw. Review for brand references. -->\n";
/** Brand home-dir prefixes that should be rewritten to ~/.kaijibot/ */
const BRAND_HOME_PREFIXES = [
	"~/.openclaw/",
	"~/.clawdbot/",
	"~/.moltbot/"
];
const BRAND_REPLACEMENTS = [
	{
		pattern: /\bOpenClaw\b/g,
		replacement: "KaijiBot"
	},
	{
		pattern: /\bopenclaw\b/g,
		replacement: "kaijibot"
	},
	{
		pattern: /\bClawdBot\b/g,
		replacement: "KaijiBot"
	},
	{
		pattern: /\bclawdbot\b/g,
		replacement: "kaijibot"
	},
	{
		pattern: /\bMoltBot\b/g,
		replacement: "KaijiBot"
	},
	{
		pattern: /\bmoltbot\b/g,
		replacement: "kaijibot"
	},
	{
		pattern: /~\/\.openclaw\//g,
		replacement: "~/.kaijibot/"
	},
	{
		pattern: /~\/\.clawdbot\//g,
		replacement: "~/.kaijibot/"
	},
	{
		pattern: /~\/\.moltbot\//g,
		replacement: "~/.kaijibot/"
	},
	{
		pattern: /avatars\/openclaw\.png/g,
		replacement: "avatars/kaijibot.png"
	},
	{
		pattern: /\bopenclaw\.png\b/g,
		replacement: "kaijibot.png"
	}
];
const STRUCTURAL_REWRITE_REGISTRY = new Map([
	["AGENTS.md", {
		filename: "AGENTS.md",
		mode: "section",
		templateName: "AGENTS.md",
		knownSections: [
			"Session Startup",
			"Memory",
			"Workspace Files",
			"Cognitive System",
			"Feishu Platform",
			"Proactive Work",
			"Heartbeat vs Cron",
			"When to Reach Out",
			"When to Stay Quiet",
			"Safe to Do Without Asking",
			"Red Lines",
			"Group Chats"
		]
	}],
	["TOOLS.md", {
		filename: "TOOLS.md",
		mode: "full",
		templateName: "TOOLS.md"
	}],
	["BOOTSTRAP.md", {
		filename: "BOOTSTRAP.md",
		mode: "full",
		templateName: "BOOTSTRAP.md"
	}]
]);
/** Apply all brand name replacement map entries to content. Pure, no I/O. */
function rewriteBrandReferences(content) {
	let result = content;
	for (const { pattern, replacement } of BRAND_REPLACEMENTS) result = result.replaceAll(pattern, replacement);
	return result;
}
/** Strip YAML front matter (between --- delimiters) from template content. */
function stripFrontMatter(content) {
	if (!content.startsWith("---")) return content;
	const end = content.indexOf("---", 3);
	if (end === -1) return content;
	const after = content.slice(end + 3);
	return after.startsWith("\n") ? after.slice(1) : after;
}
/**
* Parse markdown into preamble (before first header) and sections by H2/H3.
* Exported for reuse by migrate-workspace.ts.
*/
function extractSectionsByHeaders(content) {
	const lines = content.split("\n");
	const preambleLines = [];
	const sections = [];
	let currentSection = null;
	let capturingLines = [];
	const HEADER_RE = /^(#{1,6})\s+(.+)$/;
	function flushCurrent() {
		if (currentSection !== null) {
			currentSection.content = capturingLines.join("\n").trim();
			sections.push(currentSection);
		}
	}
	for (const line of lines) {
		const match = line.match(HEADER_RE);
		if (match) {
			const level = match[1].length;
			const header = match[2].trim();
			if (level <= 2) {
				flushCurrent();
				currentSection = {
					header,
					level,
					content: ""
				};
				capturingLines = [line];
			} else if (currentSection !== null) capturingLines.push(line);
			else preambleLines.push(line);
		} else if (currentSection !== null) capturingLines.push(line);
		else preambleLines.push(line);
	}
	flushCurrent();
	return {
		preamble: preambleLines.join("\n").trimEnd(),
		sections
	};
}
/**
* Extract specific sections by header names (backward-compatible with migrate-workspace.ts usage).
*/
function extractSectionsByHeaderNames(content, headers) {
	const { sections } = extractSectionsByHeaders(content);
	const headerSet = new Set(headers);
	const result = [];
	for (const section of sections) if (headerSet.has(section.header)) result.push(section.content);
	return result.join("\n\n").trim();
}
/**
* Extract all H1-H6 header titles from content.
*/
function extractMarkdownHeaders(content) {
	const headers = [];
	for (const line of content.split("\n")) {
		const match = line.match(/^(#{1,6})\s+(.+)$/);
		if (match) headers.push(match[2].trim());
	}
	return headers;
}
/** Load a KaijiBot template file, stripping YAML front matter. */
async function loadKaijiBotTemplate(templateName) {
	const filePath = `${await resolveWorkspaceTemplateDir()}/${templateName}`;
	return stripFrontMatter(await fs$1.readFile(filePath, "utf-8"));
}
/** Rewrite a structural file (AGENTS.md, TOOLS.md, BOOTSTRAP.md) using KaijiBot templates. */
async function rewriteStructuralFile(filename, srcContent, _options) {
	const rule = STRUCTURAL_REWRITE_REGISTRY.get(filename);
	if (!rule) return {
		content: srcContent,
		warnings: [],
		wasRewritten: false
	};
	const warnings = [];
	if (rule.mode === "full") {
		const template = await loadKaijiBotTemplate(rule.templateName);
		if (srcContent !== template) warnings.push(`${filename}: Replaced with KaijiBot template. User customizations may have been lost.`);
		return {
			content: template,
			warnings,
			wasRewritten: true
		};
	}
	const knownSet = new Set(rule.knownSections ?? []);
	const srcParsed = extractSectionsByHeaders(srcContent);
	const templateParsed = extractSectionsByHeaders(await loadKaijiBotTemplate(rule.templateName));
	const templateByHeader = /* @__PURE__ */ new Map();
	for (const section of templateParsed.sections) templateByHeader.set(section.header, section);
	const mergedSections = [];
	for (const section of srcParsed.sections) if (knownSet.has(section.header) && templateByHeader.has(section.header)) mergedSections.push(templateByHeader.get(section.header).content);
	else mergedSections.push(rewriteBrandReferences(section.content));
	const preamble = rewriteBrandReferences(srcParsed.preamble);
	const parts = [];
	if (preamble) parts.push(preamble);
	if (mergedSections.length > 0) parts.push(mergedSections.join("\n\n"));
	return {
		content: parts.join("\n\n"),
		warnings,
		wasRewritten: true
	};
}
/** Top-level dispatcher: routes file rewriting by filename and extension. */
async function rewriteWorkspaceFile(filename, srcContent, options) {
	if (!filename.endsWith(".md")) return {
		content: srcContent,
		warnings: [],
		wasRewritten: false
	};
	if (filename === "MEMORY.md" || filename === "memory.md") {
		const rewritten = rewriteBrandReferences(srcContent);
		return {
			content: rewritten,
			warnings: [],
			wasRewritten: rewritten !== srcContent
		};
	}
	if (STRUCTURAL_REWRITE_REGISTRY.has(filename)) return rewriteStructuralFile(filename, srcContent, options);
	const rewritten = rewriteBrandReferences(srcContent);
	return {
		content: rewritten,
		warnings: [],
		wasRewritten: rewritten !== srcContent
	};
}
//#endregion
//#region src/infra/openclaw-migrator/migrate-config.ts
const KAIJIBOT_CONFIG_FILENAME = "kaijibot.json";
const KAIJIBOT_CONFIG_DEFAULTS = { cognitive: {
	enabled: true,
	proactive: {
		enabled: true,
		minIntervalHours: 4,
		activeHours: "08:00-23:00"
	},
	insight: { engine: "unified" },
	evolution: { enabled: true }
} };
function deepMerge(target, source) {
	const result = { ...target };
	for (const [key, value] of Object.entries(source)) if (key in result) {
		const existing = result[key];
		if (typeof existing === "object" && existing !== null && !Array.isArray(existing) && typeof value === "object" && value !== null && !Array.isArray(value)) result[key] = deepMerge(existing, value);
	} else result[key] = value;
	return result;
}
function fileHash$1(content) {
	return crypto.createHash("sha256").update(content).digest("hex");
}
/**
* Recursively rewrite path values in a config object:
* 1. ~/.openclaw/, ~/.clawdbot/, ~/.moltbot/ → ~/.kaijibot/
* 2. Absolute paths starting with sourceDir → same relative path under targetDir
*/
function rewriteConfigPaths(config, sourceDir, targetDir) {
	const homeDir = os.homedir();
	const normalizedSource = path.normalize(sourceDir);
	const normalizedTarget = path.normalize(targetDir);
	const rewritten = {};
	for (const [key, value] of Object.entries(config)) if (typeof value === "string") rewritten[key] = rewriteStringValue(value, normalizedSource, normalizedTarget, homeDir);
	else if (Array.isArray(value)) rewritten[key] = value.map((item) => typeof item === "object" && item !== null && !Array.isArray(item) ? rewriteConfigPaths(item, sourceDir, targetDir) : typeof item === "string" ? rewriteStringValue(item, normalizedSource, normalizedTarget, homeDir) : item);
	else if (typeof value === "object" && value !== null) rewritten[key] = rewriteConfigPaths(value, sourceDir, targetDir);
	else rewritten[key] = value;
	return rewritten;
}
function rewriteStringValue(value, sourceDir, targetDir, homeDir) {
	for (const prefix of BRAND_HOME_PREFIXES) if (value.includes(prefix)) value = value.replaceAll(prefix, "~/.kaijibot/");
	if (value.startsWith("~/")) {
		const expanded = path.normalize(path.join(homeDir, value.slice(2)));
		if (expanded.startsWith(sourceDir + path.sep) || expanded === sourceDir) {
			const relative = path.relative(sourceDir, expanded);
			const rewritten = path.join(targetDir, relative);
			if (rewritten.startsWith(homeDir)) return "~/" + path.relative(homeDir, rewritten);
			return rewritten;
		}
	}
	const normalizedValue = path.normalize(value);
	if (path.isAbsolute(normalizedValue)) {
		if (normalizedValue.startsWith(sourceDir + path.sep) || normalizedValue === sourceDir) {
			const relative = path.relative(sourceDir, normalizedValue);
			return path.normalize(path.join(targetDir, relative));
		}
	}
	return value;
}
/** Check for multi-agent configs without bindings and emit a warning. */
function checkMultiAgentBindings(config, warnings) {
	const agents = config.agents;
	if (!agents) return;
	const agentList = agents.list;
	if (!agentList || !Array.isArray(agentList) || agentList.length <= 1) return;
	const bindings = config.bindings;
	if (bindings && Array.isArray(bindings) && bindings.length > 0) return;
	warnings.push("Multi-agent config migrated but no bindings found. Non-default agents will not receive messages until bindings are configured. Run `kaijibot config edit` to add route bindings for each agent.");
}
/** Validate that workspace directories referenced in agents.list exist. */
async function validateAgentWorkspaceDirs(config, targetDir, warnings) {
	const agents = config.agents;
	if (!agents) return;
	const agentList = agents.list;
	if (!agentList || !Array.isArray(agentList)) return;
	for (const agent of agentList) {
		const agentId = typeof agent.id === "string" ? agent.id : "";
		if (!agentId) continue;
		const workspaceRel = typeof agent.workspace === "string" ? agent.workspace : `workspace-${agentId}`;
		const workspaceAbs = path.resolve(targetDir, workspaceRel);
		try {
			if (!(await fs$1.stat(workspaceAbs)).isDirectory()) warnings.push(`Agent '${agentId}' workspace path exists but is not a directory: ${workspaceRel}`);
		} catch {
			warnings.push(`Agent '${agentId}' workspace directory not found after migration: ${workspaceRel}. It may be created on first use.`);
		}
	}
}
async function migrateConfig(source, targetDir, options) {
	const changes = [];
	const warnings = [];
	const skipped = [];
	const log = options.log ?? (() => {});
	const targetConfigPath = path.join(targetDir, KAIJIBOT_CONFIG_FILENAME);
	let sourceConfig;
	try {
		const raw = await fs$1.readFile(source.configPath, "utf-8");
		sourceConfig = JSON.parse(raw);
	} catch (err) {
		warnings.push(`Cannot read source config ${source.configPath}: ${String(err)}`);
		return {
			source,
			changes,
			warnings,
			skipped
		};
	}
	const rewritten = rewriteConfigPaths(deepMerge(sourceConfig, KAIJIBOT_CONFIG_DEFAULTS), source.dir, targetDir);
	checkMultiAgentBindings(rewritten, warnings);
	const rewrittenJson = JSON.stringify(rewritten, null, 2);
	try {
		await fs$1.access(targetConfigPath);
		const existingRaw = await fs$1.readFile(targetConfigPath, "utf-8");
		if (!options.overwrite) {
			const sourceJson = JSON.stringify(sourceConfig, null, 2);
			if (fileHash$1(existingRaw) === fileHash$1(sourceJson)) {
				log(`Config already up-to-date: ${targetConfigPath}`);
				skipped.push(targetConfigPath);
				return {
					source,
					changes,
					warnings,
					skipped
				};
			}
			warnings.push(`Target config already exists: ${targetConfigPath}. Use --overwrite to merge.`);
			skipped.push(targetConfigPath);
			return {
				source,
				changes,
				warnings,
				skipped
			};
		}
		const mergedWithExisting = deepMerge(rewritten, JSON.parse(existingRaw));
		if (options.dryRun) changes.push({
			kind: "merge",
			source: path.relative(source.dir, source.configPath),
			target: path.relative(targetDir, targetConfigPath),
			detail: "Would merge source config with existing KaijiBot config (existing takes precedence)"
		});
		else {
			await fs$1.writeFile(targetConfigPath, JSON.stringify(mergedWithExisting, null, 2), "utf-8");
			changes.push({
				kind: "merge",
				source: path.relative(source.dir, source.configPath),
				target: path.relative(targetDir, targetConfigPath),
				detail: "Merged source config with existing KaijiBot config (existing takes precedence)"
			});
			log(`Merged config: ${targetConfigPath}`);
		}
	} catch {
		if (options.dryRun) changes.push({
			kind: "create",
			source: path.relative(source.dir, source.configPath),
			target: path.relative(targetDir, targetConfigPath),
			detail: "Would create KaijiBot config with defaults"
		});
		else {
			await fs$1.mkdir(targetDir, { recursive: true });
			await fs$1.writeFile(targetConfigPath, rewrittenJson, "utf-8");
			changes.push({
				kind: "create",
				source: path.relative(source.dir, source.configPath),
				target: path.relative(targetDir, targetConfigPath),
				detail: "Created KaijiBot config with defaults"
			});
			log(`Created config: ${targetConfigPath}`);
		}
	}
	if (!options.dryRun && changes.length > 0) await validateAgentWorkspaceDirs(rewritten, targetDir, warnings);
	return {
		source,
		changes,
		warnings,
		skipped
	};
}
//#endregion
//#region src/infra/openclaw-migrator/migrate-sessions.ts
async function dirExists$3(p) {
	try {
		return (await fs$1.stat(p)).isDirectory();
	} catch {
		return false;
	}
}
async function fileExists$2(p) {
	try {
		return (await fs$1.stat(p)).isFile();
	} catch {
		return false;
	}
}
async function copyFile(src, dst, options, sourceDir, targetDir, changes, skipped, warnings) {
	const log = options.log ?? (() => {});
	const srcRel = path.relative(sourceDir, src);
	const dstRel = path.relative(targetDir, dst);
	if (await fileExists$2(dst)) {
		if (!options.overwrite) {
			skipped.push(dstRel);
			return;
		}
	}
	if (options.dryRun) {
		changes.push({
			kind: "copy",
			source: srcRel,
			target: dstRel,
			detail: "Would copy file"
		});
		return;
	}
	try {
		await fs$1.mkdir(path.dirname(dst), { recursive: true });
		await fs$1.copyFile(src, dst);
		changes.push({
			kind: "copy",
			source: srcRel,
			target: dstRel,
			detail: "Copied file"
		});
		log(`Copied: ${srcRel} → ${dstRel}`);
	} catch (err) {
		warnings.push(`Failed to copy ${srcRel}: ${String(err)}`);
	}
}
async function migrateSessions(source, targetDir, options) {
	const changes = [];
	const warnings = [];
	const skipped = [];
	const log = options.log ?? (() => {});
	const agents = await enumerateSourceAgents(source);
	for (const agent of agents) {
		const sourceCandidates = agent.isDefault ? [path.join(source.dir, "state", "sessions"), path.join(source.dir, "sessions")] : [path.join(source.dir, "state", `sessions-${agent.id}`)];
		let foundSourceDir = null;
		for (const candidate of sourceCandidates) if (await dirExists$3(candidate)) {
			foundSourceDir = candidate;
			break;
		}
		if (!foundSourceDir) continue;
		const agentTargetDir = path.join(targetDir, "state", "agents", agent.id, "sessions");
		await migrateAgentSessions(foundSourceDir, agentTargetDir, source, targetDir, options, changes, warnings, skipped);
	}
	const srcApprovals = path.join(source.dir, "state", "exec-approvals.json");
	if (await fileExists$2(srcApprovals)) await copyFile(srcApprovals, path.join(targetDir, "state", "exec-approvals.json"), options, source.dir, targetDir, changes, skipped, warnings);
	const srcHooksDir = path.join(source.dir, "hooks");
	if (await dirExists$3(srcHooksDir)) {
		const dstHooksDir = path.join(targetDir, "hooks");
		const hookEntries = await fs$1.readdir(srcHooksDir, { withFileTypes: true });
		for (const entry of hookEntries) {
			if (!entry.isFile()) continue;
			await copyFile(path.join(srcHooksDir, entry.name), path.join(dstHooksDir, entry.name), options, source.dir, targetDir, changes, skipped, warnings);
		}
	}
	const srcCronFile = path.join(source.dir, "cron", "jobs.json");
	if (await fileExists$2(srcCronFile)) {
		await copyFile(srcCronFile, path.join(targetDir, "cron", "jobs.json"), options, source.dir, targetDir, changes, skipped, warnings);
		warnings.push("Cron jobs migrated; KaijiBot uses ProactiveScheduler instead of cron. Review jobs.json for compatibility.");
	}
	if (options.migrateSecrets) {
		const srcCredentialsDir = path.join(source.dir, "credentials");
		if (await dirExists$3(srcCredentialsDir)) {
			const dstCredentialsDir = path.join(targetDir, "credentials");
			const credEntries = await fs$1.readdir(srcCredentialsDir, { withFileTypes: true });
			for (const entry of credEntries) {
				if (!entry.isFile()) continue;
				await copyFile(path.join(srcCredentialsDir, entry.name), path.join(dstCredentialsDir, entry.name), options, source.dir, targetDir, changes, skipped, warnings);
			}
			warnings.push("Credentials were migrated. Verify permissions and rotate any compromised keys.");
		}
		const srcEnvFile = path.join(source.dir, ".env");
		if (await fileExists$2(srcEnvFile)) {
			await copyFile(srcEnvFile, path.join(targetDir, ".env"), options, source.dir, targetDir, changes, skipped, warnings);
			warnings.push("Environment file (.env) was migrated. Review for stale or leaked secrets.");
		}
	} else {
		const srcCredentialsDir = path.join(source.dir, "credentials");
		if (await dirExists$3(srcCredentialsDir)) {
			skipped.push(path.relative(source.dir, srcCredentialsDir));
			log("Credentials directory found but not migrated (use --migrate-secrets to include).");
		}
		const srcEnvFile = path.join(source.dir, ".env");
		if (await fileExists$2(srcEnvFile)) {
			skipped.push(path.relative(source.dir, srcEnvFile));
			log(".env file found but not migrated (use --migrate-secrets to include).");
		}
	}
	return {
		source,
		changes,
		warnings,
		skipped
	};
}
async function migrateAgentSessions(sourceSessionDir, targetSessionDir, source, targetDir, options, changes, warnings, skipped) {
	const log = options.log ?? (() => {});
	if (!options.dryRun) await fs$1.mkdir(targetSessionDir, { recursive: true });
	for (const storeFile of ["sessions.json", "sessions.json5"]) {
		const srcStore = path.join(sourceSessionDir, storeFile);
		if (!await fileExists$2(srcStore)) continue;
		const dstStore = path.join(targetSessionDir, "sessions.json");
		const srcRel = path.relative(source.dir, srcStore);
		const dstRel = path.relative(targetDir, dstStore);
		if (await fileExists$2(dstStore)) {
			if (!options.overwrite) {
				skipped.push(dstRel);
				log(`Session store already exists, skipping: ${dstRel}`);
				continue;
			}
			if (options.dryRun) {
				changes.push({
					kind: "merge",
					source: srcRel,
					target: dstRel,
					detail: "Would merge session stores (most recently updated wins)"
				});
				continue;
			}
			const merged = await mergeSessionStores(srcStore, dstStore);
			if (merged) {
				await fs$1.writeFile(dstStore, JSON.stringify(merged, null, 2), "utf-8");
				changes.push({
					kind: "merge",
					source: srcRel,
					target: dstRel,
					detail: "Merged session stores (most recently updated wins)"
				});
				log(`Merged session store: ${dstRel}`);
			}
		} else {
			if (options.dryRun) {
				changes.push({
					kind: "copy",
					source: srcRel,
					target: dstRel,
					detail: "Would copy session store"
				});
				continue;
			}
			await fs$1.mkdir(path.dirname(dstStore), { recursive: true });
			await fs$1.copyFile(srcStore, dstStore);
			changes.push({
				kind: "copy",
				source: srcRel,
				target: dstRel,
				detail: "Copied session store"
			});
			log(`Copied session store: ${dstRel}`);
		}
	}
	const entries = await fs$1.readdir(sourceSessionDir, { withFileTypes: true });
	for (const entry of entries) {
		if (!entry.isFile()) continue;
		if (!entry.name.endsWith(".jsonl") && !entry.name.includes(".jsonl.")) continue;
		if (entry.name === "sessions.json" || entry.name === "sessions.json5") continue;
		const src = path.join(sourceSessionDir, entry.name);
		const dst = path.join(targetSessionDir, entry.name);
		const srcRel = path.relative(source.dir, src);
		const dstRel = path.relative(targetDir, dst);
		if (await fileExists$2(dst)) {
			skipped.push(dstRel);
			continue;
		}
		if (options.dryRun) {
			changes.push({
				kind: "move",
				source: srcRel,
				target: dstRel,
				detail: "Would move transcript"
			});
			continue;
		}
		try {
			await fs$1.copyFile(src, dst);
			changes.push({
				kind: "move",
				source: srcRel,
				target: dstRel,
				detail: "Copied transcript"
			});
			log(`Copied transcript: ${entry.name}`);
		} catch (err) {
			warnings.push(`Failed to copy transcript ${entry.name}: ${String(err)}`);
		}
	}
}
async function mergeSessionStores(srcPath, dstPath) {
	try {
		const srcRaw = await fs$1.readFile(srcPath, "utf-8");
		const dstRaw = await fs$1.readFile(dstPath, "utf-8");
		const srcStore = JSON.parse(srcRaw);
		const merged = { ...JSON.parse(dstRaw) };
		for (const [key, srcEntry] of Object.entries(srcStore)) {
			const dstEntry = merged[key];
			if (!dstEntry) {
				merged[key] = srcEntry;
				continue;
			}
			if ((typeof srcEntry.updatedAt === "number" ? srcEntry.updatedAt : 0) > (typeof dstEntry.updatedAt === "number" ? dstEntry.updatedAt : 0)) merged[key] = srcEntry;
		}
		return merged;
	} catch {
		return null;
	}
}
//#endregion
//#region src/infra/openclaw-migrator/migrate-skills.ts
async function dirExists$2(p) {
	try {
		return (await fs$1.stat(p)).isDirectory();
	} catch {
		return false;
	}
}
async function migrateSkills(source, targetDir, options) {
	const changes = [];
	const warnings = [];
	const skipped = [];
	const log = options.log ?? (() => {});
	const sourceSkillsDir = path.join(source.dir, "skills");
	const targetSkillsDir = path.join(targetDir, "skills");
	if (!await dirExists$2(sourceSkillsDir)) {
		log("No skills directory found in source.");
		return {
			source,
			changes,
			warnings,
			skipped
		};
	}
	const skillDirs = (await fs$1.readdir(sourceSkillsDir, { withFileTypes: true })).filter((e) => e.isDirectory());
	if (skillDirs.length === 0) {
		log("No skills found in source directory.");
		return {
			source,
			changes,
			warnings,
			skipped
		};
	}
	for (const skillDir of skillDirs) {
		const skillName = skillDir.name;
		const srcSkillPath = path.join(sourceSkillsDir, skillName);
		if (!await fileExists$1(path.join(srcSkillPath, "SKILL.md"))) {
			log(`Skipping non-skill directory: ${skillName}`);
			continue;
		}
		const dstSkillPath = path.join(targetSkillsDir, skillName);
		const srcRel = path.relative(source.dir, srcSkillPath);
		const dstRel = path.relative(targetDir, dstSkillPath);
		const dstExists = await dirExists$2(dstSkillPath);
		if (dstExists && !options.overwrite) {
			skipped.push(dstRel);
			log(`Skill already exists, skipping: ${skillName}`);
			continue;
		}
		if (dstExists && options.overwrite) {
			const backupName = `${skillName}.backup.${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
			const backupPath = path.join(targetSkillsDir, backupName);
			if (!options.dryRun) {
				await fs$1.rename(dstSkillPath, backupPath);
				log(`Backed up existing skill: ${skillName} → ${backupName}`);
			}
			changes.push({
				kind: "move",
				source: dstRel,
				target: path.relative(targetDir, backupPath),
				detail: `Backed up existing skill to ${backupName}`
			});
		}
		if (options.dryRun) {
			changes.push({
				kind: "copy",
				source: srcRel,
				target: dstRel,
				detail: "Would copy skill directory"
			});
			continue;
		}
		await fs$1.mkdir(targetSkillsDir, { recursive: true });
		await copyDirRecursive$1(srcSkillPath, dstSkillPath);
		const dstSkillFile = path.join(dstSkillPath, "SKILL.md");
		const skillContent = await fs$1.readFile(dstSkillFile, "utf-8");
		if (BRAND_REFERENCES.test(skillContent)) {
			const rewritten = rewriteBrandReferences(skillContent);
			await fs$1.writeFile(dstSkillFile, MIGRATION_BANNER + rewritten, "utf-8");
			log(`Rewrote brand references in skill: ${skillName}`);
		}
		changes.push({
			kind: "copy",
			source: srcRel,
			target: dstRel,
			detail: "Copied skill directory"
		});
		log(`Copied skill: ${skillName}`);
	}
	return {
		source,
		changes,
		warnings,
		skipped
	};
}
async function fileExists$1(p) {
	try {
		return (await fs$1.stat(p)).isFile();
	} catch {
		return false;
	}
}
async function copyDirRecursive$1(src, dst) {
	await fs$1.mkdir(dst, { recursive: true });
	const entries = await fs$1.readdir(src, { withFileTypes: true });
	for (const entry of entries) {
		const srcPath = path.join(src, entry.name);
		const dstPath = path.join(dst, entry.name);
		if (entry.isDirectory()) await copyDirRecursive$1(srcPath, dstPath);
		else await fs$1.copyFile(srcPath, dstPath);
	}
}
//#endregion
//#region src/infra/openclaw-migrator/migrate-workspace.ts
const WORKSPACE_BLACKLIST = new Set([
	".qmd",
	".vectors",
	"memory.db"
]);
function fileHash(content) {
	return crypto.createHash("sha256").update(content).digest("hex");
}
async function fileExists(p) {
	try {
		return (await fs$1.stat(p)).isFile();
	} catch {
		return false;
	}
}
async function dirExists$1(p) {
	try {
		return (await fs$1.stat(p)).isDirectory();
	} catch {
		return false;
	}
}
async function copyFileIfDifferent(src, dst, options, sourceDir, targetDir, changes, skipped, warnings) {
	const log = options.log ?? (() => {});
	const srcRel = path.relative(sourceDir, src);
	const dstRel = path.relative(targetDir, dst);
	if (!await fileExists(src)) return;
	const dstExists = await fileExists(dst);
	if (dstExists && !options.overwrite) {
		skipped.push(dstRel);
		return;
	}
	const srcContent = await fs$1.readFile(src, "utf-8");
	const basename = path.basename(src);
	let contentToWrite = srcContent;
	let wasRewritten = false;
	if (basename.endsWith(".md")) {
		const result = await rewriteWorkspaceFile(basename, srcContent, { dryRun: !!options.dryRun });
		contentToWrite = result.content;
		wasRewritten = result.wasRewritten;
		warnings.push(...result.warnings);
	}
	if (dstExists) {
		if (fileHash(await fs$1.readFile(dst, "utf-8")) === fileHash(contentToWrite)) {
			log(`Identical content, skipping: ${dstRel}`);
			return;
		}
	}
	if (options.dryRun) {
		changes.push({
			kind: wasRewritten ? "rewrite" : "copy",
			source: srcRel,
			target: dstRel,
			detail: wasRewritten ? "Would rewrite file" : "Would copy file"
		});
		return;
	}
	await fs$1.mkdir(path.dirname(dst), { recursive: true });
	if (wasRewritten) {
		await fs$1.writeFile(dst, contentToWrite, "utf-8");
		changes.push({
			kind: "rewrite",
			source: srcRel,
			target: dstRel,
			detail: "Rewrote file"
		});
		log(`Rewrote: ${srcRel} → ${dstRel}`);
	} else {
		await fs$1.copyFile(src, dst);
		changes.push({
			kind: "copy",
			source: srcRel,
			target: dstRel,
			detail: "Copied file"
		});
		log(`Copied: ${srcRel} → ${dstRel}`);
	}
}
async function copyWorkspaceRecursive(srcDir, dstDir, options, sourceDir, targetDir, changes, skipped, skipMemoryMd, warnings) {
	const entries = await fs$1.readdir(srcDir, { withFileTypes: true });
	for (const entry of entries) {
		if (WORKSPACE_BLACKLIST.has(entry.name)) continue;
		const src = path.join(srcDir, entry.name);
		const dst = path.join(dstDir, entry.name);
		if (entry.isDirectory()) await copyWorkspaceRecursive(src, dst, options, sourceDir, targetDir, changes, skipped, skipMemoryMd, warnings);
		else if (entry.isFile()) {
			if (skipMemoryMd && entry.name === "MEMORY.md") continue;
			await copyFileIfDifferent(src, dst, options, sourceDir, targetDir, changes, skipped, warnings);
		}
	}
}
async function mergeMemoryFile(sourceWorkspace, targetWorkspace, source, options, changes, warnings, skipped) {
	const srcMemoryFile = path.join(sourceWorkspace, "MEMORY.md");
	const dstMemoryFile = path.join(targetWorkspace, "MEMORY.md");
	if (!await fileExists(srcMemoryFile)) return;
	const srcContent = await fs$1.readFile(srcMemoryFile, "utf-8");
	const srcRel = path.relative(source.dir, srcMemoryFile);
	const dstRel = path.relative(path.dirname(targetWorkspace), dstMemoryFile);
	const dstMemoryExists = await fileExists(dstMemoryFile);
	let contentToWrite;
	if (dstMemoryExists) if (!options.overwrite) {
		const dstContent = await fs$1.readFile(dstMemoryFile, "utf-8");
		const srcHeaders = extractMarkdownHeaders(srcContent);
		const dstHeaders = new Set(extractMarkdownHeaders(dstContent));
		const newSections = srcHeaders.filter((h) => !dstHeaders.has(h));
		if (newSections.length === 0) {
			skipped.push(dstRel);
			return;
		}
		const appendedContent = extractSectionsByHeaderNames(srcContent, newSections);
		contentToWrite = dstContent + "\n\n" + appendedContent;
	} else contentToWrite = await fs$1.readFile(dstMemoryFile, "utf-8") + "\n\n" + srcContent;
	else contentToWrite = srcContent;
	contentToWrite = rewriteBrandReferences(contentToWrite);
	if (options.dryRun) changes.push({
		kind: "merge",
		source: srcRel,
		target: dstRel,
		detail: dstMemoryExists ? "Would merge MEMORY.md sections" : "Would create MEMORY.md"
	});
	else {
		await fs$1.mkdir(path.dirname(dstMemoryFile), { recursive: true });
		await fs$1.writeFile(dstMemoryFile, contentToWrite, "utf-8");
		changes.push({
			kind: "merge",
			source: srcRel,
			target: dstRel,
			detail: dstMemoryExists ? "Merged MEMORY.md sections" : "Created MEMORY.md"
		});
	}
}
async function migrateWorkspaceDir(sourceWorkspace, targetWorkspace, source, options, memoryMergeStrategy) {
	const changes = [];
	const warnings = [];
	const skipped = [];
	if (!await dirExists$1(sourceWorkspace)) return {
		changes,
		warnings,
		skipped
	};
	if (!options.dryRun) await fs$1.mkdir(targetWorkspace, { recursive: true });
	const skipMemoryMd = memoryMergeStrategy === "merge";
	await copyWorkspaceRecursive(sourceWorkspace, targetWorkspace, options, source.dir, path.dirname(targetWorkspace), changes, skipped, skipMemoryMd, warnings);
	if (memoryMergeStrategy === "merge") await mergeMemoryFile(sourceWorkspace, targetWorkspace, source, options, changes, warnings, skipped);
	return {
		changes,
		warnings,
		skipped
	};
}
async function migrateWorkspace(source, targetDir, options, memoryMergeStrategy = "merge") {
	const allChanges = [];
	const allWarnings = [];
	const allSkipped = [];
	const log = options.log ?? (() => {});
	const sourceWorkspace = path.join(source.dir, "workspace");
	const defaultResult = await migrateWorkspaceDir(sourceWorkspace, path.join(targetDir, "workspace"), source, options, memoryMergeStrategy);
	allChanges.push(...defaultResult.changes);
	allWarnings.push(...defaultResult.warnings);
	allSkipped.push(...defaultResult.skipped);
	let sourceConfig = null;
	try {
		const raw = await fs$1.readFile(source.configPath, "utf-8");
		sourceConfig = JSON.parse(raw);
	} catch {}
	if (sourceConfig) {
		const agentList = sourceConfig.agents?.list;
		if (agentList && Array.isArray(agentList)) for (const agent of agentList) {
			const agentId = typeof agent.id === "string" ? agent.id : "";
			if (!agentId) continue;
			const agentResult = await migrateWorkspaceDir(typeof agent.workspace === "string" ? path.resolve(source.dir, agent.workspace) : path.join(source.dir, `workspace-${agentId}`), path.join(targetDir, `workspace-${agentId}`), source, options, memoryMergeStrategy);
			allChanges.push(...agentResult.changes);
			allWarnings.push(...agentResult.warnings);
			allSkipped.push(...agentResult.skipped);
		}
	}
	const qmdDir = path.join(sourceWorkspace, ".qmd");
	const vectorDir = path.join(sourceWorkspace, ".vectors");
	const dbFile = path.join(sourceWorkspace, "memory.db");
	for (const nonMigratable of [
		qmdDir,
		vectorDir,
		dbFile
	]) if (!await fileExists(nonMigratable)) {
		if (await dirExists$1(nonMigratable)) {
			const rel = path.relative(source.dir, nonMigratable);
			log(`Skipping non-migratable data: ${rel} (must be rebuilt)`);
			allSkipped.push(rel);
		}
	} else {
		const rel = path.relative(source.dir, nonMigratable);
		log(`Skipping non-migratable data: ${rel} (must be rebuilt)`);
		allSkipped.push(rel);
	}
	return {
		source,
		changes: allChanges,
		warnings: allWarnings,
		skipped: allSkipped
	};
}
//#endregion
//#region src/infra/openclaw-migrator/index.ts
async function dirExists(p) {
	try {
		return (await fs$1.stat(p)).isDirectory();
	} catch {
		return false;
	}
}
async function copyDirRecursive(src, dst) {
	await fs$1.mkdir(dst, { recursive: true });
	const entries = await fs$1.readdir(src, { withFileTypes: true });
	for (const entry of entries) {
		const srcPath = path.join(src, entry.name);
		const dstPath = path.join(dst, entry.name);
		if (entry.isDirectory()) await copyDirRecursive(srcPath, dstPath);
		else await fs$1.copyFile(srcPath, dstPath);
	}
}
function buildReport(timestamp, source, scenario, results) {
	return {
		timestamp,
		source,
		scenario,
		results,
		totalChanges: results.reduce((sum, r) => sum + r.changes.length, 0),
		totalWarnings: results.reduce((sum, r) => sum + r.warnings.length, 0),
		totalSkipped: results.reduce((sum, r) => sum + r.skipped.length, 0)
	};
}
async function runFreshMigration(source, targetDir, options) {
	const timestamp = (/* @__PURE__ */ new Date()).toISOString();
	const log = options.log ?? (() => {});
	const results = [];
	if (!options.dryRun) {
		await fs$1.mkdir(targetDir, { recursive: true });
		const backupMarker = path.join(targetDir, `.migration-backup-${timestamp.replace(/[:.]/g, "-")}`);
		const metadata = {
			timestamp,
			source: source.dir,
			brand: source.brand
		};
		await fs$1.writeFile(backupMarker, JSON.stringify(metadata, null, 2), "utf-8");
		log(`Created migration marker: ${backupMarker}`);
	}
	log(`Migrating config (${source.brand})...`);
	const configResult = await migrateConfig(source, targetDir, options);
	results.push(configResult);
	log("Migrating workspace...");
	const workspaceResult = await migrateWorkspace(source, targetDir, options, "copy");
	results.push(workspaceResult);
	log("Migrating skills...");
	const skillsResult = await migrateSkills(source, targetDir, options);
	results.push(skillsResult);
	log("Migrating sessions...");
	const sessionsResult = await migrateSessions(source, targetDir, options);
	results.push(sessionsResult);
	const report = buildReport(timestamp, source, "fresh", results);
	if (!options.dryRun) {
		const reportPath = path.join(targetDir, `.migration-report-${timestamp.replace(/[:.]/g, "-")}.json`);
		await fs$1.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
		log(`Migration report saved: ${reportPath}`);
	}
	return report;
}
async function runImportMigration(source, targetDir, options, selections, selectedSkills) {
	const timestamp = (/* @__PURE__ */ new Date()).toISOString();
	const log = options.log ?? (() => {});
	const results = [];
	const changes = [];
	log(`Merging config (${source.brand})...`);
	const configResult = await migrateConfig(source, targetDir, {
		...options,
		overwrite: false
	});
	results.push(configResult);
	if (selections.length > 0) {
		log("Migrating selected agents...");
		const agents = await enumerateSourceAgents(source);
		for (const sel of selections) {
			const agent = agents.find((a) => a.id === sel.agentId);
			if (!agent) {
				log(`  Agent "${sel.agentId}" not found in source, skipping.`);
				continue;
			}
			log(`  Agent: ${agent.id} — [${sel.dataTypes.join(", ")}]`);
			if (sel.dataTypes.includes("workspace") || sel.dataTypes.includes("memory")) {
				const workspaceResult = await migrateWorkspace(source, targetDir, options, "merge");
				results.push(workspaceResult);
			}
			if (sel.dataTypes.includes("sessions")) {
				const sessionsResult = await migrateSessions(source, targetDir, options);
				results.push(sessionsResult);
			}
		}
	}
	if (selectedSkills.length > 0) {
		log(`Migrating ${selectedSkills.length} selected skills...`);
		const skillsSourceDir = path.join(source.dir, "skills");
		const skillsTargetDir = path.join(targetDir, "skills");
		if (await dirExists(skillsSourceDir)) for (const skillName of selectedSkills) {
			const srcSkill = path.join(skillsSourceDir, skillName);
			const dstSkill = path.join(skillsTargetDir, skillName);
			if (!await dirExists(srcSkill)) {
				log(`  Skill "${skillName}" not found in source, skipping.`);
				continue;
			}
			if (!options.dryRun) await copyDirRecursive(srcSkill, dstSkill);
			changes.push({
				kind: "copy",
				source: srcSkill,
				target: dstSkill,
				detail: `Copied skill: ${skillName}`
			});
			log(`  Copied skill: ${skillName}`);
		}
		results.push({
			source,
			changes,
			warnings: [],
			skipped: []
		});
	}
	const report = buildReport(timestamp, source, "import", results);
	if (!options.dryRun) {
		const reportPath = path.join(targetDir, `.migration-report-${timestamp.replace(/[:.]/g, "-")}.json`);
		await fs$1.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
		log(`Migration report saved: ${reportPath}`);
	}
	return report;
}
async function buildInteractiveSelection(source) {
	const agentInfos = await enumerateSourceAgents(source);
	const agents = [];
	for (const info of agentInfos) {
		const stats = await computeWorkspaceStats(info.workspaceDir);
		agents.push({
			...info,
			stats
		});
	}
	const skills = await enumerateSourceSkills(source);
	let hasSecrets = false;
	if (await dirExists(path.join(source.dir, "credentials"))) hasSecrets = true;
	return {
		agents,
		skills,
		hasSecrets
	};
}
//#endregion
export { enumerateSourceAgents as C, computeWorkspaceStats as S, rewriteStructuralFile as _, migrateSkills as a, detectMigrationSource as b, BRAND_HOME_PREFIXES as c, STRUCTURAL_REWRITE_REGISTRY as d, extractMarkdownHeaders as f, rewriteBrandReferences as g, loadKaijiBotTemplate as h, migrateWorkspace as i, BRAND_REFERENCES as l, extractSectionsByHeaders as m, runFreshMigration as n, migrateSessions as o, extractSectionsByHeaderNames as p, runImportMigration as r, migrateConfig as s, buildInteractiveSelection as t, MIGRATION_BANNER as u, rewriteWorkspaceFile as v, enumerateSourceSkills as w, detectScenario as x, stripFrontMatter as y };
