aboutsummaryrefslogtreecommitdiff
path: root/scripts/install-lang.sh
blob: 2f38fcdac038908be998ed3d8db18556cd699435 (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/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.
#    Prefer the bundle's own template; fall back to the language-neutral
#    default so a bundle that ships none still seeds an accurate, non-
#    mislabeling header instead of nothing. The default names no language,
#    so multi-bundle and wrong-bundle installs don't inherit a false header.
CLAUDE_SRC=""
CLAUDE_KIND=""
if [ -f "$SRC/CLAUDE.md" ]; then
  CLAUDE_SRC="$SRC/CLAUDE.md"
  CLAUDE_KIND="$LANG"
elif [ -f "$REPO_ROOT/languages/default-CLAUDE.md" ]; then
  CLAUDE_SRC="$REPO_ROOT/languages/default-CLAUDE.md"
  CLAUDE_KIND="language-neutral default"
fi
if [ -n "$CLAUDE_SRC" ]; then
  if [ -f "$PROJECT/CLAUDE.md" ] && [ "$FORCE" != "1" ]; then
    echo "  [skip] CLAUDE.md already exists (use FORCE=1 to overwrite)"
  else
    cp "$CLAUDE_SRC" "$PROJECT/CLAUDE.md"
    echo "  [ok] CLAUDE.md installed ($CLAUDE_KIND)"
  fi
fi

# 3b. coverage-makefile.txt — project-owned Makefile fragment, seed on first
#     install. Never overwrites (the project edits its own copy) unless FORCE=1.
if [ -f "$SRC/coverage-makefile.txt" ]; then
  if [ -f "$PROJECT/coverage-makefile.txt" ] && [ "$FORCE" != "1" ]; then
    echo "  [skip] coverage-makefile.txt already exists (use FORCE=1 to overwrite)"
  else
    cp "$SRC/coverage-makefile.txt" "$PROJECT/coverage-makefile.txt"
    echo "  [ok] coverage-makefile.txt installed (copy its targets into your Makefile)"
  fi
fi

# 4. .gitignore — append missing lines (deduped, skip comments)
if [ -f "$SRC/gitignore-add.txt" ]; then
  touch "$PROJECT/.gitignore"
  header="# --- $LANG ruleset ---"
  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
      # Prepend header only if it isn't already in the file
      if [ "$added" -eq 0 ] && ! grep -qxF "$header" "$PROJECT/.gitignore"; then
        printf '\n%s\n' "$header" >> "$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."