Merge pull request 'Filter Codex TUI redraw noise in Slack output' (#8) from fix/codex-tui-noise-filter into main
Reviewed-on: #8
This commit was merged in pull request #8.
This commit is contained in:
@@ -15,18 +15,67 @@ _ANSI_ESCAPE_RE = re.compile(
|
|||||||
# ESC 문자가 유실된 상태로 남는 CSI 토큰(예: [38;2;153;153;153m) 제거.
|
# ESC 문자가 유실된 상태로 남는 CSI 토큰(예: [38;2;153;153;153m) 제거.
|
||||||
_BROKEN_CSI_RE = re.compile(r"\[(?:[0-9;<=>?]|(?:\d+;))*\d*[A-Za-z]")
|
_BROKEN_CSI_RE = re.compile(r"\[(?:[0-9;<=>?]|(?:\d+;))*\d*[A-Za-z]")
|
||||||
|
|
||||||
|
# ESC(B 같은 문자셋 지정 시퀀스가 ESC 유실 후 남긴 토큰 제거.
|
||||||
|
_BROKEN_CHARSET_RE = re.compile(r"[\(\)][B0]")
|
||||||
|
|
||||||
# C0 제어문자 중 개행/탭/캐리지리턴을 제외하고 제거.
|
# C0 제어문자 중 개행/탭/캐리지리턴을 제외하고 제거.
|
||||||
_CONTROL_RE = re.compile(r"[\x00-\x08\x0B-\x1F\x7F]")
|
_CONTROL_RE = re.compile(r"[\x00-\x08\x0B-\x1F\x7F]")
|
||||||
|
|
||||||
|
_CODEX_TUI_LINE_PATTERNS = (
|
||||||
|
re.compile(r"openai codex", re.IGNORECASE),
|
||||||
|
re.compile(r"/model to change", re.IGNORECASE),
|
||||||
|
re.compile(r"^\s*[|│]\s*model:", re.IGNORECASE),
|
||||||
|
re.compile(r"^\s*[|│]\s*directory:", re.IGNORECASE),
|
||||||
|
re.compile(r"^\s*tip:\s*new\s+codex", re.IGNORECASE),
|
||||||
|
re.compile(r"find and fix a bug in @filename", re.IGNORECASE),
|
||||||
|
re.compile(r"\?\s*for shortcuts", re.IGNORECASE),
|
||||||
|
re.compile(r"\bcontext left\b", re.IGNORECASE),
|
||||||
|
re.compile(r"^\s*\w*odex\]\s+\d+:", re.IGNORECASE),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_box_border_line(line: str) -> bool:
|
||||||
|
stripped = line.strip()
|
||||||
|
if not stripped:
|
||||||
|
return False
|
||||||
|
# ESC(B 유실 잔재가 앞에 붙는 경우(예: =╭────╮)도 border로 본다.
|
||||||
|
stripped = stripped.lstrip("= ")
|
||||||
|
return all(ch in "╭╮╰╯─│┌┐└┘├┤┬┴┼" for ch in stripped)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_box_empty_line(line: str) -> bool:
|
||||||
|
stripped = line.strip()
|
||||||
|
if not stripped:
|
||||||
|
return False
|
||||||
|
return bool(re.fullmatch(r"[│|]\s+[│|]", stripped))
|
||||||
|
|
||||||
|
|
||||||
|
def _is_noise_line(line: str) -> bool:
|
||||||
|
if _is_box_border_line(line):
|
||||||
|
return True
|
||||||
|
if _is_box_empty_line(line):
|
||||||
|
return True
|
||||||
|
return any(pattern.search(line) for pattern in _CODEX_TUI_LINE_PATTERNS)
|
||||||
|
|
||||||
|
|
||||||
def clean_terminal_output(text: str) -> str:
|
def clean_terminal_output(text: str) -> str:
|
||||||
"""Slack 전송용 텍스트를 정리한다."""
|
"""Slack 전송용 텍스트를 정리한다."""
|
||||||
cleaned = _ANSI_ESCAPE_RE.sub("", text)
|
cleaned = _ANSI_ESCAPE_RE.sub("", text)
|
||||||
cleaned = _BROKEN_CSI_RE.sub("", cleaned)
|
cleaned = _BROKEN_CSI_RE.sub("", cleaned)
|
||||||
|
cleaned = _BROKEN_CHARSET_RE.sub("", cleaned)
|
||||||
cleaned = cleaned.replace("\r", "")
|
cleaned = cleaned.replace("\r", "")
|
||||||
cleaned = _CONTROL_RE.sub("", cleaned)
|
cleaned = _CONTROL_RE.sub("", cleaned)
|
||||||
|
|
||||||
# 제어 코드 제거 후 남는 빈 줄을 축소한다.
|
lines: list[str] = []
|
||||||
lines = [line.rstrip() for line in cleaned.splitlines()]
|
for raw_line in cleaned.splitlines():
|
||||||
|
line = raw_line.rstrip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
if _is_noise_line(line):
|
||||||
|
continue
|
||||||
|
if lines and lines[-1] == line:
|
||||||
|
continue
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
compact = "\n".join(lines).strip()
|
compact = "\n".join(lines).strip()
|
||||||
return compact
|
return compact
|
||||||
|
|||||||
@@ -16,3 +16,28 @@ def test_clean_terminal_output_removes_broken_csi_tokens():
|
|||||||
def test_clean_terminal_output_preserves_plain_text():
|
def test_clean_terminal_output_preserves_plain_text():
|
||||||
raw = "line1\nline2"
|
raw = "line1\nline2"
|
||||||
assert clean_terminal_output(raw) == "line1\nline2"
|
assert clean_terminal_output(raw) == "line1\nline2"
|
||||||
|
|
||||||
|
|
||||||
|
def test_clean_terminal_output_removes_broken_charset_tokens():
|
||||||
|
raw = "=(Bhello(B"
|
||||||
|
assert clean_terminal_output(raw) == "=hello"
|
||||||
|
|
||||||
|
|
||||||
|
def test_clean_terminal_output_filters_codex_tui_redraw_noise():
|
||||||
|
raw = """=(B╭─────────────────────────────────────────────╮(B
|
||||||
|
│ >_ OpenAI Codex (v0.101.0) │(B
|
||||||
|
│ model: gpt-5.3-codex /model to change │(B
|
||||||
|
│ directory: ~/repos/The-Ouroboros │(B
|
||||||
|
╰─────────────────────────────────────────────╯(B
|
||||||
|
Tip: New Codex is included in your plan for free
|
||||||
|
› Find and fix a bug in @filename
|
||||||
|
? for shortcuts
|
||||||
|
100% context left
|
||||||
|
Assistant: 작업을 시작합니다.
|
||||||
|
"""
|
||||||
|
assert clean_terminal_output(raw) == "Assistant: 작업을 시작합니다."
|
||||||
|
|
||||||
|
|
||||||
|
def test_clean_terminal_output_removes_box_empty_line_noise():
|
||||||
|
raw = "│ │\n› Explain this codebase\n"
|
||||||
|
assert clean_terminal_output(raw) == "› Explain this codebase"
|
||||||
|
|||||||
Reference in New Issue
Block a user