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.

90

Quality

90%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Risky

Do not use without reviewing

This version of the tile failed moderation
Moderation pipeline encountered an internal error
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