22set -eu
33
44# Claude Code CLI Local Feature Install Script
5- # Based on: https://github.com/anthropics/devcontainer-features/pull/25
6- # Combines CLI installation with configuration directory setup
5+ # Installs Claude Code via pixi and sets up configuration directories
76
8- # Function to install Claude Code CLI
7+ # Function to install Claude Code CLI via pixi
98install_claude_code () {
10- echo " Installing Claude Code CLI globally ..."
9+ echo " Installing Claude Code CLI via pixi ..."
1110
12- # Verify Node.js and npm are available (should be installed via dependsOn)
13- if ! command -v node > /dev/null || ! command -v npm > /dev/null; then
11+ # Verify pixi is available
12+ if ! command -v pixi > /dev/null; then
1413 cat << EOF
1514
16- ERROR: Node.js and npm are required but not found!
15+ ERROR: pixi is required but not found!
1716
18- This should not happen as the Node.js feature is declared in 'dependsOn'.
19-
20- Please check:
21- 1. The devcontainer feature specification is correct
22- 2. The Node.js feature (ghcr.io/devcontainers/features/node) is available
23- 3. Your devcontainer build logs for errors
17+ Please ensure pixi is installed in your base image.
18+ See: https://pixi.sh/latest/
2419
2520EOF
2621 exit 1
2722 fi
2823
29- # Install with npm
30- npm install -g @anthropic-ai/claude-code
24+ # Determine target user for pixi global install
25+ local target_user=" ${_REMOTE_USER:- vscode} "
26+ local target_home=" ${_REMOTE_USER_HOME:-/ home/ ${target_user} } "
27+
28+ # Install with pixi global from blooop channel
29+ # Run as target user so it installs to their home directory
30+ if [ " $( id -u) " -eq 0 ] && [ " $target_user " != " root" ]; then
31+ su - " $target_user " -c " pixi global install --channel https://prefix.dev/blooop claude-shim"
32+ else
33+ pixi global install --channel https://prefix.dev/blooop claude-shim
34+ fi
3135
32- # Verify installation
33- if command -v claude > /dev/null; then
36+ # Verify installation by checking the binary exists
37+ local pixi_bin_path=" $target_home /.pixi/bin"
38+ local claude_bin=" $pixi_bin_path /claude"
39+ if [ -x " $claude_bin " ]; then
3440 echo " Claude Code CLI installed successfully!"
35- claude --version
41+ " $claude_bin " --version
3642 return 0
3743 else
38- echo " ERROR: Claude Code CLI installation failed!"
44+ echo " ERROR: Claude Code CLI installation failed! Binary not found at $claude_bin "
3945 return 1
4046 fi
4147}
4248
4349# Function to create Claude configuration directories
44- # These directories will be mounted from the host, but we create them
45- # in the container to ensure they exist and have proper permissions
4650create_claude_directories () {
4751 echo " Creating Claude configuration directories..."
4852
4953 # Determine the target user's home directory
50- # $_REMOTE_USER is set by devcontainer, fallback to 'vscode'
5154 local target_user=" ${_REMOTE_USER:- vscode} "
5255 local target_home=" ${_REMOTE_USER_HOME:-/ home/ ${target_user} } "
5356
54- # Be defensive: if the resolved home does not exist, fall back to $HOME,
55- # then to /home/${target_user}. If neither is available, fail clearly.
57+ # Be defensive: if the resolved home does not exist, fall back
5658 if [ ! -d " $target_home " ]; then
5759 if [ -n " ${HOME:- } " ] && [ -d " $HOME " ]; then
5860 echo " Warning: target_home '$target_home ' does not exist, falling back to \$ HOME: $HOME " >&2
@@ -61,26 +63,21 @@ create_claude_directories() {
6163 echo " Warning: target_home '$target_home ' does not exist, falling back to /home/${target_user} " >&2
6264 target_home=" /home/${target_user} "
6365 else
64- echo " Error: No suitable home directory found for '${target_user} '. Tried:" >&2
65- echo " - _REMOTE_USER_HOME='${_REMOTE_USER_HOME:- } '" >&2
66- echo " - \$ HOME='${HOME:- } '" >&2
67- echo " - /home/${target_user} " >&2
68- echo " Please set _REMOTE_USER_HOME to a valid, writable directory." >&2
66+ echo " Error: No suitable home directory found for '${target_user} '." >&2
6967 exit 1
7068 fi
7169 fi
7270
7371 echo " Target home directory: $target_home "
7472 echo " Target user: $target_user "
7573
76- # Create the main .claude directory
74+ # Create the main .claude directory and subdirectories
7775 mkdir -p " $target_home /.claude"
7876 mkdir -p " $target_home /.claude/agents"
7977 mkdir -p " $target_home /.claude/commands"
8078 mkdir -p " $target_home /.claude/hooks"
8179
8280 # Create empty config files if they don't exist
83- # This ensures the bind mounts won't fail if files are missing on host
8481 if [ ! -f " $target_home /.claude/.credentials.json" ]; then
8582 echo " {}" > " $target_home /.claude/.credentials.json"
8683 chmod 600 " $target_home /.claude/.credentials.json"
@@ -92,26 +89,28 @@ create_claude_directories() {
9289 fi
9390
9491 # Set proper ownership
95- # Note: These will be overridden by bind mounts from the host,
96- # but this ensures the directories exist with correct permissions
97- # if the mounts fail or for non-mounted directories
9892 if [ " $( id -u) " -eq 0 ]; then
9993 chown -R " $target_user :$target_user " " $target_home /.claude" || true
10094 fi
10195
10296 echo " Claude directories created successfully"
10397}
10498
105- # Main script starts here
99+ # Main script
106100main () {
107101 echo " ========================================="
108102 echo " Activating feature 'claude-code' (local)"
109103 echo " ========================================="
110104
111- # Install Claude Code CLI (or verify it's already installed)
112- if command -v claude > /dev/null; then
105+ # Determine target paths
106+ local target_user=" ${_REMOTE_USER:- vscode} "
107+ local target_home=" ${_REMOTE_USER_HOME:-/ home/ ${target_user} } "
108+ local claude_bin=" $target_home /.pixi/bin/claude"
109+
110+ # Install Claude Code CLI
111+ if [ -x " $claude_bin " ]; then
113112 echo " Claude Code CLI is already installed"
114- claude --version
113+ " $claude_bin " --version
115114 else
116115 install_claude_code || exit 1
117116 fi
@@ -123,27 +122,11 @@ main() {
123122 echo " Claude Code feature activated successfully!"
124123 echo " ========================================="
125124 echo " "
126- echo " Configuration files mounted from host:"
127- echo " Read-Write (auth & state):"
128- echo " - ~/.claude/.credentials.json (OAuth tokens)"
129- echo " - ~/.claude/.claude.json (account, setup tracking)"
130- echo " "
131- echo " Read-Only (security-protected):"
132- echo " - ~/.claude/CLAUDE.md"
133- echo " - ~/.claude/settings.json"
134- echo " - ~/.claude/agents/"
135- echo " - ~/.claude/commands/"
136- echo " - ~/.claude/hooks/"
137- echo " "
138- echo " Authentication:"
139- echo " - If you're already authenticated on your host, credentials are shared"
140- echo " - Otherwise, run 'claude' and follow the OAuth flow"
141- echo " - The OAuth callback may open in your host browser"
142- echo " - Credentials are stored on your host at ~/.claude/.credentials.json"
125+ echo " Configuration is stored in a Docker volume (claude-config)"
126+ echo " and persists between container rebuilds."
143127 echo " "
144- echo " To modify config files, edit on your host machine and rebuild the container ."
128+ echo " To authenticate, run 'claude' and follow the OAuth flow ."
145129 echo " "
146130}
147131
148- # Execute main function
149132main
0 commit comments