commit 0d5d099deee68f94316b969736771d5e2480ae18 Author: Andrés Eduardo García Márquez Date: Sun Apr 26 17:30:25 2026 -0500 feat: setup inicial para Claude Code en Termux Scripts, configuraciones y documentación para ejecutar Claude Code CLI en Termux/Android. Incluye wrapper con termux-chroot para /tmp, actualizador seguro con techo de versión v2.1.112, resolver mDNS puro en Python y ProxyCommand SSH con fallback Tailscale. Co-Authored-By: Claude Opus 4.6 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e9df70c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.bak +*.tmp +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..8051250 --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +# Claude Code for Termux + +Run [Claude Code](https://claude.ai/code) CLI on Android via Termux. + +## Why + +Claude Code v2.1.113+ switched to native binaries, breaking Termux/Android compatibility. This repo provides: + +- **v2.1.112** (last pure Node.js release) with auto-update protection +- **termux-chroot wrapper** that fixes the `/tmp` permission issue +- **mDNS resolver** for SSH without hardcoded IPs +- **Safe updater** that respects the version ceiling + +## Requirements + +- [Termux](https://f-droid.org/en/packages/com.termux/) (F-Droid recommended) +- Android 10+ (ARM64) +- ~500MB free storage +- An Anthropic API key or Claude subscription + +## Quick Install + +```bash +git clone /andres/claude-code-termux.git +cd claude-code-termux +bash install.sh +``` + +Or dry-run first: +```bash +bash install.sh --dry-run +``` + +## What Gets Installed + +| Component | Location | Purpose | +|-----------|----------|---------| +| Claude Code v2.1.112 | npm global | The CLI itself | +| `clauded` | `~/.local/bin/` | Wrapper with termux-chroot for /tmp | +| `claude-check-env` | `~/.local/bin/` | Environment validator | +| `claude-update` | `~/.local/bin/` | Safe updater (ceiling-aware) | +| `mdns-resolve` | `~/.local/bin/` | Pure Python mDNS resolver | +| `ssh-mdns-proxy` | `~/.local/bin/` | SSH ProxyCommand with mDNS+Tailscale | +| `settings.json` | `~/.claude/` | Disables auto-updater | + +## Usage + +```bash +# Start Claude Code (always use this, not `claude` directly) +clauded + +# Check environment +claude-check-env + +# Update (safe, respects version ceiling) +claude-update +``` + +## SSH with Dynamic Resolution + +The included `ssh-mdns-proxy` resolves hostnames via mDNS first, then Tailscale DNS, then cache. See `config/ssh_config.example` for setup. + +```bash +# Set your Tailscale domain (optional, has a default) +export TAILSCALE_DOMAIN="your-tailnet.ts.net" +``` + +## Version Ceiling + +**v2.1.112 is the last compatible version.** Starting v2.1.113, Claude Code ships native binaries requiring glibc/musl linkers that don't exist on Android (Bionic libc). + +`claude-update` enforces this automatically. See `docs/TROUBLESHOOTING.md` for details. + +## Troubleshooting + +See [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) for: +- `/tmp` permission errors +- Native binary errors +- Auto-updater issues +- SSH resolution problems + +## License + +MIT diff --git a/bin/claude-check-env b/bin/claude-check-env new file mode 100755 index 0000000..7e9225c --- /dev/null +++ b/bin/claude-check-env @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# +# claude-check-env: Verify Claude Code environment on Termux +# + +RED="\033[0;31m" +GREEN="\033[0;32m" +YELLOW="\033[1;33m" +CYAN="\033[0;36m" +NC="\033[0m" + +echo -e "${CYAN}=== Claude Code - Environment Check ===${NC}" +echo "" + +# 1. Claude version +CLAUDE_VER=$(claude --version 2>/dev/null | grep -oE "[0-9]+\.[0-9]+\.[0-9]+" | head -1) +if [[ -n "$CLAUDE_VER" ]]; then + echo -e "${GREEN}[OK]${NC} Claude Code: v$CLAUDE_VER" +else + echo -e "${RED}[ERROR]${NC} Claude Code not installed" +fi + +# 2. Node.js +NODE_VER=$(node --version 2>/dev/null) +if [[ -n "$NODE_VER" ]]; then + echo -e "${GREEN}[OK]${NC} Node.js: $NODE_VER" +else + echo -e "${RED}[ERROR]${NC} Node.js not installed" +fi + +# 3. proot/termux-chroot +if command -v termux-chroot &>/dev/null; then + echo -e "${GREEN}[OK]${NC} termux-chroot available" +else + echo -e "${RED}[ERROR]${NC} termux-chroot not available (pkg install proot)" +fi + +# 4. Sharp WASM (image support) +SHARP_WASM=$(npm list -g @img/sharp-wasm32 2>/dev/null | grep sharp-wasm32) +if [[ -n "$SHARP_WASM" ]]; then + echo -e "${GREEN}[OK]${NC} Sharp WASM: installed (image support)" +else + echo -e "${YELLOW}[WARN]${NC} Sharp WASM not installed (no image support)" + echo " Install: npm install -g @img/sharp-wasm32 sharp --force" +fi + +# 5. Credentials +if [[ -f ~/.claude/.credentials.json ]]; then + echo -e "${GREEN}[OK]${NC} Credentials: configured" +else + echo -e "${RED}[ERROR]${NC} No credentials (run: claude auth)" +fi + +# 6. TMPDIR +echo -e "${GREEN}[OK]${NC} TMPDIR: ${TMPDIR:-not set}" + +# 7. Tailscale (generic check via interface) +if ip link show tailscale0 &>/dev/null 2>&1; then + echo -e "${GREEN}[OK]${NC} Tailscale: connected" +else + echo -e "${YELLOW}[WARN]${NC} Tailscale: not connected" +fi + +# 8. Disk space +SPACE=$(df -h "$PREFIX" 2>/dev/null | tail -1 | awk '{print $4}') +echo -e "${GREEN}[OK]${NC} Free space: $SPACE" + +echo "" +echo -e "${CYAN}=== Available Commands ===${NC}" +echo " clauded - Start Claude (interactive mode)" +echo " claude-update - Update Claude Code (safe)" +echo " claude-check-env - This check" +echo "" +echo -e "${CYAN}=== Known Limitations ===${NC}" +echo " - Background tasks may fail (use termux-chroot via clauded)" +echo " - Version ceiling: v2.1.112 (last pure Node.js release)" diff --git a/bin/claude-update b/bin/claude-update new file mode 100755 index 0000000..e6c18f7 --- /dev/null +++ b/bin/claude-update @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# claude-update: Safe updater for Claude Code on Termux +# Respects version ceiling (v2.1.112 = last pure Node.js release) +# + +set -e + +RED="\033[0;31m" +GREEN="\033[0;32m" +YELLOW="\033[1;33m" +NC="\033[0m" + +MAX_VERSION="2.1.112" + +CURRENT=$(claude --version 2>/dev/null | grep -oE "[0-9]+\.[0-9]+\.[0-9]+" | head -1) +echo "Current: v${CURRENT:-not installed}" + +# Check what latest available is +LATEST=$(npm view @anthropic-ai/claude-code version 2>/dev/null) +echo "Latest available: v${LATEST:-unknown}" +echo "Termux ceiling: v$MAX_VERSION" +echo "" + +# Compare versions (is latest > ceiling?) +version_gt() { + test "$(printf '%s\n' "$1" "$2" | sort -V | tail -1)" != "$2" +} + +if [[ "$CURRENT" == "$MAX_VERSION" ]]; then + echo -e "${GREEN}Already at ceiling version v$MAX_VERSION${NC}" + echo "Versions above this use native binaries (incompatible with Termux/Android)" + exit 0 +fi + +if version_gt "$LATEST" "$MAX_VERSION"; then + echo -e "${YELLOW}Latest (v$LATEST) exceeds ceiling.${NC}" + echo "Installing ceiling version v$MAX_VERSION instead..." + TARGET="$MAX_VERSION" +else + TARGET="$LATEST" +fi + +# Verify target is pure Node.js (has cli.js, not cli-wrapper.cjs) +echo "Verifying v$TARGET is Node.js compatible..." +FILES=$(npm pack "@anthropic-ai/claude-code@$TARGET" --dry-run 2>&1 || true) +if echo "$FILES" | grep -q "cli-wrapper.cjs"; then + echo -e "${RED}ERROR: v$TARGET uses native binaries (cli-wrapper.cjs)${NC}" + echo "This version is NOT compatible with Termux/Android." + exit 1 +fi + +# Unprotect if previously locked +CLAUDE_DIR="$PREFIX/lib/node_modules/@anthropic-ai/claude-code" +if [[ -d "$CLAUDE_DIR" ]]; then + chmod -R u+w "$CLAUDE_DIR" 2>/dev/null || true +fi + +echo "Installing v$TARGET..." +npm install -g "@anthropic-ai/claude-code@$TARGET" + +# Re-protect against auto-updater +chmod -R a-w "$CLAUDE_DIR" 2>/dev/null || true + +NEW=$(claude --version 2>/dev/null | grep -oE "[0-9]+\.[0-9]+\.[0-9]+" | head -1) +echo "" +echo -e "${GREEN}Updated: v$CURRENT -> v$NEW${NC}" +echo "Package protected against auto-updater (chmod a-w)" diff --git a/bin/clauded b/bin/clauded new file mode 100755 index 0000000..bb92624 --- /dev/null +++ b/bin/clauded @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# +# clauded: Wrapper for Claude Code on Termux +# Uses termux-chroot for working /tmp in all child processes +# + +set -e + +TERMUX_TMP="$PREFIX/tmp" + +RED="\033[0;31m" +GREEN="\033[0;32m" +YELLOW="\033[1;33m" +NC="\033[0m" + +# Verify termux-chroot +if ! command -v termux-chroot &>/dev/null; then + echo -e "${RED}Error: termux-chroot not available${NC}" + echo "Install with: pkg install proot" + exit 1 +fi + +# Verify claude +if ! command -v claude &>/dev/null; then + echo -e "${RED}Error: claude is not installed${NC}" + exit 1 +fi + +# Clean orphan Claude temp files (older than 1 day) +find "$TERMUX_TMP" -name "claude-*-cwd" -mtime +1 -delete 2>/dev/null || true + +# Check Tailscale connectivity (generic: look for tailscale0 interface) +if ! ip link show tailscale0 &>/dev/null 2>&1; then + echo -e "${YELLOW}Tailscale not connected${NC} - SSH to remote hosts will fail" + echo "" +fi + +CLAUDE_VER=$(claude --version 2>/dev/null | grep -oE "[0-9]+\.[0-9]+\.[0-9]+" | head -1) +echo -e "${GREEN}Claude Code v$CLAUDE_VER (termux-chroot)${NC}" +echo -e " /tmp -> /usr/tmp (FHS)" +echo "" + +exec termux-chroot claude --dangerously-skip-permissions "$@" diff --git a/bin/mdns-resolve b/bin/mdns-resolve new file mode 100755 index 0000000..81d954f --- /dev/null +++ b/bin/mdns-resolve @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +""" +Resolve hostname.local via mDNS multicast (pure Python, no dependencies). +Usage: mdns-resolve [timeout_seconds] +Returns: IP address on stdout, or exit 1 on failure. +""" +import sys +import socket +import struct +import select +import time + + +def build_mdns_query(hostname: str) -> bytes: + header = struct.pack('>HHHHHH', 0, 0, 1, 0, 0, 0) + question = b'' + for part in hostname.rstrip('.').split('.'): + question += bytes([len(part)]) + part.encode() + question += b'\x00' + question += struct.pack('>HH', 1, 1) # Type A, Class IN + return header + question + + +def parse_mdns_response(data: bytes) -> str | None: + if len(data) < 12: + return None + qdcount = struct.unpack('>H', data[4:6])[0] + ancount = struct.unpack('>H', data[6:8])[0] + if ancount == 0: + return None + + offset = 12 + for _ in range(qdcount): + while offset < len(data) and data[offset] != 0: + if data[offset] & 0xc0 == 0xc0: + offset += 2 + break + offset += data[offset] + 1 + else: + offset += 1 + offset += 4 + + for _ in range(ancount): + while offset < len(data): + if data[offset] & 0xc0 == 0xc0: + offset += 2 + break + elif data[offset] == 0: + offset += 1 + break + else: + offset += data[offset] + 1 + if offset + 10 > len(data): + break + rtype, rclass, ttl, rdlength = struct.unpack('>HHIH', data[offset:offset+10]) + offset += 10 + if rtype == 1 and rdlength == 4 and offset + 4 <= len(data): + return socket.inet_ntoa(data[offset:offset+4]) + offset += rdlength + return None + + +def resolve_mdns(hostname: str, timeout: float = 2.0) -> str | None: + if not hostname.endswith('.local'): + hostname += '.local' + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.setblocking(False) + sock.sendto(build_mdns_query(hostname), ('224.0.0.251', 5353)) + end_time = time.time() + timeout + while time.time() < end_time: + ready, _, _ = select.select([sock], [], [], 0.1) + if ready: + try: + data, _ = sock.recvfrom(4096) + ip = parse_mdns_response(data) + if ip: + sock.close() + return ip + except Exception: + pass + sock.close() + except Exception: + pass + return None + + +def main(): + if len(sys.argv) < 2: + print("Usage: mdns-resolve [timeout]", file=sys.stderr) + sys.exit(1) + hostname = sys.argv[1] + timeout = float(sys.argv[2]) if len(sys.argv) > 2 else 2.0 + ip = resolve_mdns(hostname, timeout) + if ip: + print(ip) + else: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/bin/ssh-mdns-proxy b/bin/ssh-mdns-proxy new file mode 100755 index 0000000..f2f945c --- /dev/null +++ b/bin/ssh-mdns-proxy @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# ssh-mdns-proxy: Resolve .local via mDNS with fallbacks, then connect +# +# Resolution order: mDNS → Tailscale DNS → cached IP +# Usage: ssh-mdns-proxy [port] +# +# Configure your Tailscale domain: +TAILSCALE_DOMAIN="${TAILSCALE_DOMAIN:-tailb0bb74.ts.net}" + +HOST="$1" +PORT="${2:-22}" +CACHE="$HOME/.ssh/mdns-cache" +QUERY="${HOST%.local}" + +# Alias mapping (short name -> mDNS hostname) +declare -A ALIASES=( + [dell]="dell-latitude3400" + [lenovo]="lenovo-ideapad" +) +[[ -n "${ALIASES[$QUERY]}" ]] && QUERY="${ALIASES[$QUERY]}" + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# 1. mDNS resolution +IP=$("$SCRIPT_DIR/mdns-resolve" "${QUERY}.local" 2 2>/dev/null) + +# 2. Fallback: Tailscale DNS +if [[ -z "$IP" ]]; then + IP=$(getent hosts "${QUERY}.${TAILSCALE_DOMAIN}" 2>/dev/null | awk '{print $1}') +fi + +# 3. Fallback: cached IP +if [[ -z "$IP" && -f "$CACHE" ]]; then + IP=$(grep "^${QUERY} " "$CACHE" 2>/dev/null | awk '{print $2}') +fi + +if [[ -z "$IP" ]]; then + echo "Error: cannot resolve ${HOST}" >&2 + exit 1 +fi + +# Save to cache +mkdir -p "$(dirname "$CACHE")" +grep -v "^${QUERY} " "$CACHE" > "${CACHE}.tmp" 2>/dev/null || true +echo "${QUERY} ${IP}" >> "${CACHE}.tmp" +mv "${CACHE}.tmp" "$CACHE" + +exec nc "$IP" "$PORT" diff --git a/config/bashrc.snippet b/config/bashrc.snippet new file mode 100644 index 0000000..c7b59d9 --- /dev/null +++ b/config/bashrc.snippet @@ -0,0 +1,15 @@ +# === Claude Code for Termux - .bashrc additions === +# Append these lines to your ~/.bashrc + +# Node-gyp / native compilation config +export CFLAGS="-I$PREFIX/include" +export LDFLAGS="-L$PREFIX/lib" +export CC=clang +export CXX=clang++ +export npm_config_nodedir=$PREFIX + +# Add local bin to PATH (for clauded, claude-check-env, etc.) +export PATH="$HOME/.local/bin:$PATH" + +# Prevent Claude Code auto-updater (enforced at shell level too) +export DISABLE_AUTOUPDATER=1 diff --git a/config/settings.json b/config/settings.json new file mode 100644 index 0000000..16274e2 --- /dev/null +++ b/config/settings.json @@ -0,0 +1,6 @@ +{ + "env": { + "DISABLE_AUTOUPDATER": "1" + }, + "skipDangerousModePermissionPrompt": true +} diff --git a/config/ssh_config.example b/config/ssh_config.example new file mode 100644 index 0000000..08e6d6f --- /dev/null +++ b/config/ssh_config.example @@ -0,0 +1,29 @@ +# SSH Config for Termux with mDNS/Tailscale resolution +# Copy relevant sections to ~/.ssh/config + +Host * + IdentityFile ~/.ssh/id_ed25519 + IdentitiesOnly yes + ServerAliveInterval 60 + ServerAliveCountMax 3 + ConnectTimeout 10 + +# === mDNS (.local) - Dynamic resolution === +Host *.local + User + ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p + +# === Short hostnames -> mDNS first, then Tailscale === +Host + User + ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p + +Host + User + ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p + +# === ProxyJump (hosts without Tailscale, via jump host) === +# Host +# HostName .local +# User +# ProxyJump diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md new file mode 100644 index 0000000..b09611d --- /dev/null +++ b/docs/TROUBLESHOOTING.md @@ -0,0 +1,106 @@ +# Troubleshooting + +## /tmp Permission Denied + +**Error:** `EACCES: permission denied, mkdir '/tmp/claude/...'` + +**Cause:** Claude Code hardcodes `/tmp` for background tasks. Termux doesn't have `/tmp`. + +**Fix:** Always start Claude via `clauded`, which uses `termux-chroot` to provide a working `/tmp`. + +```bash +# Wrong +claude + +# Right +clauded +``` + +**Why not proot?** `proot -b $PREFIX/tmp:/tmp` only affects the main process. Child processes (Bash tool, agents) run in separate shells without the binding. `termux-chroot` simulates a full FHS where `/tmp -> /usr/tmp` works for ALL processes. + +--- + +## Native Binary Error (v2.1.113+) + +**Error:** `claude native binary not installed` + +**Cause:** Starting with v2.1.113, Anthropic migrated from `cli.js` (pure Node.js) to native binaries (SEA). Termux reports `process.platform === 'android'` which is not supported. + +**Why no workaround:** +- `npm install --force` of linux-arm64 downloads the binary but needs `/lib/ld-linux-aarch64.so.1` (glibc) or `/lib/ld-musl-aarch64.so.1` (musl) +- Termux uses Bionic libc (Android), neither linker exists +- `termux-chroot` and `proot` don't help because the linkers simply don't exist + +**Fix:** Stay on v2.1.112 (last pure Node.js release). + +```bash +claude-update # Automatically respects the version ceiling +``` + +**How to verify a version is compatible:** +```bash +npm pack @anthropic-ai/claude-code@VERSION --dry-run 2>&1 | grep cli.js +# If cli.js (~13MB) appears -> compatible +# If cli-wrapper.cjs (~4KB) appears -> native binary, NOT compatible +``` + +--- + +## Auto-updater Bypasses Settings + +**Problem:** Claude Code may auto-update despite `DISABLE_AUTOUPDATER=1`. + +**Fix:** Protect the installation directory: +```bash +chmod -R a-w $PREFIX/lib/node_modules/@anthropic-ai/claude-code/ +``` + +To update later, unprotect first: +```bash +chmod -R u+w $PREFIX/lib/node_modules/@anthropic-ai/claude-code/ +claude-update +``` + +--- + +## SSH .local Resolution Fails + +**Error:** `ssh hostname.local` fails with "Could not resolve hostname" + +**Cause:** mDNS (.local) does NOT work natively in Termux (Android's Bionic libc doesn't support it). + +**Fix:** Use the included `ssh-mdns-proxy` as ProxyCommand in `~/.ssh/config`: +``` +Host *.local + ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p +``` + +This uses pure Python multicast UDP to resolve mDNS, with Tailscale DNS fallback. + +--- + +## Sharp/Image Support + +**Warning:** `Sharp WASM not installed (no image support)` + +**Fix:** +```bash +npm install -g @img/sharp-wasm32 sharp --force +``` + +This enables Claude Code to process images (screenshots, diagrams, etc.). + +--- + +## Alternative: proot-distro + +For running newer Claude Code versions (v2.1.113+), you can use proot-distro with Ubuntu, which provides real glibc: + +```bash +pkg install proot-distro +proot-distro install ubuntu +proot-distro login ubuntu +# Inside Ubuntu: install node, npm, claude-code@latest +``` + +**Trade-offs:** +2GB disk, slight performance overhead, but full Linux compatibility. diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..04acb1b --- /dev/null +++ b/install.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# +# install.sh: Set up Claude Code on Termux +# +# Usage: bash install.sh [--dry-run] +# + +set -e + +RED="\033[0;31m" +GREEN="\033[0;32m" +YELLOW="\033[1;33m" +CYAN="\033[0;36m" +NC="\033[0m" + +DRY_RUN=false +[[ "$1" == "--dry-run" ]] && DRY_RUN=true + +CLAUDE_VERSION="2.1.112" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +BIN_DIR="$HOME/.local/bin" + +step() { echo -e "\n${CYAN}[$1/$TOTAL] $2${NC}"; } +run() { + if $DRY_RUN; then + echo -e " ${YELLOW}[dry-run]${NC} $*" + else + "$@" + fi +} + +# Verify we're on Termux +if [[ -z "$PREFIX" || ! -d "$PREFIX" ]]; then + echo -e "${RED}Error: Not running on Termux${NC}" + echo "This installer is designed for Termux on Android." + exit 1 +fi + +TOTAL=8 +echo -e "${GREEN}=== Claude Code for Termux - Installer ===${NC}" +echo "Version ceiling: v$CLAUDE_VERSION" +$DRY_RUN && echo -e "${YELLOW}(dry-run mode - no changes will be made)${NC}" + +# 1. Install system packages +step 1 "Installing system packages..." +run pkg install -y nodejs python git proot + +# 2. Install Claude Code +step 2 "Installing Claude Code v$CLAUDE_VERSION..." +run npm install -g "@anthropic-ai/claude-code@$CLAUDE_VERSION" + +# 3. Protect against auto-updater +step 3 "Protecting against auto-updater..." +CLAUDE_DIR="$PREFIX/lib/node_modules/@anthropic-ai/claude-code" +if [[ -d "$CLAUDE_DIR" ]]; then + run chmod -R a-w "$CLAUDE_DIR" +fi + +# 4. Install scripts +step 4 "Installing scripts to $BIN_DIR..." +run mkdir -p "$BIN_DIR" +for script in clauded claude-check-env claude-update mdns-resolve ssh-mdns-proxy; do + run cp "$SCRIPT_DIR/bin/$script" "$BIN_DIR/$script" + run chmod +x "$BIN_DIR/$script" +done + +# 5. Claude settings +step 5 "Configuring Claude settings..." +run mkdir -p "$HOME/.claude" +if [[ ! -f "$HOME/.claude/settings.json" ]]; then + run cp "$SCRIPT_DIR/config/settings.json" "$HOME/.claude/settings.json" +else + echo " ~/.claude/settings.json already exists, skipping" +fi + +# 6. Bashrc additions +step 6 "Updating ~/.bashrc..." +if ! grep -q "DISABLE_AUTOUPDATER" "$HOME/.bashrc" 2>/dev/null; then + if ! $DRY_RUN; then + echo "" >> "$HOME/.bashrc" + cat "$SCRIPT_DIR/config/bashrc.snippet" >> "$HOME/.bashrc" + fi + echo " Added Claude-related exports to ~/.bashrc" +else + echo " ~/.bashrc already configured, skipping" +fi + +# 7. Install sharp-wasm32 (image support) +step 7 "Installing Sharp WASM (image support)..." +run npm install -g @img/sharp-wasm32 sharp --force 2>/dev/null || { + echo -e " ${YELLOW}Sharp install failed (optional, images won't work)${NC}" +} + +# 8. Done +step 8 "Verifying installation..." +if ! $DRY_RUN; then + echo "" + "$BIN_DIR/claude-check-env" +fi + +echo "" +echo -e "${GREEN}=== Installation Complete ===${NC}" +echo "" +echo "Next steps:" +echo " 1. Restart your shell: source ~/.bashrc" +echo " 2. Authenticate: claude auth" +echo " 3. Start Claude: clauded" +echo "" +echo "See docs/TROUBLESHOOTING.md for common issues."