From 88cb7156e9ab98edaca5418e520a883cf7e504c8 Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 14 Jan 2026 10:53:01 -0300 Subject: [PATCH] Refactor cockpit to use claude-code-tools-local package Architecture improvements: - Import TmuxCLIController from installed claude-code-tools-local - DockerTmuxAdapter extends TmuxCLIController for Docker execution - Override _run_tmux_command() to prepend 'docker exec' to commands - Backward compatibility alias: DockerTmuxController = DockerTmuxAdapter This enables: - Pulling upstream improvements from claude-code-tools - Maintaining Docker-specific functionality - Clean inheritance-based architecture Co-Authored-By: Claude Opus 4.5 --- lib/cockpit.py | 56 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/lib/cockpit.py b/lib/cockpit.py index 26308d0..ed6fb20 100644 --- a/lib/cockpit.py +++ b/lib/cockpit.py @@ -9,7 +9,10 @@ Key features: - tmux for human attachment when needed - Multi-turn conversation support -Uses claude-code-tools TmuxCLIController pattern for robust session management. +Architecture: + Imports TmuxCLIController from claude-code-tools-local package and extends + it with DockerTmuxAdapter for Docker-containerized tmux sessions. + This enables importing upstream improvements while adding Docker support. Usage: luzia cockpit start Start cockpit container @@ -31,40 +34,57 @@ import re from pathlib import Path from typing import Dict, Optional, Tuple, List +# Import TmuxCLIController from installed claude-code-tools-local package +from claude_code_tools.tmux_cli_controller import TmuxCLIController -class DockerTmuxController: + +class DockerTmuxAdapter(TmuxCLIController): """ - Tmux controller that executes commands inside a Docker container. + Docker adapter for TmuxCLIController. - Based on claude-code-tools TmuxCLIController pattern, adapted for - Docker containerized tmux sessions. + Extends TmuxCLIController to execute tmux commands inside a Docker container + rather than on the host system. This enables: + - Inheriting all TmuxCLIController methods automatically + - Pulling upstream improvements from claude-code-tools + - Adding Docker-specific functionality + + The key override is _run_tmux_command() which prepends 'docker exec' to all + tmux commands. """ def __init__(self, container_name: str, tmux_session: str = "agent", tmux_window: str = "main"): """ - Initialize controller for a Docker container's tmux session. + Initialize adapter for a Docker container's tmux session. Args: container_name: Docker container name tmux_session: tmux session name inside container (default: agent) tmux_window: tmux window name (default: main) """ + super().__init__(session_name=tmux_session, window_name=tmux_window) self.container_name = container_name self.tmux_session = tmux_session self.tmux_window = tmux_window self.target = f"{tmux_session}:{tmux_window}" - def _run_tmux(self, args: List[str]) -> Tuple[str, int]: + def _run_tmux_command(self, command: List[str]) -> Tuple[str, int]: """ - Run a tmux command inside the Docker container. + Override: Run tmux command inside the Docker container. + + This is the key override that makes all inherited TmuxCLIController + methods work inside Docker. Instead of running 'tmux ', we run + 'docker exec tmux '. Args: - args: tmux command arguments (without 'tmux' prefix) + command: List of command components (without 'tmux' prefix) Returns: - Tuple of (stdout, return_code) + Tuple of (output, exit_code) """ - cmd = ["docker", "exec", self.container_name, "tmux"] + args + if not self.is_container_running(): + return "", 1 + + cmd = ["docker", "exec", self.container_name, "tmux"] + command result = subprocess.run(cmd, capture_output=True, text=True) return result.stdout.strip(), result.returncode @@ -92,14 +112,14 @@ class DockerTmuxController: return False # Send text first - _, code = self._run_tmux(["send-keys", "-t", self.target, text]) + _, code = self._run_tmux_command(["send-keys", "-t", self.target, text]) if code != 0: return False if enter: if delay_enter > 0: time.sleep(delay_enter) - self._run_tmux(["send-keys", "-t", self.target, "Enter"]) + self._run_tmux_command(["send-keys", "-t", self.target, "Enter"]) return True @@ -116,7 +136,7 @@ class DockerTmuxController: if not self.is_container_running(): return "" - output, code = self._run_tmux([ + output, code = self._run_tmux_command([ "capture-pane", "-t", self.target, "-p", "-S", f"-{lines}" ]) return output if code == 0 else "" @@ -265,16 +285,20 @@ class DockerTmuxController: """Send Ctrl+C to interrupt running command.""" if not self.is_container_running(): return False - _, code = self._run_tmux(["send-keys", "-t", self.target, "C-c"]) + _, code = self._run_tmux_command(["send-keys", "-t", self.target, "C-c"]) return code == 0 def clear_pane(self) -> bool: """Clear the pane screen.""" if not self.is_container_running(): return False - _, code = self._run_tmux(["send-keys", "-t", self.target, "C-l"]) + _, code = self._run_tmux_command(["send-keys", "-t", self.target, "C-l"]) return code == 0 + +# Backward compatibility alias +DockerTmuxController = DockerTmuxAdapter + # Constants COCKPIT_IMAGE = "luzia-cockpit:latest" COCKPIT_PREFIX = "luzia-cockpit-"