#!/bin/bash # # ECC Trae Installer # Installs Everything Claude Code workflows into a Trae project. # # Usage: # ./install.sh # Install to current directory # ./install.sh ~ # Install globally to ~/.trae/ or ~/.trae-cn/ # # Environment: # TRAE_ENV=cn # Force use .trae-cn directory # 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 (the repo root) SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(dirname "$SCRIPT_DIR")" # Get the trae directory name (.trae or .trae-cn) get_trae_dir() { if [ "${TRAE_ENV:-}" = "cn" ]; then echo ".trae-cn" else echo ".trae" fi } ensure_manifest_entry() { local manifest="$1" local entry="$2" touch "$manifest" if ! grep -Fqx "$entry" "$manifest"; then echo "$entry" >> "$manifest" fi } # Install function do_install() { local target_dir="$PWD" local trae_dir="$(get_trae_dir)" # 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 .trae or .trae-cn directory local current_dir_name="$(basename "$target_dir")" local trae_full_path if [ "$current_dir_name" = ".trae" ] || [ "$current_dir_name" = ".trae-cn" ]; then # Already inside the trae directory, use it directly trae_full_path="$target_dir" else # Normal case: append trae_dir to target_dir trae_full_path="$target_dir/$trae_dir" fi echo "ECC Trae Installer" echo "==================" echo "" echo "Source: $REPO_ROOT" echo "Target: $trae_full_path/" echo "" # Subdirectories to create SUBDIRS="commands agents skills rules" # Create all required trae subdirectories for dir in $SUBDIRS; do mkdir -p "$trae_full_path/$dir" done # Manifest file to track installed files MANIFEST="$trae_full_path/.ecc-manifest" touch "$MANIFEST" # Counters for summary commands=0 agents=0 skills=0 rules=0 other=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="$trae_full_path/commands/$local_name" if [ ! -f "$target_path" ]; then cp "$f" "$target_path" ensure_manifest_entry "$MANIFEST" "commands/$local_name" commands=$((commands + 1)) else ensure_manifest_entry "$MANIFEST" "commands/$local_name" 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="$trae_full_path/agents/$local_name" if [ ! -f "$target_path" ]; then cp "$f" "$target_path" ensure_manifest_entry "$MANIFEST" "agents/$local_name" agents=$((agents + 1)) else ensure_manifest_entry "$MANIFEST" "agents/$local_name" 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="$trae_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 [ ! -f "$target_path" ]; then cp "$source_file" "$target_path" skill_copied=1 fi ensure_manifest_entry "$MANIFEST" "skills/$skill_name/$relative_path" 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="$trae_full_path/rules/$relative_path" mkdir -p "$(dirname "$target_path")" if [ ! -f "$target_path" ]; then cp "$rule_file" "$target_path" rules=$((rules + 1)) fi ensure_manifest_entry "$MANIFEST" "rules/$relative_path" done < <(find "$REPO_ROOT/rules" -type f | sort) fi # Copy README files from this 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="$trae_full_path/$local_name" if [ ! -f "$target_path" ]; then cp "$readme_file" "$target_path" ensure_manifest_entry "$MANIFEST" "$local_name" other=$((other + 1)) else ensure_manifest_entry "$MANIFEST" "$local_name" fi fi done # Copy install and uninstall scripts for script_file in "$SCRIPT_DIR/install.sh" "$SCRIPT_DIR/uninstall.sh"; do if [ -f "$script_file" ]; then local_name=$(basename "$script_file") target_path="$trae_full_path/$local_name" if [ ! -f "$target_path" ]; then cp "$script_file" "$target_path" chmod +x "$target_path" ensure_manifest_entry "$MANIFEST" "$local_name" other=$((other + 1)) else ensure_manifest_entry "$MANIFEST" "$local_name" fi 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 "$trae_full_path")" echo "" echo "Next steps:" echo " 1. Open your project in Trae" echo " 2. Type / to see available commands" echo " 3. Enjoy the ECC workflows!" echo "" echo "To uninstall later:" echo " cd $trae_full_path" echo " ./uninstall.sh" } # Main logic do_install "$@"