#!/bin/bash # # ECC CodeBuddy Installer # Installs Everything Claude Code workflows into a CodeBuddy project. # # Usage: # ./install.sh # Install to current directory # ./install.sh ~ # Install globally to ~/.codebuddy/ # set -euo pipefail # When globs match nothing, expand to empty list instead of the literal pattern shopt -s nullglob # Resolve the directory where this script lives SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # Locate the ECC repo root by walking up from SCRIPT_DIR to find the marker # file (VERSION). This keeps the script working even when it has been copied # into a target project's .codebuddy/ directory. find_repo_root() { local dir="$(dirname "$SCRIPT_DIR")" # First try the parent of SCRIPT_DIR (original layout: .codebuddy/ lives in repo root) if [ -f "$dir/VERSION" ] && [ -d "$dir/commands" ] && [ -d "$dir/agents" ]; then echo "$dir" return 0 fi echo "" return 1 } REPO_ROOT="$(find_repo_root)" if [ -z "$REPO_ROOT" ]; then echo "Error: Cannot locate the ECC repository root." echo "This script must be run from within the ECC repository's .codebuddy/ directory." exit 1 fi # CodeBuddy directory name CODEBUDDY_DIR=".codebuddy" ensure_manifest_entry() { local manifest="$1" local entry="$2" touch "$manifest" if ! grep -Fqx "$entry" "$manifest"; then echo "$entry" >> "$manifest" fi } manifest_has_entry() { local manifest="$1" local entry="$2" [ -f "$manifest" ] && grep -Fqx "$entry" "$manifest" } copy_managed_file() { local source_path="$1" local target_path="$2" local manifest="$3" local manifest_entry="$4" local make_executable="${5:-0}" local already_managed=0 if manifest_has_entry "$manifest" "$manifest_entry"; then already_managed=1 fi if [ -f "$target_path" ]; then if [ "$already_managed" -eq 1 ]; then ensure_manifest_entry "$manifest" "$manifest_entry" fi return 1 fi cp "$source_path" "$target_path" if [ "$make_executable" -eq 1 ]; then chmod +x "$target_path" fi ensure_manifest_entry "$manifest" "$manifest_entry" return 0 } # Install function do_install() { local target_dir="$PWD" # Check if ~ was specified (or expanded to $HOME) if [ "$#" -ge 1 ]; then if [ "$1" = "~" ] || [ "$1" = "$HOME" ]; then target_dir="$HOME" fi fi # Check if we're already inside a .codebuddy directory local current_dir_name="$(basename "$target_dir")" local codebuddy_full_path if [ "$current_dir_name" = ".codebuddy" ]; then # Already inside the codebuddy directory, use it directly codebuddy_full_path="$target_dir" else # Normal case: append CODEBUDDY_DIR to target_dir codebuddy_full_path="$target_dir/$CODEBUDDY_DIR" fi echo "ECC CodeBuddy Installer" echo "=======================" echo "" echo "Source: $REPO_ROOT" echo "Target: $codebuddy_full_path/" echo "" # Subdirectories to create SUBDIRS="commands agents skills rules" # Create all required codebuddy subdirectories for dir in $SUBDIRS; do mkdir -p "$codebuddy_full_path/$dir" done # Manifest file to track installed files MANIFEST="$codebuddy_full_path/.ecc-manifest" touch "$MANIFEST" # Counters for summary commands=0 agents=0 skills=0 rules=0 # Copy commands from repo root if [ -d "$REPO_ROOT/commands" ]; then for f in "$REPO_ROOT/commands"/*.md; do [ -f "$f" ] || continue local_name=$(basename "$f") target_path="$codebuddy_full_path/commands/$local_name" if copy_managed_file "$f" "$target_path" "$MANIFEST" "commands/$local_name"; then commands=$((commands + 1)) fi done fi # Copy agents from repo root if [ -d "$REPO_ROOT/agents" ]; then for f in "$REPO_ROOT/agents"/*.md; do [ -f "$f" ] || continue local_name=$(basename "$f") target_path="$codebuddy_full_path/agents/$local_name" if copy_managed_file "$f" "$target_path" "$MANIFEST" "agents/$local_name"; then agents=$((agents + 1)) fi done fi # Copy skills from repo root (if available) if [ -d "$REPO_ROOT/skills" ]; then for d in "$REPO_ROOT/skills"/*/; do [ -d "$d" ] || continue skill_name="$(basename "$d")" target_skill_dir="$codebuddy_full_path/skills/$skill_name" skill_copied=0 while IFS= read -r source_file; do relative_path="${source_file#$d}" target_path="$target_skill_dir/$relative_path" mkdir -p "$(dirname "$target_path")" if copy_managed_file "$source_file" "$target_path" "$MANIFEST" "skills/$skill_name/$relative_path"; then skill_copied=1 fi done < <(find "$d" -type f | sort) if [ "$skill_copied" -eq 1 ]; then skills=$((skills + 1)) fi done fi # Copy rules from repo root if [ -d "$REPO_ROOT/rules" ]; then while IFS= read -r rule_file; do relative_path="${rule_file#$REPO_ROOT/rules/}" target_path="$codebuddy_full_path/rules/$relative_path" mkdir -p "$(dirname "$target_path")" if copy_managed_file "$rule_file" "$target_path" "$MANIFEST" "rules/$relative_path"; then rules=$((rules + 1)) fi done < <(find "$REPO_ROOT/rules" -type f | sort) fi # Copy README files (skip install/uninstall scripts to avoid broken # path references when the copied script runs from the target directory) for readme_file in "$SCRIPT_DIR/README.md" "$SCRIPT_DIR/README.zh-CN.md"; do if [ -f "$readme_file" ]; then local_name=$(basename "$readme_file") target_path="$codebuddy_full_path/$local_name" copy_managed_file "$readme_file" "$target_path" "$MANIFEST" "$local_name" || true fi done # Add manifest file itself to manifest ensure_manifest_entry "$MANIFEST" ".ecc-manifest" # Installation summary echo "Installation complete!" echo "" echo "Components installed:" echo " Commands: $commands" echo " Agents: $agents" echo " Skills: $skills" echo " Rules: $rules" echo "" echo "Directory: $(basename "$codebuddy_full_path")" echo "" echo "Next steps:" echo " 1. Open your project in CodeBuddy" echo " 2. Type / to see available commands" echo " 3. Enjoy the ECC workflows!" echo "" echo "To uninstall later:" echo " cd $codebuddy_full_path" echo " ./uninstall.sh" } # Main logic do_install "$@"