// engine/hooks/agent-hook-migrator.ts — Auto-migrate deprecated Claude Code agent-type hooks.
// Claude Code 2.1.113+ broke type:"agent" hooks with "Messages are required for agent hooks."
// This migrator detects them in .claude/settings.json and converts to type:"command" equivalents.
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
import { homedir } from 'node:os';
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
/** Projects already migrated in this session — skip re-processing. */
const migratedPaths = new Set();
// ---------------------------------------------------------------------------
// Core migration
// ---------------------------------------------------------------------------
/**
 * Converts a single agent-type hook entry to an equivalent command-type hook.
 * Writes a shell script to hooksDir and returns the replacement entry.
 */
function migrateAgentHook(hook, hooksDir, index) {
    const prompt = hook.prompt ?? '';
    const model = hook.model ?? 'claude-haiku-4-5-20251001';
    const scriptName = `planu-migrated-agent-hook-${index}.sh`;
    const scriptPath = join(hooksDir, scriptName);
    // Escape the prompt for heredoc embedding (backslash, backtick, dollar must be escaped)
    const escapedPrompt = prompt.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$');
    // Build script as array to avoid TypeScript misinterpreting bash ${...} expansions
    const scriptLines = [
        '#!/usr/bin/env bash',
        '# Auto-generated by Planu: migrated from deprecated Claude Code agent hook.',
        '# Original type: agent — converted to type: command for Claude Code 2.1.113+ compatibility.',
        'set -euo pipefail',
        '',
        'HOOK_INPUT=$(cat)',
        "PROMPT=$(cat <<'PLANU_HOOK_PROMPT'",
        escapedPrompt,
        'PLANU_HOOK_PROMPT',
        ')',
        '',
        '# Substitute $ARGUMENTS placeholder with actual hook input',
        'FULL_PROMPT="${PROMPT/\\$ARGUMENTS/$HOOK_INPUT}"',
        '',
        `claude --print --dangerously-skip-permissions --model "${model}" -p "$FULL_PROMPT" 2>/dev/null || true`,
    ];
    const script = scriptLines.join('\n') + '\n';
    try {
        mkdirSync(hooksDir, { recursive: true });
        writeFileSync(scriptPath, script, { mode: 0o755 });
    }
    catch {
        // If we can't write the script, fall back to no-op command
        return {
            type: 'command',
            command: 'true',
            statusMessage: hook.statusMessage,
        };
    }
    return {
        type: 'command',
        command: `bash "${scriptPath}"`,
        statusMessage: hook.statusMessage,
        ...(hook.timeout !== undefined ? { timeout: hook.timeout } : {}),
    };
}
/**
 * Scan a single settings file, migrate any agent-type hooks, and write back.
 * Returns the number of hooks migrated.
 */
function migrateSettingsFile(settingsPath, hooksDir) {
    if (!existsSync(settingsPath)) {
        return 0;
    }
    let settings;
    try {
        settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
    }
    catch {
        return 0;
    }
    if (!settings.hooks || typeof settings.hooks !== 'object') {
        return 0;
    }
    let migratedCount = 0;
    let hookIndex = Date.now(); // unique index per session
    for (const [event, matchers] of Object.entries(settings.hooks)) {
        if (!Array.isArray(matchers)) {
            continue;
        }
        for (const matcher of matchers) {
            if (!Array.isArray(matcher.hooks)) {
                continue;
            }
            for (let i = 0; i < matcher.hooks.length; i++) {
                const hook = matcher.hooks[i];
                if (hook?.type === 'agent' && typeof hook.prompt === 'string') {
                    matcher.hooks[i] = migrateAgentHook(hook, hooksDir, hookIndex++);
                    migratedCount++;
                    console.error(`[Planu] Migrated agent hook in ${settingsPath} (event: ${event}, matcher: ${matcher.matcher})`);
                }
            }
        }
    }
    if (migratedCount > 0) {
        try {
            writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
        }
        catch {
            // Best-effort — never throw from a migration
        }
    }
    return migratedCount;
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Scan .claude/settings.json (project-level) and ~/.claude/settings.json (global)
 * for deprecated type:"agent" hooks and migrate them to type:"command" equivalents.
 *
 * Fire-and-forget safe: never throws, logs to stderr only.
 * Idempotent: each projectPath is only migrated once per process lifetime.
 */
export function migrateAgentHooksIfNeeded(projectPath) {
    if (migratedPaths.has(projectPath)) {
        return;
    }
    migratedPaths.add(projectPath);
    try {
        const projectSettingsPath = join(projectPath, '.claude', 'settings.json');
        const projectHooksDir = join(projectPath, '.claude', 'hooks');
        migrateSettingsFile(projectSettingsPath, projectHooksDir);
        // Also check global settings (affects all Claude Code sessions)
        const globalSettingsPath = join(homedir(), '.claude', 'settings.json');
        const globalHooksDir = join(homedir(), '.claude', 'hooks');
        migrateSettingsFile(globalSettingsPath, globalHooksDir);
    }
    catch {
        // Never block tool execution due to migration errors
    }
}
//# sourceMappingURL=agent-hook-migrator.js.map