aboutsummaryrefslogtreecommitdiff
path: root/scripts/install-lang.sh
blob: 2ee4aa7509958f53eb3be5aa0d0b5c975cdaad7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env bash
# Install a language ruleset into a target project.
# Usage: install-lang.sh <language> <project-path> [force]
#
# Copies the language's ruleset files into the project. Re-runnable
# (authoritative source overwrites). CLAUDE.md is preserved unless
# force=1, to avoid trampling project-specific customizations.

set -euo pipefail

LANG="${1:-}"
PROJECT="${2:-}"
FORCE="${3:-}"

if [ -z "$LANG" ] || [ -z "$PROJECT" ]; then
  echo "Usage: $0 <language> <project-path> [force]" >&2
  exit 1
fi

REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
SRC="$REPO_ROOT/languages/$LANG"

if [ ! -d "$SRC" ]; then
  echo "ERROR: no ruleset for language '$LANG' (expected $SRC)" >&2
  exit 1
fi

if [ ! -d "$PROJECT" ]; then
  echo "ERROR: project path does not exist: $PROJECT" >&2
  exit 1
fi

# Resolve to absolute path
PROJECT="$(cd "$PROJECT" && pwd)"

echo "Installing '$LANG' ruleset into $PROJECT"

# 1. Generic rules from claude-rules/ (shared across all languages)
if [ -d "$REPO_ROOT/claude-rules" ]; then
  mkdir -p "$PROJECT/.claude/rules"
  cp "$REPO_ROOT/claude-rules"/*.md "$PROJECT/.claude/rules/" 2>/dev/null || true
  count=$(ls -1 "$REPO_ROOT/claude-rules"/*.md 2>/dev/null | wc -l)
  echo "  [ok] .claude/rules/ — $count generic rule(s) from claude-rules/"
fi

# 2. .claude/ — language-specific rules, hooks, settings (authoritative, always overwrite)
if [ -d "$SRC/claude" ]; then
  mkdir -p "$PROJECT/.claude"
  cp -rT "$SRC/claude" "$PROJECT/.claude"
  if [ -d "$PROJECT/.claude/hooks" ]; then
    find "$PROJECT/.claude/hooks" -type f -name '*.sh' -exec chmod +x {} \;
  fi
  echo "  [ok] .claude/ — language-specific content"
fi

# 2. githooks/ — pre-commit etc.
if [ -d "$SRC/githooks" ]; then
  mkdir -p "$PROJECT/githooks"
  cp -rT "$SRC/githooks" "$PROJECT/githooks"
  find "$PROJECT/githooks" -type f -exec chmod +x {} \;
  if [ -d "$PROJECT/.git" ]; then
    git -C "$PROJECT" config core.hooksPath githooks
    echo "  [ok] githooks/ installed, core.hooksPath=githooks"
  else
    echo "  [ok] githooks/ installed (not a git repo — skipped core.hooksPath)"
  fi
fi

# 3. CLAUDE.md — seed on first install, don't overwrite unless FORCE=1
if [ -f "$SRC/CLAUDE.md" ]; then
  if [ -f "$PROJECT/CLAUDE.md" ] && [ "$FORCE" != "1" ]; then
    echo "  [skip] CLAUDE.md already exists (use FORCE=1 to overwrite)"
  else
    cp "$SRC/CLAUDE.md" "$PROJECT/CLAUDE.md"
    echo "  [ok] CLAUDE.md installed"
  fi
fi

# 4. .gitignore — append missing lines (deduped, skip comments)
if [ -f "$SRC/gitignore-add.txt" ]; then
  touch "$PROJECT/.gitignore"
  added=0
  while IFS= read -r line || [ -n "$line" ]; do
    # Skip blank lines and comments in the source file
    [ -z "$line" ] && continue
    case "$line" in \#*) continue ;; esac
    # Only add if not already present
    if ! grep -qxF "$line" "$PROJECT/.gitignore"; then
      # If this is the first line we're adding, prepend a header
      if [ "$added" -eq 0 ]; then
        printf '\n# --- %s ruleset ---\n' "$LANG" >> "$PROJECT/.gitignore"
      fi
      echo "$line" >> "$PROJECT/.gitignore"
      added=$((added + 1))
    fi
  done < "$SRC/gitignore-add.txt"
  if [ "$added" -gt 0 ]; then
    echo "  [ok] .gitignore: $added line(s) added"
  else
    echo "  [skip] .gitignore entries already present"
  fi
fi

echo ""
echo "Install complete."