/**
 * Permission Extension for Singularity Forge
 *
 * Implements layered permission control.
 *
 * Interactive mode:
 *   Use `/permission` command to view or change the level.
 *   Use `/permission-mode` to switch between ask vs block.
 *   When changing via command, you'll be asked: session-only or global?
 *
 * Print mode (sf -p):
 *   Set SF_PERMISSION_LEVEL env var: SF_PERMISSION_LEVEL=medium sf -p "task"
 *   Operations beyond level will exit with helpful error message.
 *   Use SF_PERMISSION_LEVEL=bypassed for CI/containers (dangerous!)
 *
 * Levels:
 *   minimal - Read-only mode (default)
 *             ✅ Read files, ls, grep, git status/log/diff
 *             ❌ No file modifications, no commands with side effects
 *
 *   low    - File operations only
 *            ✅ Create/edit files in project directory
 *            ❌ No package installs, no git commits, no builds
 *
 *   medium - Development operations
 *            ✅ npm/pip install, git commit/pull, make/build
 *            ❌ No git push, no sudo, no production changes
 *
 *   high   - Full operations
 *            ✅ git push, deployments, scripts
 *            ⚠️ Still prompts for destructive commands (rm -rf, etc.)
 *
 * Usage:
 *   sf --extension ./index.ts
 *
 * Or add to ~/.sf/agent/extensions/ or .sf/extensions/ for automatic loading.
 */
import { exec } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { minimatch } from "minimatch";
import {
	classifyCommand,
	invalidateConfigCache,
	LEVEL_ALLOWED_DESC,
	LEVEL_INDEX,
	LEVEL_INFO,
	LEVELS,
	loadGlobalPermission,
	loadGlobalPermissionMode,
	loadPermissionConfig,
	PERMISSION_MODE_INFO,
	PERMISSION_MODES,
	saveGlobalPermission,
	saveGlobalPermissionMode,
	savePermissionConfig,
} from "./permission-core.js";

// Re-export types and constants needed by the hook
export { LEVEL_INFO, LEVELS, PERMISSION_MODE_INFO, PERMISSION_MODES };

// ============================================================================
// SOUND NOTIFICATION
// ============================================================================
function playPermissionSound() {
	const isMac = process.platform === "darwin";
	if (isMac) {
		exec("afplay /System/Library/Sounds/Funk.aiff 2>/dev/null", (err) => {
			if (err) process.stdout.write("\x07");
		});
	} else {
		process.stdout.write("\x07");
	}
}
// ============================================================================
// STATUS TEXT
// ============================================================================
const BOLD = "\x1b[1m";
const RESET = "\x1b[0m";
const RED = "\x1b[31m";
const YELLOW = "\x1b[33m";
const GREEN = "\x1b[32m";
const CYAN = "\x1b[36m";
const DIM = "\x1b[2m";
const LEVEL_COLORS = {
	minimal: RED,
	low: YELLOW,
	medium: CYAN,
	high: GREEN,
	bypassed: DIM,
};
function getStatusText(level) {
	const info = LEVEL_INFO[level];
	const color = LEVEL_COLORS[level];
	return `${BOLD}${color}${info.label}${RESET} ${DIM}- ${info.desc}${RESET}`;
}
// ============================================================================
// MODE DETECTION
// ============================================================================
function getPiModeFromArgv(argv = process.argv) {
	// Support both: --mode rpc and --mode=rpc
	const eq = argv.find((a) => a.startsWith("--mode="));
	if (eq) return eq.slice("--mode=".length);
	const idx = argv.indexOf("--mode");
	if (idx !== -1 && idx + 1 < argv.length) return argv[idx + 1];
	return undefined;
}
function hasInteractiveUI(ctx) {
	if (!ctx?.hasUI) return false;
	// In non-interactive modes (rpc/json/print), UI prompts are not desired.
	// We still allow notifications, but block instead of asking.
	const mode = getPiModeFromArgv()?.toLowerCase();
	if (mode && mode !== "interactive") return false;
	return true;
}
function isQuietMode(ctx) {
	if (ctx?.quiet || ctx?.isQuiet) return true;
	if (ctx?.ui?.quiet || ctx?.ui?.isQuiet) return true;
	if (ctx?.settings?.quietStartup || ctx?.settings?.quiet) return true;
	const envQuiet =
		process.env.SF_QUIET?.toLowerCase() || process.env.PI_QUIET?.toLowerCase();
	if (envQuiet && ["1", "true", "yes"].includes(envQuiet)) return true;
	if (process.argv.includes("--quiet") || process.argv.includes("-q"))
		return true;
	return isQuietStartupFromSettings();
}
function isQuietStartupFromSettings() {
	const sfSettingsPath = path.join(
		os.homedir(),
		".sf",
		"agent",
		"settings.json",
	);
	try {
		if (fs.existsSync(sfSettingsPath)) {
			const raw = fs.readFileSync(sfSettingsPath, "utf-8");
			const settings = JSON.parse(raw);
			return settings.quietStartup === true;
		}
	} catch {}
	const piSettingsPath = path.join(
		os.homedir(),
		".pi",
		"agent",
		"settings.json",
	);
	try {
		if (fs.existsSync(piSettingsPath)) {
			const raw = fs.readFileSync(piSettingsPath, "utf-8");
			const settings = JSON.parse(raw);
			return settings.quietStartup === true;
		}
	} catch {}
	return false;
}
export function createInitialState() {
	return {
		currentLevel: "minimal",
		isSessionOnly: false,
		permissionMode: "ask",
		isModeSessionOnly: false,
	};
}
function setLevel(state, level, saveGlobally, ctx) {
	state.currentLevel = level;
	state.isSessionOnly = !saveGlobally;
	if (saveGlobally) {
		saveGlobalPermission(level);
	}
	// Only emit the footer indicator when there's a real TUI to render into.
	// In headless mode the "authority" badge has no consumer.
	if (ctx.hasUI && ctx.ui?.setStatus) {
		ctx.ui.setStatus("authority", getStatusText(level));
	}
}
function setMode(state, mode, saveGlobally, _ctx) {
	state.permissionMode = mode;
	state.isModeSessionOnly = !saveGlobally;
	if (saveGlobally) {
		saveGlobalPermissionMode(mode);
	}
}
// ============================================================================
// HANDLERS
// ============================================================================
/** Handle /permission config subcommand */
async function handleConfigSubcommand(_state, args, ctx) {
	const parts = args.trim().split(/\s+/);
	const action = parts[0];
	if (action === "show") {
		const config = loadPermissionConfig();
		const configStr = JSON.stringify(config, null, 2);
		ctx.ui.notify(`Permission Config:\n${configStr}`, "info");
		return;
	}
	if (action === "reset") {
		savePermissionConfig({});
		invalidateConfigCache();
		ctx.ui.notify("Permission config reset to defaults", "info");
		return;
	}
	// Show help
	const help = `Usage: /permission config <action>

Actions:
  show  - Display current configuration
  reset - Reset to default configuration

Edit ~/.sf/agent/settings.json directly for full control:

{
  "permissionConfig": {
    "overrides": {
      "minimal": ["tmux list-*", "tmux show-*"],
      "medium": ["tmux *", "screen *"],
      "high": ["rm -rf *"],
      "dangerous": ["dd if=* of=/dev/*"]
    },
    "prefixMappings": [
      { "from": "fvm flutter", "to": "flutter" },
      { "from": "nvm exec", "to": "" }
    ]
  }
}`;
	ctx.ui.notify(help, "info");
}
/** Handle /permission command */
export async function handlePermissionCommand(state, args, ctx) {
	const arg = args.trim().toLowerCase();
	// Handle config subcommand
	if (arg === "config" || arg.startsWith("config ")) {
		const configArgs = arg.replace(/^config\s*/, "");
		await handleConfigSubcommand(state, configArgs, ctx);
		return;
	}
	// Direct level set: /permission medium
	if (arg && LEVELS.includes(arg)) {
		const newLevel = arg;
		if (hasInteractiveUI(ctx)) {
			const scope = await ctx.ui.select("Save permission level to:", [
				"Session only",
				"Global (persists)",
			]);
			if (!scope) return;
			setLevel(state, newLevel, scope === "Global (persists)", ctx);
			const saveMsg =
				scope === "Global (persists)" ? " (saved globally)" : " (session only)";
			ctx.ui.notify(
				`Permission: ${LEVEL_INFO[newLevel].label}${saveMsg}`,
				"info",
			);
		} else {
			setLevel(state, newLevel, false, ctx);
			ctx.ui.notify(`Permission: ${LEVEL_INFO[newLevel].label}`, "info");
		}
		return;
	}
	// Show current level (no UI)
	if (!hasInteractiveUI(ctx)) {
		ctx.ui.notify(
			`Current permission: ${LEVEL_INFO[state.currentLevel].label} (${LEVEL_INFO[state.currentLevel].desc})`,
			"info",
		);
		return;
	}
	// Show selector
	const options = LEVELS.map((level) => {
		const info = LEVEL_INFO[level];
		const marker = level === state.currentLevel ? " ← current" : "";
		return `${info.label}: ${info.desc}${marker}`;
	});
	const choice = await ctx.ui.select("Select permission level", options);
	if (!choice) return;
	const selectedLabel = choice.split(":")[0].trim();
	const newLevel = LEVELS.find((l) => LEVEL_INFO[l].label === selectedLabel);
	if (!newLevel || newLevel === state.currentLevel) return;
	const scope = await ctx.ui.select("Save to:", [
		"Session only",
		"Global (persits)",
	]);
	if (!scope) return;
	setLevel(state, newLevel, scope === "Global (persits)", ctx);
	const saveMsg =
		scope === "Global (persits)" ? " (saved globally)" : " (session only)";
	ctx.ui.notify(`Permission: ${LEVEL_INFO[newLevel].label}${saveMsg}`, "info");
}
/** Handle /permission-mode command */
export async function handlePermissionModeCommand(state, args, ctx) {
	const arg = args.trim().toLowerCase();
	if (arg && PERMISSION_MODES.includes(arg)) {
		const newMode = arg;
		if (hasInteractiveUI(ctx)) {
			const scope = await ctx.ui.select("Save permission mode to:", [
				"Session only",
				"Global (persists)",
			]);
			if (!scope) return;
			setMode(state, newMode, scope === "Global (persists)", ctx);
			const saveMsg =
				scope === "Global (persists)" ? " (saved globally)" : " (session only)";
			ctx.ui.notify(
				`Permission mode: ${PERMISSION_MODE_INFO[newMode].label}${saveMsg}`,
				"info",
			);
		} else {
			setMode(state, newMode, false, ctx);
			ctx.ui.notify(
				`Permission mode: ${PERMISSION_MODE_INFO[newMode].label}`,
				"info",
			);
		}
		return;
	}
	if (!hasInteractiveUI(ctx)) {
		ctx.ui.notify(
			`Current permission mode: ${PERMISSION_MODE_INFO[state.permissionMode].label} (${PERMISSION_MODE_INFO[state.permissionMode].desc})`,
			"info",
		);
		return;
	}
	const options = PERMISSION_MODES.map((mode) => {
		const info = PERMISSION_MODE_INFO[mode];
		const marker = mode === state.permissionMode ? " ← current" : "";
		return `${info.label}: ${info.desc}${marker}`;
	});
	const choice = await ctx.ui.select("Select permission mode", options);
	if (!choice) return;
	const selectedLabel = choice.split(":")[0].trim();
	const newMode = PERMISSION_MODES.find(
		(m) => PERMISSION_MODE_INFO[m].label === selectedLabel,
	);
	if (!newMode || newMode === state.permissionMode) return;
	const scope = await ctx.ui.select("Save to:", [
		"Session only",
		"Global (persists)",
	]);
	if (!scope) return;
	setMode(state, newMode, scope === "Global (persists)", ctx);
	const saveMsg =
		scope === "Global (persists)" ? " (saved globally)" : " (session only)";
	ctx.ui.notify(
		`Permission mode: ${PERMISSION_MODE_INFO[newMode].label}${saveMsg}`,
		"info",
	);
}
/** Handle session_start - initialize level and show status */
export function handleSessionStart(state, ctx) {
	// Check env var first (for print mode)
	const envLevel =
		process.env.SF_PERMISSION_LEVEL?.toLowerCase() ||
		process.env.PI_PERMISSION_LEVEL?.toLowerCase();
	if (envLevel && LEVELS.includes(envLevel)) {
		state.currentLevel = envLevel;
	} else {
		const globalLevel = loadGlobalPermission();
		if (globalLevel) {
			state.currentLevel = globalLevel;
		}
	}
	if (ctx.hasUI) {
		const globalMode = loadGlobalPermissionMode();
		if (globalMode) {
			state.permissionMode = globalMode;
		}
	}
	if (ctx.hasUI) {
		if (ctx.ui?.setStatus) {
			ctx.ui.setStatus("authority", getStatusText(state.currentLevel));
		}
		if (state.currentLevel === "bypassed") {
			ctx.ui.notify("⚠️ Permission bypassed - all checks disabled!", "warning");
		} else if (!isQuietMode(ctx)) {
			ctx.ui.notify(
				`Permission: ${LEVEL_INFO[state.currentLevel].label} (use /permission to change)`,
				"info",
			);
		}
		if (state.permissionMode === "block") {
			ctx.ui.notify(
				"Permission mode: Block (use /permission-mode to change)",
				"info",
			);
		}
	}
}
// ============================================================================
// SKILL SANDBOX — allowed-tools enforcement
// ============================================================================
/** Active skill-declared allowed tool patterns for the current session. */
let activeSkillAllowedTools = null;
/** Name of the skill that declared the active allowed-tools patterns. */
let activeSkillName = null;
/**
 * Set the allowed tool patterns for the current session.
 * Called when a skill with allowed-tools is activated.
 */
export function setAllowedTools(patterns, skillName) {
	activeSkillAllowedTools = patterns.length > 0 ? [...patterns] : null;
	activeSkillName = patterns.length > 0 ? (skillName ?? null) : null;
}
/**
 * Clear the allowed tool patterns for the current session.
 * Called when a skill is deactivated or at session end.
 */
export function clearAllowedTools() {
	activeSkillAllowedTools = null;
	activeSkillName = null;
}
/**
 * Extract the command pattern from a Bash(...) allowed-tools entry.
 *
 * Purpose: skill sandbox matching and error messages need the command pattern
 * without the outer tool syntax.
 * Consumer: matchesAllowedToolPattern and blocked-command diagnostics.
 */
function extractBashAllowedPattern(pattern) {
	const trimmedPattern = pattern.trim();
	const match = trimmedPattern.match(/^Bash\((.+)\)$/i);
	if (!match) return null;
	return match[1].trim();
}
/**
 * Parse an allowed-tools pattern and check if a command matches.
 *
 * Purpose: enforce `allowed-tools: Bash(npm run:*)` as a command-prefix rule,
 * where the colon separates the command prefix from its wildcard arguments.
 * Consumer: handleBashToolCall before normal permission checks run.
 */
export function matchesAllowedToolPattern(pattern, command) {
	const commandPattern = extractBashAllowedPattern(pattern);
	if (!commandPattern) return false;
	const trimmedCommand = command.trim().replace(/\s+/g, " ");
	if (commandPattern === "*") return true;
	if (commandPattern.endsWith(":*")) {
		const prefix = commandPattern.slice(0, -2).trim().replace(/\s+/g, " ");
		return trimmedCommand === prefix || trimmedCommand.startsWith(`${prefix} `);
	}
	if (!/[*?[\]]/.test(commandPattern)) {
		return trimmedCommand === commandPattern.trim().replace(/\s+/g, " ");
	}
	return minimatch(trimmedCommand, commandPattern);
}
function formatAllowedPatternList(patterns) {
	const displayPatterns = patterns.map(
		(pattern) => extractBashAllowedPattern(pattern) ?? pattern.trim(),
	);
	return JSON.stringify(displayPatterns);
}
function unquoteFrontmatterValue(value) {
	const trimmed = value.trim();
	if (
		(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
		(trimmed.startsWith("'") && trimmed.endsWith("'"))
	) {
		return trimmed.slice(1, -1).trim();
	}
	return trimmed;
}
function splitAllowedTools(value) {
	return value
		.split(/\s*,\s*/)
		.map((entry) => unquoteFrontmatterValue(entry))
		.filter(Boolean);
}
function readSkillAllowedTools(filePath, fallbackName) {
	let raw;
	try {
		raw = fs.readFileSync(filePath, "utf-8");
	} catch {
		return null;
	}
	const frontmatter = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
	if (!frontmatter) return null;
	const name =
		frontmatter[1]
			.match(/^name:\s*(.+)$/m)?.[1]
			.trim()
			.replace(/^["']|["']$/g, "") ||
		fallbackName ||
		path.basename(path.dirname(filePath));
	const allowedTools = frontmatter[1].match(/^allowed-tools:\s*(.+)$/m)?.[1];
	if (!allowedTools) return { skillName: name, patterns: [] };
	return {
		skillName: name,
		patterns: splitAllowedTools(allowedTools),
	};
}
function extractSkillBlocks(text) {
	const blocks = [];
	const blockRe = /<skill\s+name="([^"]+)"\s+location="([^"]+)">/g;
	let match;
	while ((match = blockRe.exec(text)) !== null) {
		blocks.push({ name: match[1], location: match[2] });
	}
	return blocks;
}
/**
 * Activate skill sandbox rules from rendered skill XML blocks.
 *
 * Purpose: connect real skill invocation paths (`/skill:name` expansion and
 * the Skill tool result) to bash sandbox enforcement for the active turn.
 * Consumer: sf-permissions before_agent_start and Skill tool_result hooks.
 */
export function activateAllowedToolsFromSkillText(text) {
	const blocks = extractSkillBlocks(text);
	if (blocks.length === 0) {
		clearAllowedTools();
		return;
	}
	const names = [];
	const patterns = [];
	for (const block of blocks) {
		const loaded = readSkillAllowedTools(block.location, block.name);
		if (!loaded) continue;
		names.push(loaded.skillName);
		patterns.push(...loaded.patterns);
	}
	if (patterns.length > 0) {
		setAllowedTools(patterns, names.join(", "));
	} else {
		clearAllowedTools();
	}
}
/**
 * Check if a bash command is allowed by the active skill's allowed-tools.
 * Returns a block result if the command is not allowed, undefined if allowed.
 */
function checkSkillSandbox(command) {
	if (activeSkillAllowedTools === null) return undefined;
	if (activeSkillAllowedTools.length === 0) return undefined;
	const trimmedCommand = command.trim();
	const matched = activeSkillAllowedTools.some((pattern) =>
		matchesAllowedToolPattern(pattern, trimmedCommand),
	);
	if (matched) return undefined;
	const skillLabel = activeSkillName ? `Allowed by: ${activeSkillName}` : "";
	return {
		block: true,
		reason:
			`Command "${trimmedCommand}" blocked by skill sandbox. ` +
			`Allowed patterns: ${formatAllowedPatternList(activeSkillAllowedTools)}` +
			(skillLabel ? `\n${skillLabel}` : ""),
	};
}
/** Handle bash tool_call - check permission and prompt if needed */
export async function handleBashToolCall(state, command, ctx) {
	// Skill sandbox check — applied before permission level checks
	const sandboxResult = checkSkillSandbox(command);
	if (sandboxResult) {
		// Log the block for observability
		const logMsg = `Blocked "${command.trim()}" — no pattern matched ${formatAllowedPatternList(activeSkillAllowedTools ?? [])}`;
		if (ctx?.logInfo) {
			ctx.logInfo("skill-sandbox", logMsg);
		} else if (ctx?.ui?.notify) {
			ctx.ui.notify(`skill-sandbox: ${logMsg}`, "warning");
		}
		return sandboxResult;
	}
	if (state.currentLevel === "bypassed") return undefined;
	const classification = classifyCommand(command);
	// Dangerous commands - always prompt unless in block mode
	if (classification.dangerous) {
		if (!hasInteractiveUI(ctx)) {
			return {
				block: true,
				reason: `Dangerous command requires confirmation: ${command}\nUser can re-run with: SF_PERMISSION_LEVEL=bypassed sf -p "..."`,
			};
		}
		if (state.permissionMode === "block") {
			return {
				block: true,
				reason: `Blocked by permission mode (block). Dangerous command: ${command}\nUse /permission-mode ask to enable confirmations.`,
			};
		}
		playPermissionSound();
		const choice = await ctx.ui.select(`⚠️ Dangerous command`, [
			"Allow once",
			"Cancel",
		]);
		if (choice !== "Allow once") {
			return { block: true, reason: "Cancelled" };
		}
		return undefined;
	}
	// Check level
	const requiredIndex = LEVEL_INDEX[classification.level];
	const currentIndex = LEVEL_INDEX[state.currentLevel];
	if (requiredIndex <= currentIndex) return undefined;
	const requiredLevel = classification.level;
	const requiredInfo = LEVEL_INFO[requiredLevel];
	// Print mode: block
	if (!hasInteractiveUI(ctx)) {
		return {
			block: true,
			reason: `Blocked by permission (${state.currentLevel}). Command: ${command}\nAllowed at this level: ${LEVEL_ALLOWED_DESC[state.currentLevel]}\nUser can re-run with: SF_PERMISSION_LEVEL=${requiredLevel} sf -p "..."`,
		};
	}
	if (state.permissionMode === "block") {
		return {
			block: true,
			reason: `Blocked by permission (${state.currentLevel}, mode: block). Command: ${command}\nRequires ${requiredInfo.label}. Allowed at this level: ${LEVEL_ALLOWED_DESC[state.currentLevel]}\nUse /permission ${requiredLevel} or /permission-mode ask to enable prompts.`,
		};
	}
	// Interactive mode: prompt
	playPermissionSound();
	const choice = await ctx.ui.select(`Requires ${requiredInfo.label}`, [
		"Allow once",
		`Allow all (${requiredInfo.label})`,
		"Cancel",
	]);
	if (choice === "Allow once") return undefined;
	if (choice === `Allow all (${requiredInfo.label})`) {
		setLevel(state, requiredLevel, true, ctx);
		ctx.ui.notify(
			`Permission → ${requiredInfo.label} (saved globally)`,
			"info",
		);
		return undefined;
	}
	return { block: true, reason: "Cancelled" };
}
/** Handle write/edit tool_call - check permission and prompt if needed */
export async function handleWriteToolCall(opts) {
	const { state, toolName, filePath, ctx } = opts;
	if (state.currentLevel === "bypassed") return undefined;
	if (LEVEL_INDEX[state.currentLevel] >= LEVEL_INDEX["low"]) return undefined;
	const action = toolName === "Write" ? "Write" : "Edit";
	const message = `Requires Low: ${action} ${filePath}`;
	// Print mode: block
	if (!hasInteractiveUI(ctx)) {
		return {
			block: true,
			reason: `Blocked by permission (${state.currentLevel}). ${action}: ${filePath}\nAllowed at this level: ${LEVEL_ALLOWED_DESC[state.currentLevel]}\nUser can re-run with: SF_PERMISSION_LEVEL=low sf -p "..."`,
		};
	}
	if (state.permissionMode === "block") {
		return {
			block: true,
			reason: `Blocked by permission (${state.currentLevel}, mode: block). ${action}: ${filePath}\nRequires Low. Allowed at this level: ${LEVEL_ALLOWED_DESC[state.currentLevel]}\nUse /permission low or /permission-mode ask to enable prompts.`,
		};
	}
	// Interactive mode: prompt
	playPermissionSound();
	const choice = await ctx.ui.select(message, [
		"Allow once",
		"Allow all (Low)",
		"Cancel",
	]);
	if (choice === "Allow once") return undefined;
	if (choice === "Allow all (Low)") {
		setLevel(state, "low", true, ctx);
		ctx.ui.notify(`Permission → Low (saved globally)`, "info");
		return undefined;
	}
	return { block: true, reason: "Cancelled" };
}
// ============================================================================
// Extension entry point
// ============================================================================
export default function (pi) {
	const state = createInitialState();
	pi.registerCommand("permission", {
		description: "View or change permission level",
		handler: (args, ctx) => handlePermissionCommand(state, args, ctx),
	});
	pi.registerCommand("permission-mode", {
		description: "Set permission prompt mode (ask or block)",
		handler: (args, ctx) => handlePermissionModeCommand(state, args, ctx),
	});
	pi.on("session_start", async (_event, ctx) => {
		clearAllowedTools();
		handleSessionStart(state, ctx);
	});
	pi.on("before_agent_start", async (event) => {
		activateAllowedToolsFromSkillText(event.prompt);
	});
	pi.on("agent_end", async () => {
		clearAllowedTools();
	});
	pi.on("tool_call", async (event, ctx) => {
		if (event.toolName === "Bash") {
			return handleBashToolCall(state, event.input.command, ctx);
		}
		if (event.toolName === "Write" || event.toolName === "Edit") {
			return handleWriteToolCall({
				state,
				toolName: event.toolName,
				filePath: event.input.path,
				ctx,
			});
		}
		return undefined;
	});
	pi.on("tool_result", async (event) => {
		if (event.toolName !== "Skill" || event.isError) return undefined;
		const text = event.content
			.filter((part) => part.type === "text")
			.map((part) => part.text)
			.join("\n");
		activateAllowedToolsFromSkillText(text);
		return undefined;
	});
}
