2026-07-25 11:51:39 +07:00
|
|
|
#!/bin/bash
|
2026-07-25 11:56:26 +07:00
|
|
|
# install.sh — hub-guide installer for Claude Code
|
2026-07-26 12:06:29 +07:00
|
|
|
# Installs skills to ~/.claude/skills/ + configures hooks in settings.local.json
|
2026-07-25 11:51:39 +07:00
|
|
|
# Usage:
|
2026-07-26 12:06:29 +07:00
|
|
|
# ./install.sh # Copy + hooks
|
|
|
|
|
# ./install.sh --link # Symlink (edits live) + hooks
|
|
|
|
|
# ./install.sh --no-hooks # Skills only, no hook setup
|
|
|
|
|
# Requires: Claude Code, python3
|
2026-07-25 11:51:39 +07:00
|
|
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
2026-07-25 11:54:26 +07:00
|
|
|
SKILLS_DIR="${HOME}/.claude/skills"
|
|
|
|
|
LINK_MODE=false
|
2026-07-26 12:06:29 +07:00
|
|
|
NO_HOOKS=false
|
2026-07-25 11:51:39 +07:00
|
|
|
|
|
|
|
|
for arg in "$@"; do
|
|
|
|
|
case "$arg" in
|
|
|
|
|
--link) LINK_MODE=true ;;
|
2026-07-26 12:06:29 +07:00
|
|
|
--no-hooks) NO_HOOKS=true ;;
|
2026-07-25 11:56:26 +07:00
|
|
|
--*) echo "Unknown: $arg"; exit 1 ;;
|
2026-07-25 11:51:39 +07:00
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
2026-07-26 12:06:29 +07:00
|
|
|
echo "hub-guide installer"
|
|
|
|
|
echo " target: ${SKILLS_DIR}/hub-guide/"
|
|
|
|
|
echo " mode: $([ "$LINK_MODE" = true ] && echo 'symlink' || echo 'copy')"
|
2026-07-25 11:51:39 +07:00
|
|
|
|
2026-07-26 12:06:29 +07:00
|
|
|
# --- install skills ---
|
2026-07-25 11:54:26 +07:00
|
|
|
mkdir -p "$SKILLS_DIR"
|
2026-07-25 11:56:26 +07:00
|
|
|
DST="${SKILLS_DIR}/hub-guide"
|
2026-07-25 11:51:39 +07:00
|
|
|
|
2026-07-25 11:56:26 +07:00
|
|
|
if [ "$LINK_MODE" = true ]; then
|
|
|
|
|
rm -rf "$DST"
|
|
|
|
|
ln -sf "$SCRIPT_DIR" "$DST"
|
2026-07-25 11:51:39 +07:00
|
|
|
else
|
2026-07-25 11:56:26 +07:00
|
|
|
rm -rf "$DST"
|
|
|
|
|
cp -r "$SCRIPT_DIR" "$DST"
|
2026-07-25 11:51:39 +07:00
|
|
|
fi
|
|
|
|
|
|
2026-07-26 12:06:29 +07:00
|
|
|
echo " skills installed to ${DST}/"
|
2026-07-25 11:51:39 +07:00
|
|
|
if [ "$LINK_MODE" = true ]; then
|
2026-07-26 12:06:29 +07:00
|
|
|
echo " (symlink)"
|
2026-07-25 11:51:39 +07:00
|
|
|
fi
|
2026-07-26 12:06:29 +07:00
|
|
|
|
|
|
|
|
# --- configure hooks in settings.local.json ---
|
|
|
|
|
if [ "$NO_HOOKS" = false ]; then
|
|
|
|
|
HOOKS_DIR="${DST}/hooks"
|
|
|
|
|
SETTINGS_FILE="${HOME}/.claude/settings.local.json"
|
|
|
|
|
|
|
|
|
|
python3 << PYEOF
|
|
|
|
|
import json, os
|
|
|
|
|
|
|
|
|
|
dst = "${DST}"
|
|
|
|
|
settings_file = os.path.expanduser("${SETTINGS_FILE}")
|
|
|
|
|
|
|
|
|
|
# Read existing settings or create empty
|
|
|
|
|
settings = {}
|
|
|
|
|
if os.path.exists(settings_file):
|
|
|
|
|
with open(settings_file) as f:
|
|
|
|
|
settings = json.load(f)
|
|
|
|
|
|
|
|
|
|
# Ensure hooks key exists
|
|
|
|
|
if "hooks" not in settings:
|
|
|
|
|
settings["hooks"] = {}
|
|
|
|
|
|
|
|
|
|
hooks_config = {
|
|
|
|
|
"SessionStart": [{
|
|
|
|
|
"matcher": "startup|resume|compact",
|
|
|
|
|
"hooks": [{
|
|
|
|
|
"type": "command",
|
|
|
|
|
"command": "bash \"" + dst + "/hooks/scripts/detect-project.sh\"",
|
|
|
|
|
"timeout": 10,
|
|
|
|
|
"async": False
|
|
|
|
|
}]
|
|
|
|
|
}],
|
|
|
|
|
"PreToolUse": [{
|
|
|
|
|
"matcher": "Write|Edit",
|
|
|
|
|
"hooks": [{
|
|
|
|
|
"type": "command",
|
|
|
|
|
"command": "bash \"" + dst + "/hooks/scripts/detect-file-type.sh\"",
|
|
|
|
|
"timeout": 10
|
|
|
|
|
}]
|
|
|
|
|
}]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Merge hooks into settings (update existing, don't overwrite unrelated hooks)
|
|
|
|
|
for event, handlers in hooks_config.items():
|
|
|
|
|
settings["hooks"][event] = handlers
|
|
|
|
|
|
|
|
|
|
os.makedirs(os.path.dirname(settings_file), exist_ok=True)
|
|
|
|
|
with open(settings_file, 'w') as f:
|
|
|
|
|
json.dump(settings, f, indent=2)
|
|
|
|
|
|
|
|
|
|
print(" hooks configured in " + settings_file)
|
|
|
|
|
PYEOF
|
|
|
|
|
fi
|
|
|
|
|
|
2026-07-25 11:51:39 +07:00
|
|
|
echo ""
|
2026-07-26 12:06:29 +07:00
|
|
|
echo "Done. Restart Claude Code or run /reload."
|
|
|
|
|
echo "Skills auto-trigger when you work."
|