#!/usr/bin/env bash # Hook: enforce-rules (PreToolUse, matcher: ^(edit|write|multiedit)$) # Inspects the target file path of every edit/write/multiedit and: # 1. Blocks edits to banned file types in production paths. # 2. Injects a "did you write a failing test first?" reminder for source edits. # # Banned file types are configurable via PROJECT_BANNED_SUFFIXES (regex alternation) # and PROJECT_BANNED_ALLOW (regex of paths that are exempt, e.g. vendored code). set -euo pipefail PATH_PREFIX="${CRUSH_WORKING_DIR:-/work}" FILE_PATH="${CRUSH_TOOL_INPUT_FILE_PATH:-}" # Strip common prefixes to get a repo-relative path. REL_PATH="${FILE_PATH#"$PATH_PREFIX"/}" REL_PATH="${REL_PATH#/}" BANNED_SUFFIXES="${PROJECT_BANNED_SUFFIXES:-}" BANNED_ALLOW="${PROJECT_BANNED_ALLOW:-^$}" # --- 1. Banned file types --------------------------------------------------- if [ -n "$BANNED_SUFFIXES" ] && printf '%s' "$REL_PATH" | grep -qE "\.($BANNED_SUFFIXES)$"; then if ! printf '%s' "$REL_PATH" | grep -qE "$BANNED_ALLOW"; then echo "BLOCKED: AGENTS.md language policy — $REL_PATH is a banned file type in production. Allowed only under: $BANNED_ALLOW" >&2 exit 2 fi fi # --- 2. TDD reminder for source-file edits ---------------------------------- # Toggle via PROJECT_SOURCE_SUFFIXES (regex). Default: none (opt-in). SOURCE_SUFFIXES="${PROJECT_SOURCE_SUFFIXES:-}" TEST_PATTERN="${PROJECT_TEST_PATTERN:-_test\.}" if [ -n "$SOURCE_SUFFIXES" ] && printf '%s' "$REL_PATH" | grep -qE "\.($SOURCE_SUFFIXES)$"; then if ! printf '%s' "$REL_PATH" | grep -qE "$TEST_PATTERN"; then cat <<'EOF' {"context": "You are editing a source file. TDD is mandatory: did you write a FAILING test first? If not, stop and write the test before editing this file."} EOF exit 0 fi fi echo '{}'