@alanbem/dclaude 0.0.11 → 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 +101 -5
- 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}"
|
|
@@ -976,6 +1039,9 @@ main() {
|
|
|
976
1039
|
|
|
977
1040
|
info "Verifying environment..."
|
|
978
1041
|
debug "Host path: $HOST_PATH"
|
|
1042
|
+
if [[ "$MOUNT_ROOT" != "$HOST_PATH" ]]; then
|
|
1043
|
+
debug "Mount root: $MOUNT_ROOT"
|
|
1044
|
+
fi
|
|
979
1045
|
|
|
980
1046
|
# Check prerequisites
|
|
981
1047
|
check_docker
|
|
@@ -1108,6 +1174,9 @@ main() {
|
|
|
1108
1174
|
debug " SYSTEM_CONTEXT=$ENABLE_SYSTEM_CONTEXT"
|
|
1109
1175
|
debug " IMAGE=$IMAGE"
|
|
1110
1176
|
debug " HOST_PATH=$HOST_PATH"
|
|
1177
|
+
if [[ "$MOUNT_ROOT" != "$HOST_PATH" ]]; then
|
|
1178
|
+
debug " MOUNT_ROOT=$MOUNT_ROOT"
|
|
1179
|
+
fi
|
|
1111
1180
|
debug " PLATFORM=$platform"
|
|
1112
1181
|
fi
|
|
1113
1182
|
|
|
@@ -1145,6 +1214,16 @@ main() {
|
|
|
1145
1214
|
done
|
|
1146
1215
|
fi
|
|
1147
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
|
+
|
|
1148
1227
|
# Check if interactive (TTY available) and not in print mode
|
|
1149
1228
|
if [[ -n "$tty_flags" ]] && ! should_skip_tmux "${claude_args[@]}" "$@"; then
|
|
1150
1229
|
# Interactive and not print mode - use tmux for session management
|
|
@@ -1163,6 +1242,7 @@ main() {
|
|
|
1163
1242
|
[[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
|
|
1164
1243
|
[[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
|
|
1165
1244
|
[[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
|
|
1245
|
+
[[ -n "${DCLAUDE_ITERM2:-}" ]] && exec_env_args+=(-e "DCLAUDE_ITERM2=${DCLAUDE_ITERM2}")
|
|
1166
1246
|
|
|
1167
1247
|
debug "Creating new tmux session running Claude"
|
|
1168
1248
|
debug "Claude args count: ${#claude_args[@]}, user args: $*"
|
|
@@ -1200,11 +1280,11 @@ main() {
|
|
|
1200
1280
|
claude_volume=$(get_volume_name)
|
|
1201
1281
|
|
|
1202
1282
|
DOCKER_ARGS+=(
|
|
1203
|
-
# Mount
|
|
1204
|
-
-v "${
|
|
1283
|
+
# Mount directory tree (mount root allows access to parent directories)
|
|
1284
|
+
-v "${MOUNT_ROOT}:${MOUNT_ROOT}"
|
|
1205
1285
|
# Mount persistent Claude configuration volume
|
|
1206
1286
|
-v "${claude_volume}:/home/claude/.claude"
|
|
1207
|
-
# Set working directory
|
|
1287
|
+
# Set working directory (within the mounted tree)
|
|
1208
1288
|
-w "${HOST_PATH}"
|
|
1209
1289
|
# Network mode
|
|
1210
1290
|
--network="$network_mode"
|
|
@@ -1240,6 +1320,11 @@ main() {
|
|
|
1240
1320
|
DOCKER_ARGS+=(-e "COLORTERM=${COLORTERM}")
|
|
1241
1321
|
fi
|
|
1242
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
|
+
|
|
1243
1328
|
# Mount Docker socket if detected (detection done earlier for system context)
|
|
1244
1329
|
if [[ -n "$DOCKER_SOCKET" ]] && [[ -S "$DOCKER_SOCKET" ]]; then
|
|
1245
1330
|
DOCKER_ARGS+=(-v "${DOCKER_SOCKET}:/var/run/docker.sock")
|
|
@@ -1310,6 +1395,7 @@ main() {
|
|
|
1310
1395
|
[[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
|
|
1311
1396
|
[[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
|
|
1312
1397
|
[[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
|
|
1398
|
+
[[ -n "${DCLAUDE_ITERM2:-}" ]] && exec_env_args+=(-e "DCLAUDE_ITERM2=${DCLAUDE_ITERM2}")
|
|
1313
1399
|
|
|
1314
1400
|
debug "Creating new tmux session running Claude"
|
|
1315
1401
|
debug "Claude args count: ${#claude_args[@]}, user args: $*"
|
|
@@ -1458,6 +1544,7 @@ cmd_attach() {
|
|
|
1458
1544
|
[[ -n "${TERM_PROGRAM_VERSION:-}" ]] && exec_env_args+=(-e "TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
|
|
1459
1545
|
[[ -n "${TERM_SESSION_ID:-}" ]] && exec_env_args+=(-e "TERM_SESSION_ID=${TERM_SESSION_ID}")
|
|
1460
1546
|
[[ -n "${COLORTERM:-}" ]] && exec_env_args+=(-e "COLORTERM=${COLORTERM}")
|
|
1547
|
+
[[ -n "${DCLAUDE_ITERM2:-}" ]] && exec_env_args+=(-e "DCLAUDE_ITERM2=${DCLAUDE_ITERM2}")
|
|
1461
1548
|
|
|
1462
1549
|
# Attach to existing session
|
|
1463
1550
|
info "Attaching to session: $session_name"
|
|
@@ -1514,7 +1601,7 @@ cmd_chrome() {
|
|
|
1514
1601
|
fi
|
|
1515
1602
|
|
|
1516
1603
|
# 2. Setup profile directory
|
|
1517
|
-
local profile_dir="$HOST_PATH/.dclaude/chrome/profiles/$CHROME_PROFILE"
|
|
1604
|
+
local profile_dir="$HOST_PATH/.dclaude.d/chrome/profiles/$CHROME_PROFILE"
|
|
1518
1605
|
mkdir -p "$profile_dir"
|
|
1519
1606
|
debug "Profile directory: $profile_dir"
|
|
1520
1607
|
|
|
@@ -1989,8 +2076,9 @@ cmd_git() {
|
|
|
1989
2076
|
exit 0
|
|
1990
2077
|
}
|
|
1991
2078
|
|
|
1992
|
-
# Initialize HOST_PATH once for all commands
|
|
2079
|
+
# Initialize HOST_PATH and MOUNT_ROOT once for all commands
|
|
1993
2080
|
HOST_PATH=$(get_host_path)
|
|
2081
|
+
MOUNT_ROOT=$(get_mount_root "$HOST_PATH")
|
|
1994
2082
|
|
|
1995
2083
|
# Handle subcommands
|
|
1996
2084
|
if [[ $# -gt 0 ]]; then
|
|
@@ -2067,6 +2155,7 @@ Environment Variables:
|
|
|
2067
2155
|
DCLAUDE_NAMESPACE Namespace for isolated credentials/config
|
|
2068
2156
|
DCLAUDE_GIT_AUTH SSH auth for Git: auto, agent-forwarding, key-mount, none
|
|
2069
2157
|
DCLAUDE_NETWORK Network mode: auto, host, bridge
|
|
2158
|
+
DCLAUDE_MOUNT_ROOT Mount parent directory (absolute or relative path)
|
|
2070
2159
|
DCLAUDE_DOCKER_SOCKET Override Docker socket path
|
|
2071
2160
|
DCLAUDE_TMUX_SESSION Custom tmux session name (default: claude-TIMESTAMP)
|
|
2072
2161
|
DCLAUDE_CHROME_BIN Chrome executable path (auto-detected if not set)
|
|
@@ -2079,6 +2168,8 @@ Configuration File (.dclaude):
|
|
|
2079
2168
|
NAMESPACE=mycompany
|
|
2080
2169
|
NETWORK=host
|
|
2081
2170
|
DEBUG=true
|
|
2171
|
+
MOUNT_ROOT=. # Mount config file's directory
|
|
2172
|
+
MOUNT_ROOT=.. # Mount parent of config file's directory
|
|
2082
2173
|
dclaude walks up the directory tree to find .dclaude files.
|
|
2083
2174
|
Environment variables override .dclaude settings.
|
|
2084
2175
|
|
|
@@ -2102,6 +2193,11 @@ Examples:
|
|
|
2102
2193
|
DCLAUDE_NAMESPACE=mycompany dclaude
|
|
2103
2194
|
# Or create .dclaude file: echo "NAMESPACE=mycompany" > ~/projects/mycompany/.dclaude
|
|
2104
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
|
+
|
|
2105
2201
|
# Update image and restart container
|
|
2106
2202
|
dclaude pull # Pull latest image
|
|
2107
2203
|
dclaude update # Update Claude CLI in container
|