claude-meta/scripts/install.sh

232 lines
6.4 KiB
Bash

#!/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
# ============================================================
# PROJECT MEMORY SYMLINKS
# ============================================================
PROJECT_MAP="$REPO_ROOT/memory/projects/project-map.json"
PROJECTS_DIR="$CLAUDE_DIR/projects"
echo ""
echo "Installing project memory symlinks..."
if [ -f "$PROJECT_MAP" ] && command -v python3 &>/dev/null; then
python3 -c "
import json, sys
with open('$PROJECT_MAP') as f:
data = json.load(f)
for k, v in data.items():
print(f'{k} {v}')
" | while read encoded friendly; do
memory_src="$REPO_ROOT/memory/projects/$friendly"
project_dir="$PROJECTS_DIR/$encoded"
memory_dst="$project_dir/memory"
[ ! -d "$project_dir" ] && echo " SKIP: $encoded (project dir not found)" && continue
[ ! -d "$memory_src" ] && echo " SKIP: $friendly (repo dir not found)" && continue
if [ -L "$memory_dst" ]; then
echo " OK: $friendly (already linked)"
continue
fi
# Move existing memory files to repo before replacing
if [ -d "$memory_dst" ]; then
for f in "$memory_dst"/*; do
[ -f "$f" ] || continue
base=$(basename "$f")
[ ! -f "$memory_src/$base" ] && mv "$f" "$memory_src/" && echo " MOVED: $base -> $memory_src"
done
rm -rf "$memory_dst"
fi
ln -s "$memory_src" "$memory_dst"
echo " SYMLINK: $friendly -> $memory_src"
done
elif [ -f "$PROJECT_MAP" ]; then
echo " SKIP: python3 not found (needed to parse JSON)"
fi
# Project memory validation
if [ -f "$PROJECT_MAP" ] && command -v python3 &>/dev/null; then
echo ""
echo "Validating project memory..."
python3 -c "
import json
with open('$PROJECT_MAP') as f:
data = json.load(f)
for k, v in data.items():
print(f'{k} {v}')
" | while read encoded friendly; do
memory_dst="$PROJECTS_DIR/$encoded/memory"
if [ -L "$memory_dst" ]; then
echo " OK: $friendly memory/ (symlink)"
elif [ -d "$memory_dst" ]; then
echo " OK: $friendly memory/ (copy)"
fi
done
fi
echo ""
echo "Installation complete!"