From b3cfac365c7f046257fd944d597ae74e927f2f68 Mon Sep 17 00:00:00 2001 From: Jeff Roche Date: Wed, 22 Apr 2026 13:02:59 -0400 Subject: [PATCH 1/4] feat(plugins): add code-quality plugin with commit convention hooks Adds PreToolUse hook for conventional commit message validation, PostToolUse hook for AI attribution trailer checking, and SessionStart hook for Coderabbit config validation. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../code-quality/.claude-plugin/plugin.json | 8 +++ plugins/code-quality/README.md | 42 +++++++++++++ plugins/code-quality/hooks/hooks.json | 43 +++++++++++++ .../code-quality/scripts/check-attribution.sh | 45 ++++++++++++++ .../scripts/check-coderabbit-config.sh | 61 +++++++++++++++++++ .../scripts/check-commit-message.sh | 54 ++++++++++++++++ 6 files changed, 253 insertions(+) create mode 100644 plugins/code-quality/.claude-plugin/plugin.json create mode 100644 plugins/code-quality/README.md create mode 100644 plugins/code-quality/hooks/hooks.json create mode 100755 plugins/code-quality/scripts/check-attribution.sh create mode 100755 plugins/code-quality/scripts/check-coderabbit-config.sh create mode 100755 plugins/code-quality/scripts/check-commit-message.sh diff --git a/plugins/code-quality/.claude-plugin/plugin.json b/plugins/code-quality/.claude-plugin/plugin.json new file mode 100644 index 00000000..28fee587 --- /dev/null +++ b/plugins/code-quality/.claude-plugin/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "code-quality", + "description": "Code convention enforcement — commit messages, attribution, and Coderabbit configuration", + "version": "1.0.0", + "author": { "name": "jeff-roche" }, + "homepage": "https://github.com/openshift-eng/edge-tooling", + "license": "Apache-2.0" +} diff --git a/plugins/code-quality/README.md b/plugins/code-quality/README.md new file mode 100644 index 00000000..0dacf215 --- /dev/null +++ b/plugins/code-quality/README.md @@ -0,0 +1,42 @@ +# code-quality + +Code convention enforcement plugin for Claude Code. Validates commit messages, checks for AI attribution trailers, and verifies Coderabbit configuration. + +## Hooks + +Three hooks fire automatically: + +| Hook | Event | Behavior | +|------|-------|----------| +| `check-commit-message.sh` | PreToolUse(Bash) | **Blocks** commits that don't follow conventional commits format | +| `check-attribution.sh` | PostToolUse(Bash) | **Warns** if AI attribution trailers are missing (advisory, non-blocking) | +| `check-coderabbit-config.sh` | SessionStart | **Reports** missing or incomplete `.coderabbit.yaml` configuration | + +## Conventions Enforced + +### Commit Messages + +Subject line must match: `type(scope): description` + +Valid types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `build`, `ci`, `perf`, `style` + +### AI Attribution Trailers + +Commits should include one of: +- `Co-Authored-By: ...` +- `Assisted-by: ...` +- `Generated-by: ...` + +### Coderabbit Configuration + +Repos should have a `.coderabbit.yaml` with `auto_review`, `path_filters`/`path_instructions`, and `instructions` configured. + +## Installation + +``` +/plugin marketplace add openshift-eng/edge-tooling code-quality +``` + +## License + +Apache-2.0 diff --git a/plugins/code-quality/hooks/hooks.json b/plugins/code-quality/hooks/hooks.json new file mode 100644 index 00000000..365114c8 --- /dev/null +++ b/plugins/code-quality/hooks/hooks.json @@ -0,0 +1,43 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/scripts/check-commit-message.sh", + "timeout": 2, + "statusMessage": "Validating commit message..." + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/scripts/check-attribution.sh", + "timeout": 3, + "statusMessage": "Checking attribution..." + } + ] + } + ], + "SessionStart": [ + { + "matcher": "startup", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/scripts/check-coderabbit-config.sh", + "timeout": 5, + "statusMessage": "Checking Coderabbit config..." + } + ] + } + ] + } +} diff --git a/plugins/code-quality/scripts/check-attribution.sh b/plugins/code-quality/scripts/check-attribution.sh new file mode 100755 index 00000000..f4e7fc59 --- /dev/null +++ b/plugins/code-quality/scripts/check-attribution.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# PostToolUse(Bash) hook — warns if AI attribution trailers are missing after commits +set -euo pipefail + +if ! command -v jq &>/dev/null; then + exit 0 +fi + +INPUT=$(cat) +COMMAND=$(echo "$INPUT" | jq -r '.toolInput.command // empty') + +# Fast path: not a commit command +if [[ "$COMMAND" != *"git commit"* ]]; then + exit 0 +fi + +# Get the last commit message +COMMIT_MSG=$(git log -1 --format=%B 2>/dev/null || true) + +if [ -z "$COMMIT_MSG" ]; then + exit 0 +fi + +# Check for attribution trailers (case-insensitive) +if echo "$COMMIT_MSG" | grep -qi 'Co-Authored-By:'; then + exit 0 +fi +if echo "$COMMIT_MSG" | grep -qi 'Assisted-by:'; then + exit 0 +fi +if echo "$COMMIT_MSG" | grep -qi 'Generated-by:'; then + exit 0 +fi + +# No attribution found — emit advisory context +cat <<'EOF' +{ + "hookSpecificOutput": { + "hookEventName": "PostToolUse", + "additionalContext": "AI ATTRIBUTION MISSING: The last commit has no AI attribution trailer. Consider amending with one of:\n Co-Authored-By: Claude Opus 4.6 (1M context) \n Assisted-by: Claude Code\n Generated-by: Claude Code" + } +} +EOF + +exit 0 diff --git a/plugins/code-quality/scripts/check-coderabbit-config.sh b/plugins/code-quality/scripts/check-coderabbit-config.sh new file mode 100755 index 00000000..5b071b90 --- /dev/null +++ b/plugins/code-quality/scripts/check-coderabbit-config.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# SessionStart hook — validates .coderabbit.yaml presence and structure +set -euo pipefail + +if ! command -v jq &>/dev/null; then + exit 0 +fi + +INPUT=$(cat) +CWD=$(echo "$INPUT" | jq -r '.cwd // empty') + +if [ -z "$CWD" ]; then + exit 0 +fi + +CONFIG="$CWD/.coderabbit.yaml" + +# Check if config exists +if [ ! -f "$CONFIG" ]; then + cat <<'EOF' +{ + "hookSpecificOutput": { + "hookEventName": "SessionStart", + "additionalContext": "CODERABBIT CONFIG MISSING: No .coderabbit.yaml found. AI code review is not configured for this repo. Consider adding one with auto_review enabled, path_filters, and project-specific instructions." + } +} +EOF + exit 0 +fi + +# Validate config structure +MISSING=() + +if ! grep -q 'auto_review' "$CONFIG"; then + MISSING+=("auto_review") +fi + +if ! grep -qE 'path_filters|path_instructions' "$CONFIG"; then + MISSING+=("path_filters or path_instructions") +fi + +if ! grep -q 'instructions' "$CONFIG"; then + MISSING+=("instructions") +fi + +# If anything missing, report findings +if [ ${#MISSING[@]} -gt 0 ]; then + MISSING_LIST=$(printf '%s, ' "${MISSING[@]}" | sed 's/, $//') + MISSING_ESCAPED=$(printf '%s' "$MISSING_LIST" | sed 's/"/\\"/g') + + cat </dev/null; then + exit 0 +fi + +INPUT=$(cat) +COMMAND=$(echo "$INPUT" | jq -r '.toolInput.command // empty') + +# Fast path: not a commit command +if [[ "$COMMAND" != *"git commit"* ]]; then + exit 0 +fi + +# Extract commit message subject line +SUBJECT="" + +# Heredoc pattern: git commit -m "$(cat <<'EOF' ... EOF )" +if [[ "$COMMAND" =~ cat\ \<\<[\']?EOF ]]; then + # Extract first non-empty line after the heredoc delimiter + SUBJECT=$(echo "$COMMAND" | sed -n '/<<.*EOF/,/^EOF/{/<<.*EOF/d;/^EOF/d;/^[[:space:]]*$/d;p;}' | head -1 | sed 's/^[[:space:]]*//') +# Standard -m pattern with double or single quotes +elif [[ "$COMMAND" =~ git\ commit.*-m\ \" ]]; then + SUBJECT=$(echo "$COMMAND" | sed -n 's/.*git commit[^"]*-m "\([^"]*\)".*/\1/p' | head -1) +elif [[ "$COMMAND" =~ git\ commit.*-m\ \' ]]; then + SUBJECT=$(echo "$COMMAND" | sed -n "s/.*git commit[^']*-m '\\([^']*\\)'.*/\\1/p" | head -1) +fi + +# If we couldn't extract a message, fail open +if [ -z "$SUBJECT" ]; then + exit 0 +fi + +# For multi-line messages, take only the first line (subject) +SUBJECT=$(echo "$SUBJECT" | head -1) + +# Validate against conventional commits pattern +if echo "$SUBJECT" | grep -qE '^(feat|fix|docs|refactor|test|chore|build|ci|perf|style)(\(.+\))?: .+'; then + exit 0 +fi + +# Invalid format +cat >&2 <<'MSG' +Commit message does not follow conventional commits format. +Expected: type(scope): description +Types: feat, fix, docs, refactor, test, chore, build, ci, perf, style +Examples: + feat(api): add health endpoint + fix(deploy): correct subnet CIDR validation + docs: update contributing guide +MSG +exit 1 From 359bbf98edd953eabb5ebbb2b84d47dd5af615e7 Mon Sep 17 00:00:00 2001 From: Jeff Roche Date: Wed, 22 Apr 2026 13:17:48 -0400 Subject: [PATCH 2/4] fix(plugins): skip coderabbit config check for non-git directories Co-Authored-By: Claude Opus 4.6 (1M context) --- plugins/code-quality/scripts/check-coderabbit-config.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/code-quality/scripts/check-coderabbit-config.sh b/plugins/code-quality/scripts/check-coderabbit-config.sh index 5b071b90..05829857 100755 --- a/plugins/code-quality/scripts/check-coderabbit-config.sh +++ b/plugins/code-quality/scripts/check-coderabbit-config.sh @@ -13,6 +13,10 @@ if [ -z "$CWD" ]; then exit 0 fi +if [ ! -d "$CWD/.git" ] && ! git -C "$CWD" rev-parse --git-dir &>/dev/null; then + exit 0 +fi + CONFIG="$CWD/.coderabbit.yaml" # Check if config exists From bbd85e17665147cd4867cf0a31e4fe6944093366 Mon Sep 17 00:00:00 2001 From: Jeff Roche Date: Wed, 22 Apr 2026 13:23:10 -0400 Subject: [PATCH 3/4] fix(plugins): address PR review feedback for code-quality plugin - Add bash language tag to fenced code block in README (MD040) - Use #!/usr/bin/bash shebang across all scripts - Anchor grep patterns to YAML key positions in coderabbit config check Co-Authored-By: Claude Opus 4.6 (1M context) --- plugins/code-quality/README.md | 2 +- plugins/code-quality/scripts/check-attribution.sh | 2 +- plugins/code-quality/scripts/check-coderabbit-config.sh | 8 ++++---- plugins/code-quality/scripts/check-commit-message.sh | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/code-quality/README.md b/plugins/code-quality/README.md index 0dacf215..db01bee3 100644 --- a/plugins/code-quality/README.md +++ b/plugins/code-quality/README.md @@ -33,7 +33,7 @@ Repos should have a `.coderabbit.yaml` with `auto_review`, `path_filters`/`path_ ## Installation -``` +```bash /plugin marketplace add openshift-eng/edge-tooling code-quality ``` diff --git a/plugins/code-quality/scripts/check-attribution.sh b/plugins/code-quality/scripts/check-attribution.sh index f4e7fc59..e48374b3 100755 --- a/plugins/code-quality/scripts/check-attribution.sh +++ b/plugins/code-quality/scripts/check-attribution.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/bash # PostToolUse(Bash) hook — warns if AI attribution trailers are missing after commits set -euo pipefail diff --git a/plugins/code-quality/scripts/check-coderabbit-config.sh b/plugins/code-quality/scripts/check-coderabbit-config.sh index 05829857..05cc4344 100755 --- a/plugins/code-quality/scripts/check-coderabbit-config.sh +++ b/plugins/code-quality/scripts/check-coderabbit-config.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/bash # SessionStart hook — validates .coderabbit.yaml presence and structure set -euo pipefail @@ -35,15 +35,15 @@ fi # Validate config structure MISSING=() -if ! grep -q 'auto_review' "$CONFIG"; then +if ! grep -qE '^[[:space:]]*auto_review[[:space:]]*:' "$CONFIG"; then MISSING+=("auto_review") fi -if ! grep -qE 'path_filters|path_instructions' "$CONFIG"; then +if ! grep -qE '^[[:space:]]*(path_filters|path_instructions)[[:space:]]*:' "$CONFIG"; then MISSING+=("path_filters or path_instructions") fi -if ! grep -q 'instructions' "$CONFIG"; then +if ! grep -qE '^[[:space:]]*instructions[[:space:]]*:' "$CONFIG"; then MISSING+=("instructions") fi diff --git a/plugins/code-quality/scripts/check-commit-message.sh b/plugins/code-quality/scripts/check-commit-message.sh index e6842c27..7da2c485 100755 --- a/plugins/code-quality/scripts/check-commit-message.sh +++ b/plugins/code-quality/scripts/check-commit-message.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/bash # PreToolUse(Bash) hook — validates conventional commits format on git commit commands set -euo pipefail From 50fbabb3651b4352313acd2d73d62ca961890735 Mon Sep 17 00:00:00 2001 From: Jeff Roche Date: Wed, 22 Apr 2026 13:42:17 -0400 Subject: [PATCH 4/4] chore(plugins): register code-quality in marketplace catalog Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude-plugin/marketplace.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index ebae08ed..db84111a 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -12,6 +12,12 @@ "description": "Adversarial hypothesis reviewer — systematically attacks theories and root cause analyses to find weaknesses before they find you", "version": "1.0.0" }, + { + "name": "code-quality", + "source": "./plugins/code-quality", + "description": "Code convention enforcement — commit messages, attribution, and Coderabbit configuration", + "version": "1.0.0" + }, { "name": "edge-ocp-ci", "source": "./plugins/edge-ocp-ci",