From bbefb775d69c0652bfa491d8942ffde33a62287d Mon Sep 17 00:00:00 2001 From: Derek Scruggs Date: Sun, 28 Dec 2025 15:36:43 -0600 Subject: [PATCH] feat: add merge mode to preserve existing customizations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add --merge flag and interactive merge option to claude-setup installer, allowing users to preserve their customizations when re-running installation. Changes: - Add --merge flag to merge with existing .claude/ directory - Interactive mode now offers: Overwrite, Merge, or Cancel - Smart file merging for agents, commands, hooks, and skills - Skip existing files to preserve user customizations - Clear feedback showing files added vs. skipped - Update README with merge examples and behavior - Update CHANGELOG with feature documentation - Bump version to 0.3.0 Fixes issue where re-running installation would overwrite all custom agents, commands, and settings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CHANGELOG.md | 8 +++ README.md | 34 ++++++++++-- claude-setup | 149 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 159 insertions(+), 32 deletions(-) mode change 100644 => 100755 claude-setup diff --git a/CHANGELOG.md b/CHANGELOG.md index 018d2c1..074d111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **Merge Installation Mode** - Preserve existing customizations when re-running installation + - Added `--merge` flag to merge with existing `.claude/` directory instead of overwriting + - Interactive mode now offers three options: Overwrite, Merge, or Cancel + - Merge mode skips existing files (agents, commands, hooks, skills) to preserve customizations + - Settings.json properly merges hooks without duplicating + - Clear feedback showing which files were added vs. skipped + - Updated documentation with merge examples and behavior explanation + - **Agent Auto-Activation System** - Agents now automatically suggest themselves based on user prompts - Added activation rules for all 7 core agents (code-architecture-reviewer, refactor-planner, code-refactor-master, documentation-architect, plan-reviewer, web-research-specialist, auto-error-resolver) - Extended `skill-activation-prompt.ts` hook to process both skills and agents diff --git a/README.md b/README.md index 3c5366a..ea835f3 100644 --- a/README.md +++ b/README.md @@ -220,16 +220,40 @@ npx github:blencorp/claude-code-kit ### Re-running to Add More Kits +When you run the setup again on a project with existing `.claude/` directory: + ```bash npx github:blencorp/claude-code-kit ``` -**Detects:** Existing installation +**Interactive mode** prompts you to choose: +- `[o]` Overwrite entire `.claude` directory +- `[m]` Merge (keep existing files, add new ones) +- `[c]` Cancel -**Offers:** -- Update existing kits -- Add new kits -- Keep current setup +**Flags:** +- `--merge` - Automatically merge with existing installation (skip duplicates) +- `--force` - Overwrite everything without prompting +- `--yes` - Auto-detect and install without prompts (errors if `.claude/` exists) + +**Examples:** + +```bash +# Merge mode - preserves your customizations +npx github:blencorp/claude-code-kit -- --merge + +# Force overwrite - fresh installation +npx github:blencorp/claude-code-kit -- --force + +# Auto-install detected kits (no prompts) +npx github:blencorp/claude-code-kit -- --yes +``` + +**Merge behavior:** +- Skips existing agents, commands, hooks, and skills +- Merges `settings.json` (appends new hooks without duplicating) +- Preserves all your customizations +- Shows which files were skipped vs. added --- diff --git a/claude-setup b/claude-setup old mode 100644 new mode 100755 index e913301..c03ef0e --- a/claude-setup +++ b/claude-setup @@ -6,13 +6,14 @@ # Usage: # ./claude-setup # Interactive mode # ./claude-setup --force # Overwrite existing .claude +# ./claude-setup --merge # Merge with existing .claude (skip duplicates) # ./claude-setup --yes # Auto-detect, no prompts # ./claude-setup --help # Show help # set -e # Exit on error -VERSION="0.2.1" +VERSION="0.3.0" # Colors for output RED='\033[0;31m' @@ -25,6 +26,7 @@ NC='\033[0m' # No Color FORCE_MODE=false YES_MODE=false DEBUG_MODE=false +MERGE_MODE=false # Parse arguments for arg in "$@"; do @@ -41,6 +43,10 @@ for arg in "$@"; do DEBUG_MODE=true shift ;; + --merge|-m) + MERGE_MODE=true + shift + ;; --help|-h) echo "claude-setup v$VERSION" echo "" @@ -49,6 +55,7 @@ for arg in "$@"; do echo "Usage:" echo " claude-setup Interactive mode with detection" echo " claude-setup --force Overwrite existing .claude directory" + echo " claude-setup --merge Merge with existing .claude (skip duplicates)" echo " claude-setup --yes Auto-detect, no prompts" echo " claude-setup --debug Enable debug output for troubleshooting" echo " claude-setup --help Show this help" @@ -100,20 +107,36 @@ if [ -d ".claude" ]; then if [ "$FORCE_MODE" = true ]; then echo -e "${YELLOW}⚠ Removing existing .claude directory (--force)${NC}" rm -rf .claude + elif [ "$MERGE_MODE" = true ]; then + echo -e "${BLUE}ℹ Merging with existing .claude directory (--merge)${NC}" + echo -e "${BLUE} Existing files will be preserved${NC}" else echo -e "${YELLOW}⚠ .claude directory already exists${NC}" if [ "$YES_MODE" = false ]; then - read -p "Overwrite? (y/N): " -n 1 -r + echo "" + echo "Choose an option:" + echo " [o] Overwrite entire .claude directory" + echo " [m] Merge (keep existing files, add new ones)" + echo " [c] Cancel" + read -p "Your choice (o/m/C): " -n 1 -r echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Cancelled." - exit 0 - fi + case $REPLY in + [Oo]) + rm -rf .claude + ;; + [Mm]) + MERGE_MODE=true + echo -e "${BLUE}ℹ Merging with existing .claude directory${NC}" + ;; + *) + echo "Cancelled." + exit 0 + ;; + esac else - echo "Use --force to overwrite, or remove .claude manually" + echo "Use --force to overwrite, --merge to merge, or remove .claude manually" exit 1 fi - rm -rf .claude fi fi @@ -392,22 +415,74 @@ mkdir -p .claude/{skills,hooks,agents,commands} # Step 5: Copy core infrastructure echo -e "${GREEN}✓${NC} Creating .claude directory" -# Copy hooks -cp -r "$TEMPLATE_DIR/core/hooks/"* .claude/hooks/ +# Copy hooks (merge mode - skip existing files) +HOOKS_COPIED=0 +HOOKS_SKIPPED=0 +for hook_file in "$TEMPLATE_DIR/core/hooks/"*; do + hook_name=$(basename "$hook_file") + if [ -e ".claude/hooks/$hook_name" ]; then + HOOKS_SKIPPED=$((HOOKS_SKIPPED + 1)) + else + cp -r "$hook_file" .claude/hooks/ + HOOKS_COPIED=$((HOOKS_COPIED + 1)) + fi +done chmod +x .claude/hooks/*.sh 2>/dev/null || true -echo -e "${GREEN}✓${NC} Installed core hooks (skill-activation-prompt, post-tool-use-tracker)" +if [ $HOOKS_SKIPPED -gt 0 ]; then + echo -e "${GREEN}✓${NC} Installed $HOOKS_COPIED hook(s), skipped $HOOKS_SKIPPED existing hook(s)" +else + echo -e "${GREEN}✓${NC} Installed core hooks (skill-activation-prompt, post-tool-use-tracker)" +fi -# Copy skill-developer -cp -r "$TEMPLATE_DIR/core/skills/skill-developer" .claude/skills/ -echo -e "${GREEN}✓${NC} Installed skill-developer" +# Copy skill-developer (merge mode - skip if exists) +if [ -d ".claude/skills/skill-developer" ]; then + echo -e "${YELLOW}⚠${NC} Skipped skill-developer (already exists)" +else + cp -r "$TEMPLATE_DIR/core/skills/skill-developer" .claude/skills/ + echo -e "${GREEN}✓${NC} Installed skill-developer" +fi -# Copy core agents -cp "$TEMPLATE_DIR/core/agents/"*.md .claude/agents/ 2>/dev/null || true -echo -e "${GREEN}✓${NC} Installed core agents (6 files)" +# Copy core agents (merge mode - skip existing files) +AGENTS_COPIED=0 +AGENTS_SKIPPED=0 +for agent_file in "$TEMPLATE_DIR/core/agents/"*.md; do + [ -e "$agent_file" ] || continue + agent_name=$(basename "$agent_file") + if [ -e ".claude/agents/$agent_name" ]; then + AGENTS_SKIPPED=$((AGENTS_SKIPPED + 1)) + else + cp "$agent_file" .claude/agents/ + AGENTS_COPIED=$((AGENTS_COPIED + 1)) + fi +done +if [ $AGENTS_COPIED -gt 0 ] || [ $AGENTS_SKIPPED -gt 0 ]; then + if [ $AGENTS_SKIPPED -gt 0 ]; then + echo -e "${GREEN}✓${NC} Installed $AGENTS_COPIED agent(s), skipped $AGENTS_SKIPPED existing agent(s)" + else + echo -e "${GREEN}✓${NC} Installed core agents ($AGENTS_COPIED files)" + fi +fi -# Copy core commands -cp "$TEMPLATE_DIR/core/commands/"*.md .claude/commands/ 2>/dev/null || true -echo -e "${GREEN}✓${NC} Installed slash commands (dev-docs)" +# Copy core commands (merge mode - skip existing files) +COMMANDS_COPIED=0 +COMMANDS_SKIPPED=0 +for command_file in "$TEMPLATE_DIR/core/commands/"*.md; do + [ -e "$command_file" ] || continue + command_name=$(basename "$command_file") + if [ -e ".claude/commands/$command_name" ]; then + COMMANDS_SKIPPED=$((COMMANDS_SKIPPED + 1)) + else + cp "$command_file" .claude/commands/ + COMMANDS_COPIED=$((COMMANDS_COPIED + 1)) + fi +done +if [ $COMMANDS_COPIED -gt 0 ] || [ $COMMANDS_SKIPPED -gt 0 ]; then + if [ $COMMANDS_SKIPPED -gt 0 ]; then + echo -e "${GREEN}✓${NC} Installed $COMMANDS_COPIED command(s), skipped $COMMANDS_SKIPPED existing command(s)" + else + echo -e "${GREEN}✓${NC} Installed slash commands ($COMMANDS_COPIED files)" + fi +fi # Step 6: Install selected kits INSTALLED_SKILLS="" @@ -420,25 +495,45 @@ for kit in "${KITS_TO_INSTALL[@]}"; do continue fi - # Install skills from kit + # Install skills from kit (merge mode - skip existing) if [ -d "$kit_dir/skills" ]; then for skill_dir in "$kit_dir/skills"/*; do if [ -d "$skill_dir" ]; then skill_name=$(basename "$skill_dir") - cp -r "$skill_dir" .claude/skills/ - INSTALLED_SKILLS="$INSTALLED_SKILLS $skill_name" + if [ -d ".claude/skills/$skill_name" ]; then + echo -e " ${YELLOW}⚠${NC} Skipped skill: $skill_name (already exists)" + else + cp -r "$skill_dir" .claude/skills/ + INSTALLED_SKILLS="$INSTALLED_SKILLS $skill_name" + fi fi done fi - # Install agents from kit + # Install agents from kit (merge mode - skip existing files) if [ -d "$kit_dir/agents" ]; then - cp "$kit_dir/agents/"*.md .claude/agents/ 2>/dev/null || true + for agent_file in "$kit_dir/agents/"*.md; do + [ -e "$agent_file" ] || continue + agent_name=$(basename "$agent_file") + if [ -e ".claude/agents/$agent_name" ]; then + echo -e " ${YELLOW}⚠${NC} Skipped agent: $agent_name (already exists)" + else + cp "$agent_file" .claude/agents/ + fi + done fi - # Install commands from kit + # Install commands from kit (merge mode - skip existing files) if [ -d "$kit_dir/commands" ]; then - cp "$kit_dir/commands/"*.md .claude/commands/ 2>/dev/null || true + for command_file in "$kit_dir/commands/"*.md; do + [ -e "$command_file" ] || continue + command_name=$(basename "$command_file") + if [ -e ".claude/commands/$command_name" ]; then + echo -e " ${YELLOW}⚠${NC} Skipped command: $command_name (already exists)" + else + cp "$command_file" .claude/commands/ + fi + done fi display_name=$(get_display_name "$kit_dir")