// hosts/claude-code/runtime/native-hooks-consumer.ts — SPEC-587
// Subscribes to Claude Code stdio hook events and translates them onto
// Planu's autopilot event bus. Eliminates polling.
import { emitAutopilotEvent } from '../../../engine/autopilot/event-bus.js';
// Map from Claude Code native event type → Planu AutopilotEventName
const EVENT_MAP = {
    TaskCreated: 'spec:created',
    TaskCompleted: 'spec:status:done',
};
/**
 * Parse a raw JSON line from the Claude Code hook protocol.
 * Returns undefined (and logs a warning) when the line is not a valid hook event.
 * @internal exported for testing
 */
export function parseNativeHookLine(line) {
    let parsed;
    try {
        parsed = JSON.parse(line);
    }
    catch {
        return undefined;
    }
    if (typeof parsed !== 'object' ||
        parsed === null ||
        !('type' in parsed) ||
        !('timestamp' in parsed) ||
        !('payload' in parsed)) {
        return undefined;
    }
    return parsed;
}
/**
 * Translate a native hook event into a Planu autopilot event and emit it.
 * Unknown event types are silently ignored — never throw.
 * @internal exported for testing
 */
export function translateAndEmit(event, projectPath) {
    const autopilotName = EVENT_MAP[event.type];
    if (!autopilotName) {
        // TeammateIdle, PostToolUse, PreCompaction — handled by other consumers
        return;
    }
    // Extract specId from payload when available
    let specId;
    const payload = event.payload;
    if ('specId' in payload && typeof payload.specId === 'string') {
        specId = payload.specId;
    }
    const autopilotEvent = {
        name: autopilotName,
        projectPath,
        projectId: '', // resolved lazily by downstream handlers
        ...(specId !== undefined ? { specId } : {}),
    };
    emitAutopilotEvent(autopilotEvent);
}
/**
 * Subscribe to Claude Code native hook events from a line-emitting source.
 * Each JSON line on the source is parsed, translated, and emitted onto the
 * Planu autopilot event bus.
 *
 * Returns an unsubscribe function that removes the listener.
 *
 * When the source is unavailable (e.g., not running inside Claude Code) the
 * caller should skip calling this — there is no polling fallback here.
 */
export function subscribeToNativeHooks(source, projectPath) {
    const handler = (line) => {
        const trimmed = line.trim();
        if (!trimmed) {
            return;
        }
        try {
            const event = parseNativeHookLine(trimmed);
            if (event) {
                translateAndEmit(event, projectPath);
            }
        }
        catch {
            // best-effort — never crash the consumer
        }
    };
    source.on('line', handler);
    return () => {
        source.removeListener('line', handler);
    };
}
//# sourceMappingURL=native-hooks-consumer.js.map