#!/bin/bash
# SPDX-FileCopyrightText: 2025 OmniNode.ai Inc.
# SPDX-License-Identifier: MIT

# PostToolUse Quality Enforcement Hook - Portable Plugin Version
# Auto-fixes naming convention violations after files are written

set -euo pipefail
_OMNICLAUDE_HOOK_NAME="$(basename "${BASH_SOURCE[0]}")"
source "$(dirname "${BASH_SOURCE[0]}")/error-guard.sh" 2>/dev/null || true

# --- Lite mode guard [OMN-5398] ---
_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
_MODE_SH="${_SCRIPT_DIR}/../../lib/mode.sh"
if [[ -f "$_MODE_SH" ]]; then source "$_MODE_SH"; [[ "$(omniclaude_mode)" == "lite" ]] && exit 0; fi
unset _SCRIPT_DIR _MODE_SH

# Ensure stable CWD before any Python invocation.
# The session CWD may be on an external drive that disconnects/remounts;
# Python's <frozen getpath> calls os.getcwd() during startup and crashes
# with "failed to make path absolute" if the CWD is unavailable.
cd "$HOME" 2>/dev/null || cd /tmp || true

# Portable Plugin Configuration
# Resolve absolute path of this script, handling relative invocation (e.g. ./post-tool-use-quality.sh).
# Falls back to python3 if realpath is unavailable (non-GNU macOS without coreutils).
_SELF="$(realpath "${BASH_SOURCE[0]}" 2>/dev/null \
    || python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "${BASH_SOURCE[0]}")"
SCRIPT_DIR="$(cd "$(dirname "${_SELF}")" && pwd)"
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "${SCRIPT_DIR}/../.." && pwd)}"
unset _SELF SCRIPT_DIR
HOOKS_DIR="${PLUGIN_ROOT}/hooks"
HOOKS_LIB="${HOOKS_DIR}/lib"
LOG_FILE="${HOOKS_DIR}/logs/post-tool-use.log"

# Detect project root
PROJECT_ROOT="${PLUGIN_ROOT}/../.."
if [[ -f "${PROJECT_ROOT}/.env" ]]; then
    PROJECT_ROOT="$(cd "${PROJECT_ROOT}" && pwd)"
elif [[ -n "${CLAUDE_PROJECT_DIR:-}" ]]; then
    PROJECT_ROOT="${CLAUDE_PROJECT_DIR}"
else
    PROJECT_ROOT="$(pwd)"
fi

# Ensure log directory exists
mkdir -p "$(dirname "$LOG_FILE")"

# Guard: jq is required for JSON processing throughout this hook.
# If unavailable, exit 0 immediately so the hook never blocks the developer.
if ! command -v jq >/dev/null 2>&1; then
    echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] SKIP: jq not found, PostToolUse hook cannot process JSON" >> "$LOG_FILE" 2>/dev/null || true
    cat  # drain stdin so Claude Code doesn't hang
    exit 0
fi

# Load environment variables (before common.sh so KAFKA_BOOTSTRAP_SERVERS is available)
if [[ -f "$PROJECT_ROOT/.env" ]]; then
    set -a
    source "$PROJECT_ROOT/.env" 2>/dev/null || true
    set +a
fi

# OMN-3725: Mark as advisory — exit 0 gracefully if Python is missing
export OMNICLAUDE_HOOK_CRITICALITY="advisory"

# Source shared functions (provides PYTHON_CMD, KAFKA_ENABLED, get_time_ms)
source "${HOOKS_DIR}/scripts/common.sh"

export PYTHONPATH="${PROJECT_ROOT}:${PLUGIN_ROOT}/lib:${HOOKS_LIB}:${PYTHONPATH:-}"

# Shared language detection function — single source of truth for both
# pattern enforcement and content capture sections.
# Arguments: $1 = file path
# Returns: language string via stdout ("" if no file path given)
detect_language() {
    local fpath="$1"
    if [[ -z "$fpath" ]]; then
        echo ""
        return
    fi
    case "${fpath##*.}" in
        py) echo "python" ;;
        js) echo "javascript" ;;
        ts) echo "typescript" ;;
        tsx) echo "typescript" ;;
        jsx) echo "javascript" ;;
        rs) echo "rust" ;;
        go) echo "go" ;;
        java) echo "java" ;;
        rb) echo "ruby" ;;
        sh|bash) echo "shell" ;;
        yml|yaml) echo "yaml" ;;
        json) echo "json" ;;
        md) echo "markdown" ;;
        sql) echo "sql" ;;
        html) echo "html" ;;
        css) echo "css" ;;
        c|h) echo "c" ;;
        cpp|hpp|cc|cxx) echo "cpp" ;;
        *) echo "unknown" ;;
    esac
}

# Get tool info from stdin
TOOL_INFO=$(cat)
if ! echo "$TOOL_INFO" | jq -e . >/dev/null 2>>"$LOG_FILE"; then
    log "ERROR: Malformed JSON on stdin for PostToolUse"
    printf '%s\n' "$TOOL_INFO"
    exit 0
fi

# Debug: Save JSON structure
echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] PostToolUse JSON:" >> "$LOG_FILE"
echo "$TOOL_INFO" | jq '.' >> "$LOG_FILE" 2>&1 || echo "$TOOL_INFO" >> "$LOG_FILE"

# Extract tool name (non-critical: fall back to "unknown" on jq failure)
TOOL_NAME=$(echo "$TOOL_INFO" | jq -r '.tool_name // "unknown"' 2>/dev/null) || TOOL_NAME="unknown"
echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] PostToolUse hook triggered for $TOOL_NAME (plugin mode)" >> "$LOG_FILE"

# Extract session ID early — needed by pattern enforcement and Kafka emission.
# Wrapped in set +e to ensure the fallback chain never kills the hook.
set +e
SESSION_ID=$(echo "$TOOL_INFO" | jq -r '.sessionId // .session_id // ""' 2>/dev/null)
if [[ -z "$SESSION_ID" ]]; then
    SESSION_ID=$(uuidgen 2>/dev/null | tr '[:upper:]' '[:lower:]')
fi
if [[ -z "$SESSION_ID" ]]; then
    SESSION_ID=$("$PYTHON_CMD" -c 'import uuid; print(uuid.uuid4())' 2>/dev/null)
fi
if [[ -z "$SESSION_ID" ]]; then
    SESSION_ID="unknown-session"
fi
set -e

# -----------------------------------------------------------------------
# Pipeline Trace Logging — unified trace for Skill/Task/routing visibility
# tail -f $ONEX_LOG_DIR/pipeline-trace.log to see the full chain
# -----------------------------------------------------------------------
source "$(dirname "${BASH_SOURCE[0]}")/onex-paths.sh" || { echo "ONEX_STATE_DIR not set" >&2; exit 1; }
TRACE_LOG="${ONEX_LOG_DIR}/pipeline-trace.log"
mkdir -p "$(dirname "$TRACE_LOG")" 2>/dev/null
TS="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"

if [[ "$TOOL_NAME" == "Skill" ]]; then
    SKILL_NAME=$(echo "$TOOL_INFO" | jq -r '.tool_input.skill // .tool_input.name // "unknown"' 2>/dev/null) || SKILL_NAME="unknown"
    SKILL_ERROR=$(echo "$TOOL_INFO" | jq -r '.tool_response.error // ""' 2>/dev/null) || SKILL_ERROR=""
    if [[ -n "$SKILL_ERROR" ]]; then
        echo "[$TS] [PostToolUse] SKILL_LOAD_FAILED skill=$SKILL_NAME error=$SKILL_ERROR" >> "$TRACE_LOG"
    else
        echo "[$TS] [PostToolUse] SKILL_LOADED skill=$SKILL_NAME args=[REDACTED]" >> "$TRACE_LOG"
        # Write mode file for statusline tab label.
        # Strips "onex:" prefix so "onex:ticket_work" displays as "ticket-work".
        if [[ -n "${ITERM_SESSION_ID:-}" ]]; then
            _mode_guid="${ITERM_SESSION_ID#*:}"
            _display_skill="${SKILL_NAME#onex:}"
            mkdir -p "/tmp/omniclaude-tabs" 2>/dev/null || true
            printf '%s' "$_display_skill" > "/tmp/omniclaude-tabs/${_mode_guid}.mode" 2>/dev/null || true
        fi
        # -----------------------------------------------------------------------
        # Skill Usage Logging (OMN-3454)
        # Appends {"skill_name":..., "timestamp":..., "session_id":...} to
        # $ONEX_LOG_DIR/onex-skill-usage.log for Kaizen progression injection.
        # Non-blocking: runs in background subshell; hook exits 0 on failure.
        # -----------------------------------------------------------------------
        SKILL_USAGE_LOGGER="${HOOKS_LIB}/skill_usage_logger.py"
        if [[ -f "$SKILL_USAGE_LOGGER" ]]; then
            (
                printf '%s\n' "$TOOL_INFO" \
                    | "$PYTHON_CMD" "$SKILL_USAGE_LOGGER" \
                        2>>"$LOG_FILE" || true
            ) &
        fi
    fi
elif [[ "$TOOL_NAME" == "Task" ]]; then
    SUBAGENT_TYPE=$(echo "$TOOL_INFO" | jq -r '.tool_input.subagent_type // "unknown"' 2>/dev/null) || SUBAGENT_TYPE="unknown"
    TASK_MODEL=$(echo "$TOOL_INFO" | jq -r '.tool_input.model // "default"' 2>/dev/null) || TASK_MODEL="default"
    echo "[$TS] [PostToolUse] TASK_DISPATCHED subagent_type=$SUBAGENT_TYPE model=$TASK_MODEL description=[REDACTED]" >> "$TRACE_LOG"
elif [[ "$TOOL_NAME" == "Edit" || "$TOOL_NAME" == "Write" ]]; then
    EDIT_FILE=$(echo "$TOOL_INFO" | jq -r '.tool_input.file_path // "unknown"' 2>/dev/null) || EDIT_FILE="unknown"
    EDIT_FILE_SHORT="${EDIT_FILE##*/}"
    echo "[$TS] [PostToolUse] FILE_MODIFIED tool=$TOOL_NAME file=$EDIT_FILE_SHORT path=$EDIT_FILE" >> "$TRACE_LOG"
fi

# For Write/Edit tools, apply auto-fixes
if [ "$TOOL_NAME" = "Write" ] || [ "$TOOL_NAME" = "Edit" ]; then
    FILE_PATH=$(echo "$TOOL_INFO" | jq -r '.tool_input.file_path // .tool_response.filePath // empty' 2>/dev/null) || FILE_PATH=""

    if [ -n "$FILE_PATH" ]; then
        echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] File affected: $FILE_PATH" >> "$LOG_FILE"

        # Run Python enforcer if available
        ENFORCER_SCRIPT="${HOOKS_DIR}/scripts/post_tool_use_enforcer.py"
        if [[ -f "$ENFORCER_SCRIPT" ]]; then
            set +e
            "$PYTHON_CMD" "$ENFORCER_SCRIPT" "$FILE_PATH" 2>> "$LOG_FILE"
            EXIT_CODE=$?
            set -e

            if [ $EXIT_CODE -eq 0 ]; then
                echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Auto-fix completed successfully" >> "$LOG_FILE"
            else
                echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Auto-fix failed with code $EXIT_CODE" >> "$LOG_FILE"
            fi
        fi

        # -----------------------------------------------------------------------
        # Pattern Enforcement Advisory (OMN-2263)
        # -----------------------------------------------------------------------
        # Queries pattern store for applicable patterns, checks session cooldown,
        # runs compliance check, outputs advisory JSON. Async, non-blocking.
        # Gated behind ENABLE_LOCAL_INFERENCE_PIPELINE + ENABLE_PATTERN_ENFORCEMENT.
        # 300ms budget. All failures silent.
        # -----------------------------------------------------------------------
        PATTERN_ENFORCEMENT_ENABLED=$(_normalize_bool "${ENABLE_PATTERN_ENFORCEMENT:-false}")
        INFERENCE_PIPELINE_ENABLED=$(_normalize_bool "${ENABLE_LOCAL_INFERENCE_PIPELINE:-false}")

        if [[ "$PATTERN_ENFORCEMENT_ENABLED" == "true" && "$INFERENCE_PIPELINE_ENABLED" == "true" ]]; then
            ENFORCEMENT_SCRIPT="${HOOKS_LIB}/pattern_enforcement.py"
            if [[ -f "$ENFORCEMENT_SCRIPT" ]]; then
                (
                    # Detect language from file extension (uses shared function)
                    ENFORCE_LANGUAGE=$(detect_language "$FILE_PATH")

                    # Read bounded file content for compliance checking (text files only)
                    ENFORCE_CONTENT=""
                    ENFORCE_CONTENT_HASH=""
                    if [[ -f "$FILE_PATH" ]] && [[ -r "$FILE_PATH" ]]; then
                        if file "$FILE_PATH" 2>/dev/null | grep -qiE 'text|script|source|ascii|empty'; then
                            ENFORCE_CONTENT=$(head -c 32768 "$FILE_PATH" 2>/dev/null || true)
                            if command -v shasum >/dev/null 2>&1; then
                                ENFORCE_CONTENT_HASH=$(echo -n "$ENFORCE_CONTENT" | shasum -a 256 | cut -d' ' -f1)
                            elif command -v sha256sum >/dev/null 2>&1; then
                                ENFORCE_CONTENT_HASH=$(echo -n "$ENFORCE_CONTENT" | sha256sum | cut -d' ' -f1)
                            fi
                        fi
                    fi

                    # Build JSON input for enforcement script
                    ENFORCE_INPUT=$(jq -n \
                        --arg file_path "$FILE_PATH" \
                        --arg session_id "$SESSION_ID" \
                        --arg language "$ENFORCE_LANGUAGE" \
                        --arg content_preview "$ENFORCE_CONTENT" \
                        --arg content_sha256 "$ENFORCE_CONTENT_HASH" \
                        '{
                            file_path: $file_path,
                            session_id: $session_id,
                            language: (if $language == "" then null else $language end),
                            content_preview: $content_preview,
                            content_sha256: $content_sha256
                        }'
                    )

                    if [[ -n "$ENFORCE_INPUT" && "$ENFORCE_INPUT" != "null" ]]; then
                        ENFORCE_RESULT=$(echo "$ENFORCE_INPUT" | "$PYTHON_CMD" "$ENFORCEMENT_SCRIPT" 2>>"$LOG_FILE")
                        if [[ -n "$ENFORCE_RESULT" ]]; then
                            ADVISORY_COUNT=$(echo "$ENFORCE_RESULT" | jq -r '.advisories | length' 2>/dev/null || echo "0")
                            # Sanitize: ensure ADVISORY_COUNT is a non-negative integer
                            if ! [[ "$ADVISORY_COUNT" =~ ^[0-9]+$ ]]; then
                                ADVISORY_COUNT=0
                            fi
                            echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Pattern enforcement: $ADVISORY_COUNT advisory(ies)" >> "$LOG_FILE"
                            if [[ "$ADVISORY_COUNT" -gt 0 ]]; then
                                echo "$ENFORCE_RESULT" | jq -c '.' >> "$LOG_FILE" 2>/dev/null

                                # OMN-2269: Persist advisories for context injection on next turn
                                ADVISORY_FORMATTER="${HOOKS_LIB}/pattern_advisory_formatter.py"
                                if [[ -f "$ADVISORY_FORMATTER" ]]; then
                                    SAVE_INPUT=$(echo "$ENFORCE_RESULT" | jq -c \
                                        --arg session_id "$SESSION_ID" \
                                        '{session_id: $session_id, advisories: .advisories}' 2>/dev/null)
                                    if [[ -n "$SAVE_INPUT" && "$SAVE_INPUT" != "null" ]]; then
                                        echo "$SAVE_INPUT" | "$PYTHON_CMD" "$ADVISORY_FORMATTER" save 2>>"$LOG_FILE" \
                                            && echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Advisories persisted for context injection" >> "$LOG_FILE" \
                                            || echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Advisory persistence failed (non-fatal)" >> "$LOG_FILE"
                                    fi
                                fi
                            fi
                        fi
                    fi
                ) &
                echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Pattern enforcement started (async)" >> "$LOG_FILE"
            fi
        fi
    fi
else
    echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Tool $TOOL_NAME not applicable for auto-fix" >> "$LOG_FILE"
fi

# Error detection and logging
TOOL_ERROR=$(echo "$TOOL_INFO" | jq -r '.tool_response.error // .error // empty' 2>/dev/null) || TOOL_ERROR=""
if [[ -n "$TOOL_ERROR" ]]; then
    echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Tool error detected: $TOOL_ERROR" >> "$LOG_FILE"
fi

# Emit tool.executed event to Kafka (async, non-blocking)
# Uses omniclaude-emit CLI with 250ms hard timeout
TOOL_SUCCESS="true"
if [[ -n "$TOOL_ERROR" ]]; then
    TOOL_SUCCESS="false"
fi

# Extract duration if available
DURATION_MS=$(echo "$TOOL_INFO" | jq -r '.duration_ms // .durationMs // ""' 2>/dev/null || echo "")

if [[ "$KAFKA_ENABLED" == "true" ]]; then
    (
        TOOL_SUMMARY="${TOOL_NAME} on ${FILE_PATH:-unknown}"
        TOOL_SUMMARY="${TOOL_SUMMARY:0:500}"

        # Build action_description per OMN-3297 format spec and precedence table.
        # Precedence for file-path tools: file_path -> path -> basename
        # Bash: first 60 chars of command, stripped of newlines
        # Glob: full pattern
        # Grep: full pattern
        # Other: "{ToolName}: unknown"
        ACTION_DESCRIPTION=""
        case "$TOOL_NAME" in
            Read|Write|Edit|NotebookEdit)
                _AD_PATH=$(echo "$TOOL_INFO" | jq -r '.tool_input.file_path // .tool_input.path // ""' 2>/dev/null || echo "")
                if [[ -n "$_AD_PATH" && "$_AD_PATH" != "null" ]]; then
                    _AD_BASE="${_AD_PATH##*/}"
                    ACTION_DESCRIPTION="${TOOL_NAME}: ${_AD_BASE}"
                else
                    ACTION_DESCRIPTION="${TOOL_NAME}: unknown"
                fi
                ;;
            Bash)
                _AD_CMD=$(echo "$TOOL_INFO" | jq -r '.tool_input.command // ""' 2>/dev/null || echo "")
                _AD_CMD=$(printf '%s' "$_AD_CMD" | tr '\n\r' '  ')
                _AD_CMD="${_AD_CMD:0:60}"
                ACTION_DESCRIPTION="Bash: ${_AD_CMD:-unknown}"
                ;;
            Glob)
                _AD_PAT=$(echo "$TOOL_INFO" | jq -r '.tool_input.pattern // ""' 2>/dev/null || echo "")
                ACTION_DESCRIPTION="Glob: ${_AD_PAT:-unknown}"
                ;;
            Grep)
                _AD_PAT=$(echo "$TOOL_INFO" | jq -r '.tool_input.pattern // .tool_input.query // ""' 2>/dev/null || echo "")
                ACTION_DESCRIPTION="Grep: ${_AD_PAT:-unknown}"
                ;;
            *)
                ACTION_DESCRIPTION="${TOOL_NAME}: unknown"
                ;;
        esac
        # Normalize: strip newlines, cap at 160 chars
        ACTION_DESCRIPTION=$(printf '%s' "$ACTION_DESCRIPTION" | tr '\n\r' '  ')
        ACTION_DESCRIPTION="${ACTION_DESCRIPTION:0:160}"

        # Build JSON payload for tool.executed event
        # Use jq for proper JSON escaping of all fields
        PAYLOAD=$(jq -n \
            --arg session_id "$SESSION_ID" \
            --arg tool_name "$TOOL_NAME" \
            --argjson success "$([[ "$TOOL_SUCCESS" == "true" ]] && echo "true" || echo "false")" \
            --arg duration_ms "${DURATION_MS:-}" \
            --arg summary "$TOOL_SUMMARY" \
            --arg action_description "$ACTION_DESCRIPTION" \
            '{
                session_id: $session_id,
                tool_name: $tool_name,
                success: $success,
                summary: $summary,
                action_description: $action_description
            } + (if $duration_ms != "" then {duration_ms: ($duration_ms | tonumber)} else {} end)'
        )

        # Validate payload was constructed successfully
        if [[ -z "$PAYLOAD" || "$PAYLOAD" == "null" ]]; then
            echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] WARNING: Failed to construct tool payload (jq failed), skipping emission" >> "$LOG_FILE"
        else
            emit_via_daemon "tool.executed" "$PAYLOAD" 50
        fi
    ) &
    echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Tool event emission started" >> "$LOG_FILE"
fi

# -----------------------------------------------------------------------
# INTERIM: Code Content Capture for Pattern Learning (OMN-1702)
# -----------------------------------------------------------------------
# Captures tool execution content for omniintelligence pattern learning.
# This is an INTERIM solution emitting raw JSON until ModelToolExecutionContent
# is available in omnibase_core.
#
# Content Limits:
#   - Max content capture: 50KB (head -c 50000) - safety bound for large files
#   - Preview truncation: 2000 chars - sent to Kafka for pattern learning
#   - Full content is hashed but NOT sent to avoid oversized messages
#
# Will be migrated to use proper Pydantic model in OMN-1703.
# -----------------------------------------------------------------------
if [[ "$KAFKA_ENABLED" == "true" ]] && [[ "$TOOL_NAME" =~ ^(Read|Write|Edit)$ ]]; then
    (
        # Extract file path for Read tool (not extracted earlier for non-Write/Edit tools)
        # Note: This runs in a subshell, so FILE_PATH here won't affect outer scope
        if [[ "$TOOL_NAME" == "Read" ]]; then
            FILE_PATH=$(echo "$TOOL_INFO" | jq -r '.tool_input.file_path // .tool_response.filePath // empty' 2>/dev/null)
        fi

        # Extract content from tool response (max 50KB for safety)
        # For Read: prefer .tool_response.content if structured, fallback to raw .tool_response
        # For Write/Edit: content is in tool_input
        if [[ "$TOOL_NAME" == "Read" ]]; then
            # Try structured content first, then fall back to raw response
            CONTENT=$(echo "$TOOL_INFO" | jq -r '.tool_response.content // .tool_response // ""' 2>/dev/null | head -c 50000)
        else
            CONTENT=$(echo "$TOOL_INFO" | jq -r '.tool_input.content // .tool_input.new_string // ""' 2>/dev/null | head -c 50000)
        fi

        if [[ -n "$CONTENT" ]] && [[ "$CONTENT" != "null" ]]; then
            CONTENT_LENGTH=${#CONTENT}
            CONTENT_PREVIEW="${CONTENT:0:2000}"

            # Compute SHA256 hash (use shasum on macOS, sha256sum on Linux)
            if command -v shasum >/dev/null 2>&1; then
                CONTENT_HASH="sha256:$(echo -n "$CONTENT" | shasum -a 256 | cut -d' ' -f1)"
            elif command -v sha256sum >/dev/null 2>&1; then
                CONTENT_HASH="sha256:$(echo -n "$CONTENT" | sha256sum | cut -d' ' -f1)"
            else
                CONTENT_HASH=""
            fi

            # Detect language from file extension (uses shared function)
            LANGUAGE=$(detect_language "${FILE_PATH:-}")

            # Get correlation ID from context if available
            CORRELATION_ID_FILE="$PROJECT_ROOT/tmp/correlation_id"
            CORRELATION_ID=$(cat "$CORRELATION_ID_FILE" 2>/dev/null || true)

            # Determine success/failure flag using variable for clarity and robustness
            SUCCESS_FLAG="--success"
            [[ "$TOOL_SUCCESS" != "true" ]] && SUCCESS_FLAG="--failure"

            # Emit tool content event
            "$PYTHON_CMD" -m omniclaude.hooks.cli_emit tool-content \
                --session-id "$SESSION_ID" \
                --tool-name "$TOOL_NAME" \
                --tool-type "$TOOL_NAME" \
                ${FILE_PATH:+--file-path "$FILE_PATH"} \
                --content-preview "$CONTENT_PREVIEW" \
                --content-length "$CONTENT_LENGTH" \
                ${CONTENT_HASH:+--content-hash "$CONTENT_HASH"} \
                ${LANGUAGE:+--language "$LANGUAGE"} \
                "$SUCCESS_FLAG" \
                ${DURATION_MS:+--duration-ms "$DURATION_MS"} \
                ${CORRELATION_ID:+--correlation-id "$CORRELATION_ID"} \
                >> "$LOG_FILE" 2>&1 || { rc=$?; echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Tool content emit failed (exit=$rc, non-fatal)" >> "$LOG_FILE"; }
        fi
    ) &
    echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Tool content emission started for $TOOL_NAME" >> "$LOG_FILE"
fi

# -----------------------------------------------------------------------
# Bash Tool Content Capture for Shell Pattern Learning (OMN-1714)
# -----------------------------------------------------------------------
# Captures Bash command text (command-only, no output) for omniintelligence
# pattern learning. Output is intentionally excluded to reduce volume and
# avoid capturing sensitive runtime data.
#
# Privacy Decision (OMN-1714):
#   - Captures: command string only (tool_input.command)
#   - Excludes: tool output (may contain API responses, secrets)
#   - Sanitizes: env var assignments (KEY=val), --token/--api-key flags,
#     Authorization header patterns, URLs with embedded credentials
#   - Always sets: is_content_redacted=true, redaction_policy_version=bash-sanitize-v1
#   - Max preview: 2000 chars (consistent with file tools)
#   - Volume gate: commands shorter than 3 chars are skipped (not meaningful)
#
# Why capture Bash?
#   Shell command sequences teach omniintelligence common workflows (git, docker,
#   npm, kubectl, etc.) that are not captured by file edits alone. The command-only
#   approach provides pattern value while avoiding output sensitivity.
# -----------------------------------------------------------------------
if [[ "$KAFKA_ENABLED" == "true" ]] && [[ "$TOOL_NAME" == "Bash" ]]; then
    (
        # Extract command string from tool input
        BASH_COMMAND=$(echo "$TOOL_INFO" | jq -r '.tool_input.command // ""' 2>/dev/null)

        # Skip trivially short commands (e.g., "ls", "pwd") — low pattern value
        if [[ -n "$BASH_COMMAND" && "${#BASH_COMMAND}" -ge 3 ]]; then
            # Sanitize: redact common secret patterns before capture
            # 1. Env var assignments (KEY=value, KEY="value")
            BASH_SANITIZED=$(printf '%s' "$BASH_COMMAND" | \
                sed -E 's/([A-Z_]{4,}=[^[:space:];|&]+)/\1=<REDACTED>/g' 2>/dev/null || \
                printf '%s' "$BASH_COMMAND")
            # 2. --flag=value patterns for common secret flags
            BASH_SANITIZED=$(printf '%s' "$BASH_SANITIZED" | \
                sed -E 's/(--(?:token|api[_-]key|secret|password|auth|credential|key)[=[:space:]][^[:space:]]+)/--<REDACTED_FLAG>/gi' 2>/dev/null || \
                printf '%s' "$BASH_SANITIZED")
            # 3. Bearer/Authorization header values
            BASH_SANITIZED=$(printf '%s' "$BASH_SANITIZED" | \
                sed -E 's/(Bearer|Authorization:)[[:space:]]+[A-Za-z0-9._-]+/\1 <REDACTED>/gi' 2>/dev/null || \
                printf '%s' "$BASH_SANITIZED")
            # 4. URLs with embedded credentials (user:password pattern in URLs)
            BASH_SANITIZED=$(printf '%s' "$BASH_SANITIZED" | \
                sed -E 's|https?://[^:@]+:[^@]+@|https://<REDACTED>@|gi' 2>/dev/null || \  # pragma: allowlist secret
                printf '%s' "$BASH_SANITIZED")

            BASH_PREVIEW="${BASH_SANITIZED:0:2000}"
            BASH_CONTENT_LENGTH=${#BASH_COMMAND}

            # Compute hash of original (pre-sanitized) command for deduplication
            if command -v shasum >/dev/null 2>&1; then
                BASH_CONTENT_HASH="sha256:$(echo -n "$BASH_COMMAND" | shasum -a 256 | cut -d' ' -f1)"
            elif command -v sha256sum >/dev/null 2>&1; then
                BASH_CONTENT_HASH="sha256:$(echo -n "$BASH_COMMAND" | sha256sum | cut -d' ' -f1)"
            else
                BASH_CONTENT_HASH=""
            fi

            # Get correlation ID if available
            CORRELATION_ID_FILE="$PROJECT_ROOT/tmp/correlation_id"
            CORRELATION_ID=$(cat "$CORRELATION_ID_FILE" 2>/dev/null || true)

            # Derive success flag from outer TOOL_SUCCESS variable
            BASH_SUCCESS_FLAG="--success"
            [[ "$TOOL_SUCCESS" != "true" ]] && BASH_SUCCESS_FLAG="--failure"

            # Emit Bash content event — always marks content as redacted
            # (sanitization applied regardless of whether secrets were found)
            "$PYTHON_CMD" -m omniclaude.hooks.cli_emit tool-content \
                --session-id "$SESSION_ID" \
                --tool-name "Bash" \
                --content-preview "$BASH_PREVIEW" \
                --content-length "$BASH_CONTENT_LENGTH" \
                ${BASH_CONTENT_HASH:+--content-hash "$BASH_CONTENT_HASH"} \
                --language "shell" \
                "$BASH_SUCCESS_FLAG" \
                ${DURATION_MS:+--duration-ms "$DURATION_MS"} \
                ${CORRELATION_ID:+--correlation-id "$CORRELATION_ID"} \
                >> "$LOG_FILE" 2>&1 || { rc=$?; echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Bash content emit failed (exit=$rc, non-fatal)" >> "$LOG_FILE"; }

            echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Bash content captured (len=${BASH_CONTENT_LENGTH}, sanitized)" >> "$LOG_FILE"
        else
            echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Bash command too short to capture (len=${#BASH_COMMAND})" >> "$LOG_FILE"
        fi
    ) &
    echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] Bash content emission started" >> "$LOG_FILE"
fi

# -----------------------------------------------------------------------
# Intent-to-Commit Binding (OMN-2492)
# -----------------------------------------------------------------------
# When the Bash tool completes, check if the output contains a git commit
# and link it to the active intent via the emit daemon.  Runs in a
# background subshell — never blocks; hook exits 0 on all failures.
# -----------------------------------------------------------------------
if [[ "$TOOL_NAME" == "Bash" ]]; then
    COMMIT_BINDER="${HOOKS_LIB}/commit_intent_binder.py"
    if [[ -f "$COMMIT_BINDER" ]]; then
        (
            printf '%s\n' "$TOOL_INFO" \
                | "$PYTHON_CMD" "$COMMIT_BINDER" \
                    --session-id "$SESSION_ID" \
                    2>>"$LOG_FILE" || true
        ) &
    fi
fi

# --- Friction observation for skill failures [OMN-5747] ---
(
    echo "$TOOL_INFO" | $PYTHON_CMD -c "
import sys, json
sys.path.insert(0, '${HOOKS_LIB}')
sys.path.insert(0, '${PLUGIN_ROOT}/skills/_shared')
from post_tool_use_friction_check import check_tool_for_friction
tool_info = json.load(sys.stdin)
check_tool_for_friction(tool_info, session_id=tool_info.get('session_id', tool_info.get('sessionId', 'unknown')))
" 2>>"$LOG_FILE"
) &

# Always pass through original output
_hook_status "PASS" "$TOOL_NAME" "0"
printf '%s\n' "$TOOL_INFO"
exit 0
