#!/usr/bin/env bash
# ============================================================================
# SIGIL — Automated Security Auditing for AI Agent Code
# by NOMARK
#
# A protective mark for every line of code.
#
# Usage:
#   sigil clone <git-url>           - Clone and audit a git repo
#   sigil pip <package-name>        - Download and audit a pip package
#   sigil npm <package-name>        - Download and audit an npm package
#   sigil scan <path>               - Audit an existing directory/file
#   sigil fetch <url>               - Download and audit a file from URL
#   sigil approve <quarantine-id>   - Move approved code to working directory
#   sigil reject <quarantine-id>    - Delete quarantined code
#   sigil list                      - Show items in quarantine
#   sigil config                    - Show/edit configuration
#   sigil login                     - Authenticate with Sigil cloud API
#   sigil logout                    - Remove stored credentials
#
# Install:
#   chmod +x sigil
#   sudo cp sigil /usr/local/bin/sigil
#   sigil config --init
# ============================================================================
set -euo pipefail

# ── Configuration ──────────────────────────────────────────────────────────────
QUARANTINE_DIR="${SIGIL_QUARANTINE_DIR:-$HOME/.sigil/quarantine}"
APPROVED_DIR="${SIGIL_APPROVED_DIR:-$HOME/.sigil/approved}"
LOG_DIR="${SIGIL_LOG_DIR:-$HOME/.sigil/logs}"
REPORT_DIR="${SIGIL_REPORT_DIR:-$HOME/.sigil/reports}"
export CONFIG_FILE="${SIGIL_CONFIG:-$HOME/.sigil/config}"
TOKEN_FILE="${SIGIL_TOKEN:-$HOME/.sigil/token}"
API_BASE_URL="${SIGIL_API_URL:-https://api.sigil.nomark.dev}"

# Severity thresholds (exit codes) — exported for use in hooks/scripts
export SEVERITY_CRITICAL=1
export SEVERITY_HIGH=2
export SEVERITY_MEDIUM=3
export SEVERITY_LOW=4
export SEVERITY_CLEAN=0

# Colours
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'

# ── Helpers ────────────────────────────────────────────────────────────────────
timestamp() { date '+%Y%m%d_%H%M%S'; }
qid() { echo "$(timestamp)_$(echo "$1" | sed 's/[^a-zA-Z0-9]/_/g' | cut -c1-40)"; }

log()  { echo -e "${BLUE}[sigil]${NC} $1"; }
warn() { echo -e "${YELLOW}[warn]${NC} $1"; }
fail() { echo -e "${RED}[FAIL]${NC} $1"; }
pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
info() { echo -e "${CYAN}[info]${NC} $1"; }

ensure_dirs() {
    mkdir -p "$QUARANTINE_DIR" "$APPROVED_DIR" "$LOG_DIR" "$REPORT_DIR"
}

check_tool() {
    command -v "$1" &>/dev/null
}

# ── Tool Detection ─────────────────────────────────────────────────────────────
ensure_tools() {
    local missing=()
    for tool in grep find file; do
        check_tool "$tool" || missing+=("$tool")
    done

    local scanners_available=0
    if check_tool "semgrep"; then scanners_available=$((scanners_available + 1)); else info "semgrep not found — install via: pip install semgrep"; fi
    if check_tool "bandit"; then scanners_available=$((scanners_available + 1)); else info "bandit not found — install via: pip install bandit"; fi
    if check_tool "trufflehog"; then scanners_available=$((scanners_available + 1)); else info "trufflehog not found — install via: brew install trufflehog"; fi
    if check_tool "safety"; then scanners_available=$((scanners_available + 1)); else info "safety not found — install via: pip install safety"; fi

    if [ ${#missing[@]} -gt 0 ]; then
        fail "Missing required tools: ${missing[*]}"
        exit 1
    fi
    if [ "$scanners_available" -eq 0 ]; then
        warn "No security scanners installed. Built-in pattern matching will still run."
    fi
}

# ── .sigilignore Support ──────────────────────────────────────────────────────

# Load .sigilignore patterns
load_ignore_patterns() {
    local dir="$1"
    SIGIL_IGNORE_PATTERNS=()
    local ignore_file="$dir/.sigilignore"
    if [ -f "$ignore_file" ]; then
        while IFS= read -r line; do
            # Skip empty lines and comments
            [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
            SIGIL_IGNORE_PATTERNS+=("$line")
        done < "$ignore_file"
        log "Loaded ${#SIGIL_IGNORE_PATTERNS[@]} ignore patterns from .sigilignore"
    fi
}

# Check if a file path should be ignored
should_ignore() {
    local filepath="$1"
    for pattern in "${SIGIL_IGNORE_PATTERNS[@]}"; do
        # Use bash pattern matching (supports *, ?, **)
        # shellcheck disable=SC2053
        if [[ "$filepath" == $pattern ]] || [[ "$filepath" == */$pattern ]] || [[ "$filepath" == $pattern/* ]]; then
            return 0  # Should ignore
        fi
    done
    return 1  # Should not ignore
}

# ── Phase 1: Install Hook Scanner ──────────────────────────────────────────────
scan_install_hooks() {
    local target_dir="$1" report_file="$2" findings=0

    echo -e "\n${BOLD}=== Phase 1: Install Hook Analysis ===${NC}" | tee -a "$report_file"

    # Python setup.py
    if find "$target_dir" -name "setup.py" -exec grep -l 'cmdclass\|install_requires.*subprocess\|os\.system\|os\.popen' {} \; 2>/dev/null | head -5 | grep -q .; then
        fail "setup.py contains suspicious install hooks" | tee -a "$report_file"
        find "$target_dir" -name "setup.py" -exec grep -n 'cmdclass\|os\.system\|subprocess\|os\.popen' {} \; 2>/dev/null | head -10 | tee -a "$report_file"
        findings=$((findings + 10))
    else
        pass "No suspicious setup.py hooks" | tee -a "$report_file"
    fi

    # npm postinstall
    if find "$target_dir" -name "package.json" -exec grep -l '"preinstall"\|"postinstall"\|"preuninstall"' {} \; 2>/dev/null | head -5 | grep -q .; then
        fail "package.json contains install lifecycle scripts" | tee -a "$report_file"
        find "$target_dir" -name "package.json" -exec grep -n '"preinstall"\|"postinstall"\|"install"' {} \; 2>/dev/null | head -10 | tee -a "$report_file"
        findings=$((findings + 10))
    else
        pass "No npm install hooks" | tee -a "$report_file"
    fi

    # Makefile
    if find "$target_dir" -name "Makefile" -exec grep -l "install:" {} \; 2>/dev/null | head -5 | grep -q .; then
        local makefile_suspicious
        makefile_suspicious=$(find "$target_dir" -name "Makefile" -exec grep -n 'curl\|wget\|eval\|exec\|bash -c' {} \; 2>/dev/null | head -5)
        if [ -n "$makefile_suspicious" ]; then
            warn "Makefile install targets with network/exec calls:" | tee -a "$report_file"
            echo "$makefile_suspicious" | tee -a "$report_file"
            findings=$((findings + 5))
        fi
    fi

    return $findings
}

# ── Phase 2: Dangerous Code Patterns ──────────────────────────────────────────
scan_code_patterns() {
    local target_dir="$1" report_file="$2" findings=0

    echo -e "\n${BOLD}=== Phase 2: Code Pattern Analysis ===${NC}" | tee -a "$report_file"

    local patterns=(
        "eval(" "exec(" "compile(" "__import__("
        "importlib.import_module"
        "subprocess.call.*shell=True" "subprocess.Popen.*shell=True"
        "os.system(" "os.popen("
        "pickle.loads" "yaml.load(" "yaml.unsafe_load"
        "marshal.loads" "ctypes.cdll"
        "Function(" "child_process" "vm.runInNewContext" "new Function"
        "mcp_server" "MCPServer" "create_mcp_server"
        "allow_dangerous" "skip_confirmation" "auto_approve"
    )

    for pattern in "${patterns[@]}"; do
        local matches filtered_matches=""
        matches=$(grep -rn --include="*.py" --include="*.js" --include="*.ts" --include="*.jsx" --include="*.tsx" --include="*.mjs" --include="*.sh" "$pattern" "$target_dir" 2>/dev/null | grep -v "node_modules" | grep -v ".git/" | head -5)
        if [ -n "$matches" ]; then
            while IFS= read -r match_line; do
                local file; file=$(echo "$match_line" | cut -d: -f1)
                should_ignore "$file" && continue
                filtered_matches+="$match_line"$'\n'
            done <<< "$matches"
            filtered_matches=$(echo -n "$filtered_matches" | sed '/^$/d')
            if [ -n "$filtered_matches" ]; then
                warn "Found '$pattern':" | tee -a "$report_file"
                echo "$filtered_matches" | tee -a "$report_file"
                findings=$((findings + 5))
            fi
        fi
    done

    [ $findings -eq 0 ] && pass "No dangerous code execution patterns found" | tee -a "$report_file"
    return $findings
}

# ── Phase 3: Network & Exfiltration ───────────────────────────────────────────
scan_network_exfil() {
    local target_dir="$1" report_file="$2" findings=0

    echo -e "\n${BOLD}=== Phase 3: Network & Exfiltration Analysis ===${NC}" | tee -a "$report_file"

    local net_patterns=(
        "requests.post" "requests.put" "urllib.request.urlopen" "http.client"
        "fetch(" "XMLHttpRequest" "axios.post" "socket.connect"
        "websocket" "ngrok" "webhook"
        "discord.com/api/webhooks" "telegram.org/bot"
        "stdio_transport" "sse_transport" "mcp.*proxy"
    )

    for pattern in "${net_patterns[@]}"; do
        local matches filtered_matches=""
        matches=$(grep -rn --include="*.py" --include="*.js" --include="*.ts" --include="*.jsx" --include="*.tsx" --include="*.mjs" "$pattern" "$target_dir" 2>/dev/null | grep -v "node_modules" | grep -v ".git/" | grep -v "test" | head -3)
        if [ -n "$matches" ]; then
            while IFS= read -r match_line; do
                local file; file=$(echo "$match_line" | cut -d: -f1)
                should_ignore "$file" && continue
                filtered_matches+="$match_line"$'\n'
            done <<< "$matches"
            filtered_matches=$(echo -n "$filtered_matches" | sed '/^$/d')
            if [ -n "$filtered_matches" ]; then
                warn "Outbound network call '$pattern':" | tee -a "$report_file"
                echo "$filtered_matches" | tee -a "$report_file"
                findings=$((findings + 3))
            fi
        fi
    done

    [ $findings -eq 0 ] && pass "No suspicious network patterns found" | tee -a "$report_file"
    return $findings
}

# ── Phase 4: Credential & Secret Access ───────────────────────────────────────
scan_credentials() {
    local target_dir="$1" report_file="$2" findings=0

    echo -e "\n${BOLD}=== Phase 4: Credential & Secret Access ===${NC}" | tee -a "$report_file"

    local cred_patterns=(
        '\.env' "AWS_SECRET" "AWS_ACCESS_KEY" "PRIVATE_KEY" "API_KEY"
        "DATABASE_URL" "OPENAI_API_KEY" "ANTHROPIC_API_KEY"
        "ssh/" '\.aws/credentials' '\.kube/config'
        "keychain" "credential" "password" "secret"
        "MCP_API_KEY" "MCP_SECRET" "MCP_TOKEN"
    )

    for pattern in "${cred_patterns[@]}"; do
        local matches
        matches=$(grep -rn --include="*.py" --include="*.js" --include="*.ts" --include="*.sh" --include="*.yaml" --include="*.yml" --include="*.json" --include="*.toml" -i "$pattern" "$target_dir" 2>/dev/null | grep -v "node_modules" | grep -v ".git/" | grep -v "test" | grep -v "example" | grep -v "README" | grep -v ".lock" | head -3)
        if [ -n "$matches" ]; then
            local real_matches filtered_matches=""
            real_matches=$(echo "$matches" | grep -v "# " | grep -v "// " | grep -v "comment" | grep -v "type:" | grep -v "description" | head -2)
            if [ -n "$real_matches" ]; then
                while IFS= read -r match_line; do
                    local file; file=$(echo "$match_line" | cut -d: -f1)
                    should_ignore "$file" && continue
                    filtered_matches+="$match_line"$'\n'
                done <<< "$real_matches"
                filtered_matches=$(echo -n "$filtered_matches" | sed '/^$/d')
                if [ -n "$filtered_matches" ]; then
                    warn "Potential credential access '$pattern':" | tee -a "$report_file"
                    echo "$filtered_matches" | tee -a "$report_file"
                    findings=$((findings + 2))
                fi
            fi
        fi
    done

    [ $findings -eq 0 ] && pass "No suspicious credential access patterns" | tee -a "$report_file"
    return $findings
}

# ── Phase 5: Obfuscation Detection ────────────────────────────────────────────
scan_obfuscation() {
    local target_dir="$1" report_file="$2" findings=0

    echo -e "\n${BOLD}=== Phase 5: Obfuscation Detection ===${NC}" | tee -a "$report_file"

    local obf_patterns=(
        "base64.b64decode" "atob(" "Buffer.from.*base64"
        '\\x[0-9a-fA-F]\{2\}' "String.fromCharCode" "charCodeAt"
    )

    for pattern in "${obf_patterns[@]}"; do
        local matches filtered_matches=""
        matches=$(grep -rn --include="*.py" --include="*.js" --include="*.ts" "$pattern" "$target_dir" 2>/dev/null | grep -v "node_modules" | grep -v ".git/" | head -3)
        if [ -n "$matches" ]; then
            while IFS= read -r match_line; do
                local file; file=$(echo "$match_line" | cut -d: -f1)
                should_ignore "$file" && continue
                filtered_matches+="$match_line"$'\n'
            done <<< "$matches"
            filtered_matches=$(echo -n "$filtered_matches" | sed '/^$/d')
            if [ -n "$filtered_matches" ]; then
                warn "Potential obfuscation '$pattern':" | tee -a "$report_file"
                echo "$filtered_matches" | tee -a "$report_file"
                findings=$((findings + 5))
            fi
        fi
    done

    [ $findings -eq 0 ] && pass "No obfuscation patterns detected" | tee -a "$report_file"
    return $findings
}

# ── Phase 6: Provenance & Metadata ────────────────────────────────────────────
scan_provenance() {
    local target_dir="$1" report_file="$2" findings=0

    echo -e "\n${BOLD}=== Phase 6: Provenance & Metadata ===${NC}" | tee -a "$report_file"

    # Git history
    if [ -d "$target_dir/.git" ]; then
        local commit_count author_count first_commit latest_commit merge_count
        commit_count=$(cd "$target_dir" && git rev-list --count HEAD 2>/dev/null || echo "0")
        author_count=$(cd "$target_dir" && git log --format='%aE' | sort -u | wc -l 2>/dev/null || echo "0")
        first_commit=$(cd "$target_dir" && git log --reverse --format='%ai' | head -1 2>/dev/null || echo "unknown")
        latest_commit=$(cd "$target_dir" && git log -1 --format='%ai' 2>/dev/null || echo "unknown")
        merge_count=$(cd "$target_dir" && git log --merges --oneline 2>/dev/null | wc -l || echo "0")

        info "Git history: $commit_count commits, $author_count authors" | tee -a "$report_file"
        info "First commit: $first_commit" | tee -a "$report_file"
        info "Latest commit: $latest_commit" | tee -a "$report_file"
        info "Merge commits: $merge_count" | tee -a "$report_file"

        if [ "$commit_count" -lt 3 ]; then
            warn "Very few commits ($commit_count) — limited audit trail" | tee -a "$report_file"
            findings=$((findings + 2))
        fi
    else
        warn "No git history available — cannot verify provenance" | tee -a "$report_file"
        findings=$((findings + 3))
    fi

    # Binary files
    echo -e "\n${BOLD}-- File type breakdown --${NC}" | tee -a "$report_file"
    local binary_count
    binary_count=$(find "$target_dir" -type f -not -path "*/.git/*" -not -path "*/node_modules/*" -exec file {} \; 2>/dev/null | grep -c 'executable\|ELF\|Mach-O\|PE32' || echo "0")
    if [ "$binary_count" -gt 0 ]; then
        fail "Found $binary_count binary/executable files:" | tee -a "$report_file"
        find "$target_dir" -type f -not -path "*/.git/*" -not -path "*/node_modules/*" -exec file {} \; 2>/dev/null | grep 'executable\|ELF\|Mach-O\|PE32' | head -10 | tee -a "$report_file"
        findings=$((findings + 10))
    else
        pass "No binary executables found" | tee -a "$report_file"
    fi

    # Hidden files
    local hidden_count
    hidden_count=$(find "$target_dir" -name ".*" -not -name ".git" -not -name ".gitignore" -not -name ".env.example" -not -name ".eslintrc*" -not -name ".prettierrc*" -not -name ".editorconfig" -not -path "*/.git/*" 2>/dev/null | wc -l || echo "0")
    if [ "$hidden_count" -gt 0 ]; then
        warn "Found $hidden_count hidden files (excluding standard ones):" | tee -a "$report_file"
        find "$target_dir" -name ".*" -not -name ".git" -not -name ".gitignore" -not -name ".env.example" -not -name ".eslintrc*" -not -name ".prettierrc*" -not -name ".editorconfig" -not -path "*/.git/*" 2>/dev/null | head -10 | tee -a "$report_file"
        findings=$((findings + 1))
    fi

    # Large files
    echo -e "\n${BOLD}-- Large files (>1MB) --${NC}" | tee -a "$report_file"
    local large_files
    large_files=$(find "$target_dir" -type f -size +1M -not -path "*/.git/*" -not -path "*/node_modules/*" 2>/dev/null)
    if [ -n "$large_files" ]; then
        warn "Large files found:" | tee -a "$report_file"
        echo "$large_files" | while read -r f; do
            du -h "$f" 2>/dev/null | tee -a "$report_file"
        done
        findings=$((findings + 1))
    else
        pass "No unexpectedly large files" | tee -a "$report_file"
    fi

    # Filesystem manipulation
    echo -e "\n${BOLD}-- Filesystem access patterns --${NC}" | tee -a "$report_file"
    local fs_patterns=(
        "shutil.rmtree" "os.remove" "os.unlink" "rimraf"
        "fs.unlinkSync" "fs.writeFileSync"
        "/etc/passwd" "/etc/shadow" "chmod 777" "chmod +s"
    )
    local fs_findings=0
    for pattern in "${fs_patterns[@]}"; do
        local matches
        matches=$(grep -rn --include="*.py" --include="*.js" --include="*.ts" --include="*.sh" "$pattern" "$target_dir" 2>/dev/null | grep -v "node_modules" | grep -v ".git/" | grep -v "test" | head -3)
        if [ -n "$matches" ]; then
            warn "Filesystem operation '$pattern':" | tee -a "$report_file"
            echo "$matches" | tee -a "$report_file"
            fs_findings=$((fs_findings + 3))
        fi
    done
    [ $fs_findings -eq 0 ] && pass "No suspicious filesystem operations" | tee -a "$report_file"
    findings=$((findings + fs_findings))

    return $findings
}

# ── External Scanner Integration ───────────────────────────────────────────────
run_external_scanners() {
    local target_dir="$1" report_file="$2" findings=0

    echo -e "\n${BOLD}=== External Scanner Results ===${NC}" | tee -a "$report_file"

    # Semgrep
    if check_tool "semgrep"; then
        echo -e "\n${BOLD}-- Semgrep scan --${NC}" | tee -a "$report_file"
        local semgrep_out semgrep_count
        semgrep_out=$(semgrep --config=auto --json "$target_dir" 2>/dev/null || true)
        semgrep_count=$(echo "$semgrep_out" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('results',[])))" 2>/dev/null || echo "0")
        if [ "$semgrep_count" -gt 0 ]; then
            warn "Semgrep found $semgrep_count issues" | tee -a "$report_file"
            findings=$((findings + semgrep_count * 3))
        else
            pass "Semgrep: clean" | tee -a "$report_file"
        fi
    else
        info "Semgrep not available — skipping" | tee -a "$report_file"
    fi

    # Bandit (Python)
    if check_tool "bandit" && find "$target_dir" -name "*.py" -not -path "*/node_modules/*" | head -1 | grep -q .; then
        echo -e "\n${BOLD}-- Bandit scan (Python) --${NC}" | tee -a "$report_file"
        local bandit_out bandit_high
        bandit_out=$(bandit -r "$target_dir" -f json --exclude ".git,node_modules" 2>/dev/null || true)
        bandit_high=$(echo "$bandit_out" | python3 -c "import sys,json; d=json.load(sys.stdin); print(sum(1 for r in d.get('results',[]) if r.get('issue_severity') in ['HIGH','MEDIUM']))" 2>/dev/null || echo "0")
        if [ "$bandit_high" -gt 0 ]; then
            warn "Bandit found $bandit_high high/medium severity issues" | tee -a "$report_file"
            findings=$((findings + bandit_high * 3))
        else
            pass "Bandit: clean" | tee -a "$report_file"
        fi
    fi

    # TruffleHog (secrets)
    if check_tool "trufflehog"; then
        echo -e "\n${BOLD}-- TruffleHog scan (secrets) --${NC}" | tee -a "$report_file"
        local th_out
        th_out=$(trufflehog filesystem "$target_dir" --json --no-update 2>/dev/null | head -20 || true)
        if [ -n "$th_out" ]; then
            local secret_count
            secret_count=$(echo "$th_out" | wc -l)
            fail "TruffleHog found $secret_count potential secrets!" | tee -a "$report_file"
            findings=$((findings + secret_count * 10))
        else
            pass "TruffleHog: no secrets detected" | tee -a "$report_file"
        fi
    else
        info "TruffleHog not available — skipping" | tee -a "$report_file"
    fi

    # npm audit
    if check_tool "npm" && [ -f "$target_dir/package.json" ]; then
        echo -e "\n${BOLD}-- npm audit --${NC}" | tee -a "$report_file"
        local npm_out npm_vulns
        npm_out=$(cd "$target_dir" && npm audit --json 2>/dev/null || true)
        npm_vulns=$(echo "$npm_out" | python3 -c "import sys,json; d=json.load(sys.stdin); v=d.get('metadata',{}).get('vulnerabilities',{}); print(v.get('critical',0)+v.get('high',0))" 2>/dev/null) || npm_vulns="0"
        if [ "$npm_vulns" -gt 0 ]; then
            fail "npm audit found $npm_vulns critical/high vulnerabilities" | tee -a "$report_file"
            findings=$((findings + npm_vulns * 5))
        else
            pass "npm audit: clean" | tee -a "$report_file"
        fi
    fi

    # safety (Python deps)
    if check_tool "safety" && find "$target_dir" -name "requirements*.txt" | head -1 | grep -q .; then
        echo -e "\n${BOLD}-- Safety scan (Python deps) --${NC}" | tee -a "$report_file"
        local req_file safety_out
        req_file=$(find "$target_dir" -name "requirements*.txt" | head -1)
        safety_out=$(safety check -r "$req_file" --json 2>/dev/null || true)
        if echo "$safety_out" | grep -q "vulnerability"; then
            warn "Safety found vulnerable Python dependencies" | tee -a "$report_file"
            findings=$((findings + 5))
        else
            pass "Safety: Python dependencies clean" | tee -a "$report_file"
        fi
    fi

    return $findings
}

# ── Dependency Analysis ────────────────────────────────────────────────────────
scan_dependencies() {
    local target_dir="$1" report_file="$2" findings=0

    echo -e "\n${BOLD}=== Dependency Analysis ===${NC}" | tee -a "$report_file"

    if [ -f "$target_dir/requirements.txt" ]; then
        local dep_count unpinned
        dep_count=$(grep -c "^[a-zA-Z]" "$target_dir/requirements.txt" 2>/dev/null || echo "0")
        info "Python dependencies: $dep_count direct" | tee -a "$report_file"
        [ "$dep_count" -gt 30 ] && { warn "High dependency count ($dep_count) — larger attack surface" | tee -a "$report_file"; findings=$((findings + 2)); }
        unpinned=$(grep -c '^[a-zA-Z].*[^=<>]$' "$target_dir/requirements.txt" 2>/dev/null || echo "0")
        [ "$unpinned" -gt 0 ] && { warn "$unpinned unpinned dependencies (supply chain risk)" | tee -a "$report_file"; findings=$((findings + 2)); }
    fi

    if [ -f "$target_dir/package.json" ]; then
        local node_deps
        node_deps=$(python3 -c "import json; d=json.load(open('$target_dir/package.json')); print(len(d.get('dependencies',{})), len(d.get('devDependencies',{})))" 2>/dev/null || echo "0 0")
        info "Node dependencies: $node_deps (prod dev)" | tee -a "$report_file"
    fi

    [ -f "$target_dir/pyproject.toml" ] && info "Found pyproject.toml — modern Python packaging" | tee -a "$report_file"

    return $findings
}

# ── Permission & Scope Analysis ────────────────────────────────────────────────
scan_permissions() {
    local target_dir="$1" report_file="$2" findings=0

    echo -e "\n${BOLD}=== Permission & Scope Analysis ===${NC}" | tee -a "$report_file"

    # MCP/Agent tool definitions
    local tool_files
    tool_files=$(find "$target_dir" -name "*.json" -o -name "*.yaml" -o -name "*.yml" -o -name "*.toml" 2>/dev/null | grep -v "node_modules" | grep -v ".git")
    if [ -n "$tool_files" ]; then
        echo "$tool_files" | while read -r f; do
            if grep -q 'permissions\|scopes\|capabilities\|access' "$f" 2>/dev/null; then
                info "Permission config found: $f" | tee -a "$report_file"
                grep -n 'permissions\|scopes\|capabilities\|access\|admin\|write\|delete\|execute' "$f" 2>/dev/null | head -5 | tee -a "$report_file"
            fi
        done
    fi

    # Docker
    if find "$target_dir" -name "Dockerfile" -o -name "docker-compose*" 2>/dev/null | grep -q .; then
        info "Container configuration found" | tee -a "$report_file"
        if grep -rn 'privileged\|host\|--net=host\|network_mode.*host' "$target_dir"/Dockerfile "$target_dir"/docker-compose* 2>/dev/null | grep -q .; then
            fail "Container runs in privileged/host mode!" | tee -a "$report_file"
            findings=$((findings + 10))
        fi
    fi

    # GitHub Actions
    if find "$target_dir" -path "*/.github/workflows/*" 2>/dev/null | grep -q .; then
        info "GitHub Actions workflows found" | tee -a "$report_file"
        if grep -rn 'secrets\.\|GITHUB_TOKEN\|permissions:' "$target_dir/.github/workflows/" 2>/dev/null | head -5 | grep -q .; then
            warn "CI workflows access secrets:" | tee -a "$report_file"
            grep -rn 'secrets\.\|permissions:' "$target_dir/.github/workflows/" 2>/dev/null | head -5 | tee -a "$report_file"
            findings=$((findings + 2))
        fi
    fi

    return $findings
}

# ── Verdict ────────────────────────────────────────────────────────────────────
generate_verdict() {
    local total_score="$1" report_file="$2" q_id="$3"

    echo -e "\n${BOLD}+--------------------------------------+${NC}" | tee -a "$report_file"
    if [ "$total_score" -eq 0 ]; then
        echo -e "${BOLD}|  ${GREEN}VERDICT: CLEAN${NC}                        ${BOLD}|${NC}" | tee -a "$report_file"
        echo -e "${BOLD}|  ${GREEN}Risk Score: $total_score${NC}                           ${BOLD}|${NC}" | tee -a "$report_file"
        echo -e "${BOLD}|  Safe to approve.                      ${BOLD}|${NC}" | tee -a "$report_file"
    elif [ "$total_score" -lt 10 ]; then
        echo -e "${BOLD}|  ${BLUE}VERDICT: LOW RISK${NC}                     ${BOLD}|${NC}" | tee -a "$report_file"
        echo -e "${BOLD}|  ${BLUE}Risk Score: $total_score${NC}                            ${BOLD}|${NC}" | tee -a "$report_file"
        echo -e "${BOLD}|  Review flagged items, likely safe.    ${BOLD}|${NC}" | tee -a "$report_file"
    elif [ "$total_score" -lt 25 ]; then
        echo -e "${BOLD}|  ${YELLOW}VERDICT: MEDIUM RISK${NC}                  ${BOLD}|${NC}" | tee -a "$report_file"
        echo -e "${BOLD}|  ${YELLOW}Risk Score: $total_score${NC}                           ${BOLD}|${NC}" | tee -a "$report_file"
        echo -e "${BOLD}|  Manual review recommended.            ${BOLD}|${NC}" | tee -a "$report_file"
    elif [ "$total_score" -lt 50 ]; then
        echo -e "${BOLD}|  ${RED}VERDICT: HIGH RISK${NC}                    ${BOLD}|${NC}" | tee -a "$report_file"
        echo -e "${BOLD}|  ${RED}Risk Score: $total_score${NC}                           ${BOLD}|${NC}" | tee -a "$report_file"
        echo -e "${BOLD}|  Do NOT approve without review.        ${BOLD}|${NC}" | tee -a "$report_file"
    else
        echo -e "${BOLD}|  ${RED}VERDICT: CRITICAL RISK${NC}                ${BOLD}|${NC}" | tee -a "$report_file"
        echo -e "${BOLD}|  ${RED}Risk Score: $total_score${NC}                           ${BOLD}|${NC}" | tee -a "$report_file"
        echo -e "${BOLD}|  REJECT — multiple red flags.          ${BOLD}|${NC}" | tee -a "$report_file"
    fi
    echo -e "${BOLD}+--------------------------------------+${NC}" | tee -a "$report_file"
    echo "" | tee -a "$report_file"
    echo -e "Quarantine ID: ${CYAN}$q_id${NC}" | tee -a "$report_file"
    echo -e "Full report:   ${CYAN}$report_file${NC}" | tee -a "$report_file"
    echo "" | tee -a "$report_file"
    echo -e "Actions:" | tee -a "$report_file"
    echo -e "  ${GREEN}sigil approve $q_id${NC}  — Move to working directory" | tee -a "$report_file"
    echo -e "  ${RED}sigil reject  $q_id${NC}  — Delete from quarantine" | tee -a "$report_file"
}

# ── Cloud Threat Intelligence ─────────────────────────────────────────────────

# Compute SHA-256 hash of a directory (file paths + sizes for determinism)
compute_dir_hash() {
    local dir="$1"
    # Hash file paths and sizes in sorted order for deterministic output
    find "$dir" -type f -not -path '*/.git/*' -print0 2>/dev/null \
        | sort -z \
        | xargs -0 stat --format='%n %s' 2>/dev/null \
        | sha256sum \
        | cut -d' ' -f1
}

# Cloud threat intelligence enrichment — hash lookup + signature application
cloud_threat_enrichment() {
    local target_dir="$1" report_file="$2"
    local score=0

    # Only run if authenticated (token exists)
    [ ! -f "$TOKEN_FILE" ] && return 0

    local token
    token=$(cat "$TOKEN_FILE" 2>/dev/null)
    [ -z "$token" ] && return 0

    echo -e "\n${BOLD}[Phase 7] Cloud Threat Intelligence${NC}" | tee -a "$report_file"

    # 1. Hash-based threat lookup
    local dir_hash
    dir_hash=$(compute_dir_hash "$target_dir" 2>/dev/null || echo "")
    if [ -n "$dir_hash" ]; then
        local threat_response
        threat_response=$(curl -sf -H "Authorization: Bearer $token" \
            "${API_BASE_URL}/v1/threat/${dir_hash}" 2>/dev/null || echo "")

        if [ -n "$threat_response" ] && echo "$threat_response" | grep -q '"hash"'; then
            local threat_name threat_desc threat_severity
            threat_name=$(echo "$threat_response" | grep -o '"package_name":"[^"]*"' | head -1 | cut -d'"' -f4)
            threat_desc=$(echo "$threat_response" | grep -o '"description":"[^"]*"' | head -1 | cut -d'"' -f4)
            threat_severity=$(echo "$threat_response" | grep -o '"severity":"[^"]*"' | head -1 | cut -d'"' -f4)
            echo -e "  ${RED}THREAT MATCH${NC}: $threat_name ($threat_severity)" | tee -a "$report_file"
            echo -e "  Description: $threat_desc" | tee -a "$report_file"
            score=$((score + 50))
        else
            echo -e "  ${GREEN}No known threats${NC} for hash $dir_hash" | tee -a "$report_file"
        fi
    fi

    # 2. Fetch and apply cloud signatures (if not cached recently)
    local sigs_file="$HOME/.sigil/signatures.json"
    local sigs_age=9999
    if [ -f "$sigs_file" ]; then
        local now_epoch sig_epoch
        now_epoch=$(date +%s)
        sig_epoch=$(stat -c %Y "$sigs_file" 2>/dev/null || echo 0)
        sigs_age=$(( (now_epoch - sig_epoch) / 3600 ))
    fi

    # Refresh signatures if older than 24 hours
    if [ "$sigs_age" -gt 24 ] || [ ! -f "$sigs_file" ]; then
        local sig_response
        sig_response=$(curl -sf -H "Authorization: Bearer $token" \
            "${API_BASE_URL}/v1/signatures" 2>/dev/null || echo "")
        if [ -n "$sig_response" ]; then
            mkdir -p "$HOME/.sigil"
            echo "$sig_response" > "$sigs_file"
            local _sig_count  # unused, kept for potential future logging
            _sig_count=$(echo "$sig_response" | grep -o '"total":' | wc -l)
            echo -e "  Synced cloud signatures (refreshed)" | tee -a "$report_file"
        fi
    else
        echo -e "  Cloud signatures cached (${sigs_age}h old)" | tee -a "$report_file"
    fi

    # 3. Apply cloud signatures to scanned files
    if [ -f "$sigs_file" ]; then
        local cloud_hits=0
        # Extract patterns from the signatures JSON
        local patterns
        patterns=$(grep -o '"pattern":"[^"]*"' "$sigs_file" 2>/dev/null | cut -d'"' -f4)
        local _descriptions  # unused, kept for potential future logging
        _descriptions=$(grep -o '"description":"[^"]*"' "$sigs_file" 2>/dev/null | cut -d'"' -f4)

        while IFS= read -r pattern; do
            [ -z "$pattern" ] && continue
            local matches
            matches=$(grep -rl "$pattern" "$target_dir" 2>/dev/null | head -5)
            if [ -n "$matches" ]; then
                cloud_hits=$((cloud_hits + 1))
                while IFS= read -r match_file; do
                    echo -e "  ${YELLOW}[cloud-sig]${NC} Pattern match in: $match_file" | tee -a "$report_file"
                done <<< "$matches"
            fi
        done <<< "$patterns"

        if [ "$cloud_hits" -gt 0 ]; then
            score=$((score + cloud_hits * 5))
            echo -e "  Cloud signatures: ${YELLOW}${cloud_hits} match(es)${NC}" | tee -a "$report_file"
        else
            echo -e "  Cloud signatures: ${GREEN}no matches${NC}" | tee -a "$report_file"
        fi
    fi

    return $score
}

# ── Full Audit Pipeline ───────────────────────────────────────────────────────
run_full_audit() {
    local target_dir="$1" q_id="$2"
    local report_file="$REPORT_DIR/${q_id}_report.txt"
    local total_score=0

    echo -e "\n${BOLD}+----------------------------------------------+${NC}"
    echo -e "${BOLD}|               S I G I L                      |${NC}"
    echo -e "${BOLD}|      Automated Security Analysis             |${NC}"
    echo -e "${BOLD}|              by NOMARK                       |${NC}"
    echo -e "${BOLD}+----------------------------------------------+${NC}"
    echo ""
    echo "Target: $target_dir"
    echo "Quarantine ID: $q_id"
    echo "Report: $report_file"
    echo "Started: $(date)"
    echo ""

    cat > "$report_file" <<EOF
# SIGIL AUDIT REPORT
# Target: $target_dir
# Quarantine ID: $q_id
# Date: $(date)
EOF

    # Load .sigilignore patterns from the target directory
    load_ignore_patterns "$target_dir"

    # Run all 6 scan phases + external scanners
    scan_install_hooks "$target_dir" "$report_file"   || total_score=$((total_score + $?))
    scan_code_patterns "$target_dir" "$report_file"   || total_score=$((total_score + $?))
    scan_network_exfil "$target_dir" "$report_file"   || total_score=$((total_score + $?))
    scan_credentials "$target_dir" "$report_file"     || total_score=$((total_score + $?))
    scan_obfuscation "$target_dir" "$report_file"     || total_score=$((total_score + $?))
    scan_provenance "$target_dir" "$report_file"      || total_score=$((total_score + $?))
    run_external_scanners "$target_dir" "$report_file" || total_score=$((total_score + $?))
    scan_dependencies "$target_dir" "$report_file"    || total_score=$((total_score + $?))
    scan_permissions "$target_dir" "$report_file"     || total_score=$((total_score + $?))

    # Cloud threat intelligence enrichment (if authenticated)
    cloud_threat_enrichment "$target_dir" "$report_file" || total_score=$((total_score + $?))

    generate_verdict "$total_score" "$report_file" "$q_id"
    return 0
}

# ── Command Handlers ──────────────────────────────────────────────────────────
cmd_clone() {
    local url="$1"
    # Validate URL format
    if [[ ! "$url" =~ ^https?://[^[:space:]]+ ]] && [[ ! "$url" =~ ^git@[^[:space:]]+ ]] && [[ ! "$url" =~ ^ssh://[^[:space:]]+ ]]; then
        fail "Invalid repository URL format: $url"
        exit 1
    fi
    local q_id; q_id=$(qid "$url")
    local target="$QUARANTINE_DIR/$q_id"
    log "Cloning $url into quarantine..."
    git clone --depth 1 -- "$url" "$target" 2>/dev/null
    log "Cloned to: $target"
    run_full_audit "$target" "$q_id"
}

cmd_pip() {
    local package="$1"
    # Validate package name (alphanumeric, hyphens, underscores, dots, @scope)
    if [[ ! "$package" =~ ^(@[a-zA-Z0-9_-]+/)?[a-zA-Z0-9][a-zA-Z0-9._-]*$ ]]; then
        fail "Invalid package name: $package"
        exit 1
    fi
    local q_id; q_id=$(qid "pip_$package")
    local target="$QUARANTINE_DIR/$q_id"
    mkdir -p "$target"
    log "Downloading pip package '$package' into quarantine..."
    pip download "$package" -d "$target" --no-deps 2>/dev/null
    local wheel; wheel=$(find "$target" -name "*.whl" -o -name "*.tar.gz" | head -1)
    if [ -n "$wheel" ]; then
        local extract_dir="$target/extracted"
        mkdir -p "$extract_dir"
        if [[ "$wheel" == *.whl ]]; then
            unzip -q "$wheel" -d "$extract_dir" 2>/dev/null || true
        else
            tar xzf "$wheel" -C "$extract_dir" 2>/dev/null || true
        fi
        run_full_audit "$extract_dir" "$q_id"
    else
        warn "Could not download package"
    fi
}

cmd_npm() {
    local package="$1"
    # Validate package name (alphanumeric, hyphens, underscores, dots, @scope)
    if [[ ! "$package" =~ ^(@[a-zA-Z0-9_-]+/)?[a-zA-Z0-9][a-zA-Z0-9._-]*$ ]]; then
        fail "Invalid package name: $package"
        exit 1
    fi
    local q_id; q_id=$(qid "npm_$package")
    local target="$QUARANTINE_DIR/$q_id"
    mkdir -p "$target"
    log "Downloading npm package '$package' into quarantine..."
    (cd "$target" && npm pack "$package" 2>/dev/null)
    local tarball; tarball=$(find "$target" -name "*.tgz" | head -1)
    if [ -n "$tarball" ]; then
        local extract_dir="$target/extracted"
        mkdir -p "$extract_dir"
        tar xzf "$tarball" -C "$extract_dir" 2>/dev/null || true
        run_full_audit "$extract_dir" "$q_id"
    else
        warn "Could not download package"
    fi
}

cmd_scan() {
    local target="$1"
    [ ! -e "$target" ] && { fail "Path does not exist: $target"; exit 1; }
    local q_id; q_id=$(qid "$(basename "$target")")
    if [[ "$target" != "$QUARANTINE_DIR"* ]]; then
        local quarantine_copy="$QUARANTINE_DIR/$q_id"
        cp -r "$target" "$quarantine_copy"
        target="$quarantine_copy"
        log "Copied to quarantine: $quarantine_copy"
    fi
    run_full_audit "$target" "$q_id"
}

cmd_fetch() {
    local url="$1"
    local q_id; q_id=$(qid "$url")
    local target="$QUARANTINE_DIR/$q_id"
    mkdir -p "$target"
    log "Fetching $url into quarantine..."
    # Sanitize filename from URL
    local raw_name
    raw_name=$(basename "$url")
    local safe_name
    safe_name=$(echo "$raw_name" | sed 's/[^a-zA-Z0-9._-]/_/g' | cut -c1-100)
    curl -sL -- "$url" -o "$target/$safe_name" 2>/dev/null
    local downloaded="$target/$safe_name"
    if file "$downloaded" | grep -q 'gzip\|tar\|zip\|compressed'; then
        local extract_dir="$target/extracted"
        mkdir -p "$extract_dir"
        if [[ "$downloaded" == *.zip ]]; then
            unzip -q "$downloaded" -d "$extract_dir" 2>/dev/null || true
        elif [[ "$downloaded" == *.tar.gz ]] || [[ "$downloaded" == *.tgz ]]; then
            tar xzf "$downloaded" -C "$extract_dir" 2>/dev/null || true
        fi
        target="$extract_dir"
    fi
    run_full_audit "$target" "$q_id"
}

cmd_approve() {
    local q_id="$1"
    # Validate quarantine ID format (alphanumeric + underscore only)
    if [[ ! "$q_id" =~ ^[a-zA-Z0-9_]+$ ]]; then
        fail "Invalid quarantine ID format: $q_id"
        exit 1
    fi
    local source="$QUARANTINE_DIR/$q_id"
    # Verify path is actually inside quarantine directory
    local real_source
    real_source=$(realpath "$source" 2>/dev/null) || { fail "Quarantine ID not found: $q_id"; exit 1; }
    local real_quarantine
    real_quarantine=$(realpath "$QUARANTINE_DIR")
    if [[ "$real_source" != "$real_quarantine"/* ]]; then
        fail "Path traversal detected: $q_id"
        exit 1
    fi
    [ ! -d "$source" ] && { fail "Quarantine ID not found: $q_id"; echo "Use 'sigil list' to see available items"; exit 1; }
    mv "$source" "$APPROVED_DIR/$q_id"
    pass "Approved and moved to: $APPROVED_DIR/$q_id"
    echo "You can now copy/link from $APPROVED_DIR/$q_id to your project."
}

cmd_reject() {
    local q_id="$1"
    # Validate quarantine ID format (alphanumeric + underscore only)
    if [[ ! "$q_id" =~ ^[a-zA-Z0-9_]+$ ]]; then
        fail "Invalid quarantine ID format: $q_id"
        exit 1
    fi
    local source="$QUARANTINE_DIR/$q_id"
    # Verify path is actually inside quarantine directory
    local real_source
    real_source=$(realpath "$source" 2>/dev/null) || { fail "Quarantine ID not found: $q_id"; exit 1; }
    local real_quarantine
    real_quarantine=$(realpath "$QUARANTINE_DIR")
    if [[ "$real_source" != "$real_quarantine"/* ]]; then
        fail "Path traversal detected: $q_id"
        exit 1
    fi
    [ ! -d "$source" ] && { fail "Quarantine ID not found: $q_id"; exit 1; }
    rm -rf "$source"
    pass "Rejected and deleted: $q_id"
}

cmd_list() {
    echo -e "${BOLD}Quarantined items:${NC}"
    if [ -d "$QUARANTINE_DIR" ] && [ "$(ls -A "$QUARANTINE_DIR" 2>/dev/null)" ]; then
        for item in "$QUARANTINE_DIR"/*/; do
            local name; name=$(basename "$item")
            local size; size=$(du -sh "$item" 2>/dev/null | cut -f1)
            local report="$REPORT_DIR/${name}_report.txt"
            local verdict="no scan"
            [ -f "$report" ] && verdict=$(grep "VERDICT:" "$report" | tail -1 | sed 's/.*VERDICT: //' | sed 's/|.*//' | tr -d '[:space:]' || echo "scanned")
            echo -e "  ${CYAN}$name${NC}  ($size)  [$verdict]"
        done
    else
        echo "  (empty)"
    fi
    echo -e "\n${BOLD}Approved items:${NC}"
    if [ -d "$APPROVED_DIR" ] && [ "$(ls -A "$APPROVED_DIR" 2>/dev/null)" ]; then
        for item in "$APPROVED_DIR"/*/; do
            echo -e "  ${GREEN}$(basename "$item")${NC}  ($(du -sh "$item" 2>/dev/null | cut -f1))"
        done
    else
        echo "  (empty)"
    fi
}

cmd_config() {
    if [ "${1:-}" = "--init" ]; then
        ensure_dirs
        pass "Initialized sigil directories:"
        echo "  Quarantine: $QUARANTINE_DIR"
        echo "  Approved:   $APPROVED_DIR"
        echo "  Logs:       $LOG_DIR"
        echo "  Reports:    $REPORT_DIR"
        return
    fi
    echo -e "${BOLD}sigil configuration:${NC}"
    echo "  QUARANTINE_DIR:  $QUARANTINE_DIR"
    echo "  APPROVED_DIR:    $APPROVED_DIR"
    echo "  LOG_DIR:         $LOG_DIR"
    echo "  REPORT_DIR:      $REPORT_DIR"
    echo ""
    echo "Override via environment variables:"
    echo "  export SIGIL_QUARANTINE_DIR=/path/to/quarantine"
    echo "  export SIGIL_APPROVED_DIR=/path/to/approved"
    echo ""
    echo -e "${BOLD}Scanner availability:${NC}"
    for tool in semgrep bandit trufflehog safety npm pip git; do
        if check_tool "$tool"; then pass "$tool"; else warn "$tool (not installed)"; fi
    done
}

# ── Git Hook Installer ────────────────────────────────────────────────────────
cmd_install_hooks() {
    local git_dir="${1:-.}"
    local hooks_dir="$git_dir/.git/hooks"
    [ ! -d "$hooks_dir" ] && { fail "Not a git repository: $git_dir"; exit 1; }

    cat > "$hooks_dir/pre-commit" <<'HOOK'
#!/usr/bin/env bash
# sigil pre-commit hook — scans staged files for dangerous patterns
staged=$(git diff --cached --name-only --diff-filter=ACM)
if [ -z "$staged" ]; then exit 0; fi
issues=0
for f in $staged; do
    if grep -n 'eval(\|exec(\|__import__(\|subprocess.*shell=True\|os.system\|pickle.loads\|child_process' "$f" 2>/dev/null; then
        echo "WARNING: Dangerous pattern in staged file: $f"
        issues=$((issues + 1))
    fi
done
if [ $issues -gt 0 ]; then
    echo ""
    echo "Found $issues file(s) with dangerous patterns."
    echo "Review and commit with --no-verify to bypass."
    exit 1
fi
HOOK
    chmod +x "$hooks_dir/pre-commit"
    pass "Installed pre-commit hook to $hooks_dir"
}

# ── Shell Alias Installer ─────────────────────────────────────────────────────
ALIAS_BLOCK_START="# -- sigil aliases (auto-installed) --"
ALIAS_BLOCK_END="# -- end sigil aliases --"

generate_alias_block() {
    cat <<'ALIASES'
# -- sigil aliases (auto-installed) --

gclone()   { sigil clone "$@"; }

safepip() {
    local pkg="$1"
    [ -z "$pkg" ] && { echo "Usage: safepip <package>"; return 1; }
    sigil pip "$pkg"
    local q_id=$(ls -t "$HOME/.sigil/quarantine/" 2>/dev/null | grep "pip_${pkg}" | head -1)
    if [ -n "$q_id" ]; then
        echo ""
        read -p "Install $pkg now? [y/N]: " confirm
        [[ "$confirm" =~ ^[Yy]$ ]] && pip install "$pkg" && sigil approve "$q_id" || echo "Package remains in quarantine. Run: sigil approve $q_id"
    fi
}

safenpm() {
    local pkg="$1"
    [ -z "$pkg" ] && { echo "Usage: safenpm <package>"; return 1; }
    sigil npm "$pkg"
    local q_id=$(ls -t "$HOME/.sigil/quarantine/" 2>/dev/null | grep "npm_" | head -1)
    if [ -n "$q_id" ]; then
        echo ""
        read -p "Install $pkg now? [y/N]: " confirm
        [[ "$confirm" =~ ^[Yy]$ ]] && npm install "$pkg" && sigil approve "$q_id" || echo "Package remains in quarantine. Run: sigil approve $q_id"
    fi
}

safefetch() { sigil fetch "$@"; }
alias audit='sigil scan'
alias audithere='sigil scan .'
alias qls='sigil list'

qapprove() {
    local latest=$(ls -t "$HOME/.sigil/quarantine/" 2>/dev/null | head -1)
    [ -n "$latest" ] && sigil approve "$latest" || echo "Nothing in quarantine"
}

qreject() {
    local latest=$(ls -t "$HOME/.sigil/quarantine/" 2>/dev/null | head -1)
    [ -n "$latest" ] && sigil reject "$latest" || echo "Nothing in quarantine"
}

# -- end sigil aliases --
ALIASES
}

detect_shell_rc() {
    local shell_name; shell_name=$(basename "${SHELL:-/bin/bash}")
    case "$shell_name" in
        zsh)  echo "$HOME/.zshrc" ;;
        bash) [ -f "$HOME/.bash_profile" ] && [[ "$OSTYPE" == darwin* ]] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ;;
        fish) echo "$HOME/.config/fish/config.fish" ;;
        *)    echo "$HOME/.bashrc" ;;
    esac
}

cmd_aliases() {
    local force="${1:-}"
    [ "$force" = "--print" ] && { generate_alias_block; return 0; }

    local rc_file; rc_file=$(detect_shell_rc)
    echo -e "${BOLD}Installing sigil shell aliases...${NC}"
    echo ""

    if grep -q "$ALIAS_BLOCK_START" "$rc_file" 2>/dev/null; then
        warn "Aliases already installed in $rc_file"
        read -r -p "Reinstall/update? [y/N]: " confirm
        [[ ! "$confirm" =~ ^[Yy]$ ]] && { info "Skipped. Use 'sigil aliases --print' to view aliases."; return 0; }
        sed -i.bak "/$ALIAS_BLOCK_START/,/$ALIAS_BLOCK_END/d" "$rc_file"
        log "Removed existing aliases"
    fi

    echo "" >> "$rc_file"
    generate_alias_block >> "$rc_file"
    pass "Aliases installed to $rc_file"
    echo ""
    echo -e "${BOLD}Available commands after reload:${NC}"
    echo -e "  ${CYAN}gclone <url>${NC}        — git clone with quarantine audit"
    echo -e "  ${CYAN}safepip <pkg>${NC}       — pip install with audit"
    echo -e "  ${CYAN}safenpm <pkg>${NC}       — npm install with audit"
    echo -e "  ${CYAN}safefetch <url>${NC}     — download with quarantine audit"
    echo -e "  ${CYAN}audithere${NC}           — scan current directory"
    echo -e "  ${CYAN}qls${NC}                 — quarantine status"
    echo -e "  ${CYAN}qapprove / qreject${NC}  — approve or reject most recent item"
    echo ""
    echo -e "Run ${BOLD}source $rc_file${NC} or open a new terminal to activate."
}

# ── Full Installer ─────────────────────────────────────────────────────────────
cmd_install() {
    echo -e "${BOLD}+----------------------------------------------+${NC}"
    echo -e "${BOLD}|         SIGIL — Full Installation            |${NC}"
    echo -e "${BOLD}|              by NOMARK                       |${NC}"
    echo -e "${BOLD}+----------------------------------------------+${NC}"
    echo ""

    local script_path; script_path=$(realpath "${BASH_SOURCE[0]}" 2>/dev/null || echo "$0")

    echo -e "${BOLD}Step 1: Installing sigil to /usr/local/bin${NC}"
    if [ -w "/usr/local/bin" ]; then
        cp "$script_path" /usr/local/bin/sigil && chmod +x /usr/local/bin/sigil
    else
        sudo cp "$script_path" /usr/local/bin/sigil && sudo chmod +x /usr/local/bin/sigil
    fi
    pass "Installed to /usr/local/bin/sigil"

    echo -e "\n${BOLD}Step 2: Initializing directories${NC}"
    ensure_dirs
    pass "Created ~/.sigil/{quarantine,approved,logs,reports}"

    echo -e "\n${BOLD}Step 3: Installing shell aliases${NC}"
    cmd_aliases

    echo -e "\n${BOLD}Step 4: Security scanners${NC}"
    local missing_scanners=()
    check_tool "semgrep"    || missing_scanners+=("semgrep")
    check_tool "bandit"     || missing_scanners+=("bandit")
    check_tool "trufflehog" || missing_scanners+=("trufflehog")
    check_tool "safety"     || missing_scanners+=("safety")

    if [ ${#missing_scanners[@]} -eq 0 ]; then
        pass "All recommended scanners already installed"
    else
        warn "Missing scanners: ${missing_scanners[*]}"
        echo ""
        read -r -p "Install available scanners via pip? [y/N]: " confirm
        if [[ "$confirm" =~ ^[Yy]$ ]]; then
            local pip_scanners=()
            for s in "${missing_scanners[@]}"; do
                case "$s" in
                    semgrep|bandit|safety) pip_scanners+=("$s") ;;
                    trufflehog) info "TruffleHog: install separately — brew install trufflehog" ;;
                esac
            done
            if [ ${#pip_scanners[@]} -gt 0 ]; then
                if pip install "${pip_scanners[@]}" 2>/dev/null; then
                    pass "Installed: ${pip_scanners[*]}"
                else
                    warn "Some installs failed — try manually"
                fi
            fi
        else
            info "Skipped. sigil works without them (built-in scanner always runs)."
        fi
    fi

    echo ""
    echo -e "${BOLD}+----------------------------------------------+${NC}"
    echo -e "${BOLD}|  ${GREEN}Installation complete${NC}${BOLD}                       |${NC}"
    echo -e "${BOLD}|  Open a new terminal, then try:              |${NC}"
    echo -e "${BOLD}|    ${CYAN}gclone <url>${NC}${BOLD}     — safe git clone           |${NC}"
    echo -e "${BOLD}|    ${CYAN}safepip <pkg>${NC}${BOLD}    — safe pip install          |${NC}"
    echo -e "${BOLD}|    ${CYAN}safenpm <pkg>${NC}${BOLD}    — safe npm install          |${NC}"
    echo -e "${BOLD}|    ${CYAN}audithere${NC}${BOLD}        — scan current dir          |${NC}"
    echo -e "${BOLD}+----------------------------------------------+${NC}"
}

# ── Authentication ─────────────────────────────────────────────────────────────
get_auth_header() {
    if [ -f "$TOKEN_FILE" ]; then
        local token
        token=$(cat "$TOKEN_FILE")
        if [ -n "$token" ]; then
            echo "Authorization: Bearer $token"
            return 0
        fi
    fi
    return 1
}

api_request() {
    local method="$1" endpoint="$2"
    shift 2
    local auth_args=()
    local auth_header
    if auth_header=$(get_auth_header); then
        auth_args=(-H "$auth_header")
    fi
    curl -s -X "$method" "${API_BASE_URL}${endpoint}" \
        -H "Content-Type: application/json" \
        "${auth_args[@]}" \
        "$@"
}

cmd_login() {
    local email="" password=""

    # Parse flags
    while [ $# -gt 0 ]; do
        case "$1" in
            --email)    email="$2"; shift 2 ;;
            --password) password="$2"; shift 2 ;;
            --email=*)    email="${1#*=}"; shift ;;
            --password=*) password="${1#*=}"; shift ;;
            *) shift ;;
        esac
    done

    # Prompt interactively if not provided via flags
    if [ -z "$email" ]; then
        printf "Email: "
        read -r email
    fi
    if [ -z "$password" ]; then
        printf "Password: "
        read -rs password
        echo ""
    fi

    if [ -z "$email" ] || [ -z "$password" ]; then
        fail "Email and password are required."
        echo "Usage: sigil login [--email <email>] [--password <password>]"
        exit 1
    fi

    log "Authenticating with Sigil API..."

    local json_body
    json_body=$(python3 -c "
import json, sys
print(json.dumps({'email': sys.argv[1], 'password': sys.argv[2]}))
" "$email" "$password" 2>/dev/null)

    if [ -z "$json_body" ]; then
        # Fallback: use printf with proper escaping
        local escaped_email escaped_password
        escaped_email=$(printf '%s' "$email" | sed 's/[\\"]/\\&/g')
        escaped_password=$(printf '%s' "$password" | sed 's/[\\"]/\\&/g')
        json_body="{\"email\": \"$escaped_email\", \"password\": \"$escaped_password\"}"
    fi

    local response
    response=$(api_request POST "/v1/auth/login" \
        -d "$json_body" \
        -w "\n%{http_code}" 2>/dev/null)

    local http_code body
    http_code=$(echo "$response" | tail -1)
    body=$(echo "$response" | sed '$d')

    if [ "$http_code" = "200" ]; then
        # Extract token — prefer python3/jq, fall back to grep
        local token=""
        if check_tool "python3"; then
            token=$(echo "$body" | python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null)
        elif check_tool "jq"; then
            token=$(echo "$body" | jq -r '.access_token' 2>/dev/null)
        else
            token=$(echo "$body" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
        fi

        if [ -z "$token" ]; then
            fail "Login succeeded but could not extract token from response."
            exit 1
        fi

        mkdir -p "$(dirname "$TOKEN_FILE")"
        echo "$token" > "$TOKEN_FILE"
        chmod 600 "$TOKEN_FILE"

        # Extract user info for display
        local user_email="" user_name=""
        if check_tool "python3"; then
            user_email=$(echo "$body" | python3 -c "import sys,json; u=json.load(sys.stdin).get('user',{}); print(u.get('email',''))" 2>/dev/null)
            user_name=$(echo "$body" | python3 -c "import sys,json; u=json.load(sys.stdin).get('user',{}); print(u.get('name',''))" 2>/dev/null)
        elif check_tool "jq"; then
            user_email=$(echo "$body" | jq -r '.user.email // empty' 2>/dev/null)
            user_name=$(echo "$body" | jq -r '.user.name // empty' 2>/dev/null)
        fi

        pass "Logged in successfully!"
        [ -n "$user_name" ] && info "Name:  $user_name"
        [ -n "$user_email" ] && info "Email: $user_email"
        info "Token stored in $TOKEN_FILE"
    elif [ "$http_code" = "401" ]; then
        fail "Invalid email or password."
        exit 1
    else
        fail "Login failed (HTTP $http_code)"
        [ -n "$body" ] && echo "$body"
        exit 1
    fi
}

cmd_logout() {
    if [ -f "$TOKEN_FILE" ]; then
        rm -f "$TOKEN_FILE"
        pass "Logged out. Token removed from $TOKEN_FILE"
    else
        info "No active session found (no token at $TOKEN_FILE)"
    fi
}

# ── Main ───────────────────────────────────────────────────────────────────────
main() {
    ensure_dirs
    local command="${1:-help}"
    shift || true

    case "$command" in
        install)    cmd_install ;;
        clone)      cmd_clone "${1:?Usage: sigil clone <git-url>}" ;;
        pip)        cmd_pip "${1:?Usage: sigil pip <package-name>}" ;;
        npm)        cmd_npm "${1:?Usage: sigil npm <package-name>}" ;;
        scan)       cmd_scan "${1:?Usage: sigil scan <path>}" ;;
        fetch)      cmd_fetch "${1:?Usage: sigil fetch <url>}" ;;
        approve)    cmd_approve "${1:?Usage: sigil approve <quarantine-id>}" ;;
        reject)     cmd_reject "${1:?Usage: sigil reject <quarantine-id>}" ;;
        list)       cmd_list ;;
        config)     cmd_config "$@" ;;
        hooks)      cmd_install_hooks "${1:-.}" ;;
        aliases)    cmd_aliases "$@" ;;
        login)      cmd_login "$@" ;;
        logout)     cmd_logout ;;
        help|*)
            echo -e "${BOLD}SIGIL — Automated Security Auditing for AI Agent Code${NC}"
            echo -e "by NOMARK"
            echo ""
            echo "Usage: sigil <command> [args]"
            echo ""
            echo "Setup:"
            echo "  install                Full install (binary + aliases + scanners)"
            echo "  aliases [--print]      Install shell aliases (or print them)"
            echo "  config [--init]        Show/initialize config"
            echo "  hooks [git-dir]        Install pre-commit hook"
            echo ""
            echo "Audit:"
            echo "  clone <git-url>        Clone repo into quarantine and audit"
            echo "  pip <package>          Download pip package and audit"
            echo "  npm <package>          Download npm package and audit"
            echo "  scan <path>            Audit an existing directory/file"
            echo "  fetch <url>            Download file/archive and audit"
            echo ""
            echo "Manage:"
            echo "  approve <id>           Move approved code out of quarantine"
            echo "  reject <id>            Delete quarantined code"
            echo "  list                   Show quarantine status"
            echo ""
            echo "Account:"
            echo "  login                  Authenticate with Sigil cloud API"
            echo "  logout                 Remove stored credentials"
            echo ""
            echo "Quick start:  sigil install"
            echo ""
            echo "A protective mark for every line of code."
            ;;
    esac
}

main "$@"
