From 42ef7e30f477c275b8c9b5faf51bc6f5836de92b Mon Sep 17 00:00:00 2001 From: asepharyana Date: Sat, 25 Jul 2026 11:51:39 +0700 Subject: [PATCH] feat(hub-guide): add install scripts with settings.json registration - install.sh: copies/symlinks plugin to ~/.claude/plugins/ + auto-register in settings.json - install.ps1: PowerShell variant for Windows - Fix .claude-plugin nesting issue in install_item - Remove --project flag (unnecessary complexity) - Add progress echo for manifest installation --- install.ps1 | 82 +++++++++++++++++++++++++++++++++++ install.sh | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 install.ps1 create mode 100755 install.sh diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 0000000..f4456ba --- /dev/null +++ b/install.ps1 @@ -0,0 +1,82 @@ +# install.ps1 — hub-guide plugin installer for Claude Code (Windows) +# +# Usage: +# .\install.ps1 # Install to %USERPROFILE%\.claude\plugins\ +# .\install.ps1 -Project # Install to .claude\plugins\ (project-scoped) +# .\install.ps1 -Link # Symlink (PowerShell Admin/Dev mode) +# .\install.ps1 -NoHooks # Skills only +# .\install.ps1 clean-code testing # Install specific skills + +param( + [switch]$Project, + [switch]$Link, + [switch]$NoHooks, + [Parameter(Position=0, ValueFromRemainingArguments=$true)] + [string[]]$SkillFilter +) + +$PluginName = "hub-guide" +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path + +# Determine target +if ($Project) { + $TargetDir = Join-Path (Get-Location) ".claude\plugins" +} else { + $TargetDir = Join-Path $env:USERPROFILE ".claude\plugins" +} + +$PluginDir = Join-Path $TargetDir $PluginName + +Write-Host "📐 hub-guide installer" -ForegroundColor Cyan + +# Ensure target exists +New-Item -ItemType Directory -Force -Path $PluginDir | Out-Null + +# Install manifest +Copy-Item -Path (Join-Path $ScriptDir ".claude-plugin") -Destination $PluginDir -Recurse -Force + +# Install hooks +if (-not $NoHooks) { + Write-Host " hooks/ → $PluginDir\hooks\" + if ($Link) { + New-Item -ItemType SymbolicLink -Path "$PluginDir\hooks" -Target (Join-Path $ScriptDir "hooks") -Force | Out-Null + } else { + Copy-Item -Path (Join-Path $ScriptDir "hooks") -Destination $PluginDir -Recurse -Force + } +} + +# Install skills +if ($SkillFilter.Count -gt 0) { + $SkillDir = Join-Path $PluginDir "skills" + New-Item -ItemType Directory -Force -Path $SkillDir | Out-Null + foreach ($skill in $SkillFilter) { + $skillName = Split-Path $skill -Leaf + $src = Join-Path $ScriptDir "skills" $skillName + if (Test-Path $src) { + Write-Host " skills/$skillName/ → $SkillDir" + if ($Link) { + New-Item -ItemType SymbolicLink -Path (Join-Path $SkillDir $skillName) -Target $src -Force | Out-Null + } else { + Copy-Item -Path $src -Destination $SkillDir -Recurse -Force + } + } else { + Write-Host " ⚠️ Skill '$skillName' not found at $src" -ForegroundColor Yellow + } + } +} else { + Write-Host " skills/ (all) → $PluginDir\skills\" + if ($Link) { + New-Item -ItemType SymbolicLink -Path (Join-Path $PluginDir "skills") -Target (Join-Path $ScriptDir "skills") -Force | Out-Null + } else { + Copy-Item -Path (Join-Path $ScriptDir "skills") -Destination $PluginDir -Recurse -Force + } +} + +Write-Host "" +Write-Host "✅ hub-guide installed to $PluginDir" -ForegroundColor Green +if ($Link) { + Write-Host " (symlink — edits in this repo are live)" +} +Write-Host "" +Write-Host " Restart Claude Code or run /reload to activate." +Write-Host " Skills auto-trigger when you work — no commands needed." diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..886f199 --- /dev/null +++ b/install.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# install.sh — hub-guide plugin installer for Claude Code +# +# Usage: +# ./install.sh # Install to ~/.claude/plugins/ + register in settings.json +# ./install.sh --link # Symlink (edits in this repo are live) +# ./install.sh --no-hooks # Skills only, skip hooks +# ./install.sh clean-code testing # Install specific skills only +# +# Requires: Claude Code, python3 (for JSON manipulation) + +set -euo pipefail + +PLUGIN_NAME="hub-guide" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Determine target +if [ "${1:-}" = "--link" ]; then + LINK_MODE=true + TARGET_DIR="${HOME}/.claude/plugins" + shift +else + TARGET_DIR="${HOME}/.claude/plugins" +fi + +LINK_MODE="${LINK_MODE:-false}" +INSTALL_HOOKS=true +FILTER_SKILLS=() + +# Parse remaining args +for arg in "$@"; do + case "$arg" in + --no-hooks) INSTALL_HOOKS=false ;; + --link) LINK_MODE=true ;; + --*) echo "Unknown option: $arg"; exit 1 ;; + *) FILTER_SKILLS+=("$arg") ;; + esac +done + +echo "📐 hub-guide installer" + +# Ensure target dir exists +mkdir -p "${TARGET_DIR}/${PLUGIN_NAME}" + +install_item() { + local src="$1" + local dst="${TARGET_DIR}/${PLUGIN_NAME}/${src#${SCRIPT_DIR}/}" + + if [ "$LINK_MODE" = true ]; then + mkdir -p "$(dirname "$dst")" + ln -sf "$src" "$dst" + else + rm -rf "$dst" + cp -r "$src" "$dst" + fi +} + +# Install manifest +echo " .claude-plugin/ → ${TARGET_DIR}/${PLUGIN_NAME}/" +install_item "${SCRIPT_DIR}/.claude-plugin" + +# Install hooks +if [ "$INSTALL_HOOKS" = true ]; then + echo " hooks/ → ${TARGET_DIR}/${PLUGIN_NAME}/hooks/" + if [ "$LINK_MODE" = true ]; then + ln -sf "${SCRIPT_DIR}/hooks" "${TARGET_DIR}/${PLUGIN_NAME}/hooks" + else + cp -r "${SCRIPT_DIR}/hooks" "${TARGET_DIR}/${PLUGIN_NAME}/" + fi +fi + +# Install skills +if [ ${#FILTER_SKILLS[@]} -gt 0 ]; then + # Install only specified skills + for skill in "${FILTER_SKILLS[@]}"; do + skill_name=$(basename "$skill") + skill_src="${SCRIPT_DIR}/skills/${skill_name}" + if [ -d "$skill_src" ]; then + echo " skills/${skill_name}/ → ${TARGET_DIR}/${PLUGIN_NAME}/skills/" + if [ "$LINK_MODE" = true ]; then + ln -sf "$skill_src" "${TARGET_DIR}/${PLUGIN_NAME}/skills/${skill_name}" + else + mkdir -p "${TARGET_DIR}/${PLUGIN_NAME}/skills" + cp -r "$skill_src" "${TARGET_DIR}/${PLUGIN_NAME}/skills/" + fi + else + echo " ⚠️ Skill '${skill_name}' not found at ${skill_src}" + fi + done +else + # Install all skills + echo " skills/ (all 26) → ${TARGET_DIR}/${PLUGIN_NAME}/skills/" + if [ "$LINK_MODE" = true ]; then + ln -sf "${SCRIPT_DIR}/skills" "${TARGET_DIR}/${PLUGIN_NAME}/skills" + else + cp -r "${SCRIPT_DIR}/skills" "${TARGET_DIR}/${PLUGIN_NAME}/" + fi +fi + +# Register in settings.json so Claude Code loads the plugin +SETTINGS_FILE="${HOME}/.claude/settings.json" +if [ "$TARGET_DIR" = "${HOME}/.claude/plugins" ] && [ -f "$SETTINGS_FILE" ]; then + if grep -q "\"${PLUGIN_NAME}\"" "$SETTINGS_FILE" 2>/dev/null; then + echo " ⏭️ ${PLUGIN_NAME} already registered in settings.json" + else + # Add to enabledPlugins using Python for reliable JSON manipulation + python3 -c " +import json, sys +with open('$SETTINGS_FILE') as f: cfg = json.load(f) +cfg.setdefault('enabledPlugins', {})['${PLUGIN_NAME}'] = True +with open('$SETTINGS_FILE', 'w') as f: json.dump(cfg, f, indent=2) +" && echo " ✅ Registered ${PLUGIN_NAME} in settings.json enabledPlugins" + fi +fi + +echo "" +echo "✅ hub-guide installed to ${TARGET_DIR}/${PLUGIN_NAME}/" +if [ "$LINK_MODE" = true ]; then + echo " (symlink — edits in this repo are live)" +fi +echo "" +echo " Restart Claude Code or run /reload to activate." +echo " Skills auto-trigger when you work — no commands needed."