-
Notifications
You must be signed in to change notification settings - Fork 0
Dev Guide claude disable feedback
Claude Code 가 주기적으로 띄우는 "How is Claude doing this session?" 피드백 권유 문구를 화면에서 조용히 가리는 아주 작은 플러그인입니다. 단일 훅(server_filter_rows)만 쓰는 pytmux 플러그인 중 가장 작은 예제라, 이 문서는 "최소 플러그인" 워크드 예제(worked example)를 겸합니다. 훅 패턴을 처음 익힐 때 출발점으로 삼기 좋습니다.
- 무엇을 하는가
- UI 가 없다
- 어떤 훅 하나를 쓰는가
- 핵심 설계 결정: 키 주입이 아니라 표시 필터
- 코드 구조 따라 읽기
- 왜 비모달 오버레이엔 표시-필터 > 키-주입인가
- delete-to-disable
- 테스트 방법
- 최소 플러그인 체크리스트
- 관련 문서
Claude Code 는 두 가지 피드백 표면을 화면에 띄웁니다. 이 플러그인은 둘 다 폭을 유지한 채 공백으로 덮어 절대 보이지 않게 만듭니다.
-
시작 팁 —
Tip: Use /feedback to help us improve! -
세션 종료 배너 —
How is Claude doing this session? (optional)와 그 아래 평가 옵션 줄(1: Bad … 0: Dismiss)
"폭을 유지한 채 공백으로"가 중요합니다. 줄을 통째로 지우면 그 아래 행들이 위로 밀려 화면 레이아웃이 흔들립니다. 같은 폭의 공백으로 바꾸면 자리만 비고 레이아웃은 그대로라, 사용자 눈에는 "그 줄이 원래 없었던 것"처럼 보입니다.
이 플러그인은 자기 자신의 UI 가 전혀 없습니다. 팝업도, 상태줄 배지도, 키 바인딩도 만들지 않습니다. 하는 일은 오로지 "다른 무언가(피드백 문구)를 화면에서 제거"하는 것뿐이라, 보여줄 스크린샷도 없습니다. 정상 동작의 증거는 "아무것도 안 보임"입니다.
이 플러그인이 구현하는 레지스트리 훅은 server_filter_rows 단 하나입니다.
def server_filter_rows(self, server, pane, rows):
...
return rows코어는 패널의 render 결과(행 목록)를 클라이언트로 전송하기 직전에 등록된 모든 플러그인의 server_filter_rows 로 흘려보냅니다. 각 플러그인은 행 목록을 받아 (변형하거나 그대로) 돌려주고, 코어는 그 결과를 다음 플러그인 또는 클라이언트로 넘깁니다. 즉 이 훅은 표시 직전 마지막 변형 지점입니다.
핵심 계약:
- 이 플러그인이 없으면(디렉토리 삭제 또는 비활성) 코어는 행을 변형 없이 그대로 전송합니다. 코어는 플러그인을 직접 import 하지 않고 레지스트리 훅으로만 닿습니다.
- 따라서 기능 전체가 이 디렉토리 한 곳에 담깁니다.
피드백 배너를 없애는 방법은 두 가지를 생각할 수 있습니다.
-
(A) 키 주입 — 배너가 뜨면 패널에 Esc(또는
0: Dismiss키)를 자동으로 보내 닫는다. - (B) 표시 필터 — 배너에 해당하는 render 행만 공백으로 덮어 표시만 가린다. 패널 상태는 건드리지 않는다.
이 플러그인은 (B) 표시 필터를 택합니다. 이유:
이 배너는 컴포저(입력 줄) 위에 뜨는 비모달(non-modal) 오버레이입니다. 닫지 않아도 작업을 막지 않습니다. 그런데 단일 Esc 를 주입하면 배너를 Dismiss 하는 대신 작동 중인 턴(busy turn)을 종종 interrupt 해버립니다. 키 주입은 화면 상태와 레이스(race)가 나서 사용자의 진행 중인 작업을 깨뜨릴 수 있습니다. 표시 필터는 패널의 실제 입력/실행 상태를 일절 건드리지 않으므로 안전합니다.
즉 "보이게 하지 않는다"는 목표를 상태 변경 없이 표시 레이어에서만 달성합니다.
모듈은 ~90줄, textual/rich 같은 무거운 의존을 import 하지 않습니다(서버 프로세스도 같은 코드를 로드하고, 행 변형은 순수 문자열 연산뿐입니다). 구성은 세 덩어리입니다.
# 문구 중 식별성 높은 부분으로만 매칭해 오탐을 피한다.
_FEEDBACK_TIP_MARK = "/feedback to help us improve"
def _blank_feedback_tip(rows):
out = None
for i, row in enumerate(rows):
text = "".join(t for t, _ in row)
if _FEEDBACK_TIP_MARK in text:
if out is None:
out = list(rows)
out[i] = [[" " * len(t), st] for t, st in row]
return out if out is not None else rows-
행 = 런(run) 목록: 각 행은
[text, style]쌍(런)들의 목록입니다. 한 행은 스타일이 다른 여러 조각으로 쪼개져 있을 수 있습니다. -
매칭: 런들의 텍스트를 이어붙여(
"".join) 마커 문자열이 들어 있는지만 봅니다. 전체 문구가 아니라 식별성 높은 일부(/feedback to help us improve)로만 매칭해, 사용자가 직접/feedback을 친 줄 등 오탐을 피합니다. -
공백 치환: 매칭된 행만 각 런을
같은 길이의 공백 + 원래 스타일로 바꿉니다 → 폭 유지. -
in-place 변형 금지(중요): render 캐시의 행 객체를 공유하므로 원본 리스트나 행을 제자리에서 고치면 캐시가 오염됩니다. 그래서 매칭이 있을 때만
out = list(rows)로 얕은 복사본을 만들어 그 행만 교체하고, 매칭이 없으면 원본 객체를 그대로 반환합니다(핫패스 무할당).
_FEEDBACK_PROMPT_MARK = "How is Claude doing this session"
_FEEDBACK_OPT_MARKS = ("Bad", "Fine", "Good", "Dismiss")
def _blank_feedback_banner(rows):
texts = ["".join(t for t, _ in row) for row in rows]
if not any(_FEEDBACK_PROMPT_MARK in tx for tx in texts):
return rows
out = None
for i, tx in enumerate(texts):
if _FEEDBACK_PROMPT_MARK in tx or all(m in tx for m in _FEEDBACK_OPT_MARKS):
if out is None:
out = list(rows)
out[i] = [[" " * len(t), st] for t, st in rows[i]]
return out if out is not None else rows- 배너는 프롬프트 줄 + 평가옵션 줄 두 줄입니다.
-
선(先) 게이트: 화면 어디에도 프롬프트 줄(
How is Claude doing this session)이 없으면 즉시 원본 반환 → 핫패스에 영향이 없고, 평가옵션 줄만 단독으로 잘못 가리는 일도 막습니다. - 평가옵션 줄은
Bad·Fine·Good·Dismiss가 모두 들어 있을 때만 가립니다(오탐 방지). 그리고 이 검사는 프롬프트 줄이 화면에 함께 있을 때만 도달합니다.
class _ClaudeDisableFeedbackPlugin:
name = "claude-disable-feedback"
description = "Claude Code 피드백 문구 숨김 — 시작 팁·세션 종료 평가 배너 가림"
category = "Claude"
def server_filter_rows(self, server, pane, rows):
if not (getattr(pane, "_claude", None) or getattr(pane, "_hdr_claude", None)):
return rows
rows = _blank_feedback_tip(rows)
rows = _blank_feedback_banner(rows)
return rows
PLUGIN = _ClaudeDisableFeedbackPlugin()- 모듈은 끝에서
PLUGIN인스턴스 하나를 노출합니다. 레지스트리는 이 객체에서 훅 메서드를 찾습니다. -
패널 게이트(부드러운 참조): Claude 패널이 아니면 즉시 원본을 반환합니다. 게이트는 claude-code 플러그인이 패널에 설치하는 두 속성을
getattr로 부드럽게 봅니다.-
_claude— 현재 Claude 가 실행 중. -
_hdr_claude— 디바운스된 Claude 신호(잠깐의 전이 창 동안 True 로 남음). - claude-code 플러그인이 없으면 두 속성이 다 없어 모든 패널이 그냥 통과(no-op)합니다.
getattr(..., None)덕분에 결합 없이 안전합니다.
-
-
왜 두 속성을 OR 하나? 피드백 배너는 세션이 끝나는 순간 뜨는데, 그 시점이면
_claude가 이미None으로 떨어졌을 수 있습니다. 그러면_claude만 보면 배너가 게이트를 통과 못 해 그대로 보입니다._hdr_claude는 그 전이 창에서 한동안 True 로 남아 배너가 뜨는 바로 그 순간을 덮어줍니다.
이 플러그인이 주는 일반 교훈입니다.
| 키 주입 (Esc/Dismiss 자동 전송) | 표시 필터 (server_filter_rows) |
|
|---|---|---|
| 패널 상태 | 변경함 — 작동 중 턴을 interrupt 할 수 있음 | 건드리지 않음 |
| 타이밍 | render 와 레이스 — 키가 엉뚱한 순간에 도착 가능 | render 직전 결정적 변형, 레이스 없음 |
| 실패 모드 | 사용자 작업 중단(되돌리기 어려움) | 최악이라도 "잠깐 보임"(무해) |
| 복잡도 | 배너 출현 감지 + 쿨다운/디바운스 필요 | 행 텍스트 매칭 한 번 |
비모달 오버레이(닫지 않아도 작업이 막히지 않는 것)는 키를 주입하기보다 표시 필터로 가리는 쪽이 안전합니다. 키 주입은 busy 턴과 레이스가 나기 때문입니다. (반대로 모달이라 닫지 않으면 진행이 정말 막히는 경우엔 다른 접근이 필요할 수 있습니다.)
기능 전체가 pytmuxlib/plugins/claude-disable-feedback/ 한 디렉토리 안에 있습니다. 디렉토리를 통째로 지우거나 플러그인 관리 팝업에서 끄면 — 두 피드백 문구가 다시 화면에 보일 뿐, 코어와 claude-code 는 그대로 동작합니다. 코어는 이 플러그인을 직접 import 하지 않고 server_filter_rows 훅으로만 닿으므로, 플러그인이 없으면 행은 변형 없이 지나갑니다. 이것이 pytmux 플러그인의 delete-to-disable 계약입니다.
이 플러그인은 순수 함수에 가까워 단위 테스트가 쉽습니다. textual/PTY 같은 무거운 인프라가 필요 없습니다.
-
헬퍼 함수 직접 호출 —
_blank_feedback_tip/_blank_feedback_banner에 가짜rows(런 목록)를 만들어 넣고 결과를 확인합니다.- 매칭 행이 같은 폭의 공백으로 바뀌는지(길이 보존).
- 비매칭 행은 원본 객체 그대로(
is동일성)인지 → in-place 미변형 + 핫패스 무할당 확인. - 평가옵션 줄은 프롬프트 줄이 함께 있을 때만 가려지는지(단독이면 안 가려짐).
-
/feedback을 사용자가 직접 친 줄 같은 오탐 케이스가 살아남는지.
-
훅 게이트 테스트 —
server_filter_rows에_claude/_hdr_claude둘 다 없는 가짜 pane 을 주면 원본을 그대로 돌려주는지(no-op),_hdr_claude만 True 일 때 배너가 가려지는지(세션 종료 전이 창 회귀 방지). -
전체 스위트로 권위 확인:
python3 tests/run.py
요약줄(
N passed, 0 failed)을 직접 확인하세요.run.py는 실패해도 종료코드가 0 일 수 있고, 서브셋만 돌리면 플러그인 믹스인 poison 으로 가짜 실패가 날 수 있어 권위는 항상 전체 스위트입니다.
이 플러그인을 템플릿 삼아 새 훅 플러그인을 만들 때:
-
pytmuxlib/plugins/<name>/__init__.py한 파일로 시작. - 무거운 의존(textual/rich) import 금지 — 서버 프로세스도 같은 코드를 로드합니다.
- 필요한 레지스트리 훅 메서드만 구현(여기선
server_filter_rows하나). - 클래스에
name/description/category속성, 모듈 끝에PLUGIN = <Class>()인스턴스 노출. - 다른 플러그인의 상태에
getattr(..., None)로 부드럽게 접근 — 그 플러그인이 없어도 no-op. - 핫패스 가드(이 패널/이 화면이 아니면 즉시 원본 반환)와 in-place 변형 금지(매칭 있을 때만 복사본).
소개 · 사용
- Project-Overview
- User-Manual
- Screenshots
- Settings-Popup
- Single-Session-Model
- Tmux-Feature-Comparison
플러그인
Claude Code 플러그인
- Claude-Code-Plugins
- Dev-Guide-claude-code
- Dev-Guide-claude-token-usage-view
- Dev-Guide-claude-prompt-history
- Dev-Guide-claude-resume
- Dev-Guide-claude-disable-feedback
플랫폼 · 성능
품질 · 보안
리뷰·분석 보고서
연혁
기여