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."
|