import { init_paths, resolveSkillsDir } from "../../config/paths.js";
import { parseFrontmatter } from "../../markdown/frontmatter.js";
import { fuzzyFindText, normalizeForFuzzyMatch, normalizeToLF, restoreLineEndings } from "../tools/edit-diff.js";
import { formatScanSummary, scanSkillDirectory } from "./scanner.js";
import { mkdir, writeFile } from "fs/promises";
import { join, resolve, sep } from "path";
import { existsSync, realpathSync } from "fs";
//#region src/agent/skills/skill-manage-ops.ts
/**
* Shared logic for the skill_manage agent tool (create / edit / patch / delete / write_file / remove_file).
*/
init_paths();
const SKILL_MANAGE_ALLOWED_SUBDIRS = new Set([
	"references",
	"templates",
	"scripts",
	"assets"
]);
const SKILL_MANAGE_NAME_RE = /^[a-z0-9][a-z0-9._-]*$/;
const DEFAULT_MAX_SKILL_MD_CHARS = 1e5;
const DEFAULT_MAX_SUPPORT_FILE_BYTES = 1048576;
function effectiveAgentWritePolicy(cfg) {
	const p = cfg?.agentWritePolicy;
	if (p === "workspace" || p === "both") return p;
	return "global";
}
function maxSkillMdChars(_cfg) {
	return DEFAULT_MAX_SKILL_MD_CHARS;
}
function maxSupportFileBytes(cfg) {
	const n = cfg?.limits?.maxSkillFileBytes;
	if (typeof n === "number" && n > 0) return n;
	return DEFAULT_MAX_SUPPORT_FILE_BYTES;
}
function validateSkillNameSegment(name, label) {
	if (!name?.trim()) return `${label} is required.`;
	if (name.length > 64) return `${label} must be at most 64 characters.`;
	if (!SKILL_MANAGE_NAME_RE.test(name)) return `${label} must match ${SKILL_MANAGE_NAME_RE}: lowercase letters, digits, ., _, -`;
	return null;
}
function validateSkillMdContent(content, expectedName, maxChars) {
	if (!content.trim()) return {
		ok: false,
		error: "Content cannot be empty."
	};
	if (content.length > maxChars) return {
		ok: false,
		error: `SKILL.md exceeds limit (${maxChars} characters).`
	};
	if (!content.startsWith("---")) return {
		ok: false,
		error: "SKILL.md must start with YAML frontmatter (---)."
	};
	if (!/\r?\n---\s*\r?\n/.exec(content.slice(3))) return {
		ok: false,
		error: "Frontmatter must be closed with a line --- before the body."
	};
	const { frontmatter, content: body } = parseFrontmatter(content);
	const name = frontmatter.name;
	const desc = frontmatter.description;
	if (typeof name !== "string" || !name.trim()) return {
		ok: false,
		error: "Frontmatter must include a string name field."
	};
	if (typeof desc !== "string" || !desc.trim()) return {
		ok: false,
		error: "Frontmatter must include a string description field."
	};
	if (name.trim() !== expectedName) return {
		ok: false,
		error: `Frontmatter name "${name.trim()}" must match skill name "${expectedName}".`
	};
	if (!body?.trim()) return {
		ok: false,
		error: "SKILL.md must have a non-empty body after frontmatter."
	};
	return { ok: true };
}
function resolvedPathPrefix(dir) {
	const r = resolve(dir);
	return r.endsWith(sep) ? r : r + sep;
}
/** Whether `filePath` is equal to `dir` or strictly inside it (resolved). */
function isPathInsideDir(dir, filePath) {
	try {
		const base = resolve(dir);
		const target = resolve(filePath);
		const baseReal = existsSync(base) ? realpathSync(base) : base;
		const targetReal = existsSync(target) ? realpathSync(target) : target;
		const prefix = resolvedPathPrefix(baseReal);
		return targetReal === baseReal || targetReal.startsWith(prefix);
	} catch {
		return false;
	}
}
function resolveGlobalSkillsRoot() {
	return resolveSkillsDir();
}
function resolveWorkspaceSkillsRoot(workspace) {
	return resolve(workspace, "skills");
}
function canWriteToPath(skillBaseDir, workspace, policy) {
	try {
		const baseResolved = existsSync(skillBaseDir) ? realpathSync(skillBaseDir) : resolve(skillBaseDir);
		const globalRoot = resolveGlobalSkillsRoot();
		const wsRoot = resolveWorkspaceSkillsRoot(workspace);
		const underGlobal = isPathInsideDir(globalRoot, baseResolved);
		const underWs = isPathInsideDir(wsRoot, baseResolved);
		if (policy === "global") return underGlobal;
		if (policy === "workspace") return underWs;
		return underGlobal || underWs;
	} catch {
		return false;
	}
}
function mutatableSkillOrNull(skill, workspace, policy) {
	if (!skill) return null;
	if (skill.source === "builtin" || skill.source === "extra") return null;
	if (!canWriteToPath(skill.baseDir, workspace, policy)) return null;
	return skill;
}
function validateSupportingRelativePath(filePath) {
	if (!filePath?.trim()) return "file_path is required.";
	const norm = filePath.replace(/\\/g, "/");
	if (norm.includes("..")) return "Path traversal ('..') is not allowed.";
	if (norm.startsWith("/") || /^[A-Za-z]:/.test(norm)) return "file_path must be relative to the skill directory.";
	const parts = norm.split("/").filter(Boolean);
	if (parts.length < 2) return "Provide a file path with a subdirectory, e.g. references/notes.md";
	if (!SKILL_MANAGE_ALLOWED_SUBDIRS.has(parts[0])) return `File must be under one of: ${[...SKILL_MANAGE_ALLOWED_SUBDIRS].join(", ")}.`;
	return null;
}
async function atomicWriteUtf8(filePath, content) {
	await mkdir(resolve(filePath, ".."), { recursive: true });
	await writeFile(filePath, content, "utf-8");
}
/** Scan skill dir; returns error message if critical findings (caller rolls back). */
async function scanSkillDirOrError(skillDir, skillName) {
	const summary = await scanSkillDirectory(skillDir);
	if (summary.critical > 0) return `Security scan blocked this change:\n${formatScanSummary(summary, skillName)}`;
	return null;
}
function resolveCreateSkillDir(name, category, writeTarget, workspace, policy) {
	if (writeTarget === "global" && policy === "workspace") return {
		ok: false,
		error: "Creating under global skills is not allowed (skills.agentWritePolicy)."
	};
	if (writeTarget === "workspace" && policy === "global") return {
		ok: false,
		error: "Creating under workspace skills is not allowed (skills.agentWritePolicy)."
	};
	const root = writeTarget === "global" ? resolveGlobalSkillsRoot() : resolveWorkspaceSkillsRoot(workspace);
	return {
		ok: true,
		dir: category?.trim() ? join(root, category.trim(), name) : join(root, name)
	};
}
function ensureCategorySegment(category) {
	if (category === void 0 || category === null || category === "") return null;
	if (typeof category !== "string") return "category must be a string.";
	const c = category.trim();
	if (!c) return null;
	if (c.includes("/") || c.includes("\\")) return "category must be a single path segment.";
	return validateSkillNameSegment(c, "category");
}
function detectLineEnding(content) {
	const crlfIdx = content.indexOf("\r\n");
	const lfIdx = content.indexOf("\n");
	if (lfIdx === -1) return "\n";
	if (crlfIdx === -1) return "\n";
	return crlfIdx < lfIdx ? "\r\n" : "\n";
}
function applyPatchToContent(content, oldString, newString, replaceAll) {
	if (!oldString) return {
		ok: false,
		error: "old_string is required for patch."
	};
	if (newString === void 0) return {
		ok: false,
		error: "new_string is required (use empty string to delete matched text)."
	};
	const lineEnding = detectLineEnding(content);
	const normalized = normalizeToLF(content);
	const oldN = normalizeToLF(oldString);
	const newN = normalizeToLF(newString);
	if (replaceAll) {
		const parts = normalized.split(oldN);
		const count = parts.length - 1;
		if (count === 0) return {
			ok: false,
			error: "old_string not found for replace_all."
		};
		return {
			ok: true,
			next: restoreLineEndings(parts.join(newN), lineEnding),
			replacements: count
		};
	}
	const match = fuzzyFindText(normalized, oldN);
	if (!match.found) return {
		ok: false,
		error: "old_string not found in file."
	};
	const base = match.contentForReplacement;
	const fuzzyContent = normalizeForFuzzyMatch(normalizeToLF(base));
	const fuzzyOld = normalizeForFuzzyMatch(oldN);
	const occurrences = fuzzyOld ? fuzzyContent.split(fuzzyOld).length - 1 : 0;
	if (occurrences > 1) return {
		ok: false,
		error: `Found ${occurrences} matches; narrow old_string or use replace_all.`
	};
	return {
		ok: true,
		next: restoreLineEndings(base.slice(0, match.index) + newN + base.slice(match.index + match.matchLength), lineEnding),
		replacements: 1
	};
}
//#endregion
export { DEFAULT_MAX_SKILL_MD_CHARS, DEFAULT_MAX_SUPPORT_FILE_BYTES, SKILL_MANAGE_ALLOWED_SUBDIRS, SKILL_MANAGE_NAME_RE, applyPatchToContent, atomicWriteUtf8, canWriteToPath, effectiveAgentWritePolicy, ensureCategorySegment, isPathInsideDir, maxSkillMdChars, maxSupportFileBytes, mutatableSkillOrNull, resolveCreateSkillDir, resolveGlobalSkillsRoot, resolveWorkspaceSkillsRoot, scanSkillDirOrError, validateSkillMdContent, validateSkillNameSegment, validateSupportingRelativePath };

//# sourceMappingURL=skill-manage-ops.js.map