/**
 * ============================================================
 *  plugins/ralph-wiggum/src/stopHookRunner.ts
 * ------------------------------------------------------------
 *  ralph-wiggum 私用的 stop-hook 执行器。读 hooks/hooks.json 里 Stop 事件
 *  的命令清单，stdin 传 transcript 文本，stdout 收 JSON 决策。
 *
 *  在 minimal-agent 里 stop-hook 是"咨询式"信号：
 *    - 返回 block → driver 把 reason 注入下一轮 prompt
 *    - pass / 错误 / Windows 缺 bash → 不影响循环继续
 *
 *  与框架级 hookEngine 的关系：hookEngine 服务于 UserPromptSubmit /
 *  PreToolUse / PostToolUse 等框架事件；ralph 的 Stop 是插件领域内
 *  特有的循环-反馈机制，故而独立执行，不污染框架 hookTable。
 * ============================================================
 */
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { spawn } from 'node:child_process';
async function loadStopHookConfig(pluginRoot) {
    const hooksJsonPath = join(pluginRoot, 'hooks', 'hooks.json');
    try {
        const raw = await readFile(hooksJsonPath, 'utf8');
        const parsed = JSON.parse(raw);
        const stopEntries = parsed?.hooks?.Stop;
        if (!Array.isArray(stopEntries))
            return [];
        const commands = [];
        for (const entry of stopEntries) {
            const hooks = entry.hooks;
            if (Array.isArray(hooks)) {
                for (const h of hooks) {
                    if (h.type === 'command' && h.command) {
                        commands.push(h);
                    }
                }
            }
        }
        return commands;
    }
    catch {
        return [];
    }
}
export async function executeStopHook(pluginRoot, transcriptText) {
    // Windows 上 bash 通常缺失，hook 链路降级为 pass（咨询式信号不影响循环）
    if (process.platform === 'win32') {
        return { decision: 'pass' };
    }
    const hookConfigs = await loadStopHookConfig(pluginRoot);
    for (const hookConfig of hookConfigs) {
        const result = await runSingleStopHook(hookConfig, pluginRoot, transcriptText);
        if (result.decision === 'block') {
            return result;
        }
    }
    return { decision: 'pass' };
}
function runSingleStopHook(hookConfig, pluginRoot, transcriptText) {
    const resolvedCommand = hookConfig.command.replaceAll('${CLAUDE_PLUGIN_ROOT}', pluginRoot);
    return new Promise((resolve) => {
        const child = spawn('bash', [resolvedCommand], {
            env: {
                ...process.env,
                CLAUDE_PLUGIN_ROOT: pluginRoot,
            },
        });
        let stdout = '';
        child.stdout.on('data', (data) => {
            stdout += data.toString();
        });
        child.on('error', () => {
            resolve({ decision: 'pass' });
        });
        child.on('close', (code) => {
            if (code !== 0) {
                resolve({ decision: 'pass' });
                return;
            }
            const trimmed = stdout.trim();
            if (!trimmed) {
                resolve({ decision: 'pass' });
                return;
            }
            try {
                const parsed = JSON.parse(trimmed);
                if (parsed.decision === 'block') {
                    resolve({
                        decision: 'block',
                        reason: typeof parsed.reason === 'string' ? parsed.reason : undefined,
                        systemMessage: typeof parsed.systemMessage === 'string' ? parsed.systemMessage : undefined,
                    });
                    return;
                }
                resolve({ decision: 'pass' });
            }
            catch {
                resolve({ decision: 'pass' });
            }
        });
        child.stdin.write(transcriptText);
        child.stdin.end();
    });
}
