@alanbem/dclaude 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +72 -0
  2. package/dclaude +188 -56
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -172,6 +172,7 @@ DCLAUDE_SYSTEM_CONTEXT=false dclaude
172
172
  |----------|---------|-------------|
173
173
  | `DCLAUDE_RM` | `false` | Remove container on exit (ephemeral mode) |
174
174
  | `DCLAUDE_TAG` | `latest` | Docker image tag |
175
+ | `DCLAUDE_NAMESPACE` | (none) | Namespace for isolated credentials/config |
175
176
  | `DCLAUDE_NETWORK` | `auto` | Network mode: `auto`, `host`, `bridge` |
176
177
  | `DCLAUDE_GIT_AUTH` | `auto` | SSH auth: `auto`, `agent-forwarding`, `key-mount`, `none` |
177
178
  | `DCLAUDE_DEBUG` | `false` | Enable debug output |
@@ -179,6 +180,64 @@ DCLAUDE_SYSTEM_CONTEXT=false dclaude
179
180
  | `DCLAUDE_NO_UPDATE` | `false` | Skip image update check |
180
181
  | `DCLAUDE_SYSTEM_CONTEXT` | `true` | Inform Claude about container environment |
181
182
 
183
+ ## Configuration File
184
+
185
+ Create a `.dclaude` file at your project root to configure dclaude for that directory tree:
186
+
187
+ ```bash
188
+ # ~/projects/mycompany/.dclaude
189
+ NAMESPACE=mycompany
190
+ NETWORK=host
191
+ DEBUG=true
192
+ ```
193
+
194
+ dclaude walks up the directory tree to find `.dclaude` files. Any dclaude session started from that directory or any subdirectory will use these settings.
195
+
196
+ **Supported variables:**
197
+
198
+ | Variable | Description |
199
+ |----------|-------------|
200
+ | `NAMESPACE` | Isolate credentials/config (see Namespace Isolation) |
201
+ | `NETWORK` | Network mode (`host`, `bridge`) |
202
+ | `GIT_AUTH` | Git auth mode |
203
+ | `DEBUG` | Enable debug output (`true`, `false`) |
204
+ | `CHROME_PORT` | Chrome DevTools port |
205
+
206
+ **Precedence:** Environment variables override `.dclaude` file settings.
207
+
208
+ ## Namespace Isolation
209
+
210
+ Use namespaces to maintain completely separate environments with different credentials and settings.
211
+
212
+ **Use case:** You have both a personal Anthropic subscription and a company subscription. You want to keep them completely separate.
213
+
214
+ **Option 1: Using `.dclaude` file (recommended)**
215
+ ```bash
216
+ # Create config at company project root
217
+ echo "NAMESPACE=mycompany" > ~/projects/mycompany/.dclaude
218
+
219
+ # Now any dclaude in that tree uses company credentials
220
+ cd ~/projects/mycompany/api/src
221
+ dclaude # Uses mycompany namespace automatically
222
+ ```
223
+
224
+ **Option 2: Using environment variable**
225
+ ```bash
226
+ DCLAUDE_NAMESPACE=mycompany dclaude
227
+ ```
228
+
229
+ **Option 3: Shell alias**
230
+ ```bash
231
+ # Add to ~/.bashrc or ~/.zshrc
232
+ alias dclaude-work='DCLAUDE_NAMESPACE=mycompany dclaude'
233
+ ```
234
+
235
+ Each namespace gets its own:
236
+ - Claude credentials and API key
237
+ - Claude settings and preferences
238
+ - Git configuration
239
+ - Container instance
240
+
182
241
  ## Networking
183
242
 
184
243
  dclaude auto-detects the best networking mode:
@@ -259,6 +318,19 @@ ssh-add ~/.ssh/id_ed25519
259
318
  dclaude exec brew install <tool> # This persists
260
319
  ```
261
320
 
321
+ **Directories appear empty inside container? (OrbStack)**
322
+
323
+ This is a [known OrbStack 2.0.x bug](https://github.com/orbstack/orbstack/issues/2103) where VirtioFS caches stale directory entries. Directories that existed before upgrading to OrbStack 2.0 may appear empty inside the container.
324
+
325
+ ```bash
326
+ # Fix: rename the directory to clear the cache
327
+ mv problematic-dir problematic-dir.tmp && mv problematic-dir.tmp problematic-dir
328
+
329
+ # Or restart OrbStack completely (clears all caches)
330
+ ```
331
+
332
+ Upgrading to the latest OrbStack version may also help.
333
+
262
334
  ## Project Structure
263
335
 
264
336
  ```text
package/dclaude CHANGED
@@ -4,25 +4,30 @@
4
4
 
5
5
  set -euo pipefail
6
6
 
7
- # Configuration
7
+ # Early configuration (before config file loading)
8
8
  readonly IMAGE_NAME="${DCLAUDE_REGISTRY:-docker.io}/alanbem/dclaude"
9
9
  readonly IMAGE_TAG="${DCLAUDE_TAG:-latest}"
10
10
  readonly IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
11
11
  readonly VOLUME_PREFIX="dclaude"
12
- readonly DEBUG="${DCLAUDE_DEBUG:-false}"
13
12
  readonly QUIET="${DCLAUDE_QUIET:-false}"
14
13
  readonly REMOVE_CONTAINER="${DCLAUDE_RM:-false}"
14
+ readonly ENABLE_SYSTEM_CONTEXT="${DCLAUDE_SYSTEM_CONTEXT:-true}" # Inform Claude about dclaude environment
15
+
15
16
  # Docker socket will be detected dynamically unless overridden
16
17
  DOCKER_SOCKET="${DCLAUDE_DOCKER_SOCKET:-}"
17
- readonly GIT_AUTH_MODE="${DCLAUDE_GIT_AUTH:-auto}" # auto, agent-forwarding, key-mount, none
18
- readonly ENABLE_SYSTEM_CONTEXT="${DCLAUDE_SYSTEM_CONTEXT:-true}" # Inform Claude about dclaude environment
19
18
 
20
- # Chrome configuration (not readonly to allow --port flag override)
19
+ # Chrome configuration (not readonly to allow --port flag and config file override)
21
20
  CHROME_PROFILE="${DCLAUDE_CHROME_PROFILE:-claude}"
22
- CHROME_PORT="${DCLAUDE_CHROME_PORT:-9222}"
23
21
  CHROME_BIN="${DCLAUDE_CHROME_BIN:-}"
24
22
  CHROME_FLAGS="${DCLAUDE_CHROME_FLAGS:-}"
25
23
 
24
+ # Variables that can be set via .dclaude config file (declared after config loading)
25
+ # - DCLAUDE_NAMESPACE (namespace for volume/container isolation)
26
+ # - DCLAUDE_NETWORK (network mode: host/bridge)
27
+ # - DCLAUDE_GIT_AUTH (git auth mode)
28
+ # - DCLAUDE_DEBUG (enable debug output)
29
+ # - DCLAUDE_CHROME_PORT (chrome devtools port)
30
+
26
31
  # Colors for output (only if terminal supports it)
27
32
  if [[ -t 1 ]]; then
28
33
  readonly RED='\033[0;31m'
@@ -68,17 +73,83 @@ debug() {
68
73
  fi
69
74
  }
70
75
 
71
- # Reset terminal mouse mode after tmux exits
72
- # Prevents iTerm2 warnings about mouse reporting being left on
73
- reset_terminal_mouse() {
74
- # Disable all mouse reporting modes:
75
- # ?1000 - X10 mouse reporting
76
- # ?1002 - Cell motion mouse tracking
77
- # ?1003 - All motion mouse tracking
78
- # ?1006 - SGR extended mouse reporting
79
- printf '\033[?1000l\033[?1002l\033[?1003l\033[?1006l'
76
+ # Find .dclaude config file by walking up directory tree
77
+ find_config_file() {
78
+ local dir="$PWD"
79
+ while [[ "$dir" != "/" ]]; do
80
+ if [[ -f "$dir/.dclaude" ]]; then
81
+ echo "$dir/.dclaude"
82
+ return 0
83
+ fi
84
+ dir=$(dirname "$dir")
85
+ done
86
+ return 1
80
87
  }
81
88
 
89
+ # Load .dclaude config file (env vars take precedence)
90
+ load_config_file() {
91
+ local config_file="$1"
92
+
93
+ while IFS= read -r line || [[ -n "$line" ]]; do
94
+ # Skip comments and empty lines
95
+ [[ "$line" =~ ^[[:space:]]*# ]] && continue
96
+ [[ -z "${line// }" ]] && continue
97
+
98
+ # Parse key=value
99
+ if [[ "$line" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)[[:space:]]*=[[:space:]]*(.*)$ ]]; then
100
+ local key="${BASH_REMATCH[1]}"
101
+ local value="${BASH_REMATCH[2]}"
102
+ # Trim quotes if present
103
+ value="${value#\"}"
104
+ value="${value%\"}"
105
+ value="${value#\'}"
106
+ value="${value%\'}"
107
+
108
+ # Only set if env var not already set (env var takes precedence)
109
+ case "$key" in
110
+ NAMESPACE)
111
+ [[ -z "${DCLAUDE_NAMESPACE:-}" ]] && DCLAUDE_NAMESPACE="$value"
112
+ ;;
113
+ NETWORK)
114
+ [[ -z "${DCLAUDE_NETWORK:-}" ]] && DCLAUDE_NETWORK="$value"
115
+ ;;
116
+ GIT_AUTH)
117
+ [[ -z "${DCLAUDE_GIT_AUTH:-}" ]] && DCLAUDE_GIT_AUTH="$value"
118
+ ;;
119
+ DEBUG)
120
+ [[ -z "${DCLAUDE_DEBUG:-}" ]] && DCLAUDE_DEBUG="$value"
121
+ ;;
122
+ CHROME_PORT)
123
+ [[ -z "${DCLAUDE_CHROME_PORT:-}" ]] && DCLAUDE_CHROME_PORT="$value"
124
+ ;;
125
+ esac
126
+ fi
127
+ done < "$config_file"
128
+ }
129
+
130
+ # Load config file if found (must happen early, before readonly declarations)
131
+ DCLAUDE_CONFIG_FILE=""
132
+ if DCLAUDE_CONFIG_FILE=$(find_config_file); then
133
+ load_config_file "$DCLAUDE_CONFIG_FILE"
134
+ fi
135
+
136
+ # Now set readonly variables that may have been loaded from config file
137
+ readonly DEBUG="${DCLAUDE_DEBUG:-false}"
138
+ readonly GIT_AUTH_MODE="${DCLAUDE_GIT_AUTH:-auto}" # auto, agent-forwarding, key-mount, none
139
+ readonly NAMESPACE="${DCLAUDE_NAMESPACE:-}"
140
+ CHROME_PORT="${DCLAUDE_CHROME_PORT:-9222}"
141
+
142
+ # Show config file if loaded (always at info level, details at debug)
143
+ if [[ -n "$DCLAUDE_CONFIG_FILE" ]]; then
144
+ info "Using config: $DCLAUDE_CONFIG_FILE"
145
+ if [[ "$DEBUG" == "true" ]]; then
146
+ [[ -n "$NAMESPACE" ]] && debug " NAMESPACE=$NAMESPACE"
147
+ [[ -n "${DCLAUDE_NETWORK:-}" ]] && debug " NETWORK=$DCLAUDE_NETWORK"
148
+ [[ -n "${DCLAUDE_GIT_AUTH:-}" ]] && debug " GIT_AUTH=$DCLAUDE_GIT_AUTH"
149
+ [[ -n "${DCLAUDE_CHROME_PORT:-}" ]] && debug " CHROME_PORT=$DCLAUDE_CHROME_PORT"
150
+ fi
151
+ fi
152
+
82
153
  # Detect platform
83
154
  detect_platform() {
84
155
  case "$(uname -s)" in
@@ -416,10 +487,20 @@ detect_network_capability() {
416
487
  echo "$test_result"
417
488
  }
418
489
 
490
+ # Get volume name (includes namespace if set)
491
+ get_volume_name() {
492
+ if [[ -n "$NAMESPACE" ]]; then
493
+ echo "${VOLUME_PREFIX}-${NAMESPACE}-claude"
494
+ else
495
+ echo "${VOLUME_PREFIX}-claude"
496
+ fi
497
+ }
498
+
419
499
  # Create Docker volumes if they don't exist
420
500
  create_volumes() {
421
501
  # Create essential volume for Claude CLI persistence
422
- local volume="${VOLUME_PREFIX}-claude"
502
+ local volume
503
+ volume=$(get_volume_name)
423
504
 
424
505
  if ! docker volume inspect "$volume" &> /dev/null; then
425
506
  info "Creating volume: $volume"
@@ -496,12 +577,17 @@ get_host_path() {
496
577
  echo "$host_path"
497
578
  }
498
579
 
499
- # Generate deterministic container name from path
580
+ # Generate deterministic container name from path (and namespace if set)
500
581
  get_container_name() {
501
582
  local path="${1:-$HOST_PATH}"
583
+ local hash_input="${NAMESPACE}:${path}"
502
584
  local path_hash
503
- path_hash=$(echo -n "$path" | md5sum 2>/dev/null | cut -d' ' -f1 || echo -n "$path" | md5 | cut -d' ' -f1)
504
- echo "dclaude-${path_hash:0:12}"
585
+ path_hash=$(echo -n "$hash_input" | md5sum 2>/dev/null | cut -d' ' -f1 || echo -n "$hash_input" | md5 | cut -d' ' -f1)
586
+ if [[ -n "$NAMESPACE" ]]; then
587
+ echo "dclaude-${NAMESPACE}-${path_hash:0:8}"
588
+ else
589
+ echo "dclaude-${path_hash:0:12}"
590
+ fi
505
591
  }
506
592
 
507
593
  # Generate system context prompt for Claude
@@ -678,10 +764,30 @@ When suggesting commands or file operations, you can treat this environment as i
678
764
  EOF
679
765
  }
680
766
 
767
+ # Get SSH proxy container/volume names (includes namespace if set)
768
+ get_ssh_proxy_container_name() {
769
+ if [[ -n "$NAMESPACE" ]]; then
770
+ echo "dclaude-${NAMESPACE}-ssh-proxy-$(id -u)"
771
+ else
772
+ echo "dclaude-ssh-proxy-$(id -u)"
773
+ fi
774
+ }
775
+
776
+ get_ssh_proxy_volume_name() {
777
+ if [[ -n "$NAMESPACE" ]]; then
778
+ echo "dclaude-${NAMESPACE}-ssh-proxy"
779
+ else
780
+ echo "dclaude-ssh-proxy"
781
+ fi
782
+ }
783
+
681
784
  # Handle SSH authentication based on DCLAUDE_GIT_AUTH mode
682
785
  # Setup SSH proxy container for macOS
683
786
  setup_ssh_proxy_container() {
684
- local proxy_container="dclaude-ssh-proxy-$(id -u)"
787
+ local proxy_container
788
+ local proxy_volume
789
+ proxy_container=$(get_ssh_proxy_container_name)
790
+ proxy_volume=$(get_ssh_proxy_volume_name)
685
791
 
686
792
  # Check if proxy container already exists and is running
687
793
  if docker ps -q -f name="^${proxy_container}$" 2>/dev/null | grep -q .; then
@@ -704,7 +810,7 @@ setup_ssh_proxy_container() {
704
810
  docker run -d \
705
811
  --name "$proxy_container" \
706
812
  -v "/run/host-services/ssh-auth.sock:/run/host-services/ssh-auth.sock:ro" \
707
- -v "dclaude-ssh-proxy:/tmp/ssh-proxy" \
813
+ -v "${proxy_volume}:/tmp/ssh-proxy" \
708
814
  --rm \
709
815
  alpine:3.19 sh -c '
710
816
  # Install socat
@@ -775,10 +881,12 @@ handle_git_auth() {
775
881
  setup_ssh_proxy_container
776
882
 
777
883
  # Now mount the proxied socket from the shared volume
778
- docker_args+=(-v "dclaude-ssh-proxy:/tmp/ssh-proxy:ro"
884
+ local ssh_proxy_vol
885
+ ssh_proxy_vol=$(get_ssh_proxy_volume_name)
886
+ docker_args+=(-v "${ssh_proxy_vol}:/tmp/ssh-proxy:ro"
779
887
  -e "SSH_AUTH_SOCK=/tmp/ssh-proxy/agent")
780
888
  info "SSH agent forwarding enabled via proxy container"
781
- debug "Mounted SSH proxy volume: dclaude-ssh-proxy:/tmp/ssh-proxy"
889
+ debug "Mounted SSH proxy volume: ${ssh_proxy_vol}:/tmp/ssh-proxy"
782
890
  ;;
783
891
  windows)
784
892
  warning "SSH agent forwarding not fully supported on Windows"
@@ -847,17 +955,20 @@ detect_tty_flags() {
847
955
  echo "$tty_flags"
848
956
  }
849
957
 
850
- # Check if Claude is being run in print mode (-p/--print)
851
- # This checks arguments as separate array elements, so -p inside a string won't match
852
- is_print_mode() {
958
+ # Check if Claude is being run in a mode that should skip tmux
959
+ # These are flags that just output something and exit (no interactive session)
960
+ should_skip_tmux() {
853
961
  for arg in "$@"; do
854
- if [[ "$arg" == "-p" ]] || [[ "$arg" == "--print" ]]; then
855
- return 0
856
- fi
962
+ case "$arg" in
963
+ -p|--print|--version|-v|--help|-h)
964
+ return 0
965
+ ;;
966
+ esac
857
967
  done
858
968
  return 1
859
969
  }
860
970
 
971
+
861
972
  # Main execution
862
973
  main() {
863
974
  # No argument parsing - pass everything through to Claude
@@ -986,12 +1097,30 @@ main() {
986
1097
  debug "System context disabled (DCLAUDE_SYSTEM_CONTEXT=$ENABLE_SYSTEM_CONTEXT)"
987
1098
  fi
988
1099
 
1100
+ # Final environment summary (all variables now resolved)
1101
+ if [[ "$DEBUG" == "true" ]]; then
1102
+ debug "Resolved environment:"
1103
+ debug " NAMESPACE=${NAMESPACE:-<not set>}"
1104
+ debug " NETWORK_MODE=$network_mode"
1105
+ debug " GIT_AUTH=$resolved_git_auth"
1106
+ debug " DOCKER_SOCKET=${DOCKER_SOCKET:-<not found>}"
1107
+ debug " CHROME_PORT=${CHROME_PORT:-9222}"
1108
+ debug " SYSTEM_CONTEXT=$ENABLE_SYSTEM_CONTEXT"
1109
+ debug " IMAGE=$IMAGE"
1110
+ debug " HOST_PATH=$HOST_PATH"
1111
+ debug " PLATFORM=$platform"
1112
+ fi
1113
+
989
1114
  # Generate container name based on path (for reuse when DCLAUDE_RM=false)
990
1115
  local container_name=""
991
1116
  if [[ "$REMOVE_CONTAINER" == "false" ]]; then
992
1117
  # Create deterministic name from path hash
993
1118
  container_name=$(get_container_name "$HOST_PATH")
994
- debug "Container name: $container_name (path: $HOST_PATH)"
1119
+ if [[ -n "$NAMESPACE" ]]; then
1120
+ debug "Container name: $container_name (namespace: $NAMESPACE, path: $HOST_PATH)"
1121
+ else
1122
+ debug "Container name: $container_name (path: $HOST_PATH)"
1123
+ fi
995
1124
 
996
1125
  # Check if container already exists
997
1126
  if docker ps -a --format '{{.Names}}' | grep -q "^${container_name}$"; then
@@ -1017,7 +1146,7 @@ main() {
1017
1146
  fi
1018
1147
 
1019
1148
  # Check if interactive (TTY available) and not in print mode
1020
- if [[ -n "$tty_flags" ]] && ! is_print_mode "${claude_args[@]}" "$@"; then
1149
+ if [[ -n "$tty_flags" ]] && ! should_skip_tmux "${claude_args[@]}" "$@"; then
1021
1150
  # Interactive and not print mode - use tmux for session management
1022
1151
  local tmux_session
1023
1152
  if [[ -n "${DCLAUDE_TMUX_SESSION:-}" ]]; then
@@ -1034,18 +1163,12 @@ main() {
1034
1163
  [[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
1035
1164
  [[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
1036
1165
  [[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
1037
- # Internal env vars for tmux status bar display
1038
- exec_env_args+=(-e "_DCLAUDE_NET=${network_mode}")
1039
- exec_env_args+=(-e "_DCLAUDE_TAG=${DCLAUDE_TAG:-latest}")
1040
- exec_env_args+=(-e "_DCLAUDE_SESSION=${DCLAUDE_TMUX_SESSION:-auto}")
1041
1166
 
1042
1167
  debug "Creating new tmux session running Claude"
1043
1168
  debug "Claude args count: ${#claude_args[@]}, user args: $*"
1044
1169
  info "Starting new Claude session..."
1045
1170
  docker exec -it -u claude "${exec_env_args[@]}" "$container_name" tmux -f /home/claude/.tmux.conf new-session -s "$tmux_session" claude "${claude_args[@]}" "$@"
1046
- local tmux_exit=$?
1047
- reset_terminal_mouse
1048
- exit $tmux_exit
1171
+ exit $?
1049
1172
  else
1050
1173
  # Non-interactive or print mode - run claude directly without tmux
1051
1174
  debug "Non-interactive or print mode, running Claude directly (no tmux)"
@@ -1072,11 +1195,15 @@ main() {
1072
1195
  # Persistent containers run in background and shouldn't have TTY/stdin attached
1073
1196
 
1074
1197
 
1198
+ # Get namespaced volume name
1199
+ local claude_volume
1200
+ claude_volume=$(get_volume_name)
1201
+
1075
1202
  DOCKER_ARGS+=(
1076
1203
  # Mount current directory
1077
1204
  -v "${HOST_PATH}:${HOST_PATH}"
1078
1205
  # Mount persistent Claude configuration volume
1079
- -v "${VOLUME_PREFIX}-claude:/home/claude/.claude"
1206
+ -v "${claude_volume}:/home/claude/.claude"
1080
1207
  # Set working directory
1081
1208
  -w "${HOST_PATH}"
1082
1209
  # Network mode
@@ -1166,7 +1293,7 @@ main() {
1166
1293
  docker exec -u root "$container_name" /usr/local/bin/docker-entrypoint.sh true >/dev/null 2>&1 || true
1167
1294
 
1168
1295
  # Check if interactive (TTY available) and not in print mode
1169
- if [[ -n "$tty_flags" ]] && ! is_print_mode "${claude_args[@]}" "$@"; then
1296
+ if [[ -n "$tty_flags" ]] && ! should_skip_tmux "${claude_args[@]}" "$@"; then
1170
1297
  # Interactive and not print mode - use tmux for session management
1171
1298
  local tmux_session
1172
1299
  if [[ -n "${DCLAUDE_TMUX_SESSION:-}" ]]; then
@@ -1183,18 +1310,12 @@ main() {
1183
1310
  [[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
1184
1311
  [[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
1185
1312
  [[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
1186
- # Internal env vars for tmux status bar display
1187
- exec_env_args+=(-e "_DCLAUDE_NET=${network_mode}")
1188
- exec_env_args+=(-e "_DCLAUDE_TAG=${DCLAUDE_TAG:-latest}")
1189
- exec_env_args+=(-e "_DCLAUDE_SESSION=${DCLAUDE_TMUX_SESSION:-auto}")
1190
1313
 
1191
1314
  debug "Creating new tmux session running Claude"
1192
1315
  debug "Claude args count: ${#claude_args[@]}, user args: $*"
1193
1316
  info "Starting new Claude session..."
1194
1317
  docker exec -it -u claude "${exec_env_args[@]}" "$container_name" tmux -f /home/claude/.tmux.conf new-session -s "$tmux_session" claude "${claude_args[@]}" "$@"
1195
- local tmux_exit=$?
1196
- reset_terminal_mouse
1197
- exit $tmux_exit
1318
+ exit $?
1198
1319
  else
1199
1320
  # Non-interactive or print mode - run claude directly without tmux
1200
1321
  debug "Non-interactive or print mode, running Claude directly (no tmux)"
@@ -1341,9 +1462,7 @@ cmd_attach() {
1341
1462
  # Attach to existing session
1342
1463
  info "Attaching to session: $session_name"
1343
1464
  docker exec -it -u claude "${exec_env_args[@]}" "$container_name" tmux -f /home/claude/.tmux.conf attach-session -t "$session_name"
1344
- local tmux_exit=$?
1345
- reset_terminal_mouse
1346
- exit $tmux_exit
1465
+ exit $?
1347
1466
  }
1348
1467
 
1349
1468
  # Subcommand: launch Chrome with DevTools and ensure MCP configured
@@ -1532,16 +1651,16 @@ cmd_update() {
1532
1651
  fi
1533
1652
 
1534
1653
  info "Updating Claude CLI..."
1535
- local npm_output
1536
- if npm_output=$(docker exec -u claude "$container_name" npm update -g @anthropic-ai/claude-code 2>&1); then
1654
+ local update_output
1655
+ if update_output=$(docker exec -u claude "$container_name" claude update 2>&1); then
1537
1656
  # Show command and output together, indented to align with "Debug: " prefix
1538
- debug "npm update -g @anthropic-ai/claude-code
1539
- $(echo "$npm_output" | sed 's/^/ /')"
1657
+ debug "claude update
1658
+ $(echo "$update_output" | sed 's/^/ /')"
1540
1659
  local new_version=$(docker exec -u claude "$container_name" claude --version 2>/dev/null | head -1)
1541
1660
  success "Claude CLI updated: $new_version"
1542
1661
  else
1543
- debug "npm update -g @anthropic-ai/claude-code
1544
- $(echo "$npm_output" | sed 's/^/ /')"
1662
+ debug "claude update
1663
+ $(echo "$update_output" | sed 's/^/ /')"
1545
1664
  error "Failed to update Claude CLI"
1546
1665
  exit 1
1547
1666
  fi
@@ -1945,6 +2064,7 @@ Environment Variables:
1945
2064
  DCLAUDE_TAG Docker image tag (default: latest)
1946
2065
  DCLAUDE_RM Remove container on exit (default: false)
1947
2066
  DCLAUDE_DEBUG Enable debug output (default: false)
2067
+ DCLAUDE_NAMESPACE Namespace for isolated credentials/config
1948
2068
  DCLAUDE_GIT_AUTH SSH auth for Git: auto, agent-forwarding, key-mount, none
1949
2069
  DCLAUDE_NETWORK Network mode: auto, host, bridge
1950
2070
  DCLAUDE_DOCKER_SOCKET Override Docker socket path
@@ -1954,6 +2074,14 @@ Environment Variables:
1954
2074
  DCLAUDE_CHROME_PORT Chrome debugging port (default: 9222)
1955
2075
  DCLAUDE_CHROME_FLAGS Additional Chrome flags (default: empty)
1956
2076
 
2077
+ Configuration File (.dclaude):
2078
+ Create a .dclaude file at project root to configure dclaude for that tree:
2079
+ NAMESPACE=mycompany
2080
+ NETWORK=host
2081
+ DEBUG=true
2082
+ dclaude walks up the directory tree to find .dclaude files.
2083
+ Environment variables override .dclaude settings.
2084
+
1957
2085
  Examples:
1958
2086
  # Start new Claude session (auto-generated session name)
1959
2087
  dclaude
@@ -1970,6 +2098,10 @@ Examples:
1970
2098
  # Start with ephemeral container (removed on exit)
1971
2099
  DCLAUDE_RM=true dclaude
1972
2100
 
2101
+ # Use namespace for isolated credentials (personal vs company)
2102
+ DCLAUDE_NAMESPACE=mycompany dclaude
2103
+ # Or create .dclaude file: echo "NAMESPACE=mycompany" > ~/projects/mycompany/.dclaude
2104
+
1973
2105
  # Update image and restart container
1974
2106
  dclaude pull # Pull latest image
1975
2107
  dclaude update # Update Claude CLI in container
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alanbem/dclaude",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "Dockerized Claude Code CLI launcher with MCP support",
5
5
  "main": "dclaude",
6
6
  "bin": {