@alanbem/dclaude 0.0.10 → 0.0.12
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.
- package/README.md +53 -0
- package/dclaude +111 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -151,6 +151,21 @@ dclaude chrome # Launch Chrome with DevTools
|
|
|
151
151
|
dclaude # Claude can now interact with the browser
|
|
152
152
|
```
|
|
153
153
|
|
|
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
|
+
|
|
154
169
|
### System Context
|
|
155
170
|
|
|
156
171
|
dclaude automatically tells Claude about its container environment so it can give better suggestions:
|
|
@@ -175,10 +190,12 @@ DCLAUDE_SYSTEM_CONTEXT=false dclaude
|
|
|
175
190
|
| `DCLAUDE_NAMESPACE` | (none) | Namespace for isolated credentials/config |
|
|
176
191
|
| `DCLAUDE_NETWORK` | `auto` | Network mode: `auto`, `host`, `bridge` |
|
|
177
192
|
| `DCLAUDE_GIT_AUTH` | `auto` | SSH auth: `auto`, `agent-forwarding`, `key-mount`, `none` |
|
|
193
|
+
| `DCLAUDE_MOUNT_ROOT` | (working dir) | Mount parent directory for sibling access |
|
|
178
194
|
| `DCLAUDE_DEBUG` | `false` | Enable debug output |
|
|
179
195
|
| `DCLAUDE_QUIET` | `false` | Suppress info messages |
|
|
180
196
|
| `DCLAUDE_NO_UPDATE` | `false` | Skip image update check |
|
|
181
197
|
| `DCLAUDE_SYSTEM_CONTEXT` | `true` | Inform Claude about container environment |
|
|
198
|
+
| `DCLAUDE_ITERM2` | `true` | Enable iTerm2 shell integration (only affects iTerm2) |
|
|
182
199
|
|
|
183
200
|
## Configuration File
|
|
184
201
|
|
|
@@ -200,6 +217,7 @@ dclaude walks up the directory tree to find `.dclaude` files. Any dclaude sessio
|
|
|
200
217
|
| `NAMESPACE` | Isolate credentials/config (see Namespace Isolation) |
|
|
201
218
|
| `NETWORK` | Network mode (`host`, `bridge`) |
|
|
202
219
|
| `GIT_AUTH` | Git auth mode |
|
|
220
|
+
| `MOUNT_ROOT` | Mount directory (relative to config file, or absolute path) |
|
|
203
221
|
| `DEBUG` | Enable debug output (`true`, `false`) |
|
|
204
222
|
| `CHROME_PORT` | Chrome DevTools port |
|
|
205
223
|
|
|
@@ -238,6 +256,41 @@ Each namespace gets its own:
|
|
|
238
256
|
- Git configuration
|
|
239
257
|
- Container instance
|
|
240
258
|
|
|
259
|
+
## Mount Root (Parent Directory Access)
|
|
260
|
+
|
|
261
|
+
By default, dclaude only mounts the current working directory. Use `MOUNT_ROOT` to mount a parent directory, enabling access to sibling directories.
|
|
262
|
+
|
|
263
|
+
**Use case:** You're working in a subdirectory but need access to related projects:
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
/Users/alan/projects/mycompany/
|
|
267
|
+
├── shared-libs/ # Common libraries
|
|
268
|
+
├── api-service/ # API backend
|
|
269
|
+
├── web-app/ # Frontend
|
|
270
|
+
└── infrastructure/
|
|
271
|
+
└── terraform/ # ← You're here, but need access to siblings
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Option 1: Using `.dclaude` file (recommended)**
|
|
275
|
+
```bash
|
|
276
|
+
# Create config at the directory you want as mount root
|
|
277
|
+
echo "MOUNT_ROOT=." > ~/projects/mycompany/.dclaude
|
|
278
|
+
|
|
279
|
+
# Relative paths are resolved from config file's directory
|
|
280
|
+
echo "MOUNT_ROOT=.." > ~/projects/mycompany/subdir/.dclaude # mounts mycompany/
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Option 2: Using environment variable**
|
|
284
|
+
```bash
|
|
285
|
+
# Relative path (from working directory)
|
|
286
|
+
DCLAUDE_MOUNT_ROOT=../.. dclaude
|
|
287
|
+
|
|
288
|
+
# Absolute path
|
|
289
|
+
DCLAUDE_MOUNT_ROOT=/Users/alan/projects/mycompany dclaude
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Now Claude can see and work with all sibling directories while your working directory remains `terraform/`.
|
|
293
|
+
|
|
241
294
|
## Networking
|
|
242
295
|
|
|
243
296
|
dclaude auto-detects the best networking mode:
|
package/dclaude
CHANGED
|
@@ -27,6 +27,7 @@ CHROME_FLAGS="${DCLAUDE_CHROME_FLAGS:-}"
|
|
|
27
27
|
# - DCLAUDE_GIT_AUTH (git auth mode)
|
|
28
28
|
# - DCLAUDE_DEBUG (enable debug output)
|
|
29
29
|
# - DCLAUDE_CHROME_PORT (chrome devtools port)
|
|
30
|
+
# - DCLAUDE_MOUNT_ROOT (mount a parent directory instead of working directory)
|
|
30
31
|
|
|
31
32
|
# Colors for output (only if terminal supports it)
|
|
32
33
|
if [[ -t 1 ]]; then
|
|
@@ -122,6 +123,18 @@ load_config_file() {
|
|
|
122
123
|
CHROME_PORT)
|
|
123
124
|
[[ -z "${DCLAUDE_CHROME_PORT:-}" ]] && DCLAUDE_CHROME_PORT="$value"
|
|
124
125
|
;;
|
|
126
|
+
MOUNT_ROOT)
|
|
127
|
+
if [[ -z "${DCLAUDE_MOUNT_ROOT:-}" ]]; then
|
|
128
|
+
# Resolve relative paths relative to config file's directory
|
|
129
|
+
if [[ "$value" != /* ]]; then
|
|
130
|
+
local config_dir
|
|
131
|
+
config_dir=$(dirname "$config_file")
|
|
132
|
+
DCLAUDE_MOUNT_ROOT=$(cd "$config_dir" && cd "$value" 2>/dev/null && pwd)
|
|
133
|
+
else
|
|
134
|
+
DCLAUDE_MOUNT_ROOT="$value"
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
;;
|
|
125
138
|
esac
|
|
126
139
|
fi
|
|
127
140
|
done < "$config_file"
|
|
@@ -147,6 +160,7 @@ if [[ -n "$DCLAUDE_CONFIG_FILE" ]]; then
|
|
|
147
160
|
[[ -n "${DCLAUDE_NETWORK:-}" ]] && debug " NETWORK=$DCLAUDE_NETWORK"
|
|
148
161
|
[[ -n "${DCLAUDE_GIT_AUTH:-}" ]] && debug " GIT_AUTH=$DCLAUDE_GIT_AUTH"
|
|
149
162
|
[[ -n "${DCLAUDE_CHROME_PORT:-}" ]] && debug " CHROME_PORT=$DCLAUDE_CHROME_PORT"
|
|
163
|
+
[[ -n "${DCLAUDE_MOUNT_ROOT:-}" ]] && debug " MOUNT_ROOT=$DCLAUDE_MOUNT_ROOT"
|
|
150
164
|
fi
|
|
151
165
|
fi
|
|
152
166
|
|
|
@@ -577,6 +591,55 @@ get_host_path() {
|
|
|
577
591
|
echo "$host_path"
|
|
578
592
|
}
|
|
579
593
|
|
|
594
|
+
# Compute mount root path from DCLAUDE_MOUNT_ROOT
|
|
595
|
+
# Supports: absolute paths, relative paths (../, ../../, etc.)
|
|
596
|
+
# Returns: Absolute path to mount root, or HOST_PATH if not set
|
|
597
|
+
get_mount_root() {
|
|
598
|
+
local host_path="$1"
|
|
599
|
+
local mount_root="${DCLAUDE_MOUNT_ROOT:-}"
|
|
600
|
+
|
|
601
|
+
# If not set, use host path (current behavior)
|
|
602
|
+
if [[ -z "$mount_root" ]]; then
|
|
603
|
+
echo "$host_path"
|
|
604
|
+
return 0
|
|
605
|
+
fi
|
|
606
|
+
|
|
607
|
+
local resolved_root
|
|
608
|
+
|
|
609
|
+
# Handle relative paths (resolve relative to host_path)
|
|
610
|
+
if [[ "$mount_root" != /* ]]; then
|
|
611
|
+
# Relative path - resolve it
|
|
612
|
+
resolved_root=$(cd "$host_path" && cd "$mount_root" 2>/dev/null && pwd) || {
|
|
613
|
+
error "Mount root path not accessible: $mount_root (relative to $host_path)"
|
|
614
|
+
exit 1
|
|
615
|
+
}
|
|
616
|
+
else
|
|
617
|
+
# Absolute path - use directly
|
|
618
|
+
resolved_root="$mount_root"
|
|
619
|
+
fi
|
|
620
|
+
|
|
621
|
+
# Validate mount root exists
|
|
622
|
+
if [[ ! -d "$resolved_root" ]]; then
|
|
623
|
+
error "Mount root directory does not exist: $resolved_root"
|
|
624
|
+
exit 1
|
|
625
|
+
fi
|
|
626
|
+
|
|
627
|
+
# Validate that host_path is under mount_root
|
|
628
|
+
# Use realpath to normalize paths for comparison
|
|
629
|
+
local real_host real_root
|
|
630
|
+
real_host=$(cd "$host_path" && pwd -P)
|
|
631
|
+
real_root=$(cd "$resolved_root" && pwd -P)
|
|
632
|
+
|
|
633
|
+
if [[ "$real_host" != "$real_root" && "$real_host" != "$real_root"/* ]]; then
|
|
634
|
+
error "Working directory is not under mount root"
|
|
635
|
+
error " Working directory: $real_host"
|
|
636
|
+
error " Mount root: $real_root"
|
|
637
|
+
exit 1
|
|
638
|
+
fi
|
|
639
|
+
|
|
640
|
+
echo "$resolved_root"
|
|
641
|
+
}
|
|
642
|
+
|
|
580
643
|
# Generate deterministic container name from path (and namespace if set)
|
|
581
644
|
get_container_name() {
|
|
582
645
|
local path="${1:-$HOST_PATH}"
|
|
@@ -955,13 +1018,15 @@ detect_tty_flags() {
|
|
|
955
1018
|
echo "$tty_flags"
|
|
956
1019
|
}
|
|
957
1020
|
|
|
958
|
-
# Check if Claude is being run in
|
|
959
|
-
#
|
|
960
|
-
|
|
1021
|
+
# Check if Claude is being run in a mode that should skip tmux
|
|
1022
|
+
# These are flags that just output something and exit (no interactive session)
|
|
1023
|
+
should_skip_tmux() {
|
|
961
1024
|
for arg in "$@"; do
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1025
|
+
case "$arg" in
|
|
1026
|
+
-p|--print|--version|-v|--help|-h)
|
|
1027
|
+
return 0
|
|
1028
|
+
;;
|
|
1029
|
+
esac
|
|
965
1030
|
done
|
|
966
1031
|
return 1
|
|
967
1032
|
}
|
|
@@ -974,6 +1039,9 @@ main() {
|
|
|
974
1039
|
|
|
975
1040
|
info "Verifying environment..."
|
|
976
1041
|
debug "Host path: $HOST_PATH"
|
|
1042
|
+
if [[ "$MOUNT_ROOT" != "$HOST_PATH" ]]; then
|
|
1043
|
+
debug "Mount root: $MOUNT_ROOT"
|
|
1044
|
+
fi
|
|
977
1045
|
|
|
978
1046
|
# Check prerequisites
|
|
979
1047
|
check_docker
|
|
@@ -1106,6 +1174,9 @@ main() {
|
|
|
1106
1174
|
debug " SYSTEM_CONTEXT=$ENABLE_SYSTEM_CONTEXT"
|
|
1107
1175
|
debug " IMAGE=$IMAGE"
|
|
1108
1176
|
debug " HOST_PATH=$HOST_PATH"
|
|
1177
|
+
if [[ "$MOUNT_ROOT" != "$HOST_PATH" ]]; then
|
|
1178
|
+
debug " MOUNT_ROOT=$MOUNT_ROOT"
|
|
1179
|
+
fi
|
|
1109
1180
|
debug " PLATFORM=$platform"
|
|
1110
1181
|
fi
|
|
1111
1182
|
|
|
@@ -1143,8 +1214,18 @@ main() {
|
|
|
1143
1214
|
done
|
|
1144
1215
|
fi
|
|
1145
1216
|
|
|
1217
|
+
# Ensure SSH proxy is running if container uses agent forwarding (macOS)
|
|
1218
|
+
if [[ "$platform" == "darwin" ]]; then
|
|
1219
|
+
local container_ssh_sock
|
|
1220
|
+
container_ssh_sock=$(docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' "$container_name" 2>/dev/null | grep "^SSH_AUTH_SOCK=" | cut -d= -f2)
|
|
1221
|
+
if [[ "$container_ssh_sock" == "/tmp/ssh-proxy/agent" ]]; then
|
|
1222
|
+
debug "Container uses SSH agent forwarding, ensuring proxy is running"
|
|
1223
|
+
setup_ssh_proxy_container
|
|
1224
|
+
fi
|
|
1225
|
+
fi
|
|
1226
|
+
|
|
1146
1227
|
# Check if interactive (TTY available) and not in print mode
|
|
1147
|
-
if [[ -n "$tty_flags" ]] && !
|
|
1228
|
+
if [[ -n "$tty_flags" ]] && ! should_skip_tmux "${claude_args[@]}" "$@"; then
|
|
1148
1229
|
# Interactive and not print mode - use tmux for session management
|
|
1149
1230
|
local tmux_session
|
|
1150
1231
|
if [[ -n "${DCLAUDE_TMUX_SESSION:-}" ]]; then
|
|
@@ -1161,6 +1242,7 @@ main() {
|
|
|
1161
1242
|
[[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
|
|
1162
1243
|
[[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
|
|
1163
1244
|
[[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
|
|
1245
|
+
[[ -n "${DCLAUDE_ITERM2:-}" ]] && exec_env_args+=(-e "DCLAUDE_ITERM2=${DCLAUDE_ITERM2}")
|
|
1164
1246
|
|
|
1165
1247
|
debug "Creating new tmux session running Claude"
|
|
1166
1248
|
debug "Claude args count: ${#claude_args[@]}, user args: $*"
|
|
@@ -1198,11 +1280,11 @@ main() {
|
|
|
1198
1280
|
claude_volume=$(get_volume_name)
|
|
1199
1281
|
|
|
1200
1282
|
DOCKER_ARGS+=(
|
|
1201
|
-
# Mount
|
|
1202
|
-
-v "${
|
|
1283
|
+
# Mount directory tree (mount root allows access to parent directories)
|
|
1284
|
+
-v "${MOUNT_ROOT}:${MOUNT_ROOT}"
|
|
1203
1285
|
# Mount persistent Claude configuration volume
|
|
1204
1286
|
-v "${claude_volume}:/home/claude/.claude"
|
|
1205
|
-
# Set working directory
|
|
1287
|
+
# Set working directory (within the mounted tree)
|
|
1206
1288
|
-w "${HOST_PATH}"
|
|
1207
1289
|
# Network mode
|
|
1208
1290
|
--network="$network_mode"
|
|
@@ -1238,6 +1320,11 @@ main() {
|
|
|
1238
1320
|
DOCKER_ARGS+=(-e "COLORTERM=${COLORTERM}")
|
|
1239
1321
|
fi
|
|
1240
1322
|
|
|
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
|
+
|
|
1241
1328
|
# Mount Docker socket if detected (detection done earlier for system context)
|
|
1242
1329
|
if [[ -n "$DOCKER_SOCKET" ]] && [[ -S "$DOCKER_SOCKET" ]]; then
|
|
1243
1330
|
DOCKER_ARGS+=(-v "${DOCKER_SOCKET}:/var/run/docker.sock")
|
|
@@ -1291,7 +1378,7 @@ main() {
|
|
|
1291
1378
|
docker exec -u root "$container_name" /usr/local/bin/docker-entrypoint.sh true >/dev/null 2>&1 || true
|
|
1292
1379
|
|
|
1293
1380
|
# Check if interactive (TTY available) and not in print mode
|
|
1294
|
-
if [[ -n "$tty_flags" ]] && !
|
|
1381
|
+
if [[ -n "$tty_flags" ]] && ! should_skip_tmux "${claude_args[@]}" "$@"; then
|
|
1295
1382
|
# Interactive and not print mode - use tmux for session management
|
|
1296
1383
|
local tmux_session
|
|
1297
1384
|
if [[ -n "${DCLAUDE_TMUX_SESSION:-}" ]]; then
|
|
@@ -1308,6 +1395,7 @@ main() {
|
|
|
1308
1395
|
[[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
|
|
1309
1396
|
[[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
|
|
1310
1397
|
[[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
|
|
1398
|
+
[[ -n "${DCLAUDE_ITERM2:-}" ]] && exec_env_args+=(-e "DCLAUDE_ITERM2=${DCLAUDE_ITERM2}")
|
|
1311
1399
|
|
|
1312
1400
|
debug "Creating new tmux session running Claude"
|
|
1313
1401
|
debug "Claude args count: ${#claude_args[@]}, user args: $*"
|
|
@@ -1456,6 +1544,7 @@ cmd_attach() {
|
|
|
1456
1544
|
[[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
|
|
1457
1545
|
[[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
|
|
1458
1546
|
[[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
|
|
1547
|
+
[[ -n "${DCLAUDE_ITERM2:-}" ]] && exec_env_args+=(-e "DCLAUDE_ITERM2=${DCLAUDE_ITERM2}")
|
|
1459
1548
|
|
|
1460
1549
|
# Attach to existing session
|
|
1461
1550
|
info "Attaching to session: $session_name"
|
|
@@ -1512,7 +1601,7 @@ cmd_chrome() {
|
|
|
1512
1601
|
fi
|
|
1513
1602
|
|
|
1514
1603
|
# 2. Setup profile directory
|
|
1515
|
-
local profile_dir="$HOST_PATH/.dclaude/chrome/profiles/$CHROME_PROFILE"
|
|
1604
|
+
local profile_dir="$HOST_PATH/.dclaude.d/chrome/profiles/$CHROME_PROFILE"
|
|
1516
1605
|
mkdir -p "$profile_dir"
|
|
1517
1606
|
debug "Profile directory: $profile_dir"
|
|
1518
1607
|
|
|
@@ -1987,8 +2076,9 @@ cmd_git() {
|
|
|
1987
2076
|
exit 0
|
|
1988
2077
|
}
|
|
1989
2078
|
|
|
1990
|
-
# Initialize HOST_PATH once for all commands
|
|
2079
|
+
# Initialize HOST_PATH and MOUNT_ROOT once for all commands
|
|
1991
2080
|
HOST_PATH=$(get_host_path)
|
|
2081
|
+
MOUNT_ROOT=$(get_mount_root "$HOST_PATH")
|
|
1992
2082
|
|
|
1993
2083
|
# Handle subcommands
|
|
1994
2084
|
if [[ $# -gt 0 ]]; then
|
|
@@ -2065,6 +2155,7 @@ Environment Variables:
|
|
|
2065
2155
|
DCLAUDE_NAMESPACE Namespace for isolated credentials/config
|
|
2066
2156
|
DCLAUDE_GIT_AUTH SSH auth for Git: auto, agent-forwarding, key-mount, none
|
|
2067
2157
|
DCLAUDE_NETWORK Network mode: auto, host, bridge
|
|
2158
|
+
DCLAUDE_MOUNT_ROOT Mount parent directory (absolute or relative path)
|
|
2068
2159
|
DCLAUDE_DOCKER_SOCKET Override Docker socket path
|
|
2069
2160
|
DCLAUDE_TMUX_SESSION Custom tmux session name (default: claude-TIMESTAMP)
|
|
2070
2161
|
DCLAUDE_CHROME_BIN Chrome executable path (auto-detected if not set)
|
|
@@ -2077,6 +2168,8 @@ Configuration File (.dclaude):
|
|
|
2077
2168
|
NAMESPACE=mycompany
|
|
2078
2169
|
NETWORK=host
|
|
2079
2170
|
DEBUG=true
|
|
2171
|
+
MOUNT_ROOT=. # Mount config file's directory
|
|
2172
|
+
MOUNT_ROOT=.. # Mount parent of config file's directory
|
|
2080
2173
|
dclaude walks up the directory tree to find .dclaude files.
|
|
2081
2174
|
Environment variables override .dclaude settings.
|
|
2082
2175
|
|
|
@@ -2100,6 +2193,11 @@ Examples:
|
|
|
2100
2193
|
DCLAUDE_NAMESPACE=mycompany dclaude
|
|
2101
2194
|
# Or create .dclaude file: echo "NAMESPACE=mycompany" > ~/projects/mycompany/.dclaude
|
|
2102
2195
|
|
|
2196
|
+
# Mount parent directory to access sibling directories
|
|
2197
|
+
DCLAUDE_MOUNT_ROOT=.. dclaude # Relative path
|
|
2198
|
+
DCLAUDE_MOUNT_ROOT=/Users/alan/projects dclaude # Absolute path
|
|
2199
|
+
# Or in .dclaude file: MOUNT_ROOT=..
|
|
2200
|
+
|
|
2103
2201
|
# Update image and restart container
|
|
2104
2202
|
dclaude pull # Pull latest image
|
|
2105
2203
|
dclaude update # Update Claude CLI in container
|