Skip to content

Commit bda1828

Browse files
authored
Merge pull request #35 from alanbem/feat/iterm2-shell-integration
feat: add iTerm2 shell integration
2 parents 62d8c6b + c3568a7 commit bda1828

File tree

6 files changed

+732
-2
lines changed

6 files changed

+732
-2
lines changed

.hadolint.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ ignored:
1313
# Entrypoint starts as root for permission setup, then uses gosu to switch to claude user
1414
- DL3002 # Last USER should not be root
1515

16+
# Single quotes are intentional - expressions should expand at runtime, not build time
17+
- SC2016 # Expressions don't expand in single quotes
18+
1619
trustedRegistries:
1720
- docker.io
1821
- ghcr.io

CLAUDE.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,50 @@ set-option -g detach-on-destroy on
593593
- The `detect_tty_flags()` function returns `-i -t` flags for interactive mode, empty for non-interactive
594594
- The `is_print_mode()` function checks for `-p`/`--print` as separate arguments (safe from string matching)
595595

596+
## iTerm2 Shell Integration
597+
598+
dclaude includes [iTerm2 Shell Integration](https://iterm2.com/documentation-shell-integration.html) for enhanced terminal experience on macOS.
599+
600+
### How It Works
601+
602+
**Automatic activation:**
603+
- Script baked into image at `/home/claude/.iterm2_shell_integration.bash`
604+
- Sourced in `~/.bashrc` with opt-out check
605+
- Only activates for interactive shells in iTerm2 (script self-checks environment)
606+
- Works inside tmux via `ITERM_ENABLE_SHELL_INTEGRATION_WITH_TMUX=1` env var
607+
608+
**Features enabled:**
609+
- URL handling (click URLs to open in Mac browser)
610+
- `imgcat` command (display images inline)
611+
- `it2copy` command (copy to Mac clipboard)
612+
- Shell marks (navigate between prompts)
613+
- Command status tracking
614+
615+
### Configuration
616+
617+
**Opt-out:**
618+
```bash
619+
DCLAUDE_ITERM2=false dclaude
620+
```
621+
622+
**Environment variables:**
623+
- `DCLAUDE_ITERM2` - Set to `false` to disable (default: `true`)
624+
- `ITERM_ENABLE_SHELL_INTEGRATION_WITH_TMUX` - Set by dclaude to enable in tmux sessions
625+
626+
### Technical Details
627+
628+
**Script location:** `/home/claude/.iterm2_shell_integration.bash` (baked into image)
629+
630+
**Bashrc sourcing:**
631+
```bash
632+
[[ "${DCLAUDE_ITERM2:-true}" != "false" && -f ~/.iterm2_shell_integration.bash ]] && source ~/.iterm2_shell_integration.bash
633+
```
634+
635+
**Why tmux requires special handling:**
636+
The iTerm2 script by default skips tmux sessions (checks `$TERM` for `screen` or `tmux-256color`). Setting `ITERM_ENABLE_SHELL_INTEGRATION_WITH_TMUX=1` bypasses this check, allowing integration to work inside dclaude's tmux-managed sessions.
637+
638+
**Non-iTerm2 terminals:** The script is safe for other terminals - it checks the environment and does nothing if iTerm2 isn't detected.
639+
596640
## Chrome DevTools Integration
597641

598642
The `dclaude chrome` subcommand provides seamless integration between Claude and Chrome DevTools via the Model Context Protocol (MCP).

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ dclaude chrome # Launch Chrome with DevTools
151151
dclaude # Claude can now interact with the browser
152152
```
153153

154+
### iTerm2 Shell Integration
155+
156+
If you use iTerm2 on macOS, dclaude automatically enables [iTerm2 Shell Integration](https://iterm2.com/documentation-shell-integration.html):
157+
158+
- **Click URLs in output** - Opens in your Mac's browser
159+
- **imgcat** - Display images inline in terminal
160+
- **it2copy** - Copy to Mac clipboard from inside container
161+
- **Marks** - Navigate between command prompts
162+
163+
This only activates when running in iTerm2. To disable:
164+
165+
```bash
166+
DCLAUDE_ITERM2=false dclaude
167+
```
168+
154169
### System Context
155170

156171
dclaude automatically tells Claude about its container environment so it can give better suggestions:
@@ -180,6 +195,7 @@ DCLAUDE_SYSTEM_CONTEXT=false dclaude
180195
| `DCLAUDE_QUIET` | `false` | Suppress info messages |
181196
| `DCLAUDE_NO_UPDATE` | `false` | Skip image update check |
182197
| `DCLAUDE_SYSTEM_CONTEXT` | `true` | Inform Claude about container environment |
198+
| `DCLAUDE_ITERM2` | `true` | Enable iTerm2 shell integration (only affects iTerm2) |
183199

184200
## Configuration File
185201

dclaude

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,7 @@ main() {
12421242
[[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
12431243
[[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
12441244
[[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
1245+
[[ -n "${DCLAUDE_ITERM2:-}" ]] && exec_env_args+=(-e "DCLAUDE_ITERM2=${DCLAUDE_ITERM2}")
12451246

12461247
debug "Creating new tmux session running Claude"
12471248
debug "Claude args count: ${#claude_args[@]}, user args: $*"
@@ -1319,6 +1320,11 @@ main() {
13191320
DOCKER_ARGS+=(-e "COLORTERM=${COLORTERM}")
13201321
fi
13211322

1323+
# Pass through iTerm2 integration opt-out if set
1324+
if [[ -n "${DCLAUDE_ITERM2:-}" ]]; then
1325+
DOCKER_ARGS+=(-e "DCLAUDE_ITERM2=${DCLAUDE_ITERM2}")
1326+
fi
1327+
13221328
# Mount Docker socket if detected (detection done earlier for system context)
13231329
if [[ -n "$DOCKER_SOCKET" ]] && [[ -S "$DOCKER_SOCKET" ]]; then
13241330
DOCKER_ARGS+=(-v "${DOCKER_SOCKET}:/var/run/docker.sock")
@@ -1389,6 +1395,7 @@ main() {
13891395
[[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
13901396
[[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
13911397
[[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
1398+
[[ -n "${DCLAUDE_ITERM2:-}" ]] && exec_env_args+=(-e "DCLAUDE_ITERM2=${DCLAUDE_ITERM2}")
13921399

13931400
debug "Creating new tmux session running Claude"
13941401
debug "Claude args count: ${#claude_args[@]}, user args: $*"
@@ -1537,6 +1544,7 @@ cmd_attach() {
15371544
[[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
15381545
[[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
15391546
[[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
1547+
[[ -n "${DCLAUDE_ITERM2:-}" ]] && exec_env_args+=(-e "DCLAUDE_ITERM2=${DCLAUDE_ITERM2}")
15401548

15411549
# Attach to existing session
15421550
info "Attaching to session: $session_name"

docker/Dockerfile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,11 @@ USER claude
114114
WORKDIR /home/claude
115115

116116
# Configure npm to use user directory for global packages
117+
# Source iTerm2 shell integration if not disabled (only activates in iTerm2)
117118
RUN npm config set prefix /home/claude/.npm-global \
118119
&& echo 'export PATH=$HOME/.npm-global/bin:$PATH' >> ~/.bashrc \
119120
&& echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> ~/.bashrc \
121+
&& echo '[[ "${DCLAUDE_ITERM2:-true}" != "false" && -f ~/.iterm2_shell_integration.bash ]] && source ~/.iterm2_shell_integration.bash' >> ~/.bashrc \
120122
&& echo 'export PATH=$HOME/.npm-global/bin:$PATH' >> ~/.profile \
121123
&& echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> ~/.profile
122124

@@ -148,9 +150,10 @@ USER root
148150
# Declare volumes for persistent data
149151
VOLUME ["/home/claude/.claude"]
150152

151-
# Copy entrypoint script and tmux config (paths mirror image structure)
153+
# Copy entrypoint script, tmux config, and shell integrations (paths mirror image structure)
152154
COPY --chown=claude:claude usr/local/bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
153155
COPY --chown=claude:claude home/claude/.tmux.conf /home/claude/.tmux.conf
156+
COPY --chown=claude:claude home/claude/.iterm2_shell_integration.bash /home/claude/.iterm2_shell_integration.bash
154157
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
155158

156159
# Set working directory to workspace
@@ -170,7 +173,8 @@ ENV CLAUDE_UNSAFE_TRUST_WORKSPACE=true \
170173
LANG=en_US.UTF-8 \
171174
LC_ALL=en_US.UTF-8 \
172175
LANGUAGE=en_US:en \
173-
GIT_CONFIG_GLOBAL=/home/claude/.claude/.gitconfig
176+
GIT_CONFIG_GLOBAL=/home/claude/.claude/.gitconfig \
177+
ITERM_ENABLE_SHELL_INTEGRATION_WITH_TMUX=1
174178

175179
# Labels
176180
LABEL maintainer="alanbem" \

0 commit comments

Comments
 (0)