import { r as ux } from "./cli-B86kvKcR.mjs";
import { a as getCatalogRoot } from "./catalog-BN_wLtPe.mjs";
import { i as sumBlockLockMetrics, o as readBlockConfig, r as createEmptyBlockLockMetrics, t as collectPathSnapshot } from "./metrics-DMk3mFyO.mjs";
import { existsSync } from "node:fs";
import path from "node:path";
import process from "node:process";
import { defineCommand } from "citty";
import { mkdir, readdir, stat, writeFile } from "node:fs/promises";
import { stringify } from "yaml";
//#region src/lib/catalog-review.ts
const CURRENT_CATALOG_LOCK_VERSION = 1;
const HYPHEN_REGEX = /-/g;
const blockItemKinds = [
	"agents",
	"commands",
	"plugins",
	"references",
	"rules",
	"skills"
];
async function generateCatalogReview(options) {
	const catalogRoot = options.catalogRoot;
	const outputRoot = options.outputRoot ?? catalogRoot;
	const blocksRoot = path.join(catalogRoot, "blocks");
	if (!existsSync(blocksRoot)) throw new Error("Catalog review requires an authored `@catalog/` root with `blocks/`.");
	const issues = [];
	const blockNames = await listDirectories(blocksRoot);
	const blocks = (await Promise.all(blockNames.map((name) => readCatalogBlock(blocksRoot, name, issues, catalogRoot)))).filter((entry) => Boolean(entry));
	for (const block of blocks) for (const dependency of block.entry.dependsOn) if (!blocks.some((candidate) => candidate.name === dependency)) issues.push({
		code: "missing-block-dependency",
		level: "error",
		message: `Dependency block not found: ${dependency}`,
		path: block.entry.config,
		subject: block.name
	});
	const blockSummary = sumBlockLockMetrics(blocks.map((block) => ({
		chars: block.entry.chars,
		lines: block.entry.lines,
		tokens: block.entry.tokens
	})));
	const lock = {
		version: CURRENT_CATALOG_LOCK_VERSION,
		summary: {
			...blockSummary,
			blocks: blockSummary,
			issues: issues.length
		},
		blocks: Object.fromEntries(blocks.map((block) => [block.name, block.entry]).sort((left, right) => left[0].localeCompare(right[0]))),
		graphs: { blockEdges: blocks.flatMap((block) => block.entry.dependsOn.map((dependency) => ({
			from: block.name,
			to: dependency
		}))) },
		issues: issues.sort(compareIssues)
	};
	const outputs = {
		catalogLock: path.join(outputRoot, "catalog-lock.yaml"),
		catalogMarkdown: path.join(outputRoot, "catalog.md")
	};
	await mkdir(outputRoot, { recursive: true });
	const mermaid = renderCatalogGraphMermaid(lock);
	await writeFile(outputs.catalogLock, stringify(lock, { singleQuote: true }), "utf8");
	await writeFile(outputs.catalogMarkdown, `${renderCatalogMarkdown(lock, mermaid)}\n`, "utf8");
	return {
		lock,
		outputs
	};
}
function renderCatalogGraphMermaid(lock) {
	const lines = ["graph LR"];
	for (const blockName of Object.keys(lock.blocks)) lines.push(`  ${toBlockNodeId(blockName)}["block: ${blockName}"]`);
	for (const edge of lock.graphs.blockEdges) lines.push(`  ${toBlockNodeId(edge.from)} --> ${toBlockNodeId(edge.to)}`);
	return lines.join("\n");
}
function renderCatalogMarkdown(lock, mermaid) {
	const lines = [
		"# Catalog Review",
		"",
		"## Summary",
		"",
		`- Blocks: ${Object.keys(lock.blocks).length}`,
		`- Issues: ${lock.summary.issues}`,
		`- Metrics: chars ${lock.summary.chars}, lines ${lock.summary.lines}, tokens ${lock.summary.tokens}`,
		"",
		"## Blocks"
	];
	for (const [name, block] of Object.entries(lock.blocks)) {
		lines.push("", `### ${name}`, "", `- Version: ${block.version}`, `- Concern: ${block.concern}`, `- Depends on: ${block.dependsOn.join(", ") || "-"}`, `- Inventories: ${renderInventoryCounts(block.inventories)}`, `- Metrics: chars ${block.chars}, lines ${block.lines}, tokens ${block.tokens}`, `- Paths: config ${inlineCode(block.config)}, guide ${inlineCode(block.guide)}, changelog ${inlineCode(block.changelog)}`);
		if (block.blockLock) lines.push(`- Provenance: ${inlineCode(block.blockLock.path)} (${block.blockLock.chars} chars, ${block.blockLock.lines} lines, ${block.blockLock.tokens} tokens)`);
	}
	lines.push("", "## Issues", "");
	if (lock.issues.length === 0) lines.push("- None");
	else for (const issue of lock.issues) lines.push(`- ${issue.level}: ${issue.subject} - ${issue.message} (${inlineCode(issue.path)})`);
	lines.push("", "## Graph", "", "```mermaid", mermaid, "```");
	return lines.join("\n");
}
async function readCatalogBlock(blocksRoot, name, issues, catalogRoot) {
	const root = path.join(blocksRoot, name);
	const configPath = path.join(root, "block.yaml");
	const guidePath = path.join(root, "AGENTS.md");
	const changelogPath = path.join(root, "CHANGELOG.md");
	const contentPath = path.join(root, "content");
	const docsPath = path.join(root, "docs");
	const blockLockPath = path.join(root, "block-lock.yaml");
	let config;
	try {
		config = readBlockConfig(configPath);
	} catch (error) {
		issues.push({
			code: "invalid-block-config",
			level: "error",
			message: error instanceof Error ? error.message : "Invalid block config",
			path: relativeToCatalog(catalogRoot, configPath),
			subject: name
		});
		return null;
	}
	const [rootSnapshot, blockLockSnapshot] = await Promise.all([collectPathSnapshot(root), collectOptionalSnapshot(blockLockPath)]);
	await Promise.all([
		collectRequiredSnapshot(guidePath, "missing-agents", "AGENTS.md not found or unreadable", issues, name, catalogRoot),
		collectRequiredSnapshot(changelogPath, "missing-changelog", "CHANGELOG.md not found or unreadable", issues, name, catalogRoot),
		collectRequiredSnapshot(contentPath, "missing-content", "content/ not found or unreadable", issues, name, catalogRoot)
	]);
	if (Object.keys(config.upstreams ?? {}).length > 0 && !blockLockSnapshot) issues.push({
		code: "missing-block-lock",
		level: "warning",
		message: "block-lock.yaml is expected when the block declares upstreams",
		path: relativeToCatalog(catalogRoot, blockLockPath),
		subject: name
	});
	return {
		name,
		entry: {
			...rootSnapshot,
			path: relativeToCatalog(catalogRoot, root),
			config: relativeToCatalog(catalogRoot, configPath),
			guide: relativeToCatalog(catalogRoot, guidePath),
			changelog: relativeToCatalog(catalogRoot, changelogPath),
			content: relativeToCatalog(catalogRoot, contentPath),
			docs: existsSync(docsPath) ? relativeToCatalog(catalogRoot, docsPath) : void 0,
			blockLock: blockLockSnapshot ? {
				...blockLockSnapshot,
				path: relativeToCatalog(catalogRoot, blockLockPath)
			} : void 0,
			concern: config.concern,
			dependsOn: [...config.dependsOn].sort(),
			description: config.description,
			inventories: Object.fromEntries(blockItemKinds.map((kind) => [kind, Object.keys(config[kind] ?? {}).length])),
			updatedAt: await latestUpdatedAt(root),
			version: config.version
		}
	};
}
async function listDirectories(directoryPath) {
	return (await readdir(directoryPath, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
}
async function collectRequiredSnapshot(targetPath, code, message, issues, subject, catalogRoot) {
	try {
		return await collectPathSnapshot(targetPath);
	} catch {
		issues.push({
			code,
			level: "error",
			message,
			path: relativeToCatalog(catalogRoot, targetPath),
			subject
		});
		return {
			...createEmptyBlockLockMetrics(),
			hash: ""
		};
	}
}
async function collectOptionalSnapshot(targetPath) {
	if (!existsSync(targetPath)) return null;
	return await collectPathSnapshot(targetPath);
}
async function latestUpdatedAt(targetPath) {
	return (await collectLatestMtime(targetPath)).toISOString();
}
async function collectLatestMtime(targetPath) {
	const targetStat = await stat(targetPath);
	if (!targetStat.isDirectory()) return targetStat.mtime;
	const entries = await readdir(targetPath, { withFileTypes: true });
	let latest = targetStat.mtime;
	for (const entry of entries) {
		if (entry.name === ".git" || entry.name === "node_modules") continue;
		const child = await collectLatestMtime(path.join(targetPath, entry.name));
		if (child.getTime() > latest.getTime()) latest = child;
	}
	return latest;
}
function compareIssues(left, right) {
	return `${left.subject}:${left.code}:${left.path}`.localeCompare(`${right.subject}:${right.code}:${right.path}`);
}
function inlineCode(value) {
	return `\`${value}\``;
}
function renderInventoryCounts(inventories) {
	return blockItemKinds.map((kind) => `${kind} ${inventories[kind]}`).join(", ");
}
function relativeToCatalog(catalogRoot, filePath) {
	return path.relative(path.dirname(catalogRoot), filePath).split("\\").join("/");
}
function toBlockNodeId(name) {
	return `block_${name.replace(HYPHEN_REGEX, "_")}`;
}
//#endregion
//#region src/commands/catalog/review.ts
async function runCatalogReviewCommand() {
	try {
		const catalogRoot = getCatalogRoot();
		if (!catalogRoot) throw new Error("Catalog root not found. Expected `@catalog/` in the current workspace.");
		const { outputs } = await ux.running("Generating catalog review", async () => generateCatalogReview({ catalogRoot }));
		ux.log.info(`catalog-lock.yaml: ${outputs.catalogLock}`);
		ux.log.info(`catalog.md: ${outputs.catalogMarkdown}`);
	} catch (error) {
		ux.handleError(error);
	}
}
var review_default = defineCommand({
	meta: {
		name: "review",
		description: "Generate catalog review artifacts"
	},
	async run() {
		await runCatalogReviewCommand();
		if (!process.exitCode) process.exit(0);
	}
});
//#endregion
export { review_default as default };

//# sourceMappingURL=review-BBsjLeZN.mjs.map