import curses import threading import config from .render import init_colors, draw from .input import handle_key from .agent import log, WELCOME_ART class HendrikTUI: def __init__(self, llm_client, tools_definition, TOOLS, TOOL_HANDLERS, build_system_prompt, agent_max_iterations): self.llm = llm_client self.tools_def = tools_definition self.TOOLS = TOOLS self.TOOL_HANDLERS = TOOL_HANDLERS self.build_system_prompt = build_system_prompt self.agent_max_iterations = agent_max_iterations self.messages = None self.log = [] self.input_buffer = [""] self.input_line = 0 self.input_col = 0 self.scroll = 0 self.processing = False self.running = True self.h, self.w = 0, 0 self.agent_thread: threading.Thread | None = None self.agent_done = threading.Event() def run(self): try: curses.wrapper(self._main) except KeyboardInterrupt: pass def _main(self, stdscr): curses.use_default_colors() init_colors() stdscr.keypad(True) stdscr.refresh() self.messages = [{"role": "system", "content": self.build_system_prompt( tools_definition=self.tools_def, character=config.AGENT_CHARACTER or None, skills=config.AGENT_SKILLS.split(",") if config.AGENT_SKILLS else None, )}] log(self, "welcome", WELCOME_ART) while self.running: self.h, self.w = stdscr.getmaxyx() if self.h < 14 or self.w < 40: stdscr.erase() stdscr.addstr(0, 0, "Terminal too small (min 40x14)") stdscr.refresh() stdscr.getch() continue draw(self, stdscr) curses.curs_set(2) if self.processing: stdscr.timeout(100) else: stdscr.timeout(-1) try: key = stdscr.getch() except KeyboardInterrupt: break handle_key(self, stdscr, key) if self.agent_done.is_set(): self.agent_thread.join() self.agent_done.clear() self.processing = False self.agent_thread = None