aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile24
-rwxr-xr-xscripts/lint.sh35
2 files changed, 54 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index aa1163a..62cb9d8 100644
--- a/Makefile
+++ b/Makefile
@@ -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."