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 <noreply@anthropic.com>
This commit is contained in:
admin
2026-01-14 10:53:01 -03:00
parent ec33ac1936
commit 88cb7156e9

View File

@@ -9,7 +9,10 @@ Key features:
- tmux for human attachment when needed - tmux for human attachment when needed
- Multi-turn conversation support - 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: Usage:
luzia cockpit start <project> Start cockpit container luzia cockpit start <project> Start cockpit container
@@ -31,40 +34,57 @@ import re
from pathlib import Path from pathlib import Path
from typing import Dict, Optional, Tuple, List 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 Extends TmuxCLIController to execute tmux commands inside a Docker container
Docker containerized tmux sessions. 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"): 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: Args:
container_name: Docker container name container_name: Docker container name
tmux_session: tmux session name inside container (default: agent) tmux_session: tmux session name inside container (default: agent)
tmux_window: tmux window name (default: main) tmux_window: tmux window name (default: main)
""" """
super().__init__(session_name=tmux_session, window_name=tmux_window)
self.container_name = container_name self.container_name = container_name
self.tmux_session = tmux_session self.tmux_session = tmux_session
self.tmux_window = tmux_window self.tmux_window = tmux_window
self.target = f"{tmux_session}:{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 <cmd>', we run
'docker exec <container> tmux <cmd>'.
Args: Args:
args: tmux command arguments (without 'tmux' prefix) command: List of command components (without 'tmux' prefix)
Returns: 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) result = subprocess.run(cmd, capture_output=True, text=True)
return result.stdout.strip(), result.returncode return result.stdout.strip(), result.returncode
@@ -92,14 +112,14 @@ class DockerTmuxController:
return False return False
# Send text first # 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: if code != 0:
return False return False
if enter: if enter:
if delay_enter > 0: if delay_enter > 0:
time.sleep(delay_enter) 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 return True
@@ -116,7 +136,7 @@ class DockerTmuxController:
if not self.is_container_running(): if not self.is_container_running():
return "" return ""
output, code = self._run_tmux([ output, code = self._run_tmux_command([
"capture-pane", "-t", self.target, "-p", "-S", f"-{lines}" "capture-pane", "-t", self.target, "-p", "-S", f"-{lines}"
]) ])
return output if code == 0 else "" return output if code == 0 else ""
@@ -265,16 +285,20 @@ class DockerTmuxController:
"""Send Ctrl+C to interrupt running command.""" """Send Ctrl+C to interrupt running command."""
if not self.is_container_running(): if not self.is_container_running():
return False 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 return code == 0
def clear_pane(self) -> bool: def clear_pane(self) -> bool:
"""Clear the pane screen.""" """Clear the pane screen."""
if not self.is_container_running(): if not self.is_container_running():
return False 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 return code == 0
# Backward compatibility alias
DockerTmuxController = DockerTmuxAdapter
# Constants # Constants
COCKPIT_IMAGE = "luzia-cockpit:latest" COCKPIT_IMAGE = "luzia-cockpit:latest"
COCKPIT_PREFIX = "luzia-cockpit-" COCKPIT_PREFIX = "luzia-cockpit-"