// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package prompt import ( "regexp" "strings" "git.stormux.org/storm/fenrir/internal/screen" ) // Unlike Detector, ShellDetector excludes passwords, questions and completion // messages. It observes raw screens, independently of application speech filters. type ShellDetector struct { previous map[string]int terminal int } var shellPattern = regexp.MustCompile(`^(?:[$#>]|\[[^\r\n]+\]\s*[$#>]|[a-zA-Z0-9._-]+[$#>]|[a-zA-Z0-9._-]+@[^\s<>]+.*[$#>])\s*$`) func (d *ShellDetector) Observe(next screen.Snapshot) bool { if !next.Active || next.Rows <= 0 || next.Cols <= 0 || len(next.Cells) != next.Rows*next.Cols { return false } counts := make(map[string]int) for row := range next.Rows { counts[strings.TrimSpace(next.Text(row))]++ } text := "" if next.Y >= 0 && next.Y < next.Rows { text = strings.TrimSpace(next.Text(next.Y)) } fresh := d.previous != nil && d.terminal == next.Terminal && shellPattern.MatchString(text) && counts[text] > d.previous[text] d.previous, d.terminal = counts, next.Terminal return fresh }