- Context_Engineering.md: 에이전트 컨텍스트 엔지니어링 개념 정리 문서 추가 - Context_Engineering_Research.ipynb: 연구 노트북 업데이트 - deepagents_sourcecode/: docstring과 주석을 한국어로 번역
236 lines
6.9 KiB
Python
236 lines
6.9 KiB
Python
"""deepagents-cli용 상태 표시줄(Status bar) 위젯입니다."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from textual.containers import Horizontal
|
|
from textual.css.query import NoMatches
|
|
from textual.reactive import reactive
|
|
from textual.widgets import Static
|
|
|
|
if TYPE_CHECKING:
|
|
from textual.app import ComposeResult
|
|
|
|
TOKENS_K_THRESHOLD = 1000
|
|
|
|
|
|
class StatusBar(Horizontal):
|
|
"""모드/자동 승인/작업 디렉토리 등을 표시하는 상태 표시줄입니다."""
|
|
|
|
DEFAULT_CSS = """
|
|
StatusBar {
|
|
height: 1;
|
|
dock: bottom;
|
|
background: $surface;
|
|
padding: 0 1;
|
|
}
|
|
|
|
StatusBar .status-mode {
|
|
width: auto;
|
|
padding: 0 1;
|
|
}
|
|
|
|
StatusBar .status-mode.normal {
|
|
display: none;
|
|
}
|
|
|
|
StatusBar .status-mode.bash {
|
|
background: #ff1493;
|
|
color: white;
|
|
text-style: bold;
|
|
}
|
|
|
|
StatusBar .status-mode.command {
|
|
background: #8b5cf6;
|
|
color: white;
|
|
}
|
|
|
|
StatusBar .status-auto-approve {
|
|
width: auto;
|
|
padding: 0 1;
|
|
}
|
|
|
|
StatusBar .status-auto-approve.on {
|
|
background: #10b981;
|
|
color: black;
|
|
}
|
|
|
|
StatusBar .status-auto-approve.off {
|
|
background: #f59e0b;
|
|
color: black;
|
|
}
|
|
|
|
StatusBar .status-message {
|
|
width: auto;
|
|
padding: 0 1;
|
|
color: $text-muted;
|
|
}
|
|
|
|
StatusBar .status-message.thinking {
|
|
color: $warning;
|
|
}
|
|
|
|
StatusBar .status-cwd {
|
|
width: 1fr;
|
|
text-align: right;
|
|
color: $text-muted;
|
|
}
|
|
|
|
StatusBar .status-tokens {
|
|
width: auto;
|
|
padding: 0 1;
|
|
color: $text-muted;
|
|
}
|
|
"""
|
|
|
|
mode: reactive[str] = reactive("normal", init=False)
|
|
status_message: reactive[str] = reactive("", init=False)
|
|
auto_approve: reactive[bool] = reactive(default=False, init=False)
|
|
cwd: reactive[str] = reactive("", init=False)
|
|
tokens: reactive[int] = reactive(0, init=False)
|
|
|
|
def __init__(self, cwd: str | Path | None = None, **kwargs: Any) -> None:
|
|
"""상태 표시줄을 초기화합니다.
|
|
|
|
Args:
|
|
cwd: Current working directory to display
|
|
**kwargs: Additional arguments passed to parent
|
|
"""
|
|
super().__init__(**kwargs)
|
|
# 초기 cwd를 저장(compose()에서 사용)
|
|
self._initial_cwd = str(cwd) if cwd else str(Path.cwd())
|
|
|
|
def compose(self) -> ComposeResult:
|
|
"""상태 표시줄 레이아웃을 구성합니다."""
|
|
yield Static("", classes="status-mode normal", id="mode-indicator")
|
|
yield Static(
|
|
"manual | shift+tab to cycle",
|
|
classes="status-auto-approve off",
|
|
id="auto-approve-indicator",
|
|
)
|
|
yield Static("", classes="status-message", id="status-message")
|
|
yield Static("", classes="status-tokens", id="tokens-display")
|
|
# CWD shown in welcome banner, not pinned in status bar
|
|
|
|
def on_mount(self) -> None:
|
|
"""마운트(on_mount) 이후 reactive 값을 설정해 watcher가 안전하게 동작하도록 합니다."""
|
|
self.cwd = self._initial_cwd
|
|
|
|
def watch_mode(self, mode: str) -> None:
|
|
"""모드(mode) 변경 시 표시를 갱신합니다."""
|
|
try:
|
|
indicator = self.query_one("#mode-indicator", Static)
|
|
except NoMatches:
|
|
return
|
|
indicator.remove_class("normal", "bash", "command")
|
|
|
|
if mode == "bash":
|
|
indicator.update("BASH")
|
|
indicator.add_class("bash")
|
|
elif mode == "command":
|
|
indicator.update("CMD")
|
|
indicator.add_class("command")
|
|
else:
|
|
indicator.update("")
|
|
indicator.add_class("normal")
|
|
|
|
def watch_auto_approve(self, new_value: bool) -> None: # noqa: FBT001
|
|
"""auto-approve 상태 변경 시 표시를 갱신합니다."""
|
|
try:
|
|
indicator = self.query_one("#auto-approve-indicator", Static)
|
|
except NoMatches:
|
|
return
|
|
indicator.remove_class("on", "off")
|
|
|
|
if new_value:
|
|
indicator.update("auto | shift+tab to cycle")
|
|
indicator.add_class("on")
|
|
else:
|
|
indicator.update("manual | shift+tab to cycle")
|
|
indicator.add_class("off")
|
|
|
|
def watch_cwd(self, new_value: str) -> None:
|
|
"""작업 디렉토리(cwd) 변경 시 표시를 갱신합니다."""
|
|
try:
|
|
display = self.query_one("#cwd-display", Static)
|
|
except NoMatches:
|
|
return
|
|
display.update(self._format_cwd(new_value))
|
|
|
|
def watch_status_message(self, new_value: str) -> None:
|
|
"""상태 메시지(status message) 변경 시 표시를 갱신합니다."""
|
|
try:
|
|
msg_widget = self.query_one("#status-message", Static)
|
|
except NoMatches:
|
|
return
|
|
|
|
msg_widget.remove_class("thinking")
|
|
if new_value:
|
|
msg_widget.update(new_value)
|
|
if "thinking" in new_value.lower() or "executing" in new_value.lower():
|
|
msg_widget.add_class("thinking")
|
|
else:
|
|
msg_widget.update("")
|
|
|
|
def _format_cwd(self, cwd_path: str = "") -> str:
|
|
"""표시용으로 현재 작업 디렉토리를 포맷팅합니다."""
|
|
path = Path(cwd_path or self.cwd or self._initial_cwd)
|
|
try:
|
|
# 홈 디렉토리는 ~로 표시 시도
|
|
home = Path.home()
|
|
if path.is_relative_to(home):
|
|
return "~/" + str(path.relative_to(home))
|
|
except (ValueError, RuntimeError):
|
|
pass
|
|
return str(path)
|
|
|
|
def set_mode(self, mode: str) -> None:
|
|
"""현재 입력 모드를 설정합니다.
|
|
|
|
Args:
|
|
mode: One of "normal", "bash", or "command"
|
|
"""
|
|
self.mode = mode
|
|
|
|
def set_auto_approve(self, *, enabled: bool) -> None:
|
|
"""auto-approve 상태를 설정합니다.
|
|
|
|
Args:
|
|
enabled: Whether auto-approve is enabled
|
|
"""
|
|
self.auto_approve = enabled
|
|
|
|
def set_status_message(self, message: str) -> None:
|
|
"""상태 메시지를 설정합니다.
|
|
|
|
Args:
|
|
message: Status message to display (empty string to clear)
|
|
"""
|
|
self.status_message = message
|
|
|
|
def watch_tokens(self, new_value: int) -> None:
|
|
"""토큰 수 변경 시 표시를 갱신합니다."""
|
|
try:
|
|
display = self.query_one("#tokens-display", Static)
|
|
except NoMatches:
|
|
return
|
|
|
|
if new_value > 0:
|
|
# 천 단위는 K suffix로 표시
|
|
if new_value >= TOKENS_K_THRESHOLD:
|
|
display.update(f"{new_value / TOKENS_K_THRESHOLD:.1f}K tokens")
|
|
else:
|
|
display.update(f"{new_value} tokens")
|
|
else:
|
|
display.update("")
|
|
|
|
def set_tokens(self, count: int) -> None:
|
|
"""토큰 수를 설정합니다.
|
|
|
|
Args:
|
|
count: Current context token count
|
|
"""
|
|
self.tokens = count
|