232 lines
6.4 KiB
Bash
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!"
|