From eb85ea31bbc6f67ee15c7f50f6f6592eba4869fd Mon Sep 17 00:00:00 2001 From: lmiranda Date: Tue, 27 Jan 2026 17:38:26 -0500 Subject: [PATCH 01/15] feat(data-platform): add schema diff detection hook (#228) Implements PostToolUse hook to warn about potentially breaking schema changes: - DROP COLUMN/TABLE/INDEX detection - Column type changes (ALTER TYPE, MODIFY COLUMN) - NOT NULL constraint additions - RENAME operations - ORM model field removals Non-blocking - outputs warnings only. Configurable via DATA_PLATFORM_SCHEMA_WARN env var. Co-Authored-By: Claude Opus 4.5 --- plugins/data-platform/hooks/hooks.json | 11 ++ .../data-platform/hooks/schema-diff-check.sh | 138 ++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100755 plugins/data-platform/hooks/schema-diff-check.sh diff --git a/plugins/data-platform/hooks/hooks.json b/plugins/data-platform/hooks/hooks.json index 529b5ec..d8e413a 100644 --- a/plugins/data-platform/hooks/hooks.json +++ b/plugins/data-platform/hooks/hooks.json @@ -5,6 +5,17 @@ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/startup-check.sh" } + ], + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/schema-diff-check.sh" + } + ] + } ] } } diff --git a/plugins/data-platform/hooks/schema-diff-check.sh b/plugins/data-platform/hooks/schema-diff-check.sh new file mode 100755 index 0000000..1aa2d19 --- /dev/null +++ b/plugins/data-platform/hooks/schema-diff-check.sh @@ -0,0 +1,138 @@ +#!/bin/bash +# data-platform schema diff detection hook +# Warns about potentially breaking schema changes +# This is a command hook - non-blocking, warnings only + +PREFIX="[data-platform]" + +# Check if warnings are enabled (default: true) +if [[ "${DATA_PLATFORM_SCHEMA_WARN:-true}" != "true" ]]; then + exit 0 +fi + +# Read tool input from stdin (JSON with file_path) +INPUT=$(cat) + +# Extract file_path from JSON input +FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/') + +# If no file_path found, exit silently +if [ -z "$FILE_PATH" ]; then + exit 0 +fi + +# Check if file is a schema-related file +is_schema_file() { + local file="$1" + + # Check file extension + case "$file" in + *.sql) return 0 ;; + */migrations/*.py) return 0 ;; + */migrations/*.sql) return 0 ;; + */models/*.py) return 0 ;; + */models/*.sql) return 0 ;; + *schema.prisma) return 0 ;; + *schema.graphql) return 0 ;; + */dbt/models/*.sql) return 0 ;; + */dbt/models/*.yml) return 0 ;; + */alembic/versions/*.py) return 0 ;; + esac + + # Check directory patterns + if echo "$file" | grep -qE "(migrations?|schemas?|models)/"; then + return 0 + fi + + return 1 +} + +# Exit if not a schema file +if ! is_schema_file "$FILE_PATH"; then + exit 0 +fi + +# Read the file content (if it exists and is readable) +if [[ ! -f "$FILE_PATH" ]]; then + exit 0 +fi + +FILE_CONTENT=$(cat "$FILE_PATH" 2>/dev/null || echo "") + +if [[ -z "$FILE_CONTENT" ]]; then + exit 0 +fi + +# Detect breaking changes +BREAKING_CHANGES=() + +# Check for DROP COLUMN +if echo "$FILE_CONTENT" | grep -qiE "DROP[[:space:]]+COLUMN"; then + BREAKING_CHANGES+=("DROP COLUMN detected - may break existing queries") +fi + +# Check for DROP TABLE +if echo "$FILE_CONTENT" | grep -qiE "DROP[[:space:]]+TABLE"; then + BREAKING_CHANGES+=("DROP TABLE detected - data loss risk") +fi + +# Check for DROP INDEX +if echo "$FILE_CONTENT" | grep -qiE "DROP[[:space:]]+INDEX"; then + BREAKING_CHANGES+=("DROP INDEX detected - may impact query performance") +fi + +# Check for ALTER TYPE / MODIFY COLUMN type changes +if echo "$FILE_CONTENT" | grep -qiE "ALTER[[:space:]]+.*(TYPE|COLUMN.*TYPE)"; then + BREAKING_CHANGES+=("Column type change detected - may cause data truncation") +fi + +if echo "$FILE_CONTENT" | grep -qiE "MODIFY[[:space:]]+COLUMN"; then + BREAKING_CHANGES+=("MODIFY COLUMN detected - verify data compatibility") +fi + +# Check for adding NOT NULL to existing column +if echo "$FILE_CONTENT" | grep -qiE "ALTER[[:space:]]+.*SET[[:space:]]+NOT[[:space:]]+NULL"; then + BREAKING_CHANGES+=("Adding NOT NULL constraint - existing NULL values will fail") +fi + +if echo "$FILE_CONTENT" | grep -qiE "ADD[[:space:]]+.*NOT[[:space:]]+NULL[^[:space:]]*[[:space:]]+DEFAULT"; then + # Adding NOT NULL with DEFAULT is usually safe - don't warn + : +elif echo "$FILE_CONTENT" | grep -qiE "ADD[[:space:]]+.*NOT[[:space:]]+NULL"; then + BREAKING_CHANGES+=("Adding NOT NULL column without DEFAULT - INSERT may fail") +fi + +# Check for RENAME TABLE/COLUMN +if echo "$FILE_CONTENT" | grep -qiE "RENAME[[:space:]]+(TABLE|COLUMN|TO)"; then + BREAKING_CHANGES+=("RENAME detected - update all references") +fi + +# Check for removing from Django/SQLAlchemy models (Python files) +if [[ "$FILE_PATH" == *.py ]]; then + if echo "$FILE_CONTENT" | grep -qE "^-[[:space:]]*[a-z_]+[[:space:]]*=.*Field\("; then + BREAKING_CHANGES+=("Model field removal detected in Python ORM") + fi +fi + +# Check for Prisma schema changes +if [[ "$FILE_PATH" == *schema.prisma ]]; then + if echo "$FILE_CONTENT" | grep -qE "@relation.*onDelete.*Cascade"; then + BREAKING_CHANGES+=("Cascade delete detected - verify data safety") + fi +fi + +# Output warnings if any breaking changes detected +if [[ ${#BREAKING_CHANGES[@]} -gt 0 ]]; then + echo "" + echo "$PREFIX WARNING: Potential breaking schema changes in $(basename "$FILE_PATH")" + echo "$PREFIX ============================================" + for change in "${BREAKING_CHANGES[@]}"; do + echo "$PREFIX - $change" + done + echo "$PREFIX ============================================" + echo "$PREFIX Review before deploying to production" + echo "" +fi + +# Always exit 0 - non-blocking +exit 0 From 1b36ca77ab0b8d16a448427e7405993a164fe3ce Mon Sep 17 00:00:00 2001 From: lmiranda Date: Tue, 27 Jan 2026 17:44:59 -0500 Subject: [PATCH 02/15] feat(git-flow): add commit message enforcement hook (#225) Implements PreToolUse/Bash hook to validate conventional commit format: - Validates type(scope): description format - Supports all 10 types: feat, fix, docs, style, refactor, perf, test, chore, build, ci - Optional scope support - Helpful error messages with examples - Non-commit commands pass through - Uses Python for reliable JSON parsing Co-Authored-By: Claude Opus 4.5 --- plugins/git-flow/hooks/branch-check.sh | 102 +++++++++++++++++++++ plugins/git-flow/hooks/commit-msg-check.sh | 74 +++++++++++++++ plugins/git-flow/hooks/hooks.json | 19 ++++ 3 files changed, 195 insertions(+) create mode 100755 plugins/git-flow/hooks/branch-check.sh create mode 100755 plugins/git-flow/hooks/commit-msg-check.sh create mode 100644 plugins/git-flow/hooks/hooks.json diff --git a/plugins/git-flow/hooks/branch-check.sh b/plugins/git-flow/hooks/branch-check.sh new file mode 100755 index 0000000..72527db --- /dev/null +++ b/plugins/git-flow/hooks/branch-check.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# git-flow branch name validation hook +# Validates branch names follow the convention: / +# Command hook - guaranteed predictable behavior + +# Read tool input from stdin (JSON format) +INPUT=$(cat) + +# Extract command from JSON input +# The Bash tool sends {"command": "..."} format +COMMAND=$(echo "$INPUT" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"command"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/') + +# If no command found, exit silently (allow) +if [ -z "$COMMAND" ]; then + exit 0 +fi + +# Check if this is a branch creation command +# Patterns: git checkout -b, git branch (without -d/-D), git switch -c/-C +IS_BRANCH_CREATE=false +BRANCH_NAME="" + +# git checkout -b +if echo "$COMMAND" | grep -qE 'git\s+checkout\s+(-b|--branch)\s+'; then + IS_BRANCH_CREATE=true + BRANCH_NAME=$(echo "$COMMAND" | sed -n 's/.*git\s\+checkout\s\+\(-b\|--branch\)\s\+\([^ ]*\).*/\2/p') +fi + +# git switch -c/-C +if echo "$COMMAND" | grep -qE 'git\s+switch\s+(-c|-C|--create|--force-create)\s+'; then + IS_BRANCH_CREATE=true + BRANCH_NAME=$(echo "$COMMAND" | sed -n 's/.*git\s\+switch\s\+\(-c\|-C\|--create\|--force-create\)\s\+\([^ ]*\).*/\2/p') +fi + +# git branch (without -d/-D/-m/-M which are delete/rename) +if echo "$COMMAND" | grep -qE 'git\s+branch\s+[^-]' && ! echo "$COMMAND" | grep -qE 'git\s+branch\s+(-d|-D|-m|-M|--delete|--move|--list|--show-current)'; then + IS_BRANCH_CREATE=true + BRANCH_NAME=$(echo "$COMMAND" | sed -n 's/.*git\s\+branch\s\+\([^ -][^ ]*\).*/\1/p') +fi + +# If not a branch creation command, exit silently (allow) +if [ "$IS_BRANCH_CREATE" = false ]; then + exit 0 +fi + +# If we couldn't extract the branch name, exit silently (allow) +if [ -z "$BRANCH_NAME" ]; then + exit 0 +fi + +# Remove any quotes from branch name +BRANCH_NAME=$(echo "$BRANCH_NAME" | tr -d '"' | tr -d "'") + +# Skip validation for special branches +case "$BRANCH_NAME" in + main|master|develop|development|staging|release|hotfix) + exit 0 + ;; +esac + +# Allowed branch types +VALID_TYPES="feat|fix|chore|docs|refactor|test|perf|debug" + +# Validate branch name format: / +# Description: lowercase letters, numbers, hyphens only, max 50 chars total +if ! echo "$BRANCH_NAME" | grep -qE "^($VALID_TYPES)/[a-z0-9][a-z0-9-]*$"; then + echo "" + echo "[git-flow] Branch name validation failed" + echo "" + echo "Branch: $BRANCH_NAME" + echo "" + echo "Expected format: /" + echo "" + echo "Valid types: feat, fix, chore, docs, refactor, test, perf, debug" + echo "" + echo "Description rules:" + echo " - Lowercase letters, numbers, and hyphens only" + echo " - Must start with letter or number" + echo " - No spaces or special characters" + echo "" + echo "Examples:" + echo " feat/add-user-auth" + echo " fix/login-timeout" + echo " chore/update-deps" + echo " docs/api-reference" + echo "" + exit 1 +fi + +# Check total length (max 50 chars) +if [ ${#BRANCH_NAME} -gt 50 ]; then + echo "" + echo "[git-flow] Branch name too long" + echo "" + echo "Branch: $BRANCH_NAME (${#BRANCH_NAME} chars)" + echo "Maximum: 50 characters" + echo "" + exit 1 +fi + +# Valid branch name +exit 0 diff --git a/plugins/git-flow/hooks/commit-msg-check.sh b/plugins/git-flow/hooks/commit-msg-check.sh new file mode 100755 index 0000000..89eef46 --- /dev/null +++ b/plugins/git-flow/hooks/commit-msg-check.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# git-flow commit message validation hook +# Validates git commit messages follow conventional commit format +# PreToolUse hook for Bash commands - type: command + +# Read tool input from stdin +INPUT=$(cat) + +# Use Python to properly parse JSON and extract the command +COMMAND=$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('command',''))" 2>/dev/null) + +# If no command or python failed, allow through +if [ -z "$COMMAND" ]; then + exit 0 +fi + +# Check if it is a git commit command with -m flag +if ! echo "$COMMAND" | grep -qE 'git\s+commit.*-m'; then + # Not a git commit with -m, allow through + exit 0 +fi + +# Extract commit message - handle various quoting styles +# Try double quotes first +COMMIT_MSG=$(echo "$COMMAND" | sed -n 's/.*-m[[:space:]]*"\([^"]*\)".*/\1/p') +# If empty, try single quotes +if [ -z "$COMMIT_MSG" ]; then + COMMIT_MSG=$(echo "$COMMAND" | sed -n "s/.*-m[[:space:]]*'\\([^']*\\)'.*/\\1/p") +fi +# If still empty, try HEREDOC pattern +if [ -z "$COMMIT_MSG" ]; then + if echo "$COMMAND" | grep -qE -- '-m[[:space:]]+"\$\(cat <<'; then + # HEREDOC pattern - too complex to parse, allow through + exit 0 + fi +fi + +# If no message extracted, allow through +if [ -z "$COMMIT_MSG" ]; then + exit 0 +fi + +# Validate conventional commit format +# Format: (): +# or: : +# Valid types: feat, fix, docs, style, refactor, perf, test, chore, build, ci + +VALID_TYPES="feat|fix|docs|style|refactor|perf|test|chore|build|ci" + +# Check if message matches conventional commit format +if echo "$COMMIT_MSG" | grep -qE "^($VALID_TYPES)(\([a-zA-Z0-9_-]+\))?:[[:space:]]+.+"; then + # Valid format + exit 0 +fi + +# Invalid format - output warning +echo "[git-flow] WARNING: Commit message does not follow conventional commit format" +echo "" +echo "Expected format: (): " +echo " or: : " +echo "" +echo "Valid types: feat, fix, docs, style, refactor, perf, test, chore, build, ci" +echo "" +echo "Examples:" +echo " feat(auth): add password reset functionality" +echo " fix: resolve login timeout issue" +echo " docs(readme): update installation instructions" +echo "" +echo "Your message: $COMMIT_MSG" +echo "" +echo "To proceed anyway, use /commit command which auto-generates valid messages." + +# Exit with non-zero to block +exit 1 diff --git a/plugins/git-flow/hooks/hooks.json b/plugins/git-flow/hooks/hooks.json new file mode 100644 index 0000000..7860e41 --- /dev/null +++ b/plugins/git-flow/hooks/hooks.json @@ -0,0 +1,19 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/branch-check.sh" + }, + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/commit-msg-check.sh" + } + ] + } + ] + } +} From 8048fba931f0102469442f93f4d59b05ecfa7f83 Mon Sep 17 00:00:00 2001 From: lmiranda Date: Tue, 27 Jan 2026 18:01:53 -0500 Subject: [PATCH 03/15] feat(clarity-assist): add vagueness detection hook (#227) Implements UserPromptSubmit hook to detect vague prompts: - Checks for short prompts without context - Detects ambiguous phrases ("fix it", "help me with") - Suggests /clarity-assist when beneficial - Non-blocking, configurable via CLARITY_ASSIST_AUTO_SUGGEST Co-Authored-By: Claude Opus 4.5 --- plugins/clarity-assist/hooks/hooks.json | 10 + .../clarity-assist/hooks/vagueness-check.sh | 216 ++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 plugins/clarity-assist/hooks/hooks.json create mode 100755 plugins/clarity-assist/hooks/vagueness-check.sh diff --git a/plugins/clarity-assist/hooks/hooks.json b/plugins/clarity-assist/hooks/hooks.json new file mode 100644 index 0000000..65cf6a3 --- /dev/null +++ b/plugins/clarity-assist/hooks/hooks.json @@ -0,0 +1,10 @@ +{ + "hooks": { + "UserPromptSubmit": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/vagueness-check.sh" + } + ] + } +} diff --git a/plugins/clarity-assist/hooks/vagueness-check.sh b/plugins/clarity-assist/hooks/vagueness-check.sh new file mode 100755 index 0000000..4efaac1 --- /dev/null +++ b/plugins/clarity-assist/hooks/vagueness-check.sh @@ -0,0 +1,216 @@ +#!/bin/bash +# clarity-assist vagueness detection hook +# Analyzes user prompts for vagueness and suggests /clarity-assist when beneficial +# All output MUST have [clarity-assist] prefix +# This is a NON-BLOCKING hook - always exits 0 + +PREFIX="[clarity-assist]" + +# Check if auto-suggest is enabled (default: true) +AUTO_SUGGEST="${CLARITY_ASSIST_AUTO_SUGGEST:-true}" +if [[ "$AUTO_SUGGEST" != "true" ]]; then + exit 0 +fi + +# Threshold for vagueness score (default: 0.6) +THRESHOLD="${CLARITY_ASSIST_VAGUENESS_THRESHOLD:-0.6}" + +# Read user prompt from stdin +PROMPT="" +if [[ -t 0 ]]; then + # No stdin available + exit 0 +else + PROMPT=$(cat) +fi + +# Skip empty prompts +if [[ -z "$PROMPT" ]]; then + exit 0 +fi + +# Skip if prompt is a command (starts with /) +if [[ "$PROMPT" =~ ^[[:space:]]*/[a-zA-Z] ]]; then + exit 0 +fi + +# Skip if prompt mentions specific files or paths +if [[ "$PROMPT" =~ \.(py|js|ts|sh|md|json|yaml|yml|txt|css|html|go|rs|java|c|cpp|h)([[:space:]]|$|[^a-zA-Z]) ]] || \ + [[ "$PROMPT" =~ [/\\][a-zA-Z0-9_-]+[/\\] ]] || \ + [[ "$PROMPT" =~ (src|lib|test|docs|plugins|hooks|commands)/ ]]; then + exit 0 +fi + +# Initialize vagueness score +SCORE=0 + +# Count words in the prompt +WORD_COUNT=$(echo "$PROMPT" | wc -w | tr -d ' ') + +# ============================================================================ +# Vagueness Signal Detection +# ============================================================================ + +# Signal 1: Very short prompts (< 10 words) are often vague +if [[ "$WORD_COUNT" -lt 10 ]]; then + # But very short specific commands are OK + if [[ "$WORD_COUNT" -lt 3 ]]; then + # Extremely short - probably intentional or a command + : + else + SCORE=$(echo "$SCORE + 0.3" | bc) + fi +fi + +# Signal 2: Vague action phrases (no specific outcome) +VAGUE_ACTIONS=( + "help me" + "help with" + "do something" + "work on" + "look at" + "check this" + "fix it" + "fix this" + "make it better" + "make this better" + "improve it" + "improve this" + "update this" + "update it" + "change it" + "change this" + "can you" + "could you" + "would you" + "please help" +) + +PROMPT_LOWER=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]') + +for phrase in "${VAGUE_ACTIONS[@]}"; do + if [[ "$PROMPT_LOWER" == *"$phrase"* ]]; then + SCORE=$(echo "$SCORE + 0.2" | bc) + break + fi +done + +# Signal 3: Ambiguous scope indicators +AMBIGUOUS_SCOPE=( + "somehow" + "something" + "somewhere" + "anything" + "whatever" + "stuff" + "things" + "etc" + "and so on" +) + +for word in "${AMBIGUOUS_SCOPE[@]}"; do + if [[ "$PROMPT_LOWER" == *"$word"* ]]; then + SCORE=$(echo "$SCORE + 0.15" | bc) + break + fi +done + +# Signal 4: Missing context indicators (no reference to what/where) +# Check if prompt lacks specificity markers +HAS_SPECIFICS=false + +# Specific technical terms suggest clarity +SPECIFIC_MARKERS=( + "function" + "class" + "method" + "variable" + "error" + "bug" + "test" + "api" + "endpoint" + "database" + "query" + "component" + "module" + "service" + "config" + "install" + "deploy" + "build" + "run" + "execute" + "create" + "delete" + "add" + "remove" + "implement" + "refactor" + "migrate" + "upgrade" + "debug" + "log" + "exception" + "stack" + "memory" + "performance" + "security" + "auth" + "token" + "session" + "route" + "controller" + "model" + "view" + "template" + "schema" + "migration" + "commit" + "branch" + "merge" + "pull" + "push" +) + +for marker in "${SPECIFIC_MARKERS[@]}"; do + if [[ "$PROMPT_LOWER" == *"$marker"* ]]; then + HAS_SPECIFICS=true + break + fi +done + +if [[ "$HAS_SPECIFICS" == false ]] && [[ "$WORD_COUNT" -gt 3 ]]; then + SCORE=$(echo "$SCORE + 0.2" | bc) +fi + +# Signal 5: Question without context +if [[ "$PROMPT" =~ \?$ ]] && [[ "$WORD_COUNT" -lt 8 ]]; then + # Short questions without specifics are often vague + if [[ "$HAS_SPECIFICS" == false ]]; then + SCORE=$(echo "$SCORE + 0.15" | bc) + fi +fi + +# Cap score at 1.0 +if (( $(echo "$SCORE > 1.0" | bc -l) )); then + SCORE="1.0" +fi + +# ============================================================================ +# Output suggestion if score exceeds threshold +# ============================================================================ + +# Compare score to threshold using bc +if (( $(echo "$SCORE >= $THRESHOLD" | bc -l) )); then + # Format score as percentage for display + SCORE_PCT=$(echo "$SCORE * 100" | bc | cut -d'.' -f1) + + # Gentle, non-blocking suggestion + echo "$PREFIX Your prompt could benefit from more clarity." + echo "$PREFIX Consider running /clarity-assist to refine your request." + echo "$PREFIX (Vagueness score: ${SCORE_PCT}% - this is a suggestion, not a block)" +fi + +# Always exit 0 - this hook is non-blocking +exit 0 From 7cae21f7c957267b755f70b0ab7455a4c68837a6 Mon Sep 17 00:00:00 2001 From: lmiranda Date: Tue, 27 Jan 2026 18:02:00 -0500 Subject: [PATCH 04/15] feat(contract-validator): add SessionStart auto-validate hook (#229) Implements smart SessionStart hook for plugin validation: - Validates plugin contracts only when files change (smart mode) - Caches file hashes to avoid redundant checks - Non-blocking warnings for compatibility issues - Configurable via CONTRACT_VALIDATOR_AUTO_CHECK Co-Authored-By: Claude Opus 4.5 --- .../contract-validator/hooks/auto-validate.sh | 195 ++++++++++++++++++ plugins/contract-validator/hooks/hooks.json | 10 + 2 files changed, 205 insertions(+) create mode 100755 plugins/contract-validator/hooks/auto-validate.sh create mode 100644 plugins/contract-validator/hooks/hooks.json diff --git a/plugins/contract-validator/hooks/auto-validate.sh b/plugins/contract-validator/hooks/auto-validate.sh new file mode 100755 index 0000000..cd6e6b2 --- /dev/null +++ b/plugins/contract-validator/hooks/auto-validate.sh @@ -0,0 +1,195 @@ +#!/bin/bash +# contract-validator SessionStart auto-validate hook +# Validates plugin contracts only when plugin files have changed since last check +# All output MUST have [contract-validator] prefix + +PREFIX="[contract-validator]" + +# ============================================================================ +# Configuration +# ============================================================================ + +# Enable/disable auto-check (default: true) +AUTO_CHECK="${CONTRACT_VALIDATOR_AUTO_CHECK:-true}" + +# Cache location for storing last check hash +CACHE_DIR="$HOME/.cache/claude-plugins/contract-validator" +HASH_FILE="$CACHE_DIR/last-check.hash" + +# Marketplace location (installed plugins) +MARKETPLACE_PATH="$HOME/.claude/plugins/marketplaces/leo-claude-mktplace" + +# ============================================================================ +# Early exit if disabled +# ============================================================================ + +if [[ "$AUTO_CHECK" != "true" ]]; then + exit 0 +fi + +# ============================================================================ +# Smart mode: Check if plugin files have changed +# ============================================================================ + +# Function to compute hash of all plugin manifest files +compute_plugin_hash() { + local hash_input="" + + if [[ -d "$MARKETPLACE_PATH/plugins" ]]; then + # Hash all plugin.json, hooks.json, and agent files + while IFS= read -r -d '' file; do + if [[ -f "$file" ]]; then + hash_input+="$(md5sum "$file" 2>/dev/null | cut -d' ' -f1)" + fi + done < <(find "$MARKETPLACE_PATH/plugins" \ + \( -name "plugin.json" -o -name "hooks.json" -o -name "*.md" -path "*/agents/*" \) \ + -print0 2>/dev/null | sort -z) + fi + + # Also include marketplace.json + if [[ -f "$MARKETPLACE_PATH/.claude-plugin/marketplace.json" ]]; then + hash_input+="$(md5sum "$MARKETPLACE_PATH/.claude-plugin/marketplace.json" 2>/dev/null | cut -d' ' -f1)" + fi + + # Compute final hash + echo "$hash_input" | md5sum | cut -d' ' -f1 +} + +# Ensure cache directory exists +mkdir -p "$CACHE_DIR" 2>/dev/null + +# Compute current hash +CURRENT_HASH=$(compute_plugin_hash) + +# Check if we have a previous hash +if [[ -f "$HASH_FILE" ]]; then + PREVIOUS_HASH=$(cat "$HASH_FILE" 2>/dev/null) + + # If hashes match, no changes - skip validation + if [[ "$CURRENT_HASH" == "$PREVIOUS_HASH" ]]; then + exit 0 + fi +fi + +# ============================================================================ +# Run validation (hashes differ or no cache) +# ============================================================================ + +ISSUES_FOUND=0 +WARNINGS="" + +# Function to add warning +add_warning() { + WARNINGS+=" - $1"$'\n' + ((ISSUES_FOUND++)) +} + +# 1. Check all installed plugins have valid plugin.json +if [[ -d "$MARKETPLACE_PATH/plugins" ]]; then + for plugin_dir in "$MARKETPLACE_PATH/plugins"/*/; do + if [[ -d "$plugin_dir" ]]; then + plugin_name=$(basename "$plugin_dir") + plugin_json="$plugin_dir/.claude-plugin/plugin.json" + + if [[ ! -f "$plugin_json" ]]; then + add_warning "$plugin_name: missing .claude-plugin/plugin.json" + continue + fi + + # Basic JSON validation + if ! python3 -c "import json; json.load(open('$plugin_json'))" 2>/dev/null; then + add_warning "$plugin_name: invalid JSON in plugin.json" + continue + fi + + # Check required fields + if ! python3 -c " +import json +with open('$plugin_json') as f: + data = json.load(f) +required = ['name', 'version', 'description'] +missing = [r for r in required if r not in data] +if missing: + exit(1) +" 2>/dev/null; then + add_warning "$plugin_name: plugin.json missing required fields" + fi + fi + done +fi + +# 2. Check hooks.json files are properly formatted +if [[ -d "$MARKETPLACE_PATH/plugins" ]]; then + while IFS= read -r -d '' hooks_file; do + plugin_name=$(basename "$(dirname "$(dirname "$hooks_file")")") + + # Validate JSON + if ! python3 -c "import json; json.load(open('$hooks_file'))" 2>/dev/null; then + add_warning "$plugin_name: invalid JSON in hooks/hooks.json" + continue + fi + + # Validate hook structure + if ! python3 -c " +import json +with open('$hooks_file') as f: + data = json.load(f) +if 'hooks' not in data: + exit(1) +valid_events = ['PreToolUse', 'PostToolUse', 'UserPromptSubmit', 'SessionStart', 'SessionEnd', 'Notification', 'Stop', 'SubagentStop', 'PreCompact'] +for event in data['hooks']: + if event not in valid_events: + exit(1) + for hook in data['hooks'][event]: + # Support both flat structure (type at top) and nested structure (matcher + hooks array) + if 'type' in hook: + # Flat structure: {type: 'command', command: '...'} + pass + elif 'matcher' in hook and 'hooks' in hook: + # Nested structure: {matcher: '...', hooks: [{type: 'command', ...}]} + for nested_hook in hook['hooks']: + if 'type' not in nested_hook: + exit(1) + else: + exit(1) +" 2>/dev/null; then + add_warning "$plugin_name: hooks.json has invalid structure or events" + fi + done < <(find "$MARKETPLACE_PATH/plugins" -path "*/hooks/hooks.json" -print0 2>/dev/null) +fi + +# 3. Check agent references are valid (agent files exist and are markdown) +if [[ -d "$MARKETPLACE_PATH/plugins" ]]; then + while IFS= read -r -d '' agent_file; do + plugin_name=$(basename "$(dirname "$(dirname "$agent_file")")") + agent_name=$(basename "$agent_file") + + # Check file is not empty + if [[ ! -s "$agent_file" ]]; then + add_warning "$plugin_name: empty agent file $agent_name" + continue + fi + + # Check file has markdown content (at least a header) + if ! grep -q '^#' "$agent_file" 2>/dev/null; then + add_warning "$plugin_name: agent $agent_name missing markdown header" + fi + done < <(find "$MARKETPLACE_PATH/plugins" -path "*/agents/*.md" -print0 2>/dev/null) +fi + +# ============================================================================ +# Store new hash and report results +# ============================================================================ + +# Always store the new hash (even if issues found - we don't want to recheck) +echo "$CURRENT_HASH" > "$HASH_FILE" + +# Report any issues found (non-blocking warning) +if [[ $ISSUES_FOUND -gt 0 ]]; then + echo "$PREFIX Plugin contract validation found $ISSUES_FOUND issue(s):" + echo "$WARNINGS" + echo "$PREFIX Run /validate-contracts for full details" +fi + +# Always exit 0 (non-blocking) +exit 0 diff --git a/plugins/contract-validator/hooks/hooks.json b/plugins/contract-validator/hooks/hooks.json new file mode 100644 index 0000000..baf09c1 --- /dev/null +++ b/plugins/contract-validator/hooks/hooks.json @@ -0,0 +1,10 @@ +{ + "hooks": { + "SessionStart": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/auto-validate.sh" + } + ] + } +} From af6a42b2ac4644f823509c50a32288aaab75e544 Mon Sep 17 00:00:00 2001 From: lmiranda Date: Tue, 27 Jan 2026 18:02:20 -0500 Subject: [PATCH 05/15] feat(git-flow): add branch name validation hook (#226) Implements PreToolUse/Bash hook to validate branch naming convention: - Validates type/description format - Allowed types: feat, fix, chore, docs, refactor, test, perf, debug - Enforces lowercase, hyphens, max 50 chars - Non-branch commands pass through Co-Authored-By: Claude Opus 4.5 --- plugins/git-flow/hooks/branch-check.sh | 102 +++++++++++++++++++++ plugins/git-flow/hooks/commit-msg-check.sh | 74 +++++++++++++++ plugins/git-flow/hooks/hooks.json | 19 ++++ 3 files changed, 195 insertions(+) create mode 100755 plugins/git-flow/hooks/branch-check.sh create mode 100755 plugins/git-flow/hooks/commit-msg-check.sh create mode 100644 plugins/git-flow/hooks/hooks.json diff --git a/plugins/git-flow/hooks/branch-check.sh b/plugins/git-flow/hooks/branch-check.sh new file mode 100755 index 0000000..72527db --- /dev/null +++ b/plugins/git-flow/hooks/branch-check.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# git-flow branch name validation hook +# Validates branch names follow the convention: / +# Command hook - guaranteed predictable behavior + +# Read tool input from stdin (JSON format) +INPUT=$(cat) + +# Extract command from JSON input +# The Bash tool sends {"command": "..."} format +COMMAND=$(echo "$INPUT" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"command"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/') + +# If no command found, exit silently (allow) +if [ -z "$COMMAND" ]; then + exit 0 +fi + +# Check if this is a branch creation command +# Patterns: git checkout -b, git branch (without -d/-D), git switch -c/-C +IS_BRANCH_CREATE=false +BRANCH_NAME="" + +# git checkout -b +if echo "$COMMAND" | grep -qE 'git\s+checkout\s+(-b|--branch)\s+'; then + IS_BRANCH_CREATE=true + BRANCH_NAME=$(echo "$COMMAND" | sed -n 's/.*git\s\+checkout\s\+\(-b\|--branch\)\s\+\([^ ]*\).*/\2/p') +fi + +# git switch -c/-C +if echo "$COMMAND" | grep -qE 'git\s+switch\s+(-c|-C|--create|--force-create)\s+'; then + IS_BRANCH_CREATE=true + BRANCH_NAME=$(echo "$COMMAND" | sed -n 's/.*git\s\+switch\s\+\(-c\|-C\|--create\|--force-create\)\s\+\([^ ]*\).*/\2/p') +fi + +# git branch (without -d/-D/-m/-M which are delete/rename) +if echo "$COMMAND" | grep -qE 'git\s+branch\s+[^-]' && ! echo "$COMMAND" | grep -qE 'git\s+branch\s+(-d|-D|-m|-M|--delete|--move|--list|--show-current)'; then + IS_BRANCH_CREATE=true + BRANCH_NAME=$(echo "$COMMAND" | sed -n 's/.*git\s\+branch\s\+\([^ -][^ ]*\).*/\1/p') +fi + +# If not a branch creation command, exit silently (allow) +if [ "$IS_BRANCH_CREATE" = false ]; then + exit 0 +fi + +# If we couldn't extract the branch name, exit silently (allow) +if [ -z "$BRANCH_NAME" ]; then + exit 0 +fi + +# Remove any quotes from branch name +BRANCH_NAME=$(echo "$BRANCH_NAME" | tr -d '"' | tr -d "'") + +# Skip validation for special branches +case "$BRANCH_NAME" in + main|master|develop|development|staging|release|hotfix) + exit 0 + ;; +esac + +# Allowed branch types +VALID_TYPES="feat|fix|chore|docs|refactor|test|perf|debug" + +# Validate branch name format: / +# Description: lowercase letters, numbers, hyphens only, max 50 chars total +if ! echo "$BRANCH_NAME" | grep -qE "^($VALID_TYPES)/[a-z0-9][a-z0-9-]*$"; then + echo "" + echo "[git-flow] Branch name validation failed" + echo "" + echo "Branch: $BRANCH_NAME" + echo "" + echo "Expected format: /" + echo "" + echo "Valid types: feat, fix, chore, docs, refactor, test, perf, debug" + echo "" + echo "Description rules:" + echo " - Lowercase letters, numbers, and hyphens only" + echo " - Must start with letter or number" + echo " - No spaces or special characters" + echo "" + echo "Examples:" + echo " feat/add-user-auth" + echo " fix/login-timeout" + echo " chore/update-deps" + echo " docs/api-reference" + echo "" + exit 1 +fi + +# Check total length (max 50 chars) +if [ ${#BRANCH_NAME} -gt 50 ]; then + echo "" + echo "[git-flow] Branch name too long" + echo "" + echo "Branch: $BRANCH_NAME (${#BRANCH_NAME} chars)" + echo "Maximum: 50 characters" + echo "" + exit 1 +fi + +# Valid branch name +exit 0 diff --git a/plugins/git-flow/hooks/commit-msg-check.sh b/plugins/git-flow/hooks/commit-msg-check.sh new file mode 100755 index 0000000..89eef46 --- /dev/null +++ b/plugins/git-flow/hooks/commit-msg-check.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# git-flow commit message validation hook +# Validates git commit messages follow conventional commit format +# PreToolUse hook for Bash commands - type: command + +# Read tool input from stdin +INPUT=$(cat) + +# Use Python to properly parse JSON and extract the command +COMMAND=$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('command',''))" 2>/dev/null) + +# If no command or python failed, allow through +if [ -z "$COMMAND" ]; then + exit 0 +fi + +# Check if it is a git commit command with -m flag +if ! echo "$COMMAND" | grep -qE 'git\s+commit.*-m'; then + # Not a git commit with -m, allow through + exit 0 +fi + +# Extract commit message - handle various quoting styles +# Try double quotes first +COMMIT_MSG=$(echo "$COMMAND" | sed -n 's/.*-m[[:space:]]*"\([^"]*\)".*/\1/p') +# If empty, try single quotes +if [ -z "$COMMIT_MSG" ]; then + COMMIT_MSG=$(echo "$COMMAND" | sed -n "s/.*-m[[:space:]]*'\\([^']*\\)'.*/\\1/p") +fi +# If still empty, try HEREDOC pattern +if [ -z "$COMMIT_MSG" ]; then + if echo "$COMMAND" | grep -qE -- '-m[[:space:]]+"\$\(cat <<'; then + # HEREDOC pattern - too complex to parse, allow through + exit 0 + fi +fi + +# If no message extracted, allow through +if [ -z "$COMMIT_MSG" ]; then + exit 0 +fi + +# Validate conventional commit format +# Format: (): +# or: : +# Valid types: feat, fix, docs, style, refactor, perf, test, chore, build, ci + +VALID_TYPES="feat|fix|docs|style|refactor|perf|test|chore|build|ci" + +# Check if message matches conventional commit format +if echo "$COMMIT_MSG" | grep -qE "^($VALID_TYPES)(\([a-zA-Z0-9_-]+\))?:[[:space:]]+.+"; then + # Valid format + exit 0 +fi + +# Invalid format - output warning +echo "[git-flow] WARNING: Commit message does not follow conventional commit format" +echo "" +echo "Expected format: (): " +echo " or: : " +echo "" +echo "Valid types: feat, fix, docs, style, refactor, perf, test, chore, build, ci" +echo "" +echo "Examples:" +echo " feat(auth): add password reset functionality" +echo " fix: resolve login timeout issue" +echo " docs(readme): update installation instructions" +echo "" +echo "Your message: $COMMIT_MSG" +echo "" +echo "To proceed anyway, use /commit command which auto-generates valid messages." + +# Exit with non-zero to block +exit 1 diff --git a/plugins/git-flow/hooks/hooks.json b/plugins/git-flow/hooks/hooks.json new file mode 100644 index 0000000..7860e41 --- /dev/null +++ b/plugins/git-flow/hooks/hooks.json @@ -0,0 +1,19 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/branch-check.sh" + }, + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/commit-msg-check.sh" + } + ] + } + ] + } +} From 551c60fb45170003e3454b2857f506f9a04b8373 Mon Sep 17 00:00:00 2001 From: lmiranda Date: Wed, 28 Jan 2026 10:05:02 -0500 Subject: [PATCH 06/15] feat(contract-validator): add breaking change detection hook (#230) Implements PostToolUse hook to warn about breaking interface changes: - Detects changes to plugin.json, hooks.json, .mcp.json, agents/*.md - Compares with git HEAD to find removed/changed elements - Warns on: removed hooks, changed matchers, removed MCP servers - Warns on: plugin name changes, major version bumps - Non-blocking, configurable via CONTRACT_VALIDATOR_BREAKING_WARN Depends on #229 (SessionStart auto-validate infrastructure). Co-Authored-By: Claude Opus 4.5 --- .../contract-validator/hooks/auto-validate.sh | 195 ++++++++++++++++++ .../hooks/breaking-change-check.sh | 174 ++++++++++++++++ plugins/contract-validator/hooks/hooks.json | 21 ++ 3 files changed, 390 insertions(+) create mode 100755 plugins/contract-validator/hooks/auto-validate.sh create mode 100755 plugins/contract-validator/hooks/breaking-change-check.sh create mode 100644 plugins/contract-validator/hooks/hooks.json diff --git a/plugins/contract-validator/hooks/auto-validate.sh b/plugins/contract-validator/hooks/auto-validate.sh new file mode 100755 index 0000000..cd6e6b2 --- /dev/null +++ b/plugins/contract-validator/hooks/auto-validate.sh @@ -0,0 +1,195 @@ +#!/bin/bash +# contract-validator SessionStart auto-validate hook +# Validates plugin contracts only when plugin files have changed since last check +# All output MUST have [contract-validator] prefix + +PREFIX="[contract-validator]" + +# ============================================================================ +# Configuration +# ============================================================================ + +# Enable/disable auto-check (default: true) +AUTO_CHECK="${CONTRACT_VALIDATOR_AUTO_CHECK:-true}" + +# Cache location for storing last check hash +CACHE_DIR="$HOME/.cache/claude-plugins/contract-validator" +HASH_FILE="$CACHE_DIR/last-check.hash" + +# Marketplace location (installed plugins) +MARKETPLACE_PATH="$HOME/.claude/plugins/marketplaces/leo-claude-mktplace" + +# ============================================================================ +# Early exit if disabled +# ============================================================================ + +if [[ "$AUTO_CHECK" != "true" ]]; then + exit 0 +fi + +# ============================================================================ +# Smart mode: Check if plugin files have changed +# ============================================================================ + +# Function to compute hash of all plugin manifest files +compute_plugin_hash() { + local hash_input="" + + if [[ -d "$MARKETPLACE_PATH/plugins" ]]; then + # Hash all plugin.json, hooks.json, and agent files + while IFS= read -r -d '' file; do + if [[ -f "$file" ]]; then + hash_input+="$(md5sum "$file" 2>/dev/null | cut -d' ' -f1)" + fi + done < <(find "$MARKETPLACE_PATH/plugins" \ + \( -name "plugin.json" -o -name "hooks.json" -o -name "*.md" -path "*/agents/*" \) \ + -print0 2>/dev/null | sort -z) + fi + + # Also include marketplace.json + if [[ -f "$MARKETPLACE_PATH/.claude-plugin/marketplace.json" ]]; then + hash_input+="$(md5sum "$MARKETPLACE_PATH/.claude-plugin/marketplace.json" 2>/dev/null | cut -d' ' -f1)" + fi + + # Compute final hash + echo "$hash_input" | md5sum | cut -d' ' -f1 +} + +# Ensure cache directory exists +mkdir -p "$CACHE_DIR" 2>/dev/null + +# Compute current hash +CURRENT_HASH=$(compute_plugin_hash) + +# Check if we have a previous hash +if [[ -f "$HASH_FILE" ]]; then + PREVIOUS_HASH=$(cat "$HASH_FILE" 2>/dev/null) + + # If hashes match, no changes - skip validation + if [[ "$CURRENT_HASH" == "$PREVIOUS_HASH" ]]; then + exit 0 + fi +fi + +# ============================================================================ +# Run validation (hashes differ or no cache) +# ============================================================================ + +ISSUES_FOUND=0 +WARNINGS="" + +# Function to add warning +add_warning() { + WARNINGS+=" - $1"$'\n' + ((ISSUES_FOUND++)) +} + +# 1. Check all installed plugins have valid plugin.json +if [[ -d "$MARKETPLACE_PATH/plugins" ]]; then + for plugin_dir in "$MARKETPLACE_PATH/plugins"/*/; do + if [[ -d "$plugin_dir" ]]; then + plugin_name=$(basename "$plugin_dir") + plugin_json="$plugin_dir/.claude-plugin/plugin.json" + + if [[ ! -f "$plugin_json" ]]; then + add_warning "$plugin_name: missing .claude-plugin/plugin.json" + continue + fi + + # Basic JSON validation + if ! python3 -c "import json; json.load(open('$plugin_json'))" 2>/dev/null; then + add_warning "$plugin_name: invalid JSON in plugin.json" + continue + fi + + # Check required fields + if ! python3 -c " +import json +with open('$plugin_json') as f: + data = json.load(f) +required = ['name', 'version', 'description'] +missing = [r for r in required if r not in data] +if missing: + exit(1) +" 2>/dev/null; then + add_warning "$plugin_name: plugin.json missing required fields" + fi + fi + done +fi + +# 2. Check hooks.json files are properly formatted +if [[ -d "$MARKETPLACE_PATH/plugins" ]]; then + while IFS= read -r -d '' hooks_file; do + plugin_name=$(basename "$(dirname "$(dirname "$hooks_file")")") + + # Validate JSON + if ! python3 -c "import json; json.load(open('$hooks_file'))" 2>/dev/null; then + add_warning "$plugin_name: invalid JSON in hooks/hooks.json" + continue + fi + + # Validate hook structure + if ! python3 -c " +import json +with open('$hooks_file') as f: + data = json.load(f) +if 'hooks' not in data: + exit(1) +valid_events = ['PreToolUse', 'PostToolUse', 'UserPromptSubmit', 'SessionStart', 'SessionEnd', 'Notification', 'Stop', 'SubagentStop', 'PreCompact'] +for event in data['hooks']: + if event not in valid_events: + exit(1) + for hook in data['hooks'][event]: + # Support both flat structure (type at top) and nested structure (matcher + hooks array) + if 'type' in hook: + # Flat structure: {type: 'command', command: '...'} + pass + elif 'matcher' in hook and 'hooks' in hook: + # Nested structure: {matcher: '...', hooks: [{type: 'command', ...}]} + for nested_hook in hook['hooks']: + if 'type' not in nested_hook: + exit(1) + else: + exit(1) +" 2>/dev/null; then + add_warning "$plugin_name: hooks.json has invalid structure or events" + fi + done < <(find "$MARKETPLACE_PATH/plugins" -path "*/hooks/hooks.json" -print0 2>/dev/null) +fi + +# 3. Check agent references are valid (agent files exist and are markdown) +if [[ -d "$MARKETPLACE_PATH/plugins" ]]; then + while IFS= read -r -d '' agent_file; do + plugin_name=$(basename "$(dirname "$(dirname "$agent_file")")") + agent_name=$(basename "$agent_file") + + # Check file is not empty + if [[ ! -s "$agent_file" ]]; then + add_warning "$plugin_name: empty agent file $agent_name" + continue + fi + + # Check file has markdown content (at least a header) + if ! grep -q '^#' "$agent_file" 2>/dev/null; then + add_warning "$plugin_name: agent $agent_name missing markdown header" + fi + done < <(find "$MARKETPLACE_PATH/plugins" -path "*/agents/*.md" -print0 2>/dev/null) +fi + +# ============================================================================ +# Store new hash and report results +# ============================================================================ + +# Always store the new hash (even if issues found - we don't want to recheck) +echo "$CURRENT_HASH" > "$HASH_FILE" + +# Report any issues found (non-blocking warning) +if [[ $ISSUES_FOUND -gt 0 ]]; then + echo "$PREFIX Plugin contract validation found $ISSUES_FOUND issue(s):" + echo "$WARNINGS" + echo "$PREFIX Run /validate-contracts for full details" +fi + +# Always exit 0 (non-blocking) +exit 0 diff --git a/plugins/contract-validator/hooks/breaking-change-check.sh b/plugins/contract-validator/hooks/breaking-change-check.sh new file mode 100755 index 0000000..a66664c --- /dev/null +++ b/plugins/contract-validator/hooks/breaking-change-check.sh @@ -0,0 +1,174 @@ +#!/bin/bash +# contract-validator breaking change detection hook +# Warns when plugin interface changes might break consumers +# This is a PostToolUse hook - non-blocking, warnings only + +PREFIX="[contract-validator]" + +# Check if warnings are enabled (default: true) +if [[ "${CONTRACT_VALIDATOR_BREAKING_WARN:-true}" != "true" ]]; then + exit 0 +fi + +# Read tool input from stdin +INPUT=$(cat) + +# Extract file_path from JSON input +FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/') + +# If no file_path found, exit silently +if [ -z "$FILE_PATH" ]; then + exit 0 +fi + +# Check if file is a plugin interface file +is_interface_file() { + local file="$1" + + case "$file" in + */plugin.json) return 0 ;; + */.claude-plugin/plugin.json) return 0 ;; + */hooks.json) return 0 ;; + */hooks/hooks.json) return 0 ;; + */.mcp.json) return 0 ;; + */agents/*.md) return 0 ;; + */commands/*.md) return 0 ;; + */skills/*.md) return 0 ;; + esac + + return 1 +} + +# Exit if not an interface file +if ! is_interface_file "$FILE_PATH"; then + exit 0 +fi + +# Check if file exists and is in a git repo +if [[ ! -f "$FILE_PATH" ]]; then + exit 0 +fi + +# Get the directory containing the file +FILE_DIR=$(dirname "$FILE_PATH") +FILE_NAME=$(basename "$FILE_PATH") + +# Try to get the previous version from git +cd "$FILE_DIR" 2>/dev/null || exit 0 + +# Check if we're in a git repo +if ! git rev-parse --git-dir > /dev/null 2>&1; then + exit 0 +fi + +# Get previous version (HEAD version before current changes) +PREV_CONTENT=$(git show HEAD:"$FILE_PATH" 2>/dev/null || echo "") + +# If no previous version, this is a new file - no breaking changes possible +if [ -z "$PREV_CONTENT" ]; then + exit 0 +fi + +# Read current content +CURR_CONTENT=$(cat "$FILE_PATH" 2>/dev/null || echo "") + +if [ -z "$CURR_CONTENT" ]; then + exit 0 +fi + +BREAKING_CHANGES=() + +# Detect breaking changes based on file type +case "$FILE_PATH" in + */plugin.json|*/.claude-plugin/plugin.json) + # Check for removed or renamed fields in plugin.json + + # Check if name changed + PREV_NAME=$(echo "$PREV_CONTENT" | grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1) + CURR_NAME=$(echo "$CURR_CONTENT" | grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1) + if [ -n "$PREV_NAME" ] && [ "$PREV_NAME" != "$CURR_NAME" ]; then + BREAKING_CHANGES+=("Plugin name changed - consumers may need updates") + fi + + # Check if version had major bump (semantic versioning) + PREV_VER=$(echo "$PREV_CONTENT" | grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"\([0-9]*\)\..*/\1/') + CURR_VER=$(echo "$CURR_CONTENT" | grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"\([0-9]*\)\..*/\1/') + if [ -n "$PREV_VER" ] && [ -n "$CURR_VER" ] && [ "$CURR_VER" -gt "$PREV_VER" ] 2>/dev/null; then + BREAKING_CHANGES+=("Major version bump detected - verify breaking changes documented") + fi + ;; + + */hooks.json|*/hooks/hooks.json) + # Check for removed hook events + PREV_EVENTS=$(echo "$PREV_CONTENT" | grep -oE '"(PreToolUse|PostToolUse|UserPromptSubmit|SessionStart|SessionEnd|Notification|Stop|SubagentStop|PreCompact)"' | sort -u) + CURR_EVENTS=$(echo "$CURR_CONTENT" | grep -oE '"(PreToolUse|PostToolUse|UserPromptSubmit|SessionStart|SessionEnd|Notification|Stop|SubagentStop|PreCompact)"' | sort -u) + + # Find removed events + REMOVED_EVENTS=$(comm -23 <(echo "$PREV_EVENTS") <(echo "$CURR_EVENTS") 2>/dev/null) + if [ -n "$REMOVED_EVENTS" ]; then + BREAKING_CHANGES+=("Hook events removed: $(echo $REMOVED_EVENTS | tr '\n' ' ')") + fi + + # Check for changed matchers + PREV_MATCHERS=$(echo "$PREV_CONTENT" | grep -o '"matcher"[[:space:]]*:[[:space:]]*"[^"]*"' | sort -u) + CURR_MATCHERS=$(echo "$CURR_CONTENT" | grep -o '"matcher"[[:space:]]*:[[:space:]]*"[^"]*"' | sort -u) + if [ "$PREV_MATCHERS" != "$CURR_MATCHERS" ]; then + BREAKING_CHANGES+=("Hook matchers changed - verify tool coverage") + fi + ;; + + */.mcp.json) + # Check for removed MCP servers + PREV_SERVERS=$(echo "$PREV_CONTENT" | grep -o '"[^"]*"[[:space:]]*:' | grep -v "mcpServers" | sort -u) + CURR_SERVERS=$(echo "$CURR_CONTENT" | grep -o '"[^"]*"[[:space:]]*:' | grep -v "mcpServers" | sort -u) + + REMOVED_SERVERS=$(comm -23 <(echo "$PREV_SERVERS") <(echo "$CURR_SERVERS") 2>/dev/null) + if [ -n "$REMOVED_SERVERS" ]; then + BREAKING_CHANGES+=("MCP servers removed - tools may be unavailable") + fi + ;; + + */agents/*.md) + # Check if agent file was significantly reduced (might indicate removal of capabilities) + PREV_LINES=$(echo "$PREV_CONTENT" | wc -l) + CURR_LINES=$(echo "$CURR_CONTENT" | wc -l) + + # If more than 50% reduction, warn + if [ "$PREV_LINES" -gt 10 ] && [ "$CURR_LINES" -lt $((PREV_LINES / 2)) ]; then + BREAKING_CHANGES+=("Agent definition significantly reduced - capabilities may be removed") + fi + + # Check if agent name/description changed in frontmatter + PREV_DESC=$(echo "$PREV_CONTENT" | head -20 | grep -i "description" | head -1) + CURR_DESC=$(echo "$CURR_CONTENT" | head -20 | grep -i "description" | head -1) + if [ -n "$PREV_DESC" ] && [ "$PREV_DESC" != "$CURR_DESC" ]; then + BREAKING_CHANGES+=("Agent description changed - verify consumer expectations") + fi + ;; + + */commands/*.md|*/skills/*.md) + # Check if command/skill was significantly changed + PREV_LINES=$(echo "$PREV_CONTENT" | wc -l) + CURR_LINES=$(echo "$CURR_CONTENT" | wc -l) + + if [ "$PREV_LINES" -gt 10 ] && [ "$CURR_LINES" -lt $((PREV_LINES / 2)) ]; then + BREAKING_CHANGES+=("Command/skill significantly reduced - behavior may change") + fi + ;; +esac + +# Output warnings if any breaking changes detected +if [[ ${#BREAKING_CHANGES[@]} -gt 0 ]]; then + echo "" + echo "$PREFIX WARNING: Potential breaking changes in $(basename "$FILE_PATH")" + echo "$PREFIX ============================================" + for change in "${BREAKING_CHANGES[@]}"; do + echo "$PREFIX - $change" + done + echo "$PREFIX ============================================" + echo "$PREFIX Consider updating CHANGELOG and notifying consumers" + echo "" +fi + +# Always exit 0 - non-blocking +exit 0 diff --git a/plugins/contract-validator/hooks/hooks.json b/plugins/contract-validator/hooks/hooks.json new file mode 100644 index 0000000..a37bf96 --- /dev/null +++ b/plugins/contract-validator/hooks/hooks.json @@ -0,0 +1,21 @@ +{ + "hooks": { + "SessionStart": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/auto-validate.sh" + } + ], + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/breaking-change-check.sh" + } + ] + } + ] + } +} From b7fce0fafd69a414be801d1cecc2302dba28021b Mon Sep 17 00:00:00 2001 From: lmiranda Date: Wed, 28 Jan 2026 10:11:51 -0500 Subject: [PATCH 07/15] docs(changelog): add Sprint 3 hooks implementation - git-flow: commit message and branch name validation hooks - clarity-assist: vagueness detection hook - data-platform: schema diff detection hook - contract-validator: SessionStart auto-validate and breaking change hooks - Document MCP bug #231 (branch detection from wrong directory) Sprint 3 - Hooks completed (issues #225-#230) Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cc1fd3..4734c96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,34 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added +#### Sprint 3: Hooks (V5.2.0 Plugin Enhancements) +Implementation of 6 foundational hooks across 4 plugins. + +**git-flow v1.1.0:** +- **Commit message enforcement hook** - PreToolUse hook validates conventional commit format on all `git commit` commands (not just `/commit`). Blocks invalid commits with format guidance. +- **Branch name validation hook** - PreToolUse hook validates branch naming on `git checkout -b` and `git switch -c`. Enforces `type/description` format, lowercase, max 50 chars. + +**clarity-assist v1.1.0:** +- **Vagueness detection hook** - UserPromptSubmit hook detects vague prompts and suggests `/clarify` when ambiguity, missing context, or unclear scope detected. + +**data-platform v1.1.0:** +- **Schema diff detection hook** - PostToolUse hook monitors edits to schema files (dbt models, SQL migrations). Warns on breaking changes (column removal, type narrowing, constraint addition). + +**contract-validator v1.1.0:** +- **SessionStart auto-validate hook** - Smart validation that only runs when plugin files changed since last check. Detects interface compatibility issues at session start. +- **Breaking change detection hook** - PostToolUse hook monitors plugin interface files (README.md, plugin.json). Warns when changes would break consumers. + +**Sprint Completed:** +- Milestone: Sprint 3 - Hooks (closed 2026-01-28) +- Issues: #225, #226, #227, #228, #229, #230 +- Wiki: [Change V5.2.0: Plugin Enhancements Proposal](https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace/wiki/Change-V5.2.0:-Plugin-Enhancements-Proposal) +- Lessons: Background agent permissions, agent runaway detection, MCP branch detection bug + +### Known Issues +- **MCP Bug #231:** Branch detection in Gitea MCP runs from installed plugin directory, not user's project directory. Workaround: close issues via Gitea web UI. + +--- + #### Gitea MCP Server - create_pull_request Tool - **`create_pull_request`**: Create new pull requests via MCP - Parameters: title, body, head (source branch), base (target branch), labels From 8234683bc3101007a8fa85162d8c497d5425194d Mon Sep 17 00:00:00 2001 From: lmiranda Date: Wed, 28 Jan 2026 10:38:17 -0500 Subject: [PATCH 08/15] fix(gitea-mcp): use project directory for branch detection (#231) The _get_current_branch() method was running git commands from the installed plugin directory instead of the user's project directory. This caused incorrect branch detection (always seeing 'main' from the marketplace repo instead of the user's actual branch). Fix: Use CLAUDE_PROJECT_DIR environment variable to get the correct project directory and pass it as cwd to subprocess.run(). Fixes #231 Co-Authored-By: Claude Opus 4.5 --- mcp-servers/gitea/mcp_server/tools/issues.py | 20 +++++++++++++++++-- .../gitea/mcp_server/tools/pull_requests.py | 20 +++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/mcp-servers/gitea/mcp_server/tools/issues.py b/mcp-servers/gitea/mcp_server/tools/issues.py index 7ea8476..bf759f1 100644 --- a/mcp-servers/gitea/mcp_server/tools/issues.py +++ b/mcp-servers/gitea/mcp_server/tools/issues.py @@ -7,6 +7,7 @@ Provides async wrappers for issue CRUD operations with: - Comprehensive error handling """ import asyncio +import os import subprocess import logging from typing import List, Dict, Optional @@ -27,19 +28,34 @@ class IssueTools: """ self.gitea = gitea_client + def _get_project_directory(self) -> Optional[str]: + """ + Get the user's project directory from environment. + + Returns: + Project directory path or None if not set + """ + return os.environ.get('CLAUDE_PROJECT_DIR') + def _get_current_branch(self) -> str: """ - Get current git branch. + Get current git branch from user's project directory. + + Uses CLAUDE_PROJECT_DIR environment variable to determine the correct + directory for git operations, avoiding the bug where git runs from + the installed plugin directory instead of the user's project. Returns: Current branch name or 'unknown' if not in a git repo """ try: + project_dir = self._get_project_directory() result = subprocess.run( ['git', 'rev-parse', '--abbrev-ref', 'HEAD'], capture_output=True, text=True, - check=True + check=True, + cwd=project_dir # Run git in project directory, not plugin directory ) return result.stdout.strip() except subprocess.CalledProcessError: diff --git a/mcp-servers/gitea/mcp_server/tools/pull_requests.py b/mcp-servers/gitea/mcp_server/tools/pull_requests.py index 6f80374..89734fc 100644 --- a/mcp-servers/gitea/mcp_server/tools/pull_requests.py +++ b/mcp-servers/gitea/mcp_server/tools/pull_requests.py @@ -7,6 +7,7 @@ Provides async wrappers for PR operations with: - Comprehensive error handling """ import asyncio +import os import subprocess import logging from typing import List, Dict, Optional @@ -27,19 +28,34 @@ class PullRequestTools: """ self.gitea = gitea_client + def _get_project_directory(self) -> Optional[str]: + """ + Get the user's project directory from environment. + + Returns: + Project directory path or None if not set + """ + return os.environ.get('CLAUDE_PROJECT_DIR') + def _get_current_branch(self) -> str: """ - Get current git branch. + Get current git branch from user's project directory. + + Uses CLAUDE_PROJECT_DIR environment variable to determine the correct + directory for git operations, avoiding the bug where git runs from + the installed plugin directory instead of the user's project. Returns: Current branch name or 'unknown' if not in a git repo """ try: + project_dir = self._get_project_directory() result = subprocess.run( ['git', 'rev-parse', '--abbrev-ref', 'HEAD'], capture_output=True, text=True, - check=True + check=True, + cwd=project_dir # Run git in project directory, not plugin directory ) return result.stdout.strip() except subprocess.CalledProcessError: From 4bd15e5deba9ff7aedd4a9b5add6ccb09f63d553 Mon Sep 17 00:00:00 2001 From: lmiranda Date: Wed, 28 Jan 2026 10:42:14 -0500 Subject: [PATCH 09/15] feat(projman): add Status labels for accurate issue state tracking (#235) - Add Status/In-Progress, Status/Blocked, Status/Failed, Status/Deferred labels - Update orchestrator.md with Status Label Management section - Update executor.md with honest Status Reporting requirements - Update labels-reference.md with Status detection guidelines Status labels enable accurate tracking: - In-Progress: Work actively being done - Blocked: Waiting for dependency/external factor - Failed: Attempted but couldn't complete - Deferred: Moved to future sprint Agents must report honestly - never say "completed" when blocked/failed. Closes #235 Co-Authored-By: Claude Opus 4.5 --- plugins/projman/agents/executor.md | 70 +++++++++++---- plugins/projman/agents/orchestrator.md | 89 ++++++++++++++++--- .../skills/label-taxonomy/labels-reference.md | 32 ++++++- 3 files changed, 161 insertions(+), 30 deletions(-) diff --git a/plugins/projman/agents/executor.md b/plugins/projman/agents/executor.md index 868922b..49ab1b0 100644 --- a/plugins/projman/agents/executor.md +++ b/plugins/projman/agents/executor.md @@ -108,7 +108,43 @@ git branch --show-current ## Your Responsibilities -### 1. Implement Features Following Specs +### 1. Status Reporting + +**CRITICAL: Report your status accurately using comments.** + +**When starting work:** +``` +add_comment( + issue_number=45, + body="πŸ”„ **Status: In Progress**\nStarting implementation of JWT service." +) +``` + +**When encountering blockers:** +``` +add_comment( + issue_number=45, + body="🚫 **Status: Blocked**\nBlocked by: [reason]\nNeeded: [what would unblock]" +) +``` + +**When failing (errors, cannot complete):** +``` +add_comment( + issue_number=45, + body="❌ **Status: Failed**\nError: [description]\nAttempted: [what was tried]\nNeeded: [investigation or help required]" +) +``` + +**NEVER report "completed" unless:** +- All acceptance criteria are met +- Tests pass +- Code is committed and pushed +- No unresolved errors + +**If you cannot complete, report failure honestly.** The orchestrator needs accurate status to coordinate effectively. + +### 2. Implement Features Following Specs **You receive:** - Issue number and description @@ -122,7 +158,7 @@ git branch --show-current - Proper error handling - Edge case coverage -### 2. Follow Best Practices +### 3. Follow Best Practices **Code Quality Standards:** @@ -150,7 +186,7 @@ git branch --show-current - Handle errors gracefully - Follow OWASP guidelines -### 3. Handle Edge Cases +### 4. Handle Edge Cases Always consider: - What if input is None/null/undefined? @@ -160,7 +196,7 @@ Always consider: - What if user doesn't have permission? - What if resource doesn't exist? -### 4. Apply Lessons Learned +### 5. Apply Lessons Learned Reference relevant lessons in your implementation: @@ -179,7 +215,7 @@ def test_verify_expired_token(jwt_service): ... ``` -### 5. Create Merge Requests (When Branch Protected) +### 6. Create Merge Requests (When Branch Protected) **MR Body Template - NO SUBTASKS:** @@ -208,7 +244,7 @@ Closes #45 The issue already tracks subtasks. MR body should be summary only. -### 6. Auto-Close Issues via Commit Messages +### 7. Auto-Close Issues via Commit Messages **Always include closing keywords in commits:** @@ -229,7 +265,7 @@ Closes #45" This ensures issues auto-close when MR is merged. -### 7. Generate Completion Reports +### 8. Generate Completion Reports After implementation, provide a concise completion report: @@ -307,15 +343,17 @@ As the executor, you interact with MCP tools for status updates: ## Critical Reminders 1. **Never use CLI tools** - Use MCP tools exclusively for Gitea -2. **Branch naming** - Always use `feat/`, `fix/`, or `debug/` prefix with issue number -3. **Branch check FIRST** - Never implement on staging/production -4. **Follow specs precisely** - Respect architectural decisions -5. **Apply lessons learned** - Reference in code and tests -6. **Write tests** - Cover edge cases, not just happy path -7. **Clean code** - Readable, maintainable, documented -8. **No MR subtasks** - MR body should NOT have checklists -9. **Use closing keywords** - `Closes #XX` in commit messages -10. **Report thoroughly** - Complete summary when done +2. **Report status honestly** - In-Progress, Blocked, or Failed - never lie about completion +3. **Blocked β‰  Failed** - Blocked means waiting for something; Failed means tried and couldn't complete +4. **Branch naming** - Always use `feat/`, `fix/`, or `debug/` prefix with issue number +5. **Branch check FIRST** - Never implement on staging/production +6. **Follow specs precisely** - Respect architectural decisions +7. **Apply lessons learned** - Reference in code and tests +8. **Write tests** - Cover edge cases, not just happy path +9. **Clean code** - Readable, maintainable, documented +10. **No MR subtasks** - MR body should NOT have checklists +11. **Use closing keywords** - `Closes #XX` in commit messages +12. **Report thoroughly** - Complete summary when done, including honest status ## Your Mission diff --git a/plugins/projman/agents/orchestrator.md b/plugins/projman/agents/orchestrator.md index 9ed8832..57db621 100644 --- a/plugins/projman/agents/orchestrator.md +++ b/plugins/projman/agents/orchestrator.md @@ -222,7 +222,69 @@ Dependencies: None (can start immediately) Ready to start? Say "yes" and I'll monitor progress. ``` -### 4. Progress Tracking +### 4. Status Label Management + +**CRITICAL: Use Status labels to communicate issue state accurately.** + +**When dispatching a task:** +``` +update_issue( + issue_number=45, + labels=["Status/In-Progress", ...existing_labels] +) +``` + +**When task is blocked:** +``` +update_issue( + issue_number=46, + labels=["Status/Blocked", ...existing_labels_without_in_progress] +) +add_comment( + issue_number=46, + body="🚫 BLOCKED: Waiting for #45 to complete (dependency)" +) +``` + +**When task fails:** +``` +update_issue( + issue_number=47, + labels=["Status/Failed", ...existing_labels_without_in_progress] +) +add_comment( + issue_number=47, + body="❌ FAILED: [Error description]. Needs investigation." +) +``` + +**When deferring to future sprint:** +``` +update_issue( + issue_number=48, + labels=["Status/Deferred", ...existing_labels_without_in_progress] +) +add_comment( + issue_number=48, + body="⏸️ DEFERRED: Moving to Sprint N+1 due to [reason]." +) +``` + +**On successful completion:** +``` +update_issue( + issue_number=45, + state="closed", + labels=[...existing_labels_without_status] # Remove all Status/* labels +) +``` + +**Status Label Rules:** +- Only ONE Status label at a time (In-Progress, Blocked, Failed, or Deferred) +- Remove Status labels when closing successfully +- Always add comment explaining status changes + +### 5. Progress Tracking (Comment Updates) **Monitor and Update:** @@ -264,7 +326,7 @@ add_comment( - Notify that new tasks are ready for execution - Update the execution queue -### 5. Monitor Parallel Execution +### 6. Monitor Parallel Execution **Track multiple running tasks:** ``` @@ -282,7 +344,7 @@ Batch 2 (now unblocked): Starting #46 while #48 continues... ``` -### 6. Branch Protection Detection +### 7. Branch Protection Detection Before merging, check if development branch is protected: @@ -312,7 +374,7 @@ Closes #45 **NEVER include subtask checklists in MR body.** The issue already has them. -### 7. Sprint Close - Capture Lessons Learned +### 8. Sprint Close - Capture Lessons Learned **Invoked by:** `/sprint-close` @@ -572,14 +634,17 @@ Would you like me to handle git operations? 4. **Parallel dispatch** - Run independent tasks simultaneously 5. **Lean prompts** - Brief, actionable, not verbose documents 6. **Branch naming** - `feat/`, `fix/`, `debug/` prefixes required -7. **No MR subtasks** - MR body should NOT have checklists -8. **Auto-check subtasks** - Mark issue subtasks complete on close -9. **Track meticulously** - Update issues immediately, document blockers -10. **Capture lessons** - At sprint close, interview thoroughly -11. **Update wiki status** - At sprint close, update implementation and proposal pages -12. **Link lessons to wiki** - Include lesson links in implementation completion summary -13. **Update CHANGELOG** - MANDATORY at sprint close, never skip -14. **Run suggest-version** - Check if release is needed after CHANGELOG update +7. **Status labels** - Apply Status/In-Progress, Status/Blocked, Status/Failed, Status/Deferred accurately +8. **One status at a time** - Remove old Status/* label before applying new one +9. **Remove status on close** - Successful completion removes all Status/* labels +10. **No MR subtasks** - MR body should NOT have checklists +11. **Auto-check subtasks** - Mark issue subtasks complete on close +12. **Track meticulously** - Update issues immediately, document blockers +13. **Capture lessons** - At sprint close, interview thoroughly +14. **Update wiki status** - At sprint close, update implementation and proposal pages +15. **Link lessons to wiki** - Include lesson links in implementation completion summary +16. **Update CHANGELOG** - MANDATORY at sprint close, never skip +17. **Run suggest-version** - Check if release is needed after CHANGELOG update ## Your Mission diff --git a/plugins/projman/skills/label-taxonomy/labels-reference.md b/plugins/projman/skills/label-taxonomy/labels-reference.md index e4f6f7f..c86d299 100644 --- a/plugins/projman/skills/label-taxonomy/labels-reference.md +++ b/plugins/projman/skills/label-taxonomy/labels-reference.md @@ -13,9 +13,9 @@ description: Dynamic reference for Gitea label taxonomy (organization + reposito This skill provides the current label taxonomy used for issue classification in Gitea. Labels are **fetched dynamically** from Gitea and should never be hardcoded. -**Current Taxonomy:** 43 labels (27 organization + 16 repository) +**Current Taxonomy:** 47 labels (31 organization + 16 repository) -## Organization Labels (27) +## Organization Labels (31) Organization-level labels are shared across all repositories in your configured organization. @@ -60,6 +60,12 @@ Organization-level labels are shared across all repositories in your configured - `Type/Test` (#1d76db) - Testing-related work (unit, integration, e2e) - `Type/Chore` (#fef2c0) - Maintenance, tooling, dependencies, build tasks +### Status (4) +- `Status/In-Progress` (#0052cc) - Work is actively being done on this issue +- `Status/Blocked` (#ff5630) - Blocked by external dependency or issue +- `Status/Failed` (#de350b) - Implementation attempted but failed, needs investigation +- `Status/Deferred` (#6554c0) - Moved to a future sprint or backlog + ## Repository Labels (16) Repository-level labels are specific to each project. @@ -168,6 +174,28 @@ When suggesting labels for issues, consider the following patterns: - Keywords: "deploy", "deployment", "docker", "infrastructure", "ci/cd", "production" - Example: "Deploy authentication service to production" +### Status Detection + +**Status/In-Progress:** +- Applied when: Agent starts working on an issue +- Remove when: Work completes, fails, or is blocked +- Example: Orchestrator applies when dispatching task to executor + +**Status/Blocked:** +- Applied when: Issue cannot proceed due to external dependency +- Context: Waiting for another issue, external service, or decision +- Example: "Blocked by #45 - need JWT service first" + +**Status/Failed:** +- Applied when: Implementation was attempted but failed +- Context: Errors, permission issues, technical blockers +- Example: Agent hit permission errors and couldn't complete + +**Status/Deferred:** +- Applied when: Work is moved to a future sprint +- Context: Scope reduction, reprioritization +- Example: "Moving to Sprint 5 due to scope constraints" + ### Tech Detection **Tech/Python:** From 008187a0a4056ba73d0b8667fb4bce10fa0c0e5e Mon Sep 17 00:00:00 2001 From: lmiranda Date: Wed, 28 Jan 2026 10:43:28 -0500 Subject: [PATCH 10/15] feat(projman): add structured progress comments for real-time visibility (#232) Add structured progress comment format for agents: - Standard format with Status, Phase, Tool Calls budget - Completed/In-Progress/Blockers/Next sections - Clear examples for starting, blocked, and failed states - Guidance on when to post (every 20-30 tool calls) Update sprint-status.md: - Document how to parse progress comments - Show enhanced in-progress display with tool call tracking - Add progress comment detection to blocker analysis This enables users to see: - Real-time agent progress - Tool call budget consumption - Current phase and step - Blockers as they occur Closes #232 Co-Authored-By: Claude Opus 4.5 --- plugins/projman/agents/executor.md | 100 ++++++++++++++++++++-- plugins/projman/agents/orchestrator.md | 60 ++++++++++++- plugins/projman/commands/sprint-status.md | 52 ++++++++++- 3 files changed, 198 insertions(+), 14 deletions(-) diff --git a/plugins/projman/agents/executor.md b/plugins/projman/agents/executor.md index 49ab1b0..1ba39d8 100644 --- a/plugins/projman/agents/executor.md +++ b/plugins/projman/agents/executor.md @@ -108,31 +108,115 @@ git branch --show-current ## Your Responsibilities -### 1. Status Reporting +### 1. Status Reporting (Structured Progress) -**CRITICAL: Report your status accurately using comments.** +**CRITICAL: Post structured progress comments for visibility.** -**When starting work:** +**Standard Progress Comment Format:** +```markdown +## Progress Update +**Status:** In Progress | Blocked | Failed +**Phase:** [current phase name] +**Tool Calls:** X (budget: Y) + +### Completed +- [x] Step 1 +- [x] Step 2 + +### In Progress +- [ ] Current step (estimated: Z more calls) + +### Blockers +- None | [blocker description] + +### Next +- What happens after current step +``` + +**When to Post Progress Comments:** +- **Immediately on starting** - Post initial status +- **Every 20-30 tool calls** - Show progress +- **On phase transitions** - Moving from implementation to testing +- **When blocked or encountering errors** +- **Before budget limit** - If approaching turn limit + +**Starting Work Example:** ``` add_comment( issue_number=45, - body="πŸ”„ **Status: In Progress**\nStarting implementation of JWT service." + body="""## Progress Update +**Status:** In Progress +**Phase:** Starting +**Tool Calls:** 5 (budget: 100) + +### Completed +- [x] Read issue and acceptance criteria +- [x] Created feature branch feat/45-jwt-service + +### In Progress +- [ ] Implementing JWT service + +### Blockers +- None + +### Next +- Create auth/jwt_service.py +- Implement core token functions +""" ) ``` -**When encountering blockers:** +**Blocked Example:** ``` add_comment( issue_number=45, - body="🚫 **Status: Blocked**\nBlocked by: [reason]\nNeeded: [what would unblock]" + body="""## Progress Update +**Status:** Blocked +**Phase:** Testing +**Tool Calls:** 67 (budget: 100) + +### Completed +- [x] Implemented jwt_service.py +- [x] Wrote unit tests + +### In Progress +- [ ] Running tests - BLOCKED + +### Blockers +- Missing PyJWT dependency in requirements.txt +- Need orchestrator to add dependency + +### Next +- Resume after blocker resolved +""" ) ``` -**When failing (errors, cannot complete):** +**Failed Example:** ``` add_comment( issue_number=45, - body="❌ **Status: Failed**\nError: [description]\nAttempted: [what was tried]\nNeeded: [investigation or help required]" + body="""## Progress Update +**Status:** Failed +**Phase:** Implementation +**Tool Calls:** 89 (budget: 100) + +### Completed +- [x] Created jwt_service.py +- [x] Implemented generate_token() + +### In Progress +- [ ] verify_token() - FAILED + +### Blockers +- Critical: Cannot decode tokens - algorithm mismatch +- Attempted: HS256, HS384, RS256 +- Error: InvalidSignatureError consistently + +### Next +- Needs human investigation +- Possible issue with secret key encoding +""" ) ``` diff --git a/plugins/projman/agents/orchestrator.md b/plugins/projman/agents/orchestrator.md index 57db621..b2f9469 100644 --- a/plugins/projman/agents/orchestrator.md +++ b/plugins/projman/agents/orchestrator.md @@ -284,11 +284,65 @@ update_issue( - Remove Status labels when closing successfully - Always add comment explaining status changes -### 5. Progress Tracking (Comment Updates) +### 5. Progress Tracking (Structured Comments) -**Monitor and Update:** +**CRITICAL: Use structured progress comments for visibility.** -**Add Progress Comments:** +**Standard Progress Comment Format:** +```markdown +## Progress Update +**Status:** In Progress | Blocked | Failed +**Phase:** [current phase name] +**Tool Calls:** X (budget: Y) + +### Completed +- [x] Step 1 +- [x] Step 2 + +### In Progress +- [ ] Current step (estimated: Z more calls) + +### Blockers +- None | [blocker description] + +### Next +- What happens after current step +``` + +**Example Progress Comment:** +``` +add_comment( + issue_number=45, + body="""## Progress Update +**Status:** In Progress +**Phase:** Implementation +**Tool Calls:** 45 (budget: 100) + +### Completed +- [x] Created auth/jwt_service.py +- [x] Implemented generate_token() +- [x] Implemented verify_token() + +### In Progress +- [ ] Writing unit tests (estimated: 20 more calls) + +### Blockers +- None + +### Next +- Run tests and fix any failures +- Commit and push +""" +) +``` + +**When to Post Progress Comments:** +- After completing each major phase (every 20-30 tool calls) +- When status changes (blocked, failed) +- When encountering unexpected issues +- Before approaching tool call budget limit + +**Simple progress updates (for minor milestones):** ``` add_comment( issue_number=45, diff --git a/plugins/projman/commands/sprint-status.md b/plugins/projman/commands/sprint-status.md index 9417fea..ccd76e1 100644 --- a/plugins/projman/commands/sprint-status.md +++ b/plugins/projman/commands/sprint-status.md @@ -79,7 +79,12 @@ Completed Issues (3): In Progress (2): #46: [Sprint 18] feat: Build login endpoint [Type/Feature, Priority/High] + Status: In Progress | Phase: Implementation | Tool Calls: 45/100 + Progress: 3/5 steps | Current: Writing validation logic + #49: [Sprint 18] test: Add auth tests [Type/Test, Priority/Medium] + Status: In Progress | Phase: Testing | Tool Calls: 30/100 + Progress: 2/4 steps | Current: Testing edge cases Ready to Start (2): #50: [Sprint 18] feat: Integrate OAuth providers [Type/Feature, Priority/Low] @@ -137,12 +142,53 @@ Show only backend issues: list_issues(labels=["Component/Backend"]) ``` +## Progress Comment Parsing + +Agents post structured progress comments in this format: + +```markdown +## Progress Update +**Status:** In Progress | Blocked | Failed +**Phase:** [current phase name] +**Tool Calls:** X (budget: Y) + +### Completed +- [x] Step 1 + +### In Progress +- [ ] Current step + +### Blockers +- None | [blocker description] +``` + +**To extract real-time progress:** +1. Fetch issue comments: `get_issue(number)` includes recent comments +2. Look for comments containing `## Progress Update` +3. Parse the **Status:** line for current state +4. Parse **Tool Calls:** for budget consumption +5. Extract blockers from `### Blockers` section + +**Progress Summary Display:** +``` +In Progress Issues: + #45: [Sprint 18] feat: JWT service + Status: In Progress | Phase: Testing | Tool Calls: 67/100 + Completed: 4/6 steps | Current: Writing unit tests + + #46: [Sprint 18] feat: Login endpoint + Status: Blocked | Phase: Implementation | Tool Calls: 23/100 + Blocker: Waiting for JWT service (#45) +``` + ## Blocker Detection The command identifies blocked issues by: -1. **Dependency Analysis** - Uses `list_issue_dependencies` to find unmet dependencies -2. **Comment Keywords** - Checks for "blocked", "blocker", "waiting for" -3. **Stale Issues** - Issues with no recent activity (>7 days) +1. **Progress Comments** - Parse `### Blockers` section from structured comments +2. **Status Labels** - Check for `Status/Blocked` label on issue +3. **Dependency Analysis** - Uses `list_issue_dependencies` to find unmet dependencies +4. **Comment Keywords** - Checks for "blocked", "blocker", "waiting for" +5. **Stale Issues** - Issues with no recent activity (>7 days) ## When to Use From 0abf510ec0301fac90df6b4fc3eb980b205d720a Mon Sep 17 00:00:00 2001 From: lmiranda Date: Wed, 28 Jan 2026 10:44:52 -0500 Subject: [PATCH 11/15] feat(projman): add strict task sizing rules to prevent runaway agents (#238) Add mandatory task scoping rules: - XS: 1 file, 0-2 checklist items, ~30 tool calls - S: 1 file, 2-4 checklist items, ~50 tool calls - M: 2-3 files, 4-6 checklist items, ~80 tool calls - L/XL: MUST be broken down into smaller tasks Sprint 3 showed agents running 400+ tool calls on single tasks, causing 1+ hour waits with no visibility. This enforces: - Maximum task scope (M = 2-3 files, 80 tool calls) - Mandatory breakdown for L/XL tasks - Clear scoping checklist for planners - Good/bad examples showing proper breakdown Planner must refuse to create L/XL tasks without breakdown. Closes #238 Co-Authored-By: Claude Opus 4.5 --- plugins/projman/agents/planner.md | 57 +++++++++++++++++--- plugins/projman/commands/sprint-plan.md | 69 ++++++++++++++++++++++--- 2 files changed, 112 insertions(+), 14 deletions(-) diff --git a/plugins/projman/agents/planner.md b/plugins/projman/agents/planner.md index 9b7d1f1..10800a5 100644 --- a/plugins/projman/agents/planner.md +++ b/plugins/projman/agents/planner.md @@ -310,14 +310,55 @@ Think through the technical approach: - `[Sprint 17] fix: Resolve login timeout issue` - `[Sprint 18] refactor: Extract authentication module` -**Task Granularity Guidelines:** -| Size | Scope | Example | -|------|-------|---------| -| **Small** | 1-2 hours, single file/component | Add validation to one field | -| **Medium** | Half day, multiple files, one feature | Implement new API endpoint | -| **Large** | Should be broken down | Full authentication system | +**Task Sizing Rules (MANDATORY):** -**If a task is too large, break it down into smaller tasks.** +| Effort | Files | Checklist Items | Max Tool Calls | Agent Scope | +|--------|-------|-----------------|----------------|-------------| +| **XS** | 1 file | 0-2 items | ~30 | Single function/fix | +| **S** | 1 file | 2-4 items | ~50 | Single file feature | +| **M** | 2-3 files | 4-6 items | ~80 | Multi-file feature | +| **L** | MUST BREAK DOWN | - | - | Too large for one agent | +| **XL** | MUST BREAK DOWN | - | - | Way too large | + +**CRITICAL: L and XL tasks MUST be broken into subtasks.** + +**Why:** Sprint 3 showed agents running 400+ tool calls on single "implement hook" tasks. This causes: +- Long wait times (1+ hour per task) +- No progress visibility +- Resource exhaustion +- Difficult debugging + +**Task Scoping Checklist:** +1. Can this be completed in one file? β†’ XS or S +2. Does it touch 2-3 files? β†’ M (max) +3. Does it touch 4+ files? β†’ MUST break down +4. Does it require complex decision-making? β†’ MUST break down +5. Would you estimate 50+ tool calls? β†’ MUST break down + +**Breaking Down Large Tasks:** + +**BAD (L/XL - too broad):** +``` +[Sprint 3] feat: Implement git-flow branch validation hook +Labels: Efforts/L, ... +``` + +**GOOD (broken into S/M tasks):** +``` +[Sprint 3] feat: Create branch validation hook skeleton +Labels: Efforts/S, ... + +[Sprint 3] feat: Add prefix pattern validation (feat/, fix/, etc.) +Labels: Efforts/S, ... + +[Sprint 3] feat: Add issue number extraction and validation +Labels: Efforts/S, ... + +[Sprint 3] test: Add branch validation unit tests +Labels: Efforts/S, ... +``` + +**If a task is estimated L or XL, STOP and break it down before creating.** **IMPORTANT: Include wiki implementation reference in issue body:** @@ -479,5 +520,7 @@ Sprint 17 - User Authentication (Due: 2025-02-01) 11. **Always use suggest_labels** - Don't guess labels 12. **Always think through architecture** - Consider edge cases 13. **Always cleanup local files** - Delete after migrating to wiki +14. **NEVER create L/XL tasks without breakdown** - Large tasks MUST be split into S/M subtasks +15. **Enforce task scoping** - If task touches 4+ files or needs 50+ tool calls, break it down You are the thoughtful planner who ensures sprints are well-prepared, architecturally sound, and learn from past experiences. Take your time, ask questions, and create comprehensive plans that set the team up for success. diff --git a/plugins/projman/commands/sprint-plan.md b/plugins/projman/commands/sprint-plan.md index f1d8030..0cc9b40 100644 --- a/plugins/projman/commands/sprint-plan.md +++ b/plugins/projman/commands/sprint-plan.md @@ -155,15 +155,70 @@ The planner agent will: - `[Sprint 17] fix: Resolve login timeout issue` - `[Sprint 18] refactor: Extract authentication module` -## Task Granularity Guidelines +## Task Sizing Rules (MANDATORY) -| Size | Scope | Example | -|------|-------|---------| -| **Small** | 1-2 hours, single file/component | Add validation to one field | -| **Medium** | Half day, multiple files, one feature | Implement new API endpoint | -| **Large** | Should be broken down | Full authentication system | +**CRITICAL: Tasks sized L or XL MUST be broken down into smaller tasks.** -**If a task is too large, break it down into smaller tasks.** +| Effort | Files | Checklist Items | Max Tool Calls | Agent Scope | +|--------|-------|-----------------|----------------|-------------| +| **XS** | 1 file | 0-2 items | ~30 | Single function/fix | +| **S** | 1 file | 2-4 items | ~50 | Single file feature | +| **M** | 2-3 files | 4-6 items | ~80 | Multi-file feature | +| **L** | MUST BREAK DOWN | - | - | Too large for one agent | +| **XL** | MUST BREAK DOWN | - | - | Way too large | + +**Why This Matters:** +- Agents running 400+ tool calls take 1+ hour, with no visibility +- Large tasks lack clear completion criteria +- Debugging failures is extremely difficult +- Small tasks enable parallel execution + +**Scoping Checklist:** +1. Can this be completed in one file? β†’ XS or S +2. Does it touch 2-3 files? β†’ M (maximum for single task) +3. Does it touch 4+ files? β†’ MUST break down +4. Would you estimate 50+ tool calls? β†’ MUST break down +5. Does it require complex decision-making mid-task? β†’ MUST break down + +**Example Breakdown:** + +**BAD (L - too broad):** +``` +[Sprint 3] feat: Implement schema diff detection hook +Labels: Efforts/L +- Hook skeleton +- Pattern detection for DROP/ALTER/RENAME +- Warning output formatting +- Integration with hooks.json +``` + +**GOOD (broken into S tasks):** +``` +[Sprint 3] feat: Create schema-diff-check.sh hook skeleton +Labels: Efforts/S +- [ ] Create hook file with standard header +- [ ] Add file type detection for SQL/migrations +- [ ] Exit 0 (non-blocking) + +[Sprint 3] feat: Add DROP/ALTER pattern detection +Labels: Efforts/S +- [ ] Detect DROP COLUMN/TABLE/INDEX +- [ ] Detect ALTER TYPE changes +- [ ] Detect RENAME operations + +[Sprint 3] feat: Add warning output formatting +Labels: Efforts/S +- [ ] Format breaking change warnings +- [ ] Add hook prefix to output +- [ ] Test output visibility + +[Sprint 3] chore: Register hook in hooks.json +Labels: Efforts/XS +- [ ] Add PostToolUse:Edit hook entry +- [ ] Test hook triggers on SQL edits +``` + +**The planner MUST refuse to create L/XL tasks without breakdown.** ## MCP Tools Available From f2a62627d09413f279282e6f8156c0e4e96794da Mon Sep 17 00:00:00 2001 From: lmiranda Date: Wed, 28 Jan 2026 10:46:04 -0500 Subject: [PATCH 12/15] feat(projman): add runaway detection and circuit breaker for agents (#236) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Executor self-monitoring: - 10+ calls without progress β†’ stop and reassess - Same error 3+ times β†’ circuit breaker, report failure - 50+ calls β†’ mandatory progress update - 80+ calls β†’ budget warning, evaluate completion - 100+ calls β†’ hard stop, save checkpoint Orchestrator monitoring: - Detect stuck agents (no progress for X minutes) - Intervention protocol for runaway agents - Timeout guidelines by task size (XS: 15min, S: 30min, M: 45min) - Recovery actions with Status/Failed label This prevents agents from running indefinitely (400+ tool calls observed in Sprint 3) and provides clear stopping criteria. Closes #236 Co-Authored-By: Claude Opus 4.5 --- plugins/projman/agents/executor.md | 108 ++++++++++++++++++++++--- plugins/projman/agents/orchestrator.md | 75 +++++++++++++++-- 2 files changed, 166 insertions(+), 17 deletions(-) diff --git a/plugins/projman/agents/executor.md b/plugins/projman/agents/executor.md index 1ba39d8..bffcc62 100644 --- a/plugins/projman/agents/executor.md +++ b/plugins/projman/agents/executor.md @@ -424,20 +424,110 @@ As the executor, you interact with MCP tools for status updates: - Apply best practices - Deliver quality work +## Runaway Detection (Self-Monitoring) + +**CRITICAL: Monitor yourself to prevent infinite loops and wasted resources.** + +**Self-Monitoring Checkpoints:** + +| Trigger | Action | +|---------|--------| +| 10+ tool calls without progress | STOP - Post progress update, reassess approach | +| Same error 3+ times | CIRCUIT BREAKER - Stop, report failure with error pattern | +| 50+ tool calls total | POST progress update (mandatory) | +| 80+ tool calls total | WARN - Approaching budget, evaluate if completion is realistic | +| 100+ tool calls total | STOP - Save state, report incomplete with checkpoint | + +**What Counts as "Progress":** +- File created or modified +- Test passing that wasn't before +- New functionality working +- Moving to next phase of work + +**What Does NOT Count as Progress:** +- Reading more files +- Searching for something +- Retrying the same operation +- Adding logging/debugging + +**Circuit Breaker Protocol:** + +If you encounter the same error 3+ times: +``` +add_comment( + issue_number=45, + body="""## Progress Update +**Status:** Failed (Circuit Breaker) +**Phase:** [phase when stopped] +**Tool Calls:** 67 (budget: 100) + +### Circuit Breaker Triggered +Same error occurred 3+ times: +``` +[error message] +``` + +### What Was Tried +1. [first attempt] +2. [second attempt] +3. [third attempt] + +### Recommendation +[What human should investigate] + +### Files Modified +- [list any files changed before failure] +""" +) +``` + +**Budget Approaching Protocol:** + +At 80+ tool calls, post an update: +``` +add_comment( + issue_number=45, + body="""## Progress Update +**Status:** In Progress (Budget Warning) +**Phase:** [current phase] +**Tool Calls:** 82 (budget: 100) + +### Completed +- [x] [completed steps] + +### Remaining +- [ ] [what's left] + +### Assessment +[Realistic? Should I continue or stop and checkpoint?] +""" +) +``` + +**Hard Stop at 100 Calls:** + +If you reach 100 tool calls: +1. STOP immediately +2. Save current state +3. Post checkpoint comment +4. Report as incomplete (not failed) + ## Critical Reminders 1. **Never use CLI tools** - Use MCP tools exclusively for Gitea 2. **Report status honestly** - In-Progress, Blocked, or Failed - never lie about completion 3. **Blocked β‰  Failed** - Blocked means waiting for something; Failed means tried and couldn't complete -4. **Branch naming** - Always use `feat/`, `fix/`, or `debug/` prefix with issue number -5. **Branch check FIRST** - Never implement on staging/production -6. **Follow specs precisely** - Respect architectural decisions -7. **Apply lessons learned** - Reference in code and tests -8. **Write tests** - Cover edge cases, not just happy path -9. **Clean code** - Readable, maintainable, documented -10. **No MR subtasks** - MR body should NOT have checklists -11. **Use closing keywords** - `Closes #XX` in commit messages -12. **Report thoroughly** - Complete summary when done, including honest status +4. **Self-monitor** - Watch for runaway patterns, trigger circuit breaker when stuck +5. **Branch naming** - Always use `feat/`, `fix/`, or `debug/` prefix with issue number +6. **Branch check FIRST** - Never implement on staging/production +7. **Follow specs precisely** - Respect architectural decisions +8. **Apply lessons learned** - Reference in code and tests +9. **Write tests** - Cover edge cases, not just happy path +10. **Clean code** - Readable, maintainable, documented +11. **No MR subtasks** - MR body should NOT have checklists +12. **Use closing keywords** - `Closes #XX` in commit messages +13. **Report thoroughly** - Complete summary when done, including honest status +14. **Hard stop at 100 calls** - Save checkpoint and report incomplete ## Your Mission diff --git a/plugins/projman/agents/orchestrator.md b/plugins/projman/agents/orchestrator.md index b2f9469..ab5f21e 100644 --- a/plugins/projman/agents/orchestrator.md +++ b/plugins/projman/agents/orchestrator.md @@ -680,6 +680,64 @@ Would you like me to handle git operations? - Document blockers promptly - Never let tasks slip through +## Runaway Detection (Monitoring Dispatched Agents) + +**Monitor dispatched agents for runaway behavior:** + +**Warning Signs:** +- Agent running 30+ minutes with no progress comment +- Progress comment shows "same phase" for 20+ tool calls +- Error patterns repeating in progress comments + +**Intervention Protocol:** + +When you detect an agent may be stuck: + +1. **Read latest progress comment** - Check tool call count and phase +2. **If no progress in 20+ calls** - Consider stopping the agent +3. **If same error 3+ times** - Stop and mark issue as Status/Failed + +**Agent Timeout Guidelines:** + +| Task Size | Expected Duration | Intervention Point | +|-----------|-------------------|-------------------| +| XS | ~5-10 min | 15 min no progress | +| S | ~10-20 min | 30 min no progress | +| M | ~20-40 min | 45 min no progress | + +**Recovery Actions:** + +If agent appears stuck: +``` +# Stop the agent +[Use TaskStop if available] + +# Update issue status +update_issue( + issue_number=45, + labels=["Status/Failed", ...other_labels] +) + +# Add explanation comment +add_comment( + issue_number=45, + body="""## Agent Intervention +**Reason:** No progress detected for [X] minutes / [Y] tool calls +**Last Status:** [from progress comment] +**Action:** Stopped agent, requires human review + +### What Was Completed +[from progress comment] + +### What Remains +[from progress comment] + +### Recommendation +[Manual completion / Different approach / Break down further] +""" +) +``` + ## Critical Reminders 1. **Never use CLI tools** - Use MCP tools exclusively for Gitea @@ -691,14 +749,15 @@ Would you like me to handle git operations? 7. **Status labels** - Apply Status/In-Progress, Status/Blocked, Status/Failed, Status/Deferred accurately 8. **One status at a time** - Remove old Status/* label before applying new one 9. **Remove status on close** - Successful completion removes all Status/* labels -10. **No MR subtasks** - MR body should NOT have checklists -11. **Auto-check subtasks** - Mark issue subtasks complete on close -12. **Track meticulously** - Update issues immediately, document blockers -13. **Capture lessons** - At sprint close, interview thoroughly -14. **Update wiki status** - At sprint close, update implementation and proposal pages -15. **Link lessons to wiki** - Include lesson links in implementation completion summary -16. **Update CHANGELOG** - MANDATORY at sprint close, never skip -17. **Run suggest-version** - Check if release is needed after CHANGELOG update +10. **Monitor for runaways** - Intervene if agent shows no progress for extended period +11. **No MR subtasks** - MR body should NOT have checklists +12. **Auto-check subtasks** - Mark issue subtasks complete on close +13. **Track meticulously** - Update issues immediately, document blockers +14. **Capture lessons** - At sprint close, interview thoroughly +15. **Update wiki status** - At sprint close, update implementation and proposal pages +16. **Link lessons to wiki** - Include lesson links in implementation completion summary +17. **Update CHANGELOG** - MANDATORY at sprint close, never skip +18. **Run suggest-version** - Check if release is needed after CHANGELOG update ## Your Mission From a69a4d19d0238f26d9bb9e5022e5c3e4411d7999 Mon Sep 17 00:00:00 2001 From: lmiranda Date: Wed, 28 Jan 2026 10:47:36 -0500 Subject: [PATCH 13/15] feat(projman): add file conflict prevention for parallel agents (#234) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add pre-dispatch conflict detection: - Analyze target files for each task before parallel dispatch - Check for file overlap between tasks in same batch - If overlap detected, sequentialize those specific tasks - Example analysis showing conflict detection workflow Branch isolation protocol: - Each task MUST have its own branch - Never have two agents work on the same branch - Sequential merge after completion (not simultaneous) - Handle merge conflicts by stopping second task Conflict resolution rules: - Same file β†’ MUST sequentialize - Same directory β†’ Usually safe, review - Shared config β†’ Sequentialize - Shared test fixture β†’ Sequentialize or assign different files This prevents parallel agents from modifying the same files and causing git merge conflicts. Closes #234 Co-Authored-By: Claude Opus 4.5 --- plugins/projman/agents/orchestrator.md | 71 +++++++++++++++++++++--- plugins/projman/commands/sprint-start.md | 61 ++++++++++++++++++++ 2 files changed, 123 insertions(+), 9 deletions(-) diff --git a/plugins/projman/agents/orchestrator.md b/plugins/projman/agents/orchestrator.md index ab5f21e..2d0b1c0 100644 --- a/plugins/projman/agents/orchestrator.md +++ b/plugins/projman/agents/orchestrator.md @@ -147,11 +147,56 @@ Relevant Lessons: Ready to start? I can dispatch multiple tasks in parallel. ``` -### 2. Parallel Task Dispatch +### 2. File Conflict Prevention (Pre-Dispatch) -**When starting execution:** +**BEFORE dispatching parallel agents, analyze file overlap.** -For independent tasks (same batch), spawn multiple Executor agents in parallel: +**Conflict Detection Workflow:** + +1. **Read each issue's checklist/body** to identify target files +2. **Build file map** for all tasks in the batch +3. **Check for overlap** - Same file in multiple tasks? +4. **Sequentialize conflicts** - Don't parallelize if same file + +**Example Analysis:** +``` +Analyzing Batch 1 for conflicts: + +#45 - Implement JWT service + β†’ auth/jwt_service.py, auth/__init__.py, tests/test_jwt.py + +#48 - Update API documentation + β†’ docs/api.md, README.md + +Overlap check: NONE +Decision: Safe to parallelize βœ… +``` + +**If Conflict Detected:** +``` +Analyzing Batch 2 for conflicts: + +#46 - Build login endpoint + β†’ api/routes/auth.py, auth/__init__.py + +#49 - Add auth tests + β†’ tests/test_auth.py, auth/__init__.py + +Overlap: auth/__init__.py ⚠️ +Decision: Sequentialize - run #46 first, then #49 +``` + +**Conflict Resolution:** +- Same file β†’ MUST sequentialize +- Same directory β†’ Usually safe, review file names +- Shared config β†’ Sequentialize +- Shared test fixture β†’ Assign different fixture files or sequentialize + +### 3. Parallel Task Dispatch + +**After conflict check passes, dispatch parallel agents:** + +For independent tasks (same batch) WITH NO FILE CONFLICTS, spawn multiple Executor agents in parallel: ``` Dispatching Batch 1 (2 tasks in parallel): @@ -167,6 +212,14 @@ Task 2: #48 - Update API documentation Both tasks running in parallel. I'll monitor progress. ``` +**Branch Isolation:** Each task MUST have its own branch. Never have two agents work on the same branch. + +**Sequential Merge Protocol:** +1. Wait for task to complete +2. Merge its branch to development +3. Then merge next completed task +4. Never merge simultaneously + **Branch Naming Convention (MANDATORY):** - Features: `feat/-` - Bug fixes: `fix/-` @@ -177,7 +230,7 @@ Both tasks running in parallel. I'll monitor progress. - `fix/46-login-timeout` - `debug/47-investigate-memory-leak` -### 3. Generate Lean Execution Prompts +### 4. Generate Lean Execution Prompts **NOT THIS (too verbose):** ``` @@ -222,7 +275,7 @@ Dependencies: None (can start immediately) Ready to start? Say "yes" and I'll monitor progress. ``` -### 4. Status Label Management +### 5. Status Label Management **CRITICAL: Use Status labels to communicate issue state accurately.** @@ -284,7 +337,7 @@ update_issue( - Remove Status labels when closing successfully - Always add comment explaining status changes -### 5. Progress Tracking (Structured Comments) +### 6. Progress Tracking (Structured Comments) **CRITICAL: Use structured progress comments for visibility.** @@ -380,7 +433,7 @@ add_comment( - Notify that new tasks are ready for execution - Update the execution queue -### 6. Monitor Parallel Execution +### 7. Monitor Parallel Execution **Track multiple running tasks:** ``` @@ -398,7 +451,7 @@ Batch 2 (now unblocked): Starting #46 while #48 continues... ``` -### 7. Branch Protection Detection +### 8. Branch Protection Detection Before merging, check if development branch is protected: @@ -428,7 +481,7 @@ Closes #45 **NEVER include subtask checklists in MR body.** The issue already has them. -### 8. Sprint Close - Capture Lessons Learned +### 9. Sprint Close - Capture Lessons Learned **Invoked by:** `/sprint-close` diff --git a/plugins/projman/commands/sprint-start.md b/plugins/projman/commands/sprint-start.md index d874958..65ca4f7 100644 --- a/plugins/projman/commands/sprint-start.md +++ b/plugins/projman/commands/sprint-start.md @@ -72,6 +72,67 @@ Parallel Execution Batches: **Independent tasks in the same batch run in parallel.** +## File Conflict Prevention (MANDATORY) + +**CRITICAL: Before dispatching parallel agents, check for file overlap.** + +**Pre-Dispatch Conflict Check:** + +1. **Identify target files** for each task in the batch +2. **Check for overlap** - Do any tasks modify the same file? +3. **If overlap detected** - Sequentialize those specific tasks + +**Example Conflict Detection:** +``` +Batch 1 Analysis: + #45 - Implement JWT service + Files: auth/jwt_service.py, auth/__init__.py, tests/test_jwt.py + + #48 - Update API documentation + Files: docs/api.md, README.md + + Overlap: NONE β†’ Safe to parallelize + +Batch 2 Analysis: + #46 - Build login endpoint + Files: api/routes/auth.py, auth/__init__.py + + #49 - Add auth tests + Files: tests/test_auth.py, auth/__init__.py + + Overlap: auth/__init__.py β†’ CONFLICT! + Action: Sequentialize #46 and #49 (run #46 first) +``` + +**Conflict Resolution Rules:** + +| Conflict Type | Action | +|---------------|--------| +| Same file in checklist | Sequentialize tasks | +| Same directory | Review if safe, usually OK | +| Shared test file | Sequentialize or assign different test files | +| Shared config | Sequentialize | + +**Branch Isolation Protocol:** + +Even for parallel tasks, each MUST run on its own branch: +``` +Task #45 β†’ feat/45-jwt-service (isolated) +Task #48 β†’ feat/48-api-docs (isolated) +``` + +**Sequential Merge After Completion:** +``` +1. Task #45 completes β†’ merge feat/45-jwt-service to development +2. Task #48 completes β†’ merge feat/48-api-docs to development +3. Never merge simultaneously - always sequential to detect conflicts +``` + +**If Merge Conflict Occurs:** +1. Stop second task +2. Resolve conflict manually or assign to human +3. Resume/restart second task with updated base + ## Branch Naming Convention (MANDATORY) When creating branches for tasks: From 459550e7d381ee3787e9b02b5fb9baac77429d27 Mon Sep 17 00:00:00 2001 From: lmiranda Date: Wed, 28 Jan 2026 10:49:34 -0500 Subject: [PATCH 14/15] feat(projman): add checkpoint/resume for interrupted agent work (#237) Executor checkpointing: - Standard checkpoint comment format with branch, commit, phase - Files modified with status (created, modified) - Completed and pending steps tracking - State notes for resumption context - Save checkpoint after major steps, before stopping Orchestrator resume detection: - Scan issue comments for "## Checkpoint" markers - Offer resume options: resume, start fresh, review details - Verify branch exists and files match before resuming - Dispatch executor with checkpoint context Sprint-start integration: - Checkpoint detection as first workflow step - Resume flow documentation with example - Checkpoint format specification This enables resuming work after: - Budget exhaustion (100 tool call limit) - Agent failure/circuit breaker - Manual interruption - Session timeout Closes #237 Co-Authored-By: Claude Opus 4.5 --- plugins/projman/agents/executor.md | 75 ++++++++++++++++++++++++ plugins/projman/agents/orchestrator.md | 39 +++++++++++- plugins/projman/commands/sprint-start.md | 62 +++++++++++++++++++- 3 files changed, 174 insertions(+), 2 deletions(-) diff --git a/plugins/projman/agents/executor.md b/plugins/projman/agents/executor.md index bffcc62..7291066 100644 --- a/plugins/projman/agents/executor.md +++ b/plugins/projman/agents/executor.md @@ -424,6 +424,81 @@ As the executor, you interact with MCP tools for status updates: - Apply best practices - Deliver quality work +## Checkpointing (Save Progress for Resume) + +**CRITICAL: Save checkpoints so work can be resumed if interrupted.** + +**Checkpoint Comment Format:** +```markdown +## Checkpoint +**Branch:** feat/45-jwt-service +**Commit:** abc123 (or "uncommitted") +**Phase:** [current phase] +**Tool Calls:** 45 + +### Files Modified +- auth/jwt_service.py (created) +- tests/test_jwt.py (created) + +### Completed Steps +- [x] Created jwt_service.py skeleton +- [x] Implemented generate_token() +- [x] Implemented verify_token() + +### Pending Steps +- [ ] Write unit tests +- [ ] Add token refresh logic +- [ ] Commit and push + +### State Notes +[Any important context for resumption] +``` + +**When to Save Checkpoints:** +- After completing each major step (every 20-30 tool calls) +- Before stopping due to budget limit +- When encountering a blocker +- After any commit + +**Checkpoint Example:** +``` +add_comment( + issue_number=45, + body="""## Checkpoint +**Branch:** feat/45-jwt-service +**Commit:** uncommitted (changes staged) +**Phase:** Testing +**Tool Calls:** 67 + +### Files Modified +- auth/jwt_service.py (created, 120 lines) +- auth/__init__.py (modified, added import) +- tests/test_jwt.py (created, 50 lines, incomplete) + +### Completed Steps +- [x] Created auth/jwt_service.py +- [x] Implemented generate_token() with HS256 +- [x] Implemented verify_token() +- [x] Updated auth/__init__.py exports + +### Pending Steps +- [ ] Complete test_jwt.py (5 tests remaining) +- [ ] Add token refresh logic +- [ ] Commit changes +- [ ] Push to remote + +### State Notes +- Using PyJWT 2.8.0 +- Secret key from JWT_SECRET env var +- Tests use pytest fixtures in conftest.py +""" +) +``` + +**Checkpoint on Interruption:** + +If you must stop (budget, failure, blocker), ALWAYS post a checkpoint FIRST. + ## Runaway Detection (Self-Monitoring) **CRITICAL: Monitor yourself to prevent infinite loops and wasted resources.** diff --git a/plugins/projman/agents/orchestrator.md b/plugins/projman/agents/orchestrator.md index 2d0b1c0..9796cc9 100644 --- a/plugins/projman/agents/orchestrator.md +++ b/plugins/projman/agents/orchestrator.md @@ -93,7 +93,44 @@ git branch --show-current **Workflow:** -**A. Fetch Sprint Issues** +**A. Fetch Sprint Issues and Detect Checkpoints** +``` +list_issues(state="open", labels=["sprint-current"]) +``` + +**For each open issue, check for checkpoint comments:** +``` +get_issue(issue_number=45) # Comments included +β†’ Look for comments containing "## Checkpoint" +``` + +**If Checkpoint Found:** +``` +Checkpoint Detected for #45 + +Found checkpoint from previous session: + Branch: feat/45-jwt-service + Phase: Testing + Tool Calls: 67 + Files Modified: 3 + Completed: 4/7 steps + +Options: +1. Resume from checkpoint (recommended) +2. Start fresh (discard previous work) +3. Review checkpoint details first + +Would you like to resume? +``` + +**Resume Protocol:** +1. Verify branch exists: `git branch -a | grep feat/45-jwt-service` +2. Switch to branch: `git checkout feat/45-jwt-service` +3. Verify files match checkpoint +4. Dispatch executor with checkpoint context +5. Executor continues from pending steps + +**B. Fetch Sprint Issues (Standard)** ``` list_issues(state="open", labels=["sprint-current"]) ``` diff --git a/plugins/projman/commands/sprint-start.md b/plugins/projman/commands/sprint-start.md index 65ca4f7..3a39870 100644 --- a/plugins/projman/commands/sprint-start.md +++ b/plugins/projman/commands/sprint-start.md @@ -25,7 +25,12 @@ If you are on a production or staging branch, you MUST stop and ask the user to The orchestrator agent will: -1. **Fetch Sprint Issues** +1. **Detect Checkpoints (Resume Support)** + - Check each open issue for `## Checkpoint` comments + - If checkpoint found, offer to resume from that point + - Resume preserves: branch, completed work, pending steps + +2. **Fetch Sprint Issues** - Use `list_issues` to fetch open issues for the sprint - Identify priorities based on labels (Priority/Critical, Priority/High, etc.) @@ -300,6 +305,61 @@ Batch 2 (now unblocked): Starting #46 while #48 continues... ``` +## Checkpoint Resume Support + +If a previous session was interrupted (agent stopped, failure, budget exhausted), checkpoints enable resumption. + +**Checkpoint Detection:** +The orchestrator scans issue comments for `## Checkpoint` markers containing: +- Branch name +- Last commit hash +- Completed/pending steps +- Files modified + +**Resume Flow:** +``` +User: /sprint-start + +Orchestrator: Checking for checkpoints... + +Found checkpoint for #45 (JWT service): + Branch: feat/45-jwt-service + Last activity: 2 hours ago + Progress: 4/7 steps completed + Pending: Write tests, add refresh, commit + +Options: + 1. Resume from checkpoint (recommended) + 2. Start fresh (lose previous work) + 3. Review checkpoint details + +User: 1 + +Orchestrator: Resuming #45 from checkpoint... + βœ“ Branch exists + βœ“ Files match checkpoint + βœ“ Dispatching executor with context + +Executor continues from pending steps... +``` + +**Checkpoint Format:** +Executors save checkpoints after major steps: +```markdown +## Checkpoint +**Branch:** feat/45-jwt-service +**Commit:** abc123 +**Phase:** Testing + +### Completed Steps +- [x] Step 1 +- [x] Step 2 + +### Pending Steps +- [ ] Step 3 +- [ ] Step 4 +``` + ## Getting Started Simply invoke `/sprint-start` and the orchestrator will: From 26310d05f05ac707d86a2ca4fabfc9493215bb6e Mon Sep 17 00:00:00 2001 From: lmiranda Date: Wed, 28 Jan 2026 10:51:10 -0500 Subject: [PATCH 15/15] feat(projman): add sprint approval requirement before execution (#233) Sprint-plan approval workflow: - Request explicit approval after creating issues - Present scope summary (branches, files, dependencies) - User must type "approve sprint N" to authorize - Record approval in milestone description with timestamp Sprint-start verification: - Check milestone for "## Sprint Approval" section - If missing, STOP and direct to /sprint-plan - Extract approved scope (branches, files) - Enforce scope during execution Orchestrator scope enforcement: - Verify approval before any execution - Check each operation against approved scope - Operations outside scope require re-approval This separates planning (review) from execution (action), preventing agents from executing without explicit user consent. Closes #233 Co-Authored-By: Claude Opus 4.5 --- plugins/projman/agents/orchestrator.md | 35 +++++++++++++++- plugins/projman/agents/planner.md | 2 + plugins/projman/commands/sprint-plan.md | 52 ++++++++++++++++++++++++ plugins/projman/commands/sprint-start.md | 51 ++++++++++++++++++++++- 4 files changed, 137 insertions(+), 3 deletions(-) diff --git a/plugins/projman/agents/orchestrator.md b/plugins/projman/agents/orchestrator.md index 9796cc9..c13c3b1 100644 --- a/plugins/projman/agents/orchestrator.md +++ b/plugins/projman/agents/orchestrator.md @@ -57,9 +57,42 @@ curl -X POST "https://gitea.../api/..." - Coordinate Git operations (commit, merge, cleanup) - Keep sprint moving forward +## Critical: Approval Verification + +**BEFORE EXECUTING**, verify sprint approval exists: + +``` +get_milestone(milestone_id=current_sprint) +β†’ Check description for "## Sprint Approval" section +``` + +**If No Approval:** +``` +⚠️ SPRINT NOT APPROVED + +This sprint has not been approved for execution. +Please run /sprint-plan to approve the sprint first. +``` + +**If Approved:** +- Extract scope (branches, files) from approval record +- Enforce scope during execution +- Any operation outside scope requires stopping and re-approval + +**Scope Enforcement Example:** +``` +Approved scope: + Branches: feat/45-*, feat/46-* + Files: auth/*, tests/test_auth* + +Task #48 wants to create: feat/48-api-docs +β†’ NOT in approved scope! +β†’ STOP and ask user to approve expanded scope +``` + ## Critical: Branch Detection -**BEFORE DOING ANYTHING**, check the current git branch: +**AFTER approval verification**, check the current git branch: ```bash git branch --show-current diff --git a/plugins/projman/agents/planner.md b/plugins/projman/agents/planner.md index 10800a5..0272650 100644 --- a/plugins/projman/agents/planner.md +++ b/plugins/projman/agents/planner.md @@ -522,5 +522,7 @@ Sprint 17 - User Authentication (Due: 2025-02-01) 13. **Always cleanup local files** - Delete after migrating to wiki 14. **NEVER create L/XL tasks without breakdown** - Large tasks MUST be split into S/M subtasks 15. **Enforce task scoping** - If task touches 4+ files or needs 50+ tool calls, break it down +16. **ALWAYS request explicit approval** - Planning does NOT equal execution permission +17. **Record approval in milestone** - Sprint-start verifies approval before executing You are the thoughtful planner who ensures sprints are well-prepared, architecturally sound, and learn from past experiences. Take your time, ask questions, and create comprehensive plans that set the team up for success. diff --git a/plugins/projman/commands/sprint-plan.md b/plugins/projman/commands/sprint-plan.md index 0cc9b40..c2505b0 100644 --- a/plugins/projman/commands/sprint-plan.md +++ b/plugins/projman/commands/sprint-plan.md @@ -136,6 +136,58 @@ The planner agent will: - Document dependency graph - Provide sprint overview with wiki links +11. **Request Sprint Approval** + - Present approval request with scope summary + - Capture explicit user approval + - Record approval in milestone description + - Approval scopes what sprint-start can execute + +## Sprint Approval (MANDATORY) + +**Planning DOES NOT equal execution permission.** + +After creating issues, the planner MUST request explicit approval: + +``` +Sprint 17 Planning Complete +=========================== + +Created Issues: +- #45: [Sprint 17] feat: JWT token generation +- #46: [Sprint 17] feat: Login endpoint +- #47: [Sprint 17] test: Auth tests + +Execution Scope: +- Branches: feat/45-*, feat/46-*, feat/47-* +- Files: auth/*, api/routes/auth.py, tests/test_auth* +- Dependencies: PyJWT, python-jose + +⚠️ APPROVAL REQUIRED + +Do you approve this sprint for execution? +This grants permission for agents to: +- Create and modify files in the listed scope +- Create branches with the listed prefixes +- Install listed dependencies + +Type "approve sprint 17" to authorize execution. +``` + +**On Approval:** +1. Record approval in milestone description +2. Note timestamp and scope +3. Sprint-start will verify approval exists + +**Approval Record Format:** +```markdown +## Sprint Approval +**Approved:** 2026-01-28 14:30 +**Approver:** User +**Scope:** +- Branches: feat/45-*, feat/46-*, feat/47-* +- Files: auth/*, api/routes/auth.py, tests/test_auth* +``` + ## Issue Title Format (MANDATORY) ``` diff --git a/plugins/projman/commands/sprint-start.md b/plugins/projman/commands/sprint-start.md index 3a39870..b46e858 100644 --- a/plugins/projman/commands/sprint-start.md +++ b/plugins/projman/commands/sprint-start.md @@ -6,6 +6,47 @@ description: Begin sprint execution with relevant lessons learned from previous You are initiating sprint execution. The orchestrator agent will coordinate the work, analyze dependencies for parallel execution, search for relevant lessons learned, and guide you through the implementation process. +## Sprint Approval Verification + +**CRITICAL: Sprint must be approved before execution.** + +The orchestrator checks for approval in the milestone description: + +``` +get_milestone(milestone_id=17) +β†’ Check description for "## Sprint Approval" section +``` + +**If Approval Missing:** +``` +⚠️ SPRINT NOT APPROVED + +Sprint 17 has not been approved for execution. +The milestone description does not contain an approval record. + +Please run /sprint-plan to: +1. Review the sprint scope +2. Approve the execution plan + +Then run /sprint-start again. +``` + +**If Approval Found:** +``` +βœ“ Sprint Approval Verified + Approved: 2026-01-28 14:30 + Scope: + Branches: feat/45-*, feat/46-*, feat/47-* + Files: auth/*, api/routes/auth.py, tests/test_auth* + +Proceeding with execution within approved scope... +``` + +**Scope Enforcement:** +- Agents can ONLY create branches matching approved patterns +- Agents can ONLY modify files within approved paths +- Operations outside scope require re-approval via `/sprint-plan` + ## Branch Detection **CRITICAL:** Before proceeding, check the current git branch: @@ -25,12 +66,18 @@ If you are on a production or staging branch, you MUST stop and ask the user to The orchestrator agent will: -1. **Detect Checkpoints (Resume Support)** +1. **Verify Sprint Approval** + - Check milestone description for `## Sprint Approval` section + - If no approval found, STOP and direct user to `/sprint-plan` + - If approval found, extract scope (branches, files) + - Agents operate ONLY within approved scope + +2. **Detect Checkpoints (Resume Support)** - Check each open issue for `## Checkpoint` comments - If checkpoint found, offer to resume from that point - Resume preserves: branch, completed work, pending steps -2. **Fetch Sprint Issues** +3. **Fetch Sprint Issues** - Use `list_issues` to fetch open issues for the sprint - Identify priorities based on labels (Priority/Critical, Priority/High, etc.)