import { i as writeTextAtomic } from "./json-files-kvH2Eboc.js";
import { join } from "node:path";
import { access, mkdir, readFile, readdir, rename, rm } from "node:fs/promises";
//#region src/cognitive/evolution/skill-writer.ts
const SKILLS_DIR = "skills";
const AGENT_SKILLS_DIR = "skills/agent";
const ARCHIVE_DIR = "_archive";
const SKILL_FILE = "SKILL.md";
const STALE_THRESHOLD_MS = 720 * 60 * 60 * 1e3;
var SkillPersistenceWriter = class {
	constructor(skillBaseDir, options = {}) {
		this.skillBaseDir = skillBaseDir;
		this.options = options;
	}
	skillDir(name) {
		const subdir = this.options.agentSkills !== false ? AGENT_SKILLS_DIR : SKILLS_DIR;
		return join(this.skillBaseDir, subdir, name);
	}
	async writeSkill(draft) {
		if (draft.name.includes("..") || draft.name.startsWith("/") || draft.name.includes("\\")) throw new Error(`Invalid skill name: ${draft.name}`);
		const dir = this.skillDir(draft.name);
		await mkdir(dir, { recursive: true });
		const content = this.formatSkillMarkdown(draft);
		const targetPath = join(dir, SKILL_FILE);
		await writeTextAtomic(targetPath, content);
		await this.writeBundledFiles(dir, draft);
		return targetPath;
	}
	async writeBundledFiles(dir, draft) {
		const bundles = [
			{
				subdir: "scripts",
				files: draft.scripts
			},
			{
				subdir: "references",
				files: draft.references
			},
			{
				subdir: "assets",
				files: draft.assets
			}
		];
		for (const { subdir, files } of bundles) {
			if (!files || Object.keys(files).length === 0) continue;
			const subDir = join(dir, subdir);
			await mkdir(subDir, { recursive: true });
			for (const [filename, content] of Object.entries(files)) {
				if (filename.includes("..") || filename.startsWith("/")) continue;
				await writeTextAtomic(join(subDir, filename), content);
			}
		}
	}
	async skillExists(name) {
		try {
			await access(join(this.skillDir(name), SKILL_FILE));
			return true;
		} catch {
			return false;
		}
	}
	async removeSkill(name) {
		if (name.includes("..") || name.startsWith("/")) throw new Error(`Invalid skill name: ${name}`);
		await rm(this.skillDir(name), {
			recursive: true,
			force: true
		});
	}
	async archiveSkill(name) {
		if (name.includes("..") || name.startsWith("/")) throw new Error(`Invalid skill name: ${name}`);
		const subdir = this.options.agentSkills !== false ? AGENT_SKILLS_DIR : SKILLS_DIR;
		const sourceDir = join(this.skillBaseDir, subdir, name);
		const archiveDir = join(this.skillBaseDir, subdir, ARCHIVE_DIR);
		await mkdir(archiveDir, { recursive: true });
		const destDir = join(archiveDir, name);
		await rename(sourceDir, destDir);
		return destDir;
	}
	async listArchivedSkillNames() {
		const subdir = this.options.agentSkills !== false ? AGENT_SKILLS_DIR : SKILLS_DIR;
		const archiveDir = join(this.skillBaseDir, subdir, ARCHIVE_DIR);
		let entries;
		try {
			entries = await readdir(archiveDir);
		} catch {
			return [];
		}
		const names = [];
		for (const entry of entries) {
			const skillPath = join(archiveDir, entry, SKILL_FILE);
			try {
				await access(skillPath);
				names.push(entry);
			} catch {}
		}
		return names;
	}
	async readArchivedSkillMeta(name) {
		if (name.includes("..") || name.startsWith("/")) return null;
		const subdir = this.options.agentSkills !== false ? AGENT_SKILLS_DIR : SKILLS_DIR;
		const filePath = join(this.skillBaseDir, subdir, ARCHIVE_DIR, name, SKILL_FILE);
		let content;
		try {
			content = await readFile(filePath, "utf-8");
		} catch {
			return null;
		}
		if (!content.startsWith("---")) return null;
		const secondDash = content.indexOf("---", 3);
		if (secondDash === -1) return null;
		const frontmatter = content.slice(3, secondDash).trim();
		const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
		const descMatch = frontmatter.match(/^description:\s*"(.*)"/m);
		if (!nameMatch || !descMatch) return null;
		const createdAt = Number(frontmatter.match(/^createdAt:\s*(\d+)/m)?.[1] ?? 0);
		const lastUsedAt = Number(frontmatter.match(/^lastUsedAt:\s*(\d+)/m)?.[1] ?? 0);
		const usageCount = Number(frontmatter.match(/^usageCount:\s*(\d+)/m)?.[1] ?? 0);
		const provenanceMatch = frontmatter.match(/^provenance:\s*(agent|user)/m);
		return {
			name: nameMatch[1].trim(),
			description: descMatch[1].replace(/\\"/g, "\""),
			createdAt,
			lastUsedAt,
			usageCount,
			isStale: lastUsedAt > 0 && Date.now() - lastUsedAt > STALE_THRESHOLD_MS,
			provenance: provenanceMatch ? provenanceMatch[1] : void 0
		};
	}
	async recoverSkill(name) {
		if (name.includes("..") || name.startsWith("/")) throw new Error(`Invalid skill name: ${name}`);
		const subdir = this.options.agentSkills !== false ? AGENT_SKILLS_DIR : SKILLS_DIR;
		const archivePath = join(this.skillBaseDir, subdir, ARCHIVE_DIR, name);
		const activePath = join(this.skillBaseDir, subdir, name);
		try {
			await access(join(archivePath, SKILL_FILE));
		} catch {
			throw new Error(`Archived skill not found: ${name}`);
		}
		try {
			await access(join(activePath, SKILL_FILE));
			throw new Error(`Active skill already exists: ${name}`);
		} catch (err) {
			if (err instanceof Error && err.message.startsWith("Active skill already exists")) throw err;
		}
		await rename(archivePath, activePath);
		return activePath;
	}
	async findSkillDir(name) {
		const agentDir = join(this.skillBaseDir, AGENT_SKILLS_DIR, name);
		try {
			await access(join(agentDir, SKILL_FILE));
			return agentDir;
		} catch {}
		const userDir = join(this.skillBaseDir, SKILLS_DIR, name);
		try {
			await access(join(userDir, SKILL_FILE));
			return userDir;
		} catch {}
		return null;
	}
	async readSkill(name) {
		const raw = await this.readRawSkill(name);
		if (raw === null) return null;
		if (!raw.startsWith("---")) return null;
		const secondDash = raw.indexOf("---", 3);
		if (secondDash === -1) return null;
		const frontmatter = raw.slice(3, secondDash).trim();
		const bodyAndTriggers = raw.slice(secondDash + 3).trim();
		const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
		const descMatch = frontmatter.match(/^description:\s*"(.*)"/m);
		if (!nameMatch || !descMatch) return null;
		const skillName = nameMatch[1].trim();
		const description = descMatch[1].replace(/\\"/g, "\"");
		const triggerPhrases = [];
		const triggersMatch = bodyAndTriggers.match(/^## Triggers\s*\n([\s\S]*?)(?=\n## |\s*$)/);
		if (triggersMatch) {
			const triggerLines = triggersMatch[1];
			for (const line of triggerLines.split("\n")) {
				const trimmed = line.trim();
				if (trimmed.startsWith("- ")) triggerPhrases.push(trimmed.slice(2));
			}
		}
		let bodyMarkdown = bodyAndTriggers;
		if (triggersMatch?.index !== void 0) {
			bodyMarkdown = bodyAndTriggers.slice(0, triggersMatch.index) + bodyAndTriggers.slice(triggersMatch.index + triggersMatch[0].length);
			bodyMarkdown = bodyMarkdown.trim();
		}
		return {
			name: skillName,
			description,
			triggerPhrases,
			bodyMarkdown
		};
	}
	async readRawSkill(name) {
		const dir = await this.findSkillDir(name);
		if (!dir) return null;
		const filePath = join(dir, SKILL_FILE);
		try {
			return await readFile(filePath, "utf-8");
		} catch {
			return null;
		}
	}
	async updateSkill(name, content) {
		if (name.includes("..") || name.startsWith("/") || name.includes("\\")) throw new Error(`Invalid skill name: ${name}`);
		const dir = await this.findSkillDir(name);
		if (!dir) throw new Error(`Skill not found: ${name}`);
		const targetPath = join(dir, SKILL_FILE);
		const existing = await this.readRawSkill(name);
		if (existing !== null) await writeTextAtomic(join(dir, `${SKILL_FILE}.bak`), existing);
		await writeTextAtomic(targetPath, content);
		return targetPath;
	}
	async listSkillNames() {
		const subdir = this.options.agentSkills !== false ? AGENT_SKILLS_DIR : SKILLS_DIR;
		const skillsDir = join(this.skillBaseDir, subdir);
		let entries;
		try {
			entries = await readdir(skillsDir);
		} catch {
			return [];
		}
		const names = [];
		for (const entry of entries) {
			if (entry === ARCHIVE_DIR) continue;
			const skillPath = join(skillsDir, entry, SKILL_FILE);
			try {
				await access(skillPath);
				names.push(entry);
			} catch {}
		}
		return names;
	}
	async readSkillMeta(name) {
		const dir = await this.findSkillDir(name);
		if (!dir) return null;
		const filePath = join(dir, SKILL_FILE);
		let content;
		try {
			content = await readFile(filePath, "utf-8");
		} catch {
			return null;
		}
		if (!content.startsWith("---")) return null;
		const secondDash = content.indexOf("---", 3);
		if (secondDash === -1) return null;
		const frontmatter = content.slice(3, secondDash).trim();
		const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
		const descMatch = frontmatter.match(/^description:\s*"(.*)"/m);
		if (!nameMatch || !descMatch) return null;
		const createdAt = Number(frontmatter.match(/^createdAt:\s*(\d+)/m)?.[1] ?? 0);
		const lastUsedAt = Number(frontmatter.match(/^lastUsedAt:\s*(\d+)/m)?.[1] ?? 0);
		const usageCount = Number(frontmatter.match(/^usageCount:\s*(\d+)/m)?.[1] ?? 0);
		const provenanceMatch = frontmatter.match(/^provenance:\s*(agent|user)/m);
		return {
			name: nameMatch[1].trim(),
			description: descMatch[1].replace(/\\"/g, "\""),
			createdAt,
			lastUsedAt,
			usageCount,
			isStale: lastUsedAt > 0 && Date.now() - lastUsedAt > STALE_THRESHOLD_MS,
			provenance: provenanceMatch ? provenanceMatch[1] : void 0
		};
	}
	async touchSkill(name) {
		const dir = await this.findSkillDir(name);
		if (!dir) return;
		const filePath = join(dir, SKILL_FILE);
		let content;
		try {
			content = await readFile(filePath, "utf-8");
		} catch {
			return;
		}
		if (!content.startsWith("---")) return;
		const secondDash = content.indexOf("---", 3);
		if (secondDash === -1) return;
		const frontmatter = content.slice(3, secondDash);
		const rest = content.slice(secondDash + 3);
		const usageCountMatch = frontmatter.match(/^usageCount:\s*(\d+)/m);
		const currentCount = usageCountMatch ? Number(usageCountMatch[1]) : 0;
		let updated = frontmatter;
		const now = Date.now();
		if (usageCountMatch) updated = updated.replace(/^usageCount:\s*\d+/m, `usageCount: ${currentCount + 1}`);
		else updated = updated.trimEnd() + `\nusageCount: 1`;
		if (/^lastUsedAt:\s*\d+/m.test(updated)) updated = updated.replace(/^lastUsedAt:\s*\d+/m, `lastUsedAt: ${now}`);
		else updated = updated.trimEnd() + `\nlastUsedAt: ${now}`;
		await writeTextAtomic(filePath, `---${updated}---${rest}`);
	}
	formatSkillMarkdown(draft) {
		const now = Date.now();
		const frontmatter = [
			"---",
			`name: ${draft.name}`,
			`description: "${draft.description.replace(/"/g, "\\\"")}"`,
			`createdAt: ${now}`,
			`lastUsedAt: ${now}`,
			`usageCount: 0`,
			...this.options.agentSkills !== false ? ["provenance: agent"] : [],
			"metadata:",
			"  kaijibot:",
			"    generated: true",
			"    version: 1",
			"---",
			""
		];
		const triggers = draft.triggerPhrases.length > 0 ? [
			"## Triggers",
			"",
			...draft.triggerPhrases.map((p) => `- ${p}`),
			""
		] : [];
		return [
			...frontmatter,
			...triggers,
			draft.bodyMarkdown
		].join("\n");
	}
};
//#endregion
export { SkillPersistenceWriter };
