Skip to content

Commit 1eebfc5

Browse files
committed
fix(native-installer): POSIX compat, bash installer, onboarding hook
- Replace &>/dev/null with >/dev/null 2>&1 (POSIX) - Require curl only (not wget) — matches official installer dependency - Use bash instead of sh for piping installer (it requires bash) - Quote ${TARGET} in su -c to prevent word splitting - Pre-create ~/.local/state and ~/.claude directories - Add 99-claude-onboarding.sh post-start hook to ensure hasCompletedOnboarding is set when token auth is configured
1 parent aebb2a3 commit 1eebfc5

File tree

2 files changed

+55
-11
lines changed

2 files changed

+55
-11
lines changed

.devcontainer/CHANGELOG.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,15 @@
1010
#### Skills
1111
- **worktree** — New skill for git worktree creation, management, and cleanup. Covers `EnterWorktree` tool, `--worktree` CLI flag, `.worktreeinclude` setup, worktree naming conventions, cleanup lifecycle, and CodeForge integration (Project Manager auto-detection, agent isolation). Includes two reference files: manual worktree commands and parallel workflow patterns.
1212

13+
#### Claude Code Installation
14+
- **Post-start onboarding hook** (`99-claude-onboarding.sh`) — ensures `hasCompletedOnboarding: true` in `.claude.json` when token auth is configured; catches overwrites from Claude Code CLI/extension that race with `postStartCommand`
15+
1316
### Changed
1417

1518
#### Claude Code Installation
1619
- **Claude Code now installs as a native binary** — uses Anthropic's official installer (`https://claude.ai/install.sh`) via new `./features/claude-code-native` feature, replacing the npm-based `ghcr.io/anthropics/devcontainer-features/claude-code:1.0.5`
1720
- **In-session auto-updater now works without root** — native binary at `~/.local/bin/claude` is owned by the container user, so `claude update` succeeds without permission issues
1821

19-
### Fixed
20-
21-
#### Claude Code Installation
22-
- **Update script no longer silently discards errors** — background update output now captured to log file instead of being discarded via `&>/dev/null`
23-
- **Update script simplified to native-binary-only** — removed npm fallback and `claude install` bootstrap code; added 60s timeout and transitional npm cleanup
24-
- **Alias resolution simplified**`_CLAUDE_BIN` now resolves directly to native binary path (removed npm and `/usr/local/bin` fallbacks)
25-
2622
#### System Prompt
2723
- **`<git_worktrees>` section** — Updated to document Claude Code native worktree convention (`<repo>/.claude/worktrees/`) as the recommended approach alongside the legacy `.worktrees/` convention. Added `EnterWorktree` tool guidance, `.worktreeinclude` file documentation, and path convention comparison table.
2824

@@ -59,6 +55,15 @@
5955

6056
### Fixed
6157

58+
#### Claude Code Installation
59+
- **Update script no longer silently discards errors** — background update output now captured to log file instead of being discarded via `&>/dev/null`
60+
- **Update script simplified to native-binary-only** — removed npm fallback and `claude install` bootstrap code; added 60s timeout and transitional npm cleanup
61+
- **Alias resolution simplified**`_CLAUDE_BIN` now resolves directly to native binary path (removed npm and `/usr/local/bin` fallbacks)
62+
- **POSIX redirect** — replaced `&>/dev/null` with `>/dev/null 2>&1` in dependency check for portability
63+
- **Installer shell** — changed `sh -s` to `bash -s` when piping the official installer (it requires bash)
64+
- **Unquoted `${TARGET}`** — quoted variable in `su -c` command to prevent word splitting
65+
- **Directory prep** — added `~/.local/state` and `~/.claude` pre-creation; consolidated `chown` to cover entire `~/.local` tree
66+
6267
#### Plugin Marketplace
6368
- **`marketplace.json` schema fix** — changed all 11 plugin `source` fields from bare names (e.g., `"codeforge-lsp"`) to relative paths (`"./plugins/codeforge-lsp"`) so `claude plugin marketplace add` passes schema validation and all plugins register correctly
6469

.devcontainer/features/claude-code-native/install.sh

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ echo "[claude-code-native] Version: ${VERSION}"
1515

1616
# === VALIDATE DEPENDENCIES ===
1717
# The official installer (claude.ai/install.sh) requires curl internally
18-
if ! command -v curl &>/dev/null; then
18+
if ! command -v curl >/dev/null 2>&1; then
1919
echo "[claude-code-native] ERROR: curl is required"
2020
echo " Ensure common-utils feature is installed first"
2121
exit 1
@@ -47,7 +47,9 @@ echo "[claude-code-native] Installing for user: ${USERNAME} (home: ${USER_HOME})
4747
# === PREPARE DIRECTORIES ===
4848
mkdir -p "${USER_HOME}/.local/bin"
4949
mkdir -p "${USER_HOME}/.local/share/claude"
50-
chown -R "${USERNAME}:" "${USER_HOME}/.local/bin" "${USER_HOME}/.local/share/claude"
50+
mkdir -p "${USER_HOME}/.local/state"
51+
mkdir -p "${USER_HOME}/.claude"
52+
chown -R "${USERNAME}:" "${USER_HOME}/.local" "${USER_HOME}/.claude"
5153

5254
# === DETERMINE TARGET ===
5355
# The official installer accepts: stable, latest, or a specific semver
@@ -68,9 +70,9 @@ fi
6870
echo "[claude-code-native] Downloading official installer..."
6971

7072
if [ "${USERNAME}" = "root" ]; then
71-
curl -fsSL https://claude.ai/install.sh | sh -s -- "${TARGET}"
73+
curl -fsSL https://claude.ai/install.sh | bash -s -- "${TARGET}"
7274
else
73-
su - "${USERNAME}" -c "curl -fsSL https://claude.ai/install.sh | sh -s -- \"${TARGET}\""
75+
su - "${USERNAME}" -c "curl -fsSL https://claude.ai/install.sh | bash -s -- \"${TARGET}\""
7476
fi
7577

7678
# === VERIFICATION ===
@@ -87,4 +89,41 @@ else
8789
exit 1
8890
fi
8991

92+
# === POST-START HOOK ===
93+
# Ensures hasCompletedOnboarding is set when token auth is configured.
94+
# Runs as the LAST post-start hook (99- prefix) to catch overwrites from
95+
# Claude Code CLI/extension that may race with postStartCommand.
96+
HOOK_DIR="/usr/local/devcontainer-poststart.d"
97+
mkdir -p "$HOOK_DIR"
98+
cat > "$HOOK_DIR/99-claude-onboarding.sh" << 'HOOK_EOF'
99+
#!/bin/bash
100+
# Ensure hasCompletedOnboarding: true in .claude.json when token auth exists.
101+
# Runs after all setup scripts to catch any overwrites by Claude Code CLI/extension.
102+
_USERNAME="${SUDO_USER:-${USER:-vscode}}"
103+
_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6)
104+
_USER_HOME="${_USER_HOME:-/home/$_USERNAME}"
105+
CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}"
106+
CLAUDE_JSON="$CLAUDE_DIR/.claude.json"
107+
CRED_FILE="$CLAUDE_DIR/.credentials.json"
108+
109+
# Only act when token auth is configured
110+
[ -f "$CRED_FILE" ] || exit 0
111+
112+
if [ -f "$CLAUDE_JSON" ]; then
113+
if ! grep -q '"hasCompletedOnboarding"' "$CLAUDE_JSON" 2>/dev/null; then
114+
if command -v jq >/dev/null 2>&1; then
115+
jq '. + {"hasCompletedOnboarding": true}' "$CLAUDE_JSON" > "${CLAUDE_JSON}.tmp" && \
116+
mv "${CLAUDE_JSON}.tmp" "$CLAUDE_JSON"
117+
else
118+
sed -i '$ s/}$/,\n "hasCompletedOnboarding": true\n}/' "$CLAUDE_JSON"
119+
fi
120+
echo "[claude-onboarding] Injected hasCompletedOnboarding into .claude.json"
121+
fi
122+
else
123+
printf '{\n "hasCompletedOnboarding": true\n}\n' > "$CLAUDE_JSON"
124+
echo "[claude-onboarding] Created .claude.json with hasCompletedOnboarding"
125+
fi
126+
HOOK_EOF
127+
chmod +x "$HOOK_DIR/99-claude-onboarding.sh"
128+
90129
echo "[claude-code-native] Installation complete"

0 commit comments

Comments
 (0)