import { existsSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { findAnvilManifest, loadAnvilManifest, parseAnvilManifest, readAnvilManifest, validateAnvilManifest } from "@prata.ma/anvil-schema";
//#region src/manifest.ts
const blockNameRegex = /^[a-z][a-z0-9-]*$/;
const commandItemSegmentRegex = /^[a-z][a-z0-9]*$/;
const commandItemSeparatorRegex = /[.-]/;
const commitScopeNameRegex = /^[a-z*][a-z0-9-]*$/;
const commitTypeNameRegex = /^[a-z][a-z0-9-]*$/;
const installRefProviderRegex = /^[a-z][a-z0-9-]*$/;
const providerSourceTypes = [
	"git",
	"jsr",
	"local",
	"npm",
	"url"
];
const schemaRelativePath = "../src/schema/anvil.schema.json";
function getAnvilSchemaPath() {
	return path.resolve(path.dirname(fileURLToPath(import.meta.url)), schemaRelativePath);
}
function findAnvilManifest$1(startDir) {
	return findAnvilManifest(startDir);
}
function findAnvilLock(startDir) {
	return findNearestFile("anvil-lock.yaml", startDir) ?? findNearestFile("anvil.lock", startDir);
}
function findNearestFile(fileName, startDir) {
	let currentDir = path.resolve(startDir);
	while (true) {
		const candidate = path.join(currentDir, fileName);
		if (existsSync(candidate)) return candidate;
		const parentDir = path.dirname(currentDir);
		if (parentDir === currentDir) return null;
		currentDir = parentDir;
	}
}
function loadAnvilManifest$1(startDir) {
	const loadedManifest = loadAnvilManifest(startDir);
	if (!loadedManifest) return null;
	const validation = validateManifestSemantics(loadedManifest.manifest);
	if (validation.length > 0) throw new Error(formatManifestIssues(validation));
	return loadedManifest;
}
function readAnvilManifest$1(filePath) {
	const loadedManifest = readAnvilManifest(filePath);
	const validation = validateManifestSemantics(loadedManifest.manifest);
	if (validation.length > 0) throw new Error(formatManifestIssues(validation));
	return loadedManifest;
}
function parseAnvilManifest$1(raw) {
	const manifest = parseAnvilManifest(raw);
	const validation = validateManifestSemantics(manifest);
	if (validation.length > 0) throw new Error(formatManifestIssues(validation));
	return manifest;
}
function validateAnvilManifest$1(value) {
	const validation = validateAnvilManifest(value);
	if (!validation.valid) return validation;
	const issues = validateManifestSemantics(validation.manifest);
	if (issues.length > 0) return {
		valid: false,
		issues
	};
	return {
		manifest: validation.manifest,
		valid: true
	};
}
function resolveForgeRoot(loadedManifest) {
	return path.resolve(loadedManifest.directory, loadedManifest.manifest.forge.root);
}
function resolveSmithRoot(loadedManifest) {
	return path.resolve(loadedManifest.directory, loadedManifest.manifest.smith.root);
}
function resolveStateRoot(loadedManifest) {
	return path.resolve(loadedManifest.directory, loadedManifest.manifest.state.root);
}
function resolveForgeCase(loadedManifest) {
	return loadedManifest.manifest.forge.case;
}
function resolveSmithCase(loadedManifest) {
	return loadedManifest.manifest.smith.case;
}
function resolveStateCase(loadedManifest) {
	return loadedManifest.manifest.state.case;
}
function resolveAiRoot(loadedManifest) {
	return path.resolve(loadedManifest.directory, loadedManifest.manifest.ai.root);
}
function resolveAiRuleMode(loadedManifest) {
	return loadedManifest.manifest.ai.rules?.mode ?? "compiled";
}
function resolveHubConfig(loadedManifest) {
	return loadedManifest.manifest.hub;
}
function getCatalogProviderDeclaration(loadedManifest, providerKey) {
	return loadedManifest.manifest.catalogs?.[providerKey];
}
function parseInstallReference(raw) {
	const separatorIndex = raw.indexOf(":");
	if (separatorIndex === -1) return {
		item: raw,
		raw
	};
	return {
		item: raw.slice(separatorIndex + 1),
		provider: raw.slice(0, separatorIndex),
		raw
	};
}
function parseProviderSource(raw) {
	const separatorIndex = raw.indexOf(":");
	if (separatorIndex === -1) return {
		raw,
		type: "local",
		value: raw
	};
	const prefix = raw.slice(0, separatorIndex);
	const value = raw.slice(separatorIndex + 1);
	if (!providerSourceTypes.includes(prefix)) return {
		raw,
		type: "local",
		value: raw
	};
	return {
		raw,
		type: prefix,
		value
	};
}
function resolveAnvilLockPath(loadedManifest) {
	return path.resolve(loadedManifest.directory, "anvil-lock.yaml");
}
function validateManifestSemantics(manifest) {
	const issues = [];
	validateCatalogs(manifest.catalogs, issues);
	validateCommit(manifest.commit, issues);
	validateInstallSection(manifest.blocks, "$.blocks", issues, "block");
	validateInstallSection(manifest.commands, "$.commands", issues, "command");
	validateInstallSection(manifest.plugins, "$.plugins", issues, "plugin");
	validateInstallSection(manifest.rules, "$.rules", issues, "rule");
	validateSkillSection(manifest.skills, manifest.catalogs, issues);
	validateZones(manifest.zones, issues);
	return issues;
}
function validateCatalogs(value, issues) {
	if (!value) return;
	for (const [providerKey, declaration] of Object.entries(value)) {
		if (!installRefProviderRegex.test(providerKey)) issues.push({
			path: `$.catalogs.${providerKey}`,
			message: "provider keys must be kebab-case"
		});
		if (typeof declaration === "string") {
			validateProviderSource(declaration, `$.catalogs.${providerKey}`, issues);
			continue;
		}
		validateProviderSource(declaration.source, `$.catalogs.${providerKey}.source`, issues);
	}
}
function validateProviderSource(source, pathValue, issues) {
	const separatorIndex = source.indexOf(":");
	if (separatorIndex !== -1) {
		const prefix = source.slice(0, separatorIndex);
		if (!providerSourceTypes.includes(prefix)) {
			issues.push({
				path: pathValue,
				message: `must use one of: ${providerSourceTypes.join(", ")}`
			});
			return;
		}
	}
	if (parseProviderSource(source).value.length === 0) issues.push({
		path: pathValue,
		message: "must include a non-empty source value"
	});
}
function validateCommit(value, issues) {
	if (!value) return;
	if (value.types) for (const [typeName, typeDefinition] of Object.entries(value.types)) {
		if (!commitTypeNameRegex.test(typeName)) issues.push({
			path: `$.commit.types.${typeName}`,
			message: "commit type keys must be kebab-case"
		});
		if (typeDefinition.title.length === 0) issues.push({
			path: `$.commit.types.${typeName}.title`,
			message: "must be a non-empty string"
		});
		if (typeDefinition.description.length === 0) issues.push({
			path: `$.commit.types.${typeName}.description`,
			message: "must be a non-empty string"
		});
		if (typeDefinition.emoji.length === 0) issues.push({
			path: `$.commit.types.${typeName}.emoji`,
			message: "must be a non-empty string"
		});
	}
	if (value.scopes) for (const [scopeName, description] of Object.entries(value.scopes)) {
		if (!commitScopeNameRegex.test(scopeName)) issues.push({
			path: `$.commit.scopes.${scopeName}`,
			message: "commit scope keys must be kebab-case or *"
		});
		if (description.length === 0) issues.push({
			path: `$.commit.scopes.${scopeName}`,
			message: "must be a non-empty string"
		});
	}
}
function validateZones(value, issues) {
	if (!value?.permanent) return;
	for (const [zoneName, zoneDefinition] of Object.entries(value.permanent)) {
		if (!blockNameRegex.test(zoneName)) issues.push({
			path: `$.zones.permanent.${zoneName}`,
			message: "zone names must be kebab-case"
		});
		if (zoneDefinition.description.length === 0) issues.push({
			path: `$.zones.permanent.${zoneName}.description`,
			message: "must be a non-empty string"
		});
	}
}
function validateInstallSection(value, parentPath, issues, kind) {
	if (!value) return;
	for (const [ref, installValue] of Object.entries(value)) {
		if (!isValidInstallReference(ref, kind)) issues.push({
			path: `${parentPath}.${ref}`,
			message: `invalid ${kind} reference`
		});
		if (installValue !== true) issues.push({
			path: `${parentPath}.${ref}`,
			message: `${kind} install values must be true`
		});
	}
}
function validateSkillSection(value, catalogs, issues) {
	if (!value) return;
	for (const [ref, installValue] of Object.entries(value)) {
		if (!isValidInstallReference(ref, "skill")) {
			issues.push({
				path: `$.skills.${ref}`,
				message: "invalid skill reference"
			});
			continue;
		}
		const parsedRef = parseInstallReference(ref);
		const providerDeclaration = parsedRef.provider ? catalogs?.[parsedRef.provider] : void 0;
		if ((providerDeclaration ? parseProviderSource(typeof providerDeclaration === "string" ? providerDeclaration : providerDeclaration.source) : null)?.type === "git") {
			if (typeof installValue !== "string" || installValue.length === 0) issues.push({
				path: `$.skills.${ref}`,
				message: "git-backed skill declarations must use a repo-relative path string"
			});
			continue;
		}
		if (installValue !== true) issues.push({
			path: `$.skills.${ref}`,
			message: "non-git skill install values must be true"
		});
	}
}
function isValidInstallReference(raw, kind) {
	const parsed = parseInstallReference(raw);
	if (parsed.provider && !installRefProviderRegex.test(parsed.provider)) return false;
	if (kind === "command") return isValidCommandItem(parsed.item);
	return blockNameRegex.test(parsed.item);
}
function isValidCommandItem(value) {
	if (value.length === 0 || value.startsWith(".") || value.startsWith("-") || value.endsWith(".") || value.endsWith("-")) return false;
	return value.split(commandItemSeparatorRegex).every((segment) => commandItemSegmentRegex.test(segment));
}
function formatManifestIssues(issues) {
	return `Invalid anvil.yaml: ${issues.map((issue) => `${issue.path}: ${issue.message}`).join("; ")}`;
}
//#endregion
export { resolveSmithRoot as _, loadAnvilManifest$1 as a, validateAnvilManifest$1 as b, parseProviderSource as c, resolveAiRuleMode as d, resolveAnvilLockPath as f, resolveSmithCase as g, resolveHubConfig as h, getCatalogProviderDeclaration as i, readAnvilManifest$1 as l, resolveForgeRoot as m, findAnvilManifest$1 as n, parseAnvilManifest$1 as o, resolveForgeCase as p, getAnvilSchemaPath as r, parseInstallReference as s, findAnvilLock as t, resolveAiRoot as u, resolveStateCase as v, resolveStateRoot as y };

//# sourceMappingURL=manifest-CslkSMd8.mjs.map