//#region src/routes.ts
/**
* Default tool routing rules.
*
* This is the SINGLE source of truth for tool routing policies.
* Must mirror scaffold/definitions/policies.mjs — update both in sync.
* All downstream consumers (compliance-scorer, check_tool_compliance tool,
* prompt-analysis, agent instructions) derive from this list.
*/
const DEFAULT_ROUTES = [
	{
		id: "no-grep-search",
		toolName: "grep_search",
		description: "grep_search / semantic_search → use search",
		alternative: "search({ query })",
		reason: "Hybrid search across all indexed + curated content — richer results than grep",
		severity: "error",
		action: "redirect"
	},
	{
		id: "no-semantic-search",
		toolName: "semantic_search",
		description: "semantic_search → use search",
		alternative: "search({ query })",
		reason: "Hybrid search across all indexed + curated content — vector + FTS fusion",
		severity: "error",
		action: "redirect"
	},
	{
		id: "no-grep-symbol",
		toolName: "grep_search",
		description: "grep_search for a symbol → use symbol",
		alternative: "symbol({ name })",
		reason: "Definition + references with scope and call context",
		severity: "error",
		action: "redirect",
		heuristic: "grep_search for a specific symbol name"
	},
	{
		id: "no-read-file-understanding",
		toolName: "read_file",
		description: "read_file to understand a file → use file_summary",
		alternative: "file_summary({ path })",
		reason: "Structure, exports, imports — 10x fewer tokens than raw file reads",
		severity: "error",
		action: "warn",
		heuristic: "read_file with range > 50 lines and no subsequent edit"
	},
	{
		id: "no-read-file-find-code",
		toolName: "read_file",
		description: "read_file to find specific code → use compact",
		alternative: "compact({ path, query })",
		reason: "Fresh compression: compact({ path, query }); query is required unless ref is supplied",
		severity: "error",
		action: "warn",
		heuristic: "read_file to locate specific code without editing"
	},
	{
		id: "no-read-file-multiple",
		toolName: "read_file",
		description: "Multiple read_file calls → use digest",
		alternative: "digest({ sources, query: \"<task description>\" })",
		reason: "Compresses multiple files into token-budgeted summary",
		severity: "error",
		action: "warn",
		heuristic: "consecutive read_file calls on different files without edits"
	},
	{
		id: "no-read-file-large",
		toolName: "read_file",
		description: "read_file (>50 lines to understand) → use file_summary → compact → digest",
		alternative: "file_summary → compact → digest",
		reason: "Use compressed context, not raw reads — 10x fewer tokens",
		severity: "warning",
		action: "warn",
		heuristic: "read_file with endLine - startLine > 50 and no subsequent edit on same file"
	},
	{
		id: "no-ctxc-misuse",
		toolName: "compact",
		description: "Cached ctxc ref to refocus prior output → use compact with ref",
		alternative: "compact({ ref }) or compact({ ref, query? })",
		reason: "ctxc_... values are reversible refs, not ids or file paths. Prefer enrich: true on follow-up retrieval",
		severity: "error",
		action: "warn",
		heuristic: "using ctxc_ value as a file path instead of as a ref"
	},
	{
		id: "no-terminal-test",
		toolName: "run_in_terminal",
		description: "run_in_terminal for tests → use test_run",
		alternative: "test_run({})",
		reason: "Run tests with structured output — no shell needed",
		severity: "error",
		action: "redirect",
		commandPatterns: [
			"vitest",
			"jest",
			"mocha",
			"pnpm test",
			"npm test"
		]
	},
	{
		id: "no-terminal-typecheck",
		toolName: "run_in_terminal",
		description: "run_in_terminal for tsc/lint → use check",
		alternative: "check({})",
		reason: "Typecheck + lint combined, summary output — no shell needed",
		severity: "error",
		action: "redirect",
		commandPatterns: [
			"\\btsc\\b",
			"pnpm check",
			"pnpm typecheck",
			"pnpm lint",
			"biome"
		]
	},
	{
		id: "no-terminal-grep",
		toolName: "run_in_terminal",
		description: "run_in_terminal for find/grep → use find or search",
		alternative: "find({ pattern }) or search({ query })",
		reason: "No shell needed, richer results with AI Kit search",
		severity: "error",
		action: "redirect",
		commandPatterns: [
			"\\bgrep\\b",
			"\\bfind\\b",
			"\\brg\\b",
			"Select-String"
		]
	},
	{
		id: "no-terminal-code-edits",
		toolName: "run_in_terminal",
		description: "run_in_terminal for code edits → use replace_string_in_file",
		alternative: "replace_string_in_file",
		reason: "Avoid shell-edit loops; use native edit tool instead",
		severity: "error",
		action: "redirect",
		commandPatterns: [
			"sed",
			"awk",
			"Set-Content",
			"Out-File",
			">>",
			"\\| tee"
		]
	},
	{
		id: "no-edit-without-reading",
		toolName: "replace_string_in_file",
		description: "Editing without reading → use file_summary then targeted read_file",
		alternative: "file_summary({ path }) → read_file({ path, offset, limit })",
		reason: "Safer edits: understand structure before modifying",
		severity: "error",
		action: "warn",
		heuristic: "replace_string_in_file on a file not previously read"
	},
	{
		id: "no-get-changed-files",
		toolName: "get_changed_files",
		description: "get_changed_files → use run_in_terminal with git diff",
		alternative: "run_in_terminal with `git diff <specific-file>`",
		reason: "Diff only target file instead of all uncommitted diffs (100K+ tokens)",
		severity: "error",
		action: "redirect"
	},
	{
		id: "no-fetch-webpage",
		toolName: "fetch_webpage",
		description: "fetch_webpage → use web_fetch",
		alternative: "web_fetch({ url })",
		reason: "Readability extract + token budget — richer output than raw fetch",
		severity: "error",
		action: "redirect"
	},
	{
		id: "no-subagent-present",
		toolName: "present",
		description: "present (from subagent) → return structured text",
		alternative: "Return findings as structured text",
		reason: "Subagent present calls are invisible to the user (only Orchestrator should present)",
		severity: "error",
		action: "warn",
		heuristic: "present called in subagent context"
	},
	{
		id: "no-apply-patch",
		toolName: "apply_patch",
		description: "apply_patch → use native edit tool",
		alternative: "edit file tool",
		reason: "AI Kit does not manage apply_patch; use the host environment edit tool",
		severity: "warning",
		action: "warn"
	},
	{
		id: "no-memory-native",
		toolName: "memory",
		description: "memory tool → use knowledge tool",
		alternative: "knowledge (remember / search / list)",
		reason: "AI Kit knowledge tool provides persistent cross-session memory with categories and tags",
		severity: "warning",
		action: "warn"
	}
];
/**
* Get routes filtered by severity.
*/
function getRoutesBySeverity(routes, severity) {
	return routes.filter((route) => route.severity === severity);
}
/**
* Get routes for a specific tool name.
*/
function getRoutesForTool(routes, toolName) {
	return routes.filter((route) => route.toolName === toolName);
}
//#endregion
//#region src/checker.ts
/**
* Check if a command string matches any of the given patterns.
* Each pattern is a RegExp source string — tested case-insensitively.
*/
function matchesCommandPattern(command, patterns) {
	for (const pattern of patterns) try {
		if (new RegExp(pattern, "i").test(command)) return true;
	} catch {}
	return false;
}
/**
* Check a single route against a tool call.
* Returns a violation result if the route matches, null if compliant.
*/
function checkSingleRoute(route, toolName, context) {
	if (route.toolName !== toolName) return null;
	if (route.commandPatterns && route.commandPatterns.length > 0) {
		if (!context?.command) return null;
		if (!matchesCommandPattern(context.command, route.commandPatterns)) return null;
	}
	if (route.toolName === "read_file" && route.heuristic?.includes("read_file")) {
		const startLine = context?.startLine;
		const endLine = context?.endLine;
		if (startLine === void 0 || endLine === void 0) return null;
		if (endLine - startLine + 1 <= 50) return null;
	}
	return {
		compliant: false,
		route,
		alternative: route.alternative,
		message: buildViolationMessage(route, context)
	};
}
/**
* Build a human-readable violation message.
*/
function buildViolationMessage(route, _context) {
	return [
		`${route.description}.`,
		`Use \`${route.alternative}\` instead.`,
		route.reason
	].join(" ");
}
/**
* Create a RouteRegistry with the given routes (defaults to DEFAULT_ROUTES).
*
* The registry provides:
* - `check(toolName, context?)` — single-result check (first matching route)
* - `checkAll(toolName, context?)` — ALL matching routes
* - `add(route)` — register a new route at runtime
* - `remove(id)` — deregister a route by ID
*/
function createRouteRegistry(options) {
	const routes = [...options?.routes ?? DEFAULT_ROUTES];
	return {
		get routes() {
			return [...routes];
		},
		check(toolName, context) {
			for (const route of routes) {
				const result = checkSingleRoute(route, toolName, context);
				if (result !== null) return result;
			}
			return {
				compliant: true,
				message: `No routing violations for tool: ${toolName}`
			};
		},
		checkAll(toolName, context) {
			const results = [];
			for (const route of routes) {
				const result = checkSingleRoute(route, toolName, context);
				if (result !== null) results.push(result);
			}
			return results;
		},
		add(route) {
			const existingIndex = routes.findIndex((r) => r.id === route.id);
			if (existingIndex >= 0) routes[existingIndex] = route;
			else routes.push(route);
		},
		remove(id) {
			const index = routes.findIndex((r) => r.id === id);
			if (index >= 0) {
				routes.splice(index, 1);
				return true;
			}
			return false;
		}
	};
}
const defaultRegistry = /*#__PURE__*/ createRouteRegistry();
/**
* Convenience function: check a single tool call against DEFAULT_ROUTES.
*
* @param toolName — the tool being called
* @param context — optional context (command, line ranges, etc.)
* @returns RouteCheckResult with compliant status and violation details
*/
function checkToolRoute(toolName, context) {
	if (toolName.startsWith("mcp_aikit_") || toolName.startsWith("aikit_")) return {
		compliant: true,
		message: `${toolName} is an AI Kit tool — always compliant`
	};
	return defaultRegistry.check(toolName, context);
}
/**
* Convenience: get all routes that are violations (for summary/display).
*/
function getViolationSummary(results) {
	const violations = results.filter((r) => !r.compliant && r.route);
	const errors = violations.filter((r) => r.route?.severity === "error");
	const warnings = violations.filter((r) => r.route?.severity === "warning");
	return {
		total: violations.length,
		errors: errors.length,
		warnings: warnings.length,
		redirects: errors.filter((r) => r.route?.action === "redirect").length
	};
}
//#endregion
//#region src/prompt.ts
/**
* Generate the FORBIDDEN tool routing markdown table for agent instructions.
*
* This replaces the hardcoded tables in CLAUDE.md and AGENTS.md.
* Usage:
*   console.log(generateRoutingTable());  // full routing table
*/
function generateRoutingTable() {
	const errorRoutes = getRoutesBySeverity(DEFAULT_ROUTES, "error");
	const warningRoutes = getRoutesBySeverity(DEFAULT_ROUTES, "warning");
	const header = "| NEVER use this | USE THIS AI Kit TOOL INSTEAD | Why |";
	const separator = "|---|---|---|";
	const errorRows = errorRoutes.map((r) => formatTableRow(r));
	const warningRows = warningRoutes.map((r) => formatTableRow(r));
	const parts = [
		"## Tool Routing Rules",
		"",
		"These rules are enforced by AI Kit compliance checking. Violations are flagged in reports.",
		"",
		"### ⛔ Errors — DO NOT use these tools",
		header,
		separator,
		...errorRows
	];
	if (warningRows.length > 0) parts.push("", "### ⚠️ Warnings — Prefer alternatives", header, separator, ...warningRows);
	return parts.join("\n");
}
function formatTableRow(route) {
	return `| ${route.commandPatterns ? `\`${route.toolName}\` (for ${route.heuristic ?? route.description})` : `\`${route.toolName}\``} | ${`\`${route.alternative}\``} | ${route.reason} |`;
}
/**
* Generate compact routing rules text for the Orchestrator pre-dispatch gate.
* Returns a minimal set of instructions an agent should follow.
*/
function generateRoutingRules() {
	return [
		"### Tool Compliance Rules",
		"Before calling any tool, check these rules:",
		...getRoutesBySeverity(DEFAULT_ROUTES, "error").map((r) => {
			if (r.commandPatterns && r.commandPatterns.length > 0) {
				const patterns = r.commandPatterns.join(", ");
				return `- NEVER \`${r.toolName}\` for (${patterns}) — use \`${r.alternative}\` instead`;
			}
			return `- NEVER \`${r.toolName}\` — use \`${r.alternative}\` instead`;
		}),
		"",
		"If a tool call violates these rules, use the suggested alternative."
	].join("\n");
}
/**
* Generate forbidden tool names list for programmatic use.
*/
function getForbiddenToolNames(severity) {
	const routes = severity ? getRoutesBySeverity(DEFAULT_ROUTES, severity) : [...DEFAULT_ROUTES];
	return [...new Set(routes.map((r) => r.toolName))].sort();
}
//#endregion
export { DEFAULT_ROUTES, checkSingleRoute, checkToolRoute, createRouteRegistry, generateRoutingRules, generateRoutingTable, getForbiddenToolNames, getRoutesBySeverity, getRoutesForTool, getViolationSummary };
