/**
 * ClaudeCodeAdapter -- Maps Claude Code PreToolUse/PostToolUse hooks
 * to the GovernanceAdapter interface.
 *
 * Claude Code hooks receive JSON on stdin with the format:
 *   PreToolUse:  { tool_name: string, tool_input: Record<string, unknown> }
 *   PostToolUse: { tool_name: string, tool_input: Record<string, unknown>, tool_output?: unknown, error?: string }
 *
 * Exit code 0 = allow, exit code 2 = block (PreToolUse only).
 *
 * @connexum/ai-governance
 */
import * as fs from 'fs';
import * as path from 'path';
import { BaseGovernanceAdapter, ActionDecisionType, allow, deny, requireApproval, } from '../adapters/index.js';
import { GovernanceRuntime } from '../governance/governance-runtime.js';
import { CircuitBreaker } from '../security/circuit-breaker.js';
import { EventBus } from '../audit/event-bus.js';
// --- Adapter Implementation ---
export class ClaudeCodeAdapter extends BaseGovernanceAdapter {
    runtime = null;
    circuitBreaker = null;
    eventBus = null;
    agentName = 'unknown';
    initialize(config) {
        super.initialize(config);
        this.agentName = config.adapterName ?? 'claude-code';
        const governanceDir = config.governancePath ?? config.platformOptions?.governanceDir ?? '.governance';
        // Ensure required directories exist
        const logDir = config.logDir ?? path.join(governanceDir, 'logs');
        const stateDir = path.join(governanceDir, 'state');
        const permissionsDir = path.join(governanceDir, 'permissions');
        const eventDir = path.join(governanceDir, 'events');
        for (const dir of [logDir, stateDir, permissionsDir, eventDir]) {
            if (!fs.existsSync(dir)) {
                fs.mkdirSync(dir, { recursive: true });
            }
        }
        // Initialize GovernanceRuntime for tamper detection and permission checks
        const governanceFile = path.join(governanceDir, 'GOVERNANCE_RULES.md');
        const runtimeConfig = {
            governanceFile,
            permissionsDir,
            logDir,
            tamperDetection: true,
        };
        this.runtime = new GovernanceRuntime(runtimeConfig);
        // Initialize CircuitBreaker for agent suspension
        this.circuitBreaker = new CircuitBreaker({
            stateDir,
            agentName: this.agentName,
            failureThreshold: 3,
        });
        // Initialize EventBus for audit logging
        this.eventBus = new EventBus({
            eventDir,
            agentName: this.agentName,
        });
    }
    /**
     * Map Claude Code PreToolUse input to ActionContext, run governance checks,
     * and return an ActionDecision.
     */
    preAction(context) {
        if (!this.runtime || !this.circuitBreaker || !this.eventBus) {
            return deny('Adapter not initialized. Call initialize() first.');
        }
        // Circuit breaker check: if OPEN, deny all actions
        if (!this.circuitBreaker.allowExecution()) {
            this.eventBus.emit('circuit:opened', {
                toolName: context.toolName,
                agentName: this.agentName,
                reason: 'Circuit breaker is OPEN. Agent suspended.',
            });
            return deny('Circuit breaker is OPEN. Agent suspended.');
        }
        // Tamper detection: verify governance file integrity
        try {
            this.runtime.verifyGovernance();
        }
        catch (err) {
            this.eventBus.emit('governance:tampered', {
                toolName: context.toolName,
                error: err.message,
            });
            return deny(`Governance tamper detected: ${err.message}`);
        }
        // Permission check via GovernanceRuntime
        const access = this.runtime.checkToolAccess(context.toolName, this.agentName);
        // Log the pre-action check
        this.runtime.logActivity({
            timestamp: new Date().toISOString(),
            agent: this.agentName,
            action: `preAction:${context.toolName}`,
            target: context.toolName,
            result: access.allowed ? 'allowed' : 'blocked',
            detail: access.reason,
        });
        this.eventBus.emit('session:started', {
            hookPoint: 'PreToolUse',
            toolName: context.toolName,
            decision: access.allowed ? 'ALLOW' : access.level === 'APPROVAL_REQUIRED' ? 'REQUIRE_APPROVAL' : 'DENY',
        });
        if (!access.allowed) {
            if (access.level === 'APPROVAL_REQUIRED') {
                return requireApproval(access.reason);
            }
            return deny(access.reason);
        }
        return allow(access.reason);
    }
    /**
     * Map Claude Code PostToolUse input to ActionResult, update circuit breaker,
     * and log to EventBus.
     */
    postAction(context, result) {
        if (!this.circuitBreaker || !this.eventBus || !this.runtime) {
            return;
        }
        // Update circuit breaker based on result
        if (result.success) {
            this.circuitBreaker.recordSuccess();
        }
        else {
            this.circuitBreaker.recordFailure(result.error);
        }
        // Log to governance activity log
        this.runtime.logActivity({
            timestamp: new Date().toISOString(),
            agent: this.agentName,
            action: `postAction:${context.toolName}`,
            target: context.toolName,
            result: result.success ? 'allowed' : 'error',
            detail: result.error ?? undefined,
        });
        // Emit audit event
        this.eventBus.emit('session:ended', {
            hookPoint: 'PostToolUse',
            toolName: context.toolName,
            success: result.success,
            error: result.error ?? null,
            durationMs: result.durationMs ?? null,
        });
    }
    /**
     * Claude Code supports the full governance capability set.
     */
    getCapabilities() {
        return {
            preAction: true,
            postAction: true,
            auditLogging: true,
            circuitBreaker: true,
            approvalQueue: true,
            tamperDetection: true,
        };
    }
    // --- Claude Code-specific helpers ---
    /**
     * Parse raw PreToolUse JSON (from stdin) into an ActionContext.
     */
    static parsePreToolUse(input, agent) {
        return {
            toolName: input.tool_name,
            input: input.tool_input ?? {},
            agent: agent ?? { name: 'unknown' },
            timestamp: new Date().toISOString(),
        };
    }
    /**
     * Parse raw PostToolUse JSON (from stdin) into ActionContext + ActionResult pair.
     */
    static parsePostToolUse(input, agent) {
        const context = {
            toolName: input.tool_name,
            input: input.tool_input ?? {},
            agent: agent ?? { name: 'unknown' },
            timestamp: new Date().toISOString(),
        };
        const result = {
            success: !input.error,
            outputSummary: input.tool_output != null ? String(input.tool_output).slice(0, 200) : '',
            error: input.error,
        };
        return { context, result };
    }
    /**
     * Full PreToolUse hook handler: parse stdin JSON, run governance, return exit code.
     * Returns 0 for allow, 2 for block.
     */
    handlePreToolUse(rawJson, agentIdentity) {
        let input;
        try {
            input = JSON.parse(rawJson);
        }
        catch {
            return { exitCode: 2, message: 'Failed to parse PreToolUse JSON input.' };
        }
        const context = ClaudeCodeAdapter.parsePreToolUse(input, agentIdentity);
        const decision = this.preAction(context);
        if (decision.type === ActionDecisionType.ALLOW) {
            return { exitCode: 0 };
        }
        return { exitCode: 2, message: decision.reason };
    }
    /**
     * Full PostToolUse hook handler: parse stdin JSON, run post-action audit.
     * Always returns exit code 0 (post-action hooks do not block).
     */
    handlePostToolUse(rawJson, agentIdentity) {
        let input;
        try {
            input = JSON.parse(rawJson);
        }
        catch {
            return { exitCode: 0 };
        }
        const { context, result } = ClaudeCodeAdapter.parsePostToolUse(input, agentIdentity);
        this.postAction(context, result);
        return { exitCode: 0 };
    }
    // --- Accessors for testing ---
    /** Get the underlying GovernanceRuntime instance. */
    getRuntime() {
        return this.runtime;
    }
    /** Get the underlying CircuitBreaker instance. */
    getCircuitBreaker() {
        return this.circuitBreaker;
    }
    /** Get the underlying EventBus instance. */
    getEventBus() {
        return this.eventBus;
    }
}
//# sourceMappingURL=claude.js.map