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:
@@ -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 <project> 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 <cmd>', we run
|
||||
'docker exec <container> tmux <cmd>'.
|
||||
|
||||
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-"
|
||||
|
||||
Reference in New Issue
Block a user