CtrlK
BlogDocsLog inGet started
Tessl Logo

giuseppe-trisciuoglio/developer-kit

Comprehensive developer toolkit providing reusable skills for Java/Spring Boot, TypeScript/NestJS/React/Next.js, Python, PHP, AWS CloudFormation, AI/RAG, DevOps, and more.

89

Quality

89%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Risky

Do not use without reviewing

Overview
Quality
Evals
Security
Files

install-claude.shscripts/

#!/bin/bash
# Claude Code Interactive Developer Kit Installer Script
# Usage: install-claude.sh "<plugin_json_files>"

set -e

# Colors
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# Get plugin files from argument
PLUGIN_JSON_FILES="$1"
DEVKIT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"

# Confirm installation
read -p "Are you installing for Claude Code? (y/N): " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
    echo -e "${RED}✗ Installation cancelled.${NC}"
    exit 1
fi

echo ""
echo -e "${GREEN}Step 1: Target Project${NC}"
read -p "Enter the project path (absolute or relative, or press Enter for current directory): " project_path

if [[ -z "$project_path" ]]; then
    project_path="."
fi

if [[ ! -d "$project_path" ]]; then
    echo -n "  ⚠ $project_path does not exist. Create it? (y/N): "
    read create_dir
    if [[ "$create_dir" != "y" && "$create_dir" != "Y" ]]; then
        echo -e "${RED}✗ Installation cancelled.${NC}"
        exit 1
    fi
    mkdir -p "$project_path"
fi

TARGET_PROJECT="$(cd "$project_path" && pwd)"
TARGET_DIR="$TARGET_PROJECT/.claude"

mkdir -p "$TARGET_DIR/agents"
mkdir -p "$TARGET_DIR/commands"
mkdir -p "$TARGET_DIR/skills"
mkdir -p "$TARGET_DIR/rules"
mkdir -p "$TARGET_DIR/hooks"

echo "  → Target: $TARGET_PROJECT"
echo ""

# List available plugins
echo -e "${GREEN}Step 2: Available Plugins${NC}"
echo ""

declare -a PLUGIN_NAMES
declare -a PLUGIN_PATHS
declare -a PLUGIN_DIRS
declare -a PLUGIN_BASE_DIRS

plugin_num=0
for plugin_json in $PLUGIN_JSON_FILES; do
    if [[ -f "$plugin_json" ]]; then
        plugin_num=$((plugin_num + 1))
        plugin_name=$(jq -r '.name' "$plugin_json" 2>/dev/null || echo "unknown")
        plugin_desc=$(jq -r '.description' "$plugin_json" 2>/dev/null || echo "")
        num_agents=$(jq -r '.agents | length' "$plugin_json" 2>/dev/null || echo "0")
        num_commands=$(jq -r '.commands | length' "$plugin_json" 2>/dev/null || echo "0")
        num_skills=$(jq -r '.skills | length' "$plugin_json" 2>/dev/null || echo "0")
        num_rules=$(jq -r '.rules | length' "$plugin_json" 2>/dev/null || echo "0")

        PLUGIN_NAMES[$plugin_num]="$plugin_name"
        PLUGIN_PATHS[$plugin_num]="$plugin_json"
        PLUGIN_DIRS[$plugin_num]=$(dirname "$plugin_json")
        PLUGIN_BASE_DIRS[$plugin_num]=$(dirname "$(dirname "$plugin_json")")

        printf "  %2d) ${GREEN}%s${NC}\n" "$plugin_num" "$plugin_name"
        echo "      $plugin_desc"
        echo "      Components: $num_agents agents, $num_commands commands, $num_skills skills, $num_rules rules"
        echo ""
    fi
done

if [[ $plugin_num -eq 0 ]]; then
    echo -e "${RED}✗ No plugins found.${NC}"
    exit 1
fi

read -p "Select plugins to install (comma-separated numbers, or 'all'): " selected_plugins

echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BLUE}ℹ Starting Installation...${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""

installed_count=0
skipped_count=0

# Register legacy single-file PreToolUse Bash hook in .claude/settings.json
_register_legacy_hook_in_settings() {
    local target_dir="$1"
    local hook_name="$2"
    local settings_file="$target_dir/settings.json"
    local hook_command="python3 \"\$CLAUDE_PROJECT_DIR/.claude/hooks/$hook_name\""

    if [[ ! -f "$settings_file" ]]; then
        cat > "$settings_file" << EOF
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$hook_command"
          }
        ]
      }
    ]
  }
}
EOF
        echo "  ✓ Created .claude/settings.json with legacy hook: $hook_name"
        return
    fi

    python3 - << PYEOF
import json, sys

settings_file = "$settings_file"
hook_command = "$hook_command"

with open(settings_file) as f:
    settings = json.load(f)

settings.setdefault("hooks", {})
settings["hooks"].setdefault("PreToolUse", [])

bash_entry = None
for entry in settings["hooks"]["PreToolUse"]:
    if entry.get("matcher") == "Bash":
        bash_entry = entry
        break

if bash_entry is None:
    bash_entry = {"matcher": "Bash", "hooks": []}
    settings["hooks"]["PreToolUse"].append(bash_entry)

bash_entry.setdefault("hooks", [])

for h in bash_entry["hooks"]:
    if h.get("command") == hook_command:
        print("  ○ Legacy hook already registered in settings.json")
        sys.exit(0)

bash_entry["hooks"].append({"type": "command", "command": hook_command})

with open(settings_file, "w") as f:
    json.dump(settings, f, indent=2)
    f.write("\n")

print("  ✓ Registered legacy hook in .claude/settings.json: $hook_name")
PYEOF
}

_copy_hook_file() {
    local source_file="$1"
    local target_dir="$2"
    local hook_name
    local target_file

    hook_name=$(basename "$source_file")
    target_file="$target_dir/hooks/$hook_name"

    if [[ -f "$target_file" ]]; then
        echo -n "  ⚠ $hook_name already exists. [O]verwrite, [S]kip? "
        read -n 1 conflict_action
        echo ""
        case $conflict_action in
            O|o)
                cp "$source_file" "$target_file"
                chmod +x "$target_file"
                echo "    ✓ Overwritten: $hook_name"
                installed_count=$((installed_count + 1))
                return 0
                ;;
            *)
                echo "    ○ Skipped: $hook_name"
                skipped_count=$((skipped_count + 1))
                return 1
                ;;
        esac
    fi

    cp "$source_file" "$target_file"
    chmod +x "$target_file"
    echo "  ✓ Hook file: $hook_name"
    installed_count=$((installed_count + 1))
    return 0
}

_register_hooks_config_in_settings() {
    local target_dir="$1"
    local hooks_config_file="$2"
    local settings_file="$target_dir/settings.json"

    python3 - << PYEOF
import json
from pathlib import Path

settings_file = Path("$settings_file")
hooks_file = Path("$hooks_config_file")

if settings_file.exists():
    with settings_file.open() as f:
        settings = json.load(f)
else:
    settings = {}

with hooks_file.open() as f:
    hook_config = json.load(f)

settings.setdefault("hooks", {})

for event_name, matcher_entries in hook_config.get("hooks", {}).items():
    target_entries = settings["hooks"].setdefault(event_name, [])

    for matcher_entry in matcher_entries:
        normalized_entry = json.loads(json.dumps(matcher_entry))

        for hook in normalized_entry.get("hooks", []):
            command = hook.get("command")
            if isinstance(command, str):
                hook["command"] = command.replace(
                    "${CLAUDE_PLUGIN_ROOT}",
                    "$CLAUDE_PROJECT_DIR/.claude"
                )

        if normalized_entry not in target_entries:
            target_entries.append(normalized_entry)

with settings_file.open("w") as f:
    json.dump(settings, f, indent=2)
    f.write("\n")
PYEOF

    echo "  ✓ Registered hooks config in .claude/settings.json: $(basename "$hooks_config_file")"
}

# Install selected plugins
for ((i=1; i<=plugin_num; i++)); do
    should_install=0

    if [[ "$selected_plugins" == "all" ]]; then
        should_install=1
    else
        # Check if current plugin number is in selected list
        for num in $(echo "$selected_plugins" | tr ',' ' '); do
            if [[ "$num" == "$i" ]]; then
                should_install=1
                break
            fi
        done
    fi

    if [[ "$should_install" == "1" ]]; then
        plugin_json="${PLUGIN_PATHS[$i]}"
        base_dir="${PLUGIN_BASE_DIRS[$i]}"
        plugin_name="${PLUGIN_NAMES[$i]}"

        echo ""
        echo -e "${CYAN}Installing from: $plugin_name${NC}"

        # Install agents
        agents=$(jq -r '.agents[]? // empty' "$plugin_json" 2>/dev/null)
        if [[ -n "$agents" ]]; then
            for agent in $agents; do
                agent_path="$base_dir/$agent"
                if [[ -f "$agent_path" ]]; then
                    agent_name=$(basename "$agent")
                    target_file="$TARGET_DIR/agents/$agent_name"

                    if [[ -f "$target_file" ]]; then
                        echo -n "  ⚠ $agent_name already exists. [O]verwrite, [S]kip, [R]ename? "
                        read -n 1 conflict_action
                        echo ""
                        case $conflict_action in
                            O|o)
                                cp "$agent_path" "$target_file"
                                echo "    ✓ Overwritten: $agent_name"
                                installed_count=$((installed_count + 1))
                                ;;
                            R|r)
                                read -p "    Enter new name: " new_name
                                cp "$agent_path" "$TARGET_DIR/agents/$new_name.md"
                                echo "    ✓ Installed as: $new_name.md"
                                installed_count=$((installed_count + 1))
                                ;;
                            *)
                                echo "    ○ Skipped: $agent_name"
                                skipped_count=$((skipped_count + 1))
                                ;;
                        esac
                    else
                        cp "$agent_path" "$target_file"
                        echo "  ✓ Agent: $agent_name"
                        installed_count=$((installed_count + 1))
                    fi
                fi
            done
        fi

        # Install commands
        commands=$(jq -r '.commands[]? // empty' "$plugin_json" 2>/dev/null)
        if [[ -n "$commands" ]]; then
            for cmd in $commands; do
                cmd_path="$base_dir/$cmd"
                if [[ -f "$cmd_path" ]]; then
                    cmd_name=$(basename "$cmd")
                    cmd_subdir=$(dirname "$cmd")
                    cmd_subdir_rel=""

                    if [[ "$cmd_subdir" != "." ]]; then
                        cmd_subdir_rel=$(echo "$cmd_subdir" | sed 's|^\./commands||' | sed 's|^\./commands/||')
                        if [[ -n "$cmd_subdir_rel" ]]; then
                            mkdir -p "$TARGET_DIR/commands/$cmd_subdir_rel"
                            target_file="$TARGET_DIR/commands/$cmd_subdir_rel/$cmd_name"
                        else
                            target_file="$TARGET_DIR/commands/$cmd_name"
                        fi
                    else
                        target_file="$TARGET_DIR/commands/$cmd_name"
                    fi

                    if [[ -f "$target_file" ]]; then
                        echo -n "  ⚠ $cmd_name already exists. [O]verwrite, [S]kip, [R]ename? "
                        read -n 1 conflict_action
                        echo ""
                        case $conflict_action in
                            O|o)
                                cp "$cmd_path" "$target_file"
                                echo "    ✓ Overwritten: $cmd_name"
                                installed_count=$((installed_count + 1))
                                ;;
                            R|r)
                                read -p "    Enter new name: " new_name
                                if [[ -n "$cmd_subdir_rel" ]]; then
                                    cp "$cmd_path" "$TARGET_DIR/commands/$cmd_subdir_rel/$new_name.md"
                                else
                                    cp "$cmd_path" "$TARGET_DIR/commands/$new_name.md"
                                fi
                                echo "    ✓ Installed as: $new_name.md"
                                installed_count=$((installed_count + 1))
                                ;;
                            *)
                                echo "    ○ Skipped: $cmd_name"
                                skipped_count=$((skipped_count + 1))
                                ;;
                        esac
                    else
                        cp "$cmd_path" "$target_file"
                        echo "  ✓ Command: $cmd_name"
                        installed_count=$((installed_count + 1))
                    fi
                fi
            done
        fi

        # Install skills
        skills=$(jq -r '.skills[]? // empty' "$plugin_json" 2>/dev/null)
        if [[ -n "$skills" ]]; then
            for skill_pattern in $skills; do
                for skill_dir in $base_dir/$skill_pattern; do
                    if [[ -d "$skill_dir" ]]; then
                        skill_name=$(basename "$skill_dir")
                        target_skill_dir="$TARGET_DIR/skills/$skill_name"

                        if [[ -d "$target_skill_dir" ]]; then
                            echo -n "  ⚠ $skill_name already exists. [O]verwrite, [S]kip, [R]ename? "
                            read -n 1 conflict_action
                            echo ""
                            case $conflict_action in
                                O|o)
                                    rm -rf "$target_skill_dir"
                                    cp -r "$skill_dir" "$target_skill_dir"
                                    echo "    ✓ Overwritten: $skill_name"
                                    installed_count=$((installed_count + 1))
                                    ;;
                                R|r)
                                    read -p "    Enter new name: " new_name
                                    cp -r "$skill_dir" "$TARGET_DIR/skills/$new_name"
                                    echo "    ✓ Installed as: $new_name"
                                    installed_count=$((installed_count + 1))
                                    ;;
                                *)
                                    echo "    ○ Skipped: $skill_name"
                                    skipped_count=$((skipped_count + 1))
                                    ;;
                            esac
                        else
                            cp -r "$skill_dir" "$target_skill_dir"
                            echo "  ✓ Skill: $skill_name"
                            installed_count=$((installed_count + 1))
                        fi
                    fi
                done
            done
        fi

        # Install rules
        rules=$(jq -r '.rules[]? // empty' "$plugin_json" 2>/dev/null)
        if [[ -n "$rules" ]]; then
            rules_target_dir="$TARGET_DIR/rules/$plugin_name"
            mkdir -p "$rules_target_dir"
            for rule in $rules; do
                rule_path="$base_dir/$rule"
                if [[ -f "$rule_path" ]]; then
                    rule_name=$(basename "$rule")
                    target_file="$rules_target_dir/$rule_name"

                    if [[ -f "$target_file" ]]; then
                        echo -n "  ⚠ Rule $rule_name already exists. [O]verwrite, [S]kip? "
                        read -n 1 conflict_action
                        echo ""
                        case $conflict_action in
                            O|o)
                                cp "$rule_path" "$target_file"
                                echo "    ✓ Overwritten: $rule_name"
                                installed_count=$((installed_count + 1))
                                ;;
                            *)
                                echo "    ○ Skipped: $rule_name"
                                skipped_count=$((skipped_count + 1))
                                ;;
                        esac
                    else
                        cp "$rule_path" "$target_file"
                        echo "  ✓ Rule: $plugin_name/$rule_name"
                        installed_count=$((installed_count + 1))
                    fi
                fi
            done
        fi

        # Install hooks
        hooks=$(jq -r '
            if .hooks == null then empty
            elif (.hooks | type) == "array" then .hooks[]
            elif (.hooks | type) == "string" then .hooks
            else empty
            end
        ' "$plugin_json" 2>/dev/null)
        if [[ -n "$hooks" ]]; then
            for hook in $hooks; do
                hook_path="$base_dir/$hook"
                if [[ -f "$hook_path" ]]; then
                    hook_name=$(basename "$hook_path")

                    if [[ "$hook_name" == "hooks.json" ]]; then
                        hook_dir=$(dirname "$hook_path")

                        for hook_impl in "$hook_dir"/*; do
                            if [[ -f "$hook_impl" && "$(basename "$hook_impl")" != "hooks.json" ]]; then
                                _copy_hook_file "$hook_impl" "$TARGET_DIR"
                            fi
                        done

                        _register_hooks_config_in_settings "$TARGET_DIR" "$hook_path"
                    else
                        if _copy_hook_file "$hook_path" "$TARGET_DIR"; then
                            _register_legacy_hook_in_settings "$TARGET_DIR" "$hook_name"
                        fi
                    fi
                fi
            done
        fi
    fi
done

echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}✓ Installation Complete!${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo "  Target directory: $TARGET_DIR/"
echo "  Files installed:  $installed_count"
echo "  Files skipped:    $skipped_count"
echo ""
echo -e "${YELLOW}Next Steps:${NC}"
echo "  1. Navigate to your project: cd $TARGET_PROJECT"
echo "  2. Start Claude Code in the project directory"
echo "  3. Your skills, agents, and commands are now available!"
echo ""
echo -e "${YELLOW}Usage:${NC}"
echo "  - Skills are automatically discovered by Claude"
echo "  - Rules are loaded from .claude/rules/ to enforce coding standards"
echo "  - Hooks run automatically on tool events (configured in .claude/settings.json)"
echo "  - Use @agent-name to invoke agents"
echo "  - Use /command-name to run commands"
echo ""

CHANGELOG.md

context7.json

CONTRIBUTING.md

README_CN.md

README_ES.md

README_IT.md

README.md

tessl.json

tile.json