diff options
| -rw-r--r-- | Makefile | 24 | ||||
| -rwxr-xr-x | scripts/lint.sh | 35 |
2 files changed, 54 insertions, 5 deletions
@@ -4,11 +4,7 @@ SHELL := /bin/bash SKILLS_DIR := $(HOME)/.claude/skills RULES_DIR := $(HOME)/.claude/rules HOOKS_DIR := $(HOME)/.claude/hooks -SKILLS := c4-analyze c4-diagram debug add-tests respond-to-review review-code start-work security-check \ - arch-design arch-decide arch-document arch-evaluate \ - brainstorm codify root-cause-trace five-whys prompt-engineering \ - playwright-js playwright-py frontend-design pairwise-tests \ - finish-branch create-v2mom +SKILLS := $(patsubst %/SKILL.md,%,$(wildcard */SKILL.md)) RULES := $(wildcard claude-rules/*.md) HOOKS := $(wildcard hooks/*.sh hooks/*.py) LANGUAGES := $(notdir $(wildcard languages/*)) @@ -125,6 +121,16 @@ install: ## Symlink skills and rules into ~/.claude/ fi \ done @echo "" + @echo "Bridge symlink (lets SKILL.md cross-refs to ../claude-rules/ resolve from the install layout):" + @if [ -L "$(SKILLS_DIR)/claude-rules" ]; then \ + echo " skip claude-rules (already linked)"; \ + elif [ -e "$(SKILLS_DIR)/claude-rules" ]; then \ + echo " WARN claude-rules exists and is not a symlink — skipping"; \ + else \ + ln -s "$(CURDIR)/claude-rules" "$(SKILLS_DIR)/claude-rules"; \ + echo " link claude-rules → $(SKILLS_DIR)/claude-rules"; \ + fi + @echo "" @echo "done" uninstall: ## Remove global symlinks from ~/.claude/ @@ -149,6 +155,14 @@ uninstall: ## Remove global symlinks from ~/.claude/ fi \ done @echo "" + @echo "Bridge symlink:" + @if [ -L "$(SKILLS_DIR)/claude-rules" ]; then \ + rm "$(SKILLS_DIR)/claude-rules"; \ + echo " rm claude-rules"; \ + else \ + echo " skip claude-rules (not a symlink)"; \ + fi + @echo "" @echo "done" list: ## Show global install status diff --git a/scripts/lint.sh b/scripts/lint.sh index 2956aff..ae30aa5 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -5,6 +5,9 @@ # - Every rule file has an 'Applies to:' header # - Every language CLAUDE.md has a top-level heading # - Every hook script has a shebang and is executable +# - Every cross-reference to claude-rules/ from a SKILL.md or +# claude-rules/*.md resolves to a real file (catches the install-layout +# drift that the bridge symlink fixes) set -u @@ -45,6 +48,32 @@ check_hook() { fi } +check_md_links() { + # Validate cross-references to claude-rules/ — the install-layout problem + # solved by the bridge symlink in `make install`. Doesn't validate + # example file names that skills cite illustratively (e.g. ADR templates, + # arc42 section files), which are intentionally not real source files. + local f="$1" + [ -f "$f" ] || return 0 + local dir + dir="$(dirname "$f")" + while IFS= read -r link; do + local url="${link##*\(}" + url="${url%\)}" + case "$url" in + *claude-rules/*) ;; + *) continue ;; + esac + url="${url%%#*}" + url="${url%%\?*}" + local resolved + resolved="$(cd "$dir" 2>/dev/null && readlink -m "$url" 2>/dev/null)" + if [ -z "$resolved" ] || [ ! -e "$resolved" ]; then + warn "$f — broken claude-rules link: $url" + fi + done < <(grep -oE '\[[^]]*\]\([^)]+\)' "$f" 2>/dev/null || true) +} + echo "Linting rulesets in $REPO_ROOT" # Generic rules @@ -82,6 +111,12 @@ for s in scripts/*.sh; do check_hook "$s" done +# Markdown link validation across rules and skills +for f in claude-rules/*.md */SKILL.md; do + [ -f "$f" ] || continue + check_md_links "$f" +done + echo "---" if [ "$errors" -eq 0 ]; then echo "All checks passed." |
