"use strict";
/**
 * Common CLI tool patterns for response detection
 * Shared between response-poller.ts and API routes
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = exports.CLAUDE_SESSION_ERROR_PATTERNS = exports.VIBE_LOCAL_THINKING_PATTERN = exports.VIBE_LOCAL_PROMPT_PATTERN = exports.OPENCODE_SKIP_PATTERNS = exports.OPENCODE_SEPARATOR_PATTERN = exports.CLAUDE_SELECTION_LIST_FOOTER = exports.OPENCODE_SELECTION_LIST_PATTERN = exports.OPENCODE_PROCESSING_INDICATOR = exports.OPENCODE_RESPONSE_COMPLETE = exports.OPENCODE_LOADING_PATTERN = exports.OPENCODE_THINKING_PATTERN = exports.OPENCODE_PROMPT_AFTER_RESPONSE = exports.OPENCODE_PROMPT_PATTERN = exports.GEMINI_THINKING_PATTERN = exports.GEMINI_PROMPT_PATTERN = exports.MAX_PASTED_TEXT_RETRIES = exports.PASTED_TEXT_DETECT_DELAY = exports.PASTED_TEXT_PATTERN = exports.CODEX_SEPARATOR_PATTERN = exports.CODEX_PROMPT_PATTERN = exports.CLAUDE_TRUST_DIALOG_PATTERN = exports.CLAUDE_SEPARATOR_PATTERN = exports.CLAUDE_PROMPT_PATTERN = exports.CODEX_THINKING_PATTERN = exports.CLAUDE_THINKING_PATTERN = exports.CLAUDE_SPINNER_CHARS = void 0;
exports.detectThinking = detectThinking;
exports.getCliToolPatterns = getCliToolPatterns;
exports.stripAnsi = stripAnsi;
exports.stripBoxDrawing = stripBoxDrawing;
exports.buildDetectPromptOptions = buildDetectPromptOptions;
const logger_1 = require("../../lib/logger");
const logger = (0, logger_1.createLogger)('cli-patterns');
/**
 * Claude CLI spinner characters (expanded set)
 * These are shown when Claude is thinking/processing
 */
exports.CLAUDE_SPINNER_CHARS = [
    '✻', '✽', '⏺', '·', '∴', '✢', '✳', '✶',
    '⦿', '◉', '●', '○', '◌', '◎', '⊙', '⊚',
    '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏', // Braille spinner
];
/**
 * Claude thinking pattern
 * Matches spinner character followed by activity text ending with …
 * The text can contain spaces (e.g., "Verifying implementation (dead code detection)…")
 *
 * Alternative 2: "esc to interrupt" status bar text (Issue #188)
 * Claude Code shows "esc to interrupt" in the terminal status bar during active processing.
 * Previous pattern required closing paren `to interrupt\)` matching `(esc to interrupt)`,
 * but Claude Code v2.x status bar format uses `· esc to interrupt ·` without parens.
 * Updated to match `esc to interrupt` which covers both formats.
 */
exports.CLAUDE_THINKING_PATTERN = new RegExp(`[${exports.CLAUDE_SPINNER_CHARS.join('')}]\\s+.+…|esc to interrupt`, 'm');
/**
 * Codex thinking pattern
 * Matches activity indicators like "• Planning", "• Searching", etc.
 * T1.1: Extended to include "Ran" and "Deciding"
 */
exports.CODEX_THINKING_PATTERN = /•\s*(Planning|Searching|Exploring|Running|Thinking|Working|Reading|Writing|Analyzing|Ran|Deciding)/m;
/**
 * Claude prompt pattern (waiting for input)
 * Supports both legacy '>' and new '❯' (U+276F) prompt characters
 * Issue #132: Also matches prompts with recommended commands (e.g., "❯ /work-plan")
 *
 * Matches:
 * - Empty prompt: "❯ " or "> "
 * - Prompt with command: "❯ /work-plan" or "> npm install"
 */
exports.CLAUDE_PROMPT_PATTERN = /^[>❯](\s*$|\s+\S)/m;
/**
 * Claude separator pattern
 */
exports.CLAUDE_SEPARATOR_PATTERN = /^─{10,}$/m;
/**
 * Claude trust dialog pattern (Issue #201)
 *
 * Matches the "Quick safety check" dialog displayed by Claude CLI v2.x
 * when accessing a workspace for the first time.
 *
 * Intentionally uses partial matching (no line-start anchor ^):
 * Other pattern constants (CLAUDE_PROMPT_PATTERN, CLAUDE_SEPARATOR_PATTERN, etc.)
 * use line-start anchors (^), but this pattern needs to match at any position
 * within the tmux output buffer because the dialog text may appear after
 * tmux padding or other output. (SF-001)
 */
exports.CLAUDE_TRUST_DIALOG_PATTERN = /Yes, I trust this folder/m;
/**
 * Codex prompt pattern
 * T1.2: Improved to detect empty prompts as well
 */
exports.CODEX_PROMPT_PATTERN = /^›\s*/m;
/**
 * Codex separator pattern
 */
exports.CODEX_SEPARATOR_PATTERN = /^─.*Worked for.*─+$/m;
/**
 * Pasted text pattern
 *
 * Claude CLI displays this when it detects multi-line text paste in the
 * ink-based TextInput. The pattern matches the folded display format.
 *
 * @example "[Pasted text #1 +46 lines]"
 * @see Issue #212, #163
 * @designNote PASTE-001: Pattern matches the start of the indicator only.
 *   The line count (+XX lines) is variable, so we match the fixed prefix
 *   to minimize false negatives. False positive risk is low because
 *   "[Pasted text #" is a unique format generated by Claude CLI's ink renderer.
 * @designNote PASTE-001-FP (SF-S4-002): When used in skipPatterns,
 *   line-level matching could filter legitimate response lines if Claude's
 *   answer text happens to contain "[Pasted text #". This is unlikely and
 *   acceptable -- only the affected line would be lost.
 */
exports.PASTED_TEXT_PATTERN = /\[Pasted text #\d+/;
/**
 * Pasted text detection delay (milliseconds)
 *
 * Wait time after sendKeys for tmux buffer to reflect [Pasted text] display.
 *
 * @see Issue #212
 * @designNote PASTE-002: 500ms is the empirically measured time for
 *   Claude CLI's ink rendering to complete. capturePane({ startLine: -10 })
 *   reads only the last 10 lines since [Pasted text] appears in the most
 *   recent few lines.
 */
exports.PASTED_TEXT_DETECT_DELAY = 500;
/**
 * Pasted text detection max retries
 *
 * @see Issue #212
 * @designNote PASTE-003: 3 retries x 500ms = max 1500ms additional delay.
 *   Typically resolves on the first attempt (+500ms).
 */
exports.MAX_PASTED_TEXT_RETRIES = 3;
/**
 * Gemini interactive REPL prompt pattern
 * Gemini CLI shows a `>` or `❯` prompt when waiting for user input in interactive mode.
 *
 * Two branches (Issue #386):
 * - Branch 1: `^[>❯]\s*$` -- bare prompt character (empty input line)
 * - Branch 2: `^\s*[>❯]\s+Type your message.*$` -- new-format prompt with placeholder text
 *   (e.g., " >   Type your message or @path/to/file"). Leading whitespace is allowed
 *   because tmux capture-pane output may include padding.
 *
 * Branch 2 requires "Type your message" after the indicator to avoid false positives
 * on quoted response lines (e.g., "> some quoted text").
 *
 * @see CLAUDE_PROMPT_PATTERN for similar dual-format matching approach
 */
// [S4-5] /g flag prohibited: would make test() stateful
exports.GEMINI_PROMPT_PATTERN = /^[>❯]\s*$|^\s*[>❯]\s+Type your message.*$/m;
/**
 * Gemini thinking/processing pattern
 * Gemini CLI shows braille spinner characters and status text while processing.
 */
exports.GEMINI_THINKING_PATTERN = /[\u2800-\u28FF]|Thinking\.\.\./;
/**
 * OpenCode prompt pattern (Issue #379)
 * OpenCode TUI shows "Ask anything..." in the input area when waiting for user input.
 * Unlike Claude/Codex (which use > or ❯), OpenCode uses a text-based prompt indicator.
 */
exports.OPENCODE_PROMPT_PATTERN = /Ask anything\.\.\./;
/**
 * OpenCode prompt pattern after response completion (Issue #379)
 * Shows "tab agents  ctrl+p commands" in the TUI status bar after a response finishes.
 * Used as extraction stop condition in response-poller.ts [D2-003].
 */
exports.OPENCODE_PROMPT_AFTER_RESPONSE = /tab agents\s+ctrl\+p commands/;
/**
 * OpenCode thinking/processing pattern (Issue #379)
 * OpenCode TUI shows "Thinking:" prefix while the Ollama model is generating a response.
 * Used by detectThinking() to determine if the tool is actively processing.
 */
exports.OPENCODE_THINKING_PATTERN = /Thinking:/;
/**
 * OpenCode loading indicator pattern (Issue #379)
 * Shows a series of 4+ filled square characters (U+2B1D) during initial loading/model warm-up.
 * Filtered from response extraction via OPENCODE_SKIP_PATTERNS.
 */
exports.OPENCODE_LOADING_PATTERN = /\u2B1D{4,}/;
/**
 * OpenCode response completion pattern (Issue #379)
 * Matches the action summary line: "&#x25A3; {Action} · model" with optional timing "· Ns".
 * (U+25A3 square + action word + middle dot + model name [+ middle dot + timing]).
 * Action can be "Build", "Compaction", or other OpenCode action names.
 * Short responses may omit the timing portion (e.g., "▣ Build · qwen3.5:27b").
 * This is the primary completion signal for OpenCode [D2-002].
 */
exports.OPENCODE_RESPONSE_COMPLETE = /\u25A3\s+\w+\s+\u00b7\s+\S+(?:\s+\u00b7\s+(?:[\d]+h\s*)?(?:[\d]+m\s*)?[\d.]+s)?/;
/**
 * OpenCode processing indicator pattern (Issue #379)
 * Shows "esc interrupt" in the TUI status bar during active model processing.
 * Filtered from response extraction via OPENCODE_SKIP_PATTERNS.
 */
exports.OPENCODE_PROCESSING_INDICATOR = /esc interrupt/;
/**
 * OpenCode TUI selection list pattern (Issue #473)
 * Detects the fuzzy-search selection list overlay in OpenCode TUI
 * (e.g., /models, /providers, /connect commands).
 *
 * Matches header lines of selection overlays. Known headers:
 *   "              Select model                                     esc"
 *   "              Connect a provider                               esc"
 */
exports.OPENCODE_SELECTION_LIST_PATTERN = /^\s*(Select\s+(model|provider)|Connect\s+a\s+provider)/m;
/**
 * Claude CLI selection list footer pattern
 * Detects Claude CLI's interactive selection prompts that require
 * arrow key navigation and Enter to select/toggle.
 *
 * Matches footer instruction lines (two known variants):
 *   "Enter to select · Tab/Arrow keys to navigate · Esc to cancel"
 *   "Enter to select · ↑/↓ to navigate · n to add notes · Esc to cancel"
 */
exports.CLAUDE_SELECTION_LIST_FOOTER = /Enter\s+to\s+select\s+.*to\s+navigate/;
/**
 * OpenCode TUI separator pattern (Issue #379)
 * Matches lines composed entirely of box-drawing / TUI decoration characters.
 * Covers: vertical lines (U+2503), box corners, horizontal lines, and other TUI elements.
 */
exports.OPENCODE_SEPARATOR_PATTERN = /^[\u2503\u2579\u25A3\u2580\u2500\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C]+$/;
/**
 * OpenCode skip patterns for response cleaning (Issue #379)
 * Lines matching any of these patterns are filtered from extracted responses.
 * Includes: TUI separators, loading indicators, Build summary prefix,
 * status bar prompts, processing indicators, input prompt, and pasted text markers.
 */
exports.OPENCODE_SKIP_PATTERNS = [
    exports.OPENCODE_SEPARATOR_PATTERN,
    exports.OPENCODE_LOADING_PATTERN,
    /^Build\s+/,
    exports.OPENCODE_PROMPT_AFTER_RESPONSE,
    exports.OPENCODE_PROCESSING_INDICATOR,
    exports.OPENCODE_PROMPT_PATTERN,
    exports.PASTED_TEXT_PATTERN,
];
/**
 * Vibe Local prompt pattern
 * vibe-local (vibe-coder) shows `ctx:N% ❯` prompt when waiting for user input.
 * The prompt line includes a context usage percentage prefix.
 * Examples: "ctx:9% ❯", "ctx:30% ❯", "ctx:9% ❯ /model"
 */
exports.VIBE_LOCAL_PROMPT_PATTERN = /ctx:\d+%\s*[>❯]/m;
/**
 * Vibe Local thinking/processing pattern
 * vibe-local shows spinner characters and status text while processing.
 * Matches braille spinners, "Thinking", and tool execution indicators.
 */
exports.VIBE_LOCAL_THINKING_PATTERN = /[\u2800-\u28FF]|Thinking|⠋|⠙|⠹|⠸|⠼|⠴|⠦|⠧|⠇|⠏|Running|Executing/;
/**
 * Detect if CLI tool is showing "thinking" indicator
 */
function detectThinking(cliToolId, content) {
    const log = logger.withContext({ cliToolId });
    log.debug('detectThinking:check', { contentLength: content.length });
    let result;
    switch (cliToolId) {
        case 'claude':
            result = exports.CLAUDE_THINKING_PATTERN.test(content);
            break;
        case 'codex':
            result = exports.CODEX_THINKING_PATTERN.test(content);
            break;
        case 'gemini':
            result = exports.GEMINI_THINKING_PATTERN.test(content);
            break;
        case 'vibe-local':
            result = exports.VIBE_LOCAL_THINKING_PATTERN.test(content);
            break;
        case 'opencode':
            result = exports.OPENCODE_THINKING_PATTERN.test(content);
            break;
        default:
            result = exports.CLAUDE_THINKING_PATTERN.test(content);
    }
    log.debug('detectThinking:result', { isThinking: result });
    return result;
}
/**
 * Get CLI tool patterns for response extraction
 */
function getCliToolPatterns(cliToolId) {
    switch (cliToolId) {
        case 'claude':
            return {
                promptPattern: exports.CLAUDE_PROMPT_PATTERN,
                separatorPattern: exports.CLAUDE_SEPARATOR_PATTERN,
                thinkingPattern: exports.CLAUDE_THINKING_PATTERN,
                skipPatterns: [
                    /^─{10,}$/, // Separator lines
                    /^[>❯]\s*$/, // Prompt line (legacy '>' and new '❯')
                    exports.CLAUDE_THINKING_PATTERN, // Thinking indicators
                    /^\s*[⎿⏋]\s+Tip:/, // Tip lines
                    /^\s*Tip:/, // Tip lines
                    /^\s*\?\s*for shortcuts/, // Shortcuts hint
                    /to interrupt\)/, // Part of "esc to interrupt" message
                    exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines] (Issue #212)
                ],
            };
        case 'codex':
            return {
                promptPattern: exports.CODEX_PROMPT_PATTERN,
                separatorPattern: exports.CODEX_SEPARATOR_PATTERN,
                thinkingPattern: exports.CODEX_THINKING_PATTERN,
                skipPatterns: [
                    /^─.*─+$/, // Separator lines
                    /^›\s*$/, // Empty prompt line
                    /^›\s+(Implement|Find and fix|Type)/, // New prompt suggestions
                    exports.CODEX_THINKING_PATTERN, // Activity indicators
                    /^\s*\d+%\s+context left/, // Context indicator
                    /^\s*for shortcuts$/, // Shortcuts hint
                    /╭─+╮/, // Box drawing (top)
                    /╰─+╯/, // Box drawing (bottom)
                    // T1.3: Additional skip patterns for Codex
                    /•\s*Ran\s+/, // Command execution lines
                    /^\s*└/, // Tree output (completion indicator)
                    /^\s*│/, // Continuation lines
                    /\(.*esc to interrupt\)/, // Interrupt hint
                    exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines] (Issue #212, defensive)
                ],
            };
        case 'gemini':
            return {
                promptPattern: exports.GEMINI_PROMPT_PATTERN,
                separatorPattern: /^[─━]{3,}$/m,
                thinkingPattern: exports.GEMINI_THINKING_PATTERN,
                skipPatterns: [
                    exports.GEMINI_PROMPT_PATTERN, // Prompt line (DRY: shared with GEMINI_PROMPT_PATTERN)
                    exports.GEMINI_THINKING_PATTERN, // Thinking indicators
                    /^\s*$/, // Empty lines
                    /Gemini\s+\d+\.\d+/, // Version line
                    exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines]
                ],
            };
        case 'vibe-local':
            return {
                promptPattern: exports.VIBE_LOCAL_PROMPT_PATTERN,
                separatorPattern: /^[·]{10,}$/m, // vibe-local uses middle dot separators
                thinkingPattern: exports.VIBE_LOCAL_THINKING_PATTERN,
                skipPatterns: [
                    exports.VIBE_LOCAL_PROMPT_PATTERN, // Prompt line (ctx:N% ❯)
                    exports.VIBE_LOCAL_THINKING_PATTERN, // Thinking indicators
                    /^\s*$/, // Empty lines
                    /vibe-local|vibe-coder/, // Version/banner lines
                    /ctx:\s*\d+%/, // Context usage indicator
                    /Model\s+\w/, // Model info line
                    /Engine\s+\w/, // Engine info line
                    /Mode\s+/, // Mode info line
                    /RAM\s+/, // RAM info line
                    /CWD\s+/, // Working directory line
                    /^[·]{10,}$/, // Middle dot separator lines
                    /✦\s*Ready/, // Status bar "Ready" indicator
                    /ESC:\s*stop/, // Status bar "ESC: stop" hint
                    exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines]
                ],
            };
        case 'opencode':
            return {
                promptPattern: exports.OPENCODE_PROMPT_PATTERN,
                separatorPattern: exports.OPENCODE_SEPARATOR_PATTERN,
                thinkingPattern: exports.OPENCODE_THINKING_PATTERN,
                skipPatterns: [...exports.OPENCODE_SKIP_PATTERNS],
            };
        default:
            // Default to Claude patterns
            return getCliToolPatterns('claude');
    }
}
/**
 * Strip ANSI escape codes from a string.
 * Optimized version at module level for performance.
 *
 * Covers:
 * - SGR sequences: ESC[Nm (colors, bold, underline, etc.)
 * - OSC sequences: ESC]...BEL (window title, hyperlinks, etc.)
 * - CSI sequences: ESC[...letter (cursor movement, erase, etc.)
 *
 * Known limitations (SEC-002):
 * - 8-bit CSI (0x9B): C1 control code form of CSI is not covered
 * - DEC private modes: ESC[?25h and similar are not covered
 * - Character set switching: ESC(0, ESC(B are not covered
 * - Some RGB color forms: ESC[38;2;r;g;bm may not be fully matched
 *
 * In practice, tmux capture-pane output rarely contains these sequences,
 * so the risk is low. Future consideration: adopt the `strip-ansi` npm package
 * for more comprehensive coverage.
 */
const ANSI_PATTERN = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\[[0-9;]*m/g;
function stripAnsi(str) {
    return str.replace(ANSI_PATTERN, '');
}
/**
 * Strip box-drawing border characters from CLI output.
 * Gemini CLI wraps Action Required prompts in ╭─╮│╰─╯ borders.
 * Removes │ (U+2502) prefix/suffix and border-only lines (╭╮╰╯─).
 *
 * @param str - Input string (typically after stripAnsi())
 * @returns String with box-drawing borders removed
 */
function stripBoxDrawing(str) {
    return str.split('\n').map(line => {
        // Remove border-only lines (╭──╮, ╰──╯, │ only, ┃ only, ╹▀▀▀, █ scrollbar, etc.)
        // U+2502 │ (light vertical), U+2503 ┃ (heavy vertical - OpenCode TUI)
        // U+2579 ╹ (heavy up), U+2580 ▀ (upper half block - OpenCode separator)
        // U+2588 █ (full block - OpenCode scrollbar)
        if (/^[\u2502\u2503\u256D\u256E\u256F\u2570\u2500\u2579\u2580\u2588\s]+$/.test(line))
            return '';
        // Strip leading whitespace + │/┃ + optional space, trailing space + │/┃/█
        // OpenCode TUI adds 2-space padding before ┃ borders (e.g., "  ┃  content")
        // OpenCode scrollbar █ appears at end of content lines
        return line.replace(/^\s*[\u2502\u2503]\s?/, '').replace(/\s*[\u2502\u2503\u2588]$/, '');
    }).join('\n');
}
/**
 * Error patterns that indicate a Claude session failed to start properly
 * Used by isSessionHealthy() to detect broken sessions (MF-001: SRP)
 * Style: readonly + as const for type safety (SF-S2-001: follows response-poller.ts precedent)
 *
 * SEC-SF-004: Pattern maintenance process:
 * - When Claude CLI is updated, verify that error messages still match these patterns.
 * - Test procedure: Intentionally trigger each error condition (e.g., nested session launch)
 *   and confirm the error message is captured by the patterns.
 * - If Claude CLI introduces localized error messages, add locale-aware patterns or
 *   consider switching to exit code-based detection as a more robust alternative.
 * - Pattern additions should be accompanied by corresponding test cases in
 *   claude-session.test.ts.
 *
 * C-S3-001: Codex/Gemini monitoring note:
 * These patterns are currently Claude-specific. If Codex or Gemini exhibit similar
 * "nested session" or startup failure behaviors, analogous error patterns should be
 * added to their respective tool configurations (codex.ts, gemini.ts) rather than
 * extending these arrays, to maintain SRP per CLI tool type.
 */
exports.CLAUDE_SESSION_ERROR_PATTERNS = [
    'Claude Code cannot be launched inside another Claude Code session',
];
/**
 * Regex patterns for Claude session errors requiring context matching
 * Used by isSessionHealthy() for multi-condition error detection (MF-001: SRP)
 * Style: readonly + as const for type safety (SF-S2-001: follows response-poller.ts precedent)
 *
 * SEC-SF-004: See CLAUDE_SESSION_ERROR_PATTERNS JSDoc for pattern maintenance process.
 */
exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = [
    /^Error:.*Claude Code/,
];
/**
 * Build DetectPromptOptions for a given CLI tool.
 * Centralizes cliToolId-to-options mapping logic (DRY - MF-001).
 *
 * prompt-detector.ts remains CLI tool independent (Issue #161 principle);
 * this function lives in cli-patterns.ts which already depends on CLIToolType.
 *
 * [Future extension memo (C-002)]
 * If CLI tool count grows significantly (currently 5), consider migrating
 * to a CLIToolConfig registry pattern where tool-specific settings
 * (including promptDetectionOptions) are managed in a Record<CLIToolType, CLIToolConfig>.
 * Migration threshold: 6th tool addition triggers registry pattern migration [D1-003].
 *
 * @param cliToolId - CLI tool identifier
 * @returns DetectPromptOptions for the tool, or undefined for default behavior
 */
function buildDetectPromptOptions(cliToolId) {
    if (cliToolId === 'claude') {
        return { requireDefaultIndicator: false };
    }
    // [D2-006] OpenCode prompt "Ask anything..." does not use standard indicators (> / ❯),
    // so requireDefaultIndicator must be false to avoid missing prompt detection.
    if (cliToolId === 'opencode') {
        return { requireDefaultIndicator: false };
    }
    return undefined; // Default behavior (requireDefaultIndicator = true)
}
