install.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #!/usr/bin/env bash
  2. #
  3. # claude-mods Installer (Linux / macOS / Windows Git Bash)
  4. # Copies commands, skills, agents, and rules to ~/.claude/
  5. # Handles cleanup of deprecated items and command-to-skill migrations.
  6. #
  7. # Usage:
  8. # Linux/macOS: ./scripts/install.sh
  9. # Windows Git Bash: bash scripts/install.sh
  10. set -e
  11. BLUE='\033[0;34m'
  12. GREEN='\033[0;32m'
  13. YELLOW='\033[1;33m'
  14. RED='\033[0;31m'
  15. NC='\033[0m'
  16. echo -e "${BLUE}╔══════════════════════════════════════════════════════════════╗${NC}"
  17. echo -e "${BLUE}║ claude-mods Installer (Linux / macOS / Git Bash) ║${NC}"
  18. echo -e "${BLUE}╚══════════════════════════════════════════════════════════════╝${NC}"
  19. echo ""
  20. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  21. PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
  22. CLAUDE_DIR="$HOME/.claude"
  23. # Detect Windows (Git Bash / MINGW / MSYS) — chmod is a no-op on NTFS
  24. IS_WINDOWS=false
  25. case "$(uname -s)" in
  26. MINGW*|MSYS*|CYGWIN*) IS_WINDOWS=true ;;
  27. esac
  28. # Wrapper: chmod is meaningful only on Unix
  29. make_executable() {
  30. $IS_WINDOWS || chmod +x "$1"
  31. }
  32. # Ensure ~/.claude directories exist
  33. for dir in commands skills agents rules output-styles; do
  34. mkdir -p "$CLAUDE_DIR/$dir"
  35. done
  36. # =============================================================================
  37. # DEPRECATED ITEMS - Remove these from user's config
  38. # =============================================================================
  39. echo -e "${YELLOW}Cleaning up deprecated items...${NC}"
  40. deprecated_items=(
  41. # Removed commands (migrated to skills or deleted)
  42. "$CLAUDE_DIR/commands/review.md" # Migrated to skill
  43. "$CLAUDE_DIR/commands/testgen.md" # Migrated to skill
  44. "$CLAUDE_DIR/commands/conclave.md" # Deprecated
  45. "$CLAUDE_DIR/commands/pulse.md" # Now a skill only
  46. # Removed skills
  47. "$CLAUDE_DIR/skills/conclave" # Deprecated
  48. "$CLAUDE_DIR/skills/claude-code-templates" # Replaced by skill-creator
  49. "$CLAUDE_DIR/skills/agentmail" # Renamed to pigeon (v2.3.0)
  50. "$CLAUDE_DIR/skills/claude-code-debug" # Merged into claude-code-ops (v3.0)
  51. "$CLAUDE_DIR/skills/claude-code-headless" # Merged into claude-code-ops (v3.0)
  52. "$CLAUDE_DIR/skills/claude-code-hooks" # Merged into claude-code-ops (v3.0)
  53. # Deprecated agents (v3.0): folded into their -ops skill twins
  54. "$CLAUDE_DIR/agents/python-expert.md"
  55. "$CLAUDE_DIR/agents/typescript-expert.md"
  56. "$CLAUDE_DIR/agents/javascript-expert.md"
  57. "$CLAUDE_DIR/agents/go-expert.md"
  58. "$CLAUDE_DIR/agents/rust-expert.md"
  59. "$CLAUDE_DIR/agents/react-expert.md"
  60. "$CLAUDE_DIR/agents/vue-expert.md"
  61. "$CLAUDE_DIR/agents/astro-expert.md"
  62. "$CLAUDE_DIR/agents/laravel-expert.md"
  63. "$CLAUDE_DIR/agents/sql-expert.md"
  64. "$CLAUDE_DIR/agents/postgres-expert.md"
  65. "$CLAUDE_DIR/agents/cypress-expert.md" # -> skills/cypress-ops
  66. "$CLAUDE_DIR/agents/cloudflare-expert.md" # -> skills/cloudflare-ops
  67. "$CLAUDE_DIR/agents/wrangler-expert.md" # -> skills/cloudflare-ops
  68. "$CLAUDE_DIR/agents/bash-expert.md" # -> skills/bash-ops
  69. "$CLAUDE_DIR/agents/claude-architect.md" # -> skills/claude-code-ops
  70. "$CLAUDE_DIR/agents/aws-fargate-ecs-expert.md" # -> skills/container-orchestration
  71. "$CLAUDE_DIR/agents/craftcms-expert.md" # -> skills/craftcms-ops
  72. "$CLAUDE_DIR/agents/payloadcms-expert.md" # -> skills/payloadcms-ops
  73. "$CLAUDE_DIR/agents/asus-router-expert.md" # -> skills/asus-router-ops
  74. )
  75. # Renamed skills: -patterns -> -ops (March 2026)
  76. renamed_skills=(
  77. cli-patterns
  78. mcp-patterns
  79. python-async-patterns
  80. python-cli-patterns
  81. python-database-patterns
  82. python-fastapi-patterns
  83. python-observability-patterns
  84. python-pytest-patterns
  85. python-typing-patterns
  86. rest-patterns
  87. security-patterns
  88. sql-patterns
  89. tailwind-patterns
  90. testing-patterns
  91. )
  92. for old_skill in "${renamed_skills[@]}"; do
  93. old_path="$CLAUDE_DIR/skills/$old_skill"
  94. if [ -d "$old_path" ]; then
  95. rm -rf "$old_path"
  96. echo -e " ${RED}Removed renamed: $old_skill (now ${old_skill%-patterns}-ops)${NC}"
  97. fi
  98. done
  99. for item in "${deprecated_items[@]}"; do
  100. if [ -e "$item" ]; then
  101. rm -rf "$item"
  102. echo -e " ${RED}Removed: $item${NC}"
  103. fi
  104. done
  105. echo ""
  106. # =============================================================================
  107. # COMMANDS - Only copy commands that haven't been migrated to skills
  108. # =============================================================================
  109. echo -e "${BLUE}Installing commands...${NC}"
  110. # Commands that should NOT be copied (migrated to skills)
  111. skip_commands=("review.md" "testgen.md")
  112. for file in "$PROJECT_ROOT/commands"/*.md; do
  113. [ -f "$file" ] || continue
  114. filename=$(basename "$file")
  115. # Skip migrated commands
  116. skip=false
  117. for skip_cmd in "${skip_commands[@]}"; do
  118. if [ "$filename" = "$skip_cmd" ]; then
  119. skip=true
  120. break
  121. fi
  122. done
  123. # Skip archive directory contents
  124. [[ "$file" == *"/archive/"* ]] && continue
  125. if [ "$skip" = false ]; then
  126. cp "$file" "$CLAUDE_DIR/commands/"
  127. echo -e " ${GREEN}$filename${NC}"
  128. fi
  129. done
  130. echo ""
  131. # =============================================================================
  132. # SKILLS - Copy all skill directories
  133. # =============================================================================
  134. echo -e "${BLUE}Installing skills...${NC}"
  135. for skill_dir in "$PROJECT_ROOT/skills"/*/; do
  136. [ -d "$skill_dir" ] || continue
  137. skill_name=$(basename "$skill_dir")
  138. # _lib is the shared terminal library (skills/_lib/term.sh) that many skill
  139. # scripts source. It is NOT a skill, but it MUST be refreshed — scripts that
  140. # use newer term.sh features (TERM_DOT, brand glyphs, term_pip_bar) break with
  141. # an "unbound variable" under `set -u` against a stale copy.
  142. if [ "$skill_name" = "_lib" ]; then
  143. rm -rf "$CLAUDE_DIR/skills/_lib"
  144. cp -r "${skill_dir%/}" "$CLAUDE_DIR/skills/"
  145. echo -e " ${GREEN}_lib/${NC} (shared term library)"
  146. continue
  147. fi
  148. # Remove existing and copy fresh. Strip trailing slash from $skill_dir
  149. # so cp creates a subdirectory rather than merging contents (the *.*/* glob
  150. # always returns paths with trailing slashes, which makes cp behave as if
  151. # asked to copy contents — that's a long-standing bug we just fixed).
  152. rm -rf "$CLAUDE_DIR/skills/$skill_name"
  153. cp -r "${skill_dir%/}" "$CLAUDE_DIR/skills/"
  154. echo -e " ${GREEN}$skill_name/${NC}"
  155. done
  156. echo ""
  157. # =============================================================================
  158. # AGENTS - Copy all agent files
  159. # =============================================================================
  160. echo -e "${BLUE}Installing agents...${NC}"
  161. for file in "$PROJECT_ROOT/agents"/*.md; do
  162. [ -f "$file" ] || continue
  163. cp "$file" "$CLAUDE_DIR/agents/"
  164. echo -e " ${GREEN}$(basename "$file")${NC}"
  165. done
  166. echo ""
  167. # =============================================================================
  168. # RULES - Copy all rule files
  169. # =============================================================================
  170. echo -e "${BLUE}Installing rules...${NC}"
  171. for file in "$PROJECT_ROOT/rules"/*.md; do
  172. [ -f "$file" ] || continue
  173. cp "$file" "$CLAUDE_DIR/rules/"
  174. echo -e " ${GREEN}$(basename "$file")${NC}"
  175. done
  176. echo ""
  177. # =============================================================================
  178. # OUTPUT STYLES - Copy all output style files
  179. # =============================================================================
  180. echo -e "${BLUE}Installing output styles...${NC}"
  181. if [ -d "$PROJECT_ROOT/output-styles" ]; then
  182. for file in "$PROJECT_ROOT/output-styles"/*.md; do
  183. [ -f "$file" ] || continue
  184. cp "$file" "$CLAUDE_DIR/output-styles/"
  185. echo -e " ${GREEN}$(basename "$file")${NC}"
  186. done
  187. fi
  188. echo ""
  189. # =============================================================================
  190. # PIGEON - Global install (scripts + hook config hint)
  191. # =============================================================================
  192. echo -e "${BLUE}Installing pigeon (pmail)...${NC}"
  193. # Clean up old agentmail install if present
  194. if [ -d "$CLAUDE_DIR/agentmail" ]; then
  195. rm -rf "$CLAUDE_DIR/agentmail"
  196. echo -e " ${RED}Removed old agentmail/ (renamed to pigeon/)${NC}"
  197. fi
  198. mkdir -p "$CLAUDE_DIR/pigeon"
  199. if [ -f "$PROJECT_ROOT/skills/pigeon/scripts/mail-db.sh" ]; then
  200. cp "$PROJECT_ROOT/skills/pigeon/scripts/mail-db.sh" "$CLAUDE_DIR/pigeon/"
  201. make_executable "$CLAUDE_DIR/pigeon/mail-db.sh"
  202. echo -e " ${GREEN}mail-db.sh${NC}"
  203. fi
  204. if [ -f "$PROJECT_ROOT/hooks/check-mail.sh" ]; then
  205. cp "$PROJECT_ROOT/hooks/check-mail.sh" "$CLAUDE_DIR/pigeon/"
  206. make_executable "$CLAUDE_DIR/pigeon/check-mail.sh"
  207. echo -e " ${GREEN}check-mail.sh${NC}"
  208. fi
  209. # Migrate stale agentmail hook path → pigeon
  210. if grep -q "agentmail/check-mail.sh" "$CLAUDE_DIR/settings.json" 2>/dev/null; then
  211. sed -i 's|agentmail/check-mail\.sh|pigeon/check-mail.sh|g' "$CLAUDE_DIR/settings.json"
  212. echo -e " ${GREEN}Migrated agentmail hook → pigeon in settings.json${NC}"
  213. fi
  214. # Check if hook is already configured (pigeon path)
  215. if grep -q "pigeon/check-mail.sh" "$CLAUDE_DIR/settings.json" 2>/dev/null; then
  216. echo -e " ${GREEN}Hook already configured in settings.json${NC}"
  217. else
  218. echo ""
  219. echo -e " ${YELLOW}To enable automatic pmail notifications, add this to ~/.claude/settings.json:${NC}"
  220. echo ""
  221. echo ' "hooks": {'
  222. echo ' "PreToolUse": [{'
  223. echo ' "matcher": "*",'
  224. echo ' "hooks": [{'
  225. echo ' "type": "command",'
  226. echo ' "command": "bash \"$HOME/.claude/pigeon/check-mail.sh\"",'
  227. echo ' "timeout": 5'
  228. echo ' }]'
  229. echo ' }]'
  230. echo ' }'
  231. echo ""
  232. echo -e " ${YELLOW}Without this, pigeon works but you must check manually (pigeon read).${NC}"
  233. fi
  234. echo ""
  235. # =============================================================================
  236. # AUTO-SKILL - Global install (tracking + evaluation hooks)
  237. # =============================================================================
  238. echo -e "${BLUE}Installing auto-skill...${NC}"
  239. mkdir -p "$CLAUDE_DIR/auto-skill"
  240. for script in track-tools.sh evaluate.sh; do
  241. if [ -f "$PROJECT_ROOT/skills/auto-skill/scripts/$script" ]; then
  242. cp "$PROJECT_ROOT/skills/auto-skill/scripts/$script" "$CLAUDE_DIR/auto-skill/"
  243. make_executable "$CLAUDE_DIR/auto-skill/$script"
  244. echo -e " ${GREEN}$script${NC}"
  245. fi
  246. done
  247. # Check if hooks are already configured
  248. if grep -q "auto-skill" "$CLAUDE_DIR/settings.json" 2>/dev/null; then
  249. echo -e " ${GREEN}Hooks already configured in settings.json${NC}"
  250. else
  251. echo ""
  252. echo -e " ${YELLOW}To enable automatic skill suggestions, add these hooks to ~/.claude/settings.json:${NC}"
  253. echo ""
  254. echo ' "PostToolUse": [{ "matcher": "*", "hooks": [{'
  255. echo ' "type": "command",'
  256. echo ' "command": "bash \"$HOME/.claude/auto-skill/track-tools.sh\"", "timeout": 2'
  257. echo ' }] }],'
  258. echo ' "Stop": [{ "hooks": [{'
  259. echo ' "type": "command",'
  260. echo ' "command": "bash \"$HOME/.claude/auto-skill/evaluate.sh\"", "timeout": 5'
  261. echo ' }] }]'
  262. echo ""
  263. echo -e " ${YELLOW}Without this, /auto-skill still works but won't suggest automatically.${NC}"
  264. fi
  265. echo ""
  266. # =============================================================================
  267. # SUMMARY
  268. # =============================================================================
  269. echo -e "${BLUE}════════════════════════════════════════════════════════════════${NC}"
  270. echo -e " ${GREEN}Installation complete!${NC}"
  271. echo -e "${BLUE}════════════════════════════════════════════════════════════════${NC}"
  272. echo ""
  273. echo -e "${YELLOW}Restart Claude Code to load the new extensions.${NC}"
  274. echo ""