#!/bin/bash # install.sh — Link claude-meta config into ~/.claude/ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(dirname "$SCRIPT_DIR")" SOURCE_DIR="$REPO_ROOT/home-claude" CLAUDE_DIR="$HOME/.claude" # Verify source exists if [ ! -d "$SOURCE_DIR" ]; then echo "ERROR: home-claude/ not found at $SOURCE_DIR" exit 1 fi # Ensure ~/.claude/ is a real directory if [ -L "$CLAUDE_DIR" ]; then echo "ERROR: ~/.claude/ is a symlink. It must be a real directory." echo "Claude Code Bug #764: symlinked ~/.claude/ breaks file detection." exit 1 fi mkdir -p "$CLAUDE_DIR" echo "Installing claude-meta config..." echo " Source: $SOURCE_DIR" echo " Target: $CLAUDE_DIR" echo "" # File symlinks for file in CLAUDE.md settings.json; do src="$SOURCE_DIR/$file" dst="$CLAUDE_DIR/$file" [ ! -f "$src" ] && echo " SKIP: $file" && continue # Remove existing [ -e "$dst" ] || [ -L "$dst" ] && rm -f "$dst" ln -s "$src" "$dst" echo " SYMLINK: $file -> $src" done # Directory symlinks for dir in skills rules commands; do src="$SOURCE_DIR/$dir" dst="$CLAUDE_DIR/$dir" [ ! -d "$src" ] && echo " SKIP: $dir/" && continue # Remove existing if [ -L "$dst" ]; then rm "$dst" elif [ -d "$dst" ]; then rm -rf "$dst" fi ln -s "$src" "$dst" echo " SYMLINK: $dir/ -> $src" done # Validation echo "" echo "Validating..." for file in CLAUDE.md settings.json; do dst="$CLAUDE_DIR/$file" if [ -f "$dst" ] && [ -s "$dst" ]; then echo " OK: $file readable" else echo " FAIL: $file" fi done for dir in skills rules commands; do dst="$CLAUDE_DIR/$dir" if [ -d "$dst" ]; then count=$(find "$dst" -type f | wc -l) echo " OK: $dir/ ($count files)" else echo " FAIL: $dir/" fi done echo "" echo "Installing Gemini config..." GEMINI_SOURCE_DIR="$REPO_ROOT/home-gemini" GEMINI_DIR="$HOME/.gemini" CLAUDE_RULES_DIR="$SOURCE_DIR/rules" if [ ! -d "$GEMINI_SOURCE_DIR" ]; then echo " SKIP: home-gemini/ not found" else echo " Source: $GEMINI_SOURCE_DIR" echo " Target: $GEMINI_DIR" echo "" # Ensure ~/.gemini/ is a real directory if [ -L "$GEMINI_DIR" ]; then echo " ERROR: ~/.gemini/ is a symlink. Must be a real directory." exit 1 fi mkdir -p "$GEMINI_DIR" # GEMINI.md — always copy (no symlink, Gemini CLI issue #11547) src="$GEMINI_SOURCE_DIR/GEMINI.md" dst="$GEMINI_DIR/GEMINI.md" if [ -f "$src" ]; then cp -f "$src" "$dst" echo " COPY: GEMINI.md (symlinks not supported by Gemini CLI)" fi # settings.json — symlink src="$GEMINI_SOURCE_DIR/settings.json" dst="$GEMINI_DIR/settings.json" if [ -f "$src" ]; then [ -e "$dst" ] || [ -L "$dst" ] && rm -f "$dst" ln -s "$src" "$dst" echo " SYMLINK: settings.json -> $src" fi # rules/ — symlink to home-claude/rules/ (shared source of truth) dst="$GEMINI_DIR/rules" if [ -d "$CLAUDE_RULES_DIR" ]; then [ -L "$dst" ] && rm "$dst" || { [ -d "$dst" ] && rm -rf "$dst"; } ln -s "$CLAUDE_RULES_DIR" "$dst" echo " SYMLINK: rules/ -> $CLAUDE_RULES_DIR" fi # skills/ — selective symlinks for review/ and plan-project/ mkdir -p "$GEMINI_DIR/skills" for skill in review plan-project; do skill_src="$SOURCE_DIR/skills/$skill" skill_dst="$GEMINI_DIR/skills/$skill" if [ ! -d "$skill_src" ]; then echo " SKIP: skills/$skill/ (not in repo)" continue fi [ -L "$skill_dst" ] && rm "$skill_dst" || { [ -d "$skill_dst" ] && rm -rf "$skill_dst"; } ln -s "$skill_src" "$skill_dst" echo " SYMLINK: skills/$skill/ -> $skill_src" done echo "" echo "Validating Gemini..." dst="$GEMINI_DIR/GEMINI.md" if [ -f "$dst" ] && [ ! -L "$dst" ] && [ -s "$dst" ]; then echo " OK: ~/.gemini/GEMINI.md (real file)" elif [ -L "$dst" ]; then echo " FAIL: ~/.gemini/GEMINI.md is a symlink (Gemini CLI rejects this!)" else echo " FAIL: ~/.gemini/GEMINI.md" fi dst="$GEMINI_DIR/rules" if [ -d "$dst" ]; then count=$(find "$dst" -type f | wc -l) echo " OK: ~/.gemini/rules/ ($count files)" else echo " FAIL: ~/.gemini/rules/" fi fi echo "" echo "Installation complete!"