4.0 KiB
claude-remote — Plan
Purpose
Send text to the Claude Code instance running inside a Docker container from the host, by locating the correct Konsole window and using xdotool to focus it and paste input.
Current Status
Core paste/focus machinery works. The remaining problem is reliably identifying the correct Konsole window.
Window Detection: What We Tried and Why It Failed
Approach 1: _NET_WM_PID via xprop
Walk up the process tree from the docker-compose process to find the Konsole ancestor,
then call xprop -id <window> _NET_WM_PID to match that PID to a window.
Why it failed: Konsole runs as a single process managing multiple windows. All windows
owned by the same Konsole instance report the same _NET_WM_PID. With 15+ Konsole windows
running, get_net_client_list() returns the first matching window — which is the wrong one.
Approach 2: Read /proc/<pid>/environ for WINDOWID
Konsole sets WINDOWID in the environment of each shell it spawns. That variable is
inherited by child processes (bash → sudo → docker → docker-compose). Reading it from
the right process in the chain would give us the exact window ID without any ambiguity.
Why it failed: /proc/<pid>/environ is not readable for processes owned by other
users (or protected by ptrace_scope). Permission denied even for same-user processes
in this environment.
Approach 3: Konsole D-Bus API
Query org.kde.konsole-<pid> via qdbus to enumerate sessions and find which one owns
the relevant pts/tty.
Why it was ruled out: D-Bus is not available in this environment.
Planned Fix: Pass WINDOWID into Docker
Modify the docker launch command to pass WINDOWID as an environment variable:
docker compose run --rm -e WINDOWID=$WINDOWID claude-code claude --dangerously-skip-permissions
Inside the container, WINDOWID is then available to the Claude process. It can write
this value to a known path on a bind-mounted volume (e.g. /workspace/.claude-windowid)
at startup.
find-window.js on the host reads that file as its primary window detection strategy,
falling back to the existing process-tree approach for cases where the file is absent.
Steps to implement
- Update the docker-compose config (or launch script) to pass
-e WINDOWID=$WINDOWID - Add startup logic inside the container to write
$WINDOWIDto/workspace/.claude-windowid - Update
find_claude_windowin find-window.js to check that file first - Clean up the file on container exit (optional)
Future: Replace xdotool with Direct PTY Write
The current xdotool approach has two significant drawbacks:
- Focus stealing — every paste steals window focus from whatever the user is doing, which is especially disruptive when input arrives from background sources like mail-buddy
- Fragility — depends on X11, clipboard state, and window geometry
Docker containers launched with -t are backed by a /dev/pts/X device on the host.
Writing directly to that device would send input to the container's Claude process with
no window manager involvement at all — completely invisible to the desktop.
Approach:
- Find the pts device by reading
/proc/<container_pid>/fd/0symlink on the host (points to e.g./dev/pts/7) - Write the text + newline directly:
echo "text" > /dev/pts/7 - May require membership in the
ttygroup or a small sudo helper for write permission
This would make claude-remote dramatically simpler and eliminate focus stealing entirely.
Important caveat — keep PTY input short:
Writing large payloads (e.g. full email bodies) directly to the PTY risks triggering
terminal control sequence interpretation, line length limits, and input buffer overflows.
The PTY should only carry short trigger commands like check email or new message from mikael.
Actual message content should be fetched by Claude via a CCC action, keeping the PTY
as a lightweight signalling channel only.