Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:20:25 +08:00
commit 0d6226e0d8
69 changed files with 20934 additions and 0 deletions

View File

@@ -0,0 +1,342 @@
#!/usr/bin/env python3
"""
Commit Planner - Create optimal commit sequence
Purpose: Generate optimal commit sequence from file groups
Version: 1.0.0
Usage: ./commit-planner.py [--input FILE] [--output plan|script]
Returns:
Exit 0: Success
Exit 1: Error
Exit 2: Invalid parameters
Dependencies: git, python3
"""
import sys
import json
import subprocess
from collections import defaultdict, deque
from typing import List, Dict, Set, Tuple, Optional
# Type priority for ordering
TYPE_PRIORITY = {
'feat': 1, # Features enable other changes
'fix': 2, # Fixes should be applied early
'refactor': 3, # Restructuring before additions
'perf': 4, # Performance after stability
'test': 5, # Tests after implementation
'docs': 6, # Documentation last
'style': 7, # Style changes last
'chore': 8, # Housekeeping last
'ci': 9, # CI changes last
'build': 10 # Build changes last
}
class CommitPlanner:
def __init__(self, verbose: bool = False):
self.verbose = verbose
self.commits = []
self.dependencies = defaultdict(set)
def log(self, message: str):
"""Print message if verbose mode enabled"""
if self.verbose:
print(f"[DEBUG] {message}", file=sys.stderr)
def run_git_command(self, args: List[str]) -> str:
"""Execute git command and return output"""
try:
result = subprocess.run(
['git'] + args,
capture_output=True,
text=True,
check=True
)
return result.stdout
except subprocess.CalledProcessError as e:
print(f"Error running git command: {e}", file=sys.stderr)
return ""
def load_suggestions(self, input_file: Optional[str] = None) -> List[Dict]:
"""Load commit suggestions from file or stdin"""
try:
if input_file:
with open(input_file, 'r') as f:
data = json.load(f)
else:
# Read from stdin
data = json.load(sys.stdin)
return data.get('suggestions', [])
except Exception as e:
print(f"Error loading suggestions: {e}", file=sys.stderr)
sys.exit(1)
def detect_dependencies(self, commit1: Dict, commit2: Dict) -> bool:
"""Check if commit2 depends on commit1"""
# Test files depend on implementation files
if commit1['type'] != 'test' and commit2['type'] == 'test':
# Check if test scope matches implementation scope
if commit1.get('scope') == commit2.get('scope'):
return True
# Docs depend on features they document
if commit1['type'] == 'feat' and commit2['type'] == 'docs':
# Check if docs reference the feature scope
if commit1.get('scope') in commit2.get('subject', ''):
return True
# Fixes may depend on features
if commit1['type'] == 'feat' and commit2['type'] == 'fix':
# Check if same scope
if commit1.get('scope') == commit2.get('scope'):
return True
# Check file dependencies (imports)
files1 = set(commit1.get('files', []))
files2 = set(commit2.get('files', []))
# If commit2 files might import from commit1 files
for file2 in files2:
for file1 in files1:
if self.has_import_dependency(file1, file2):
return True
return False
def has_import_dependency(self, source_file: str, target_file: str) -> bool:
"""Check if target_file imports from source_file"""
try:
# Get content of target file
content = self.run_git_command(['show', f':{target_file}'])
# Extract module path from source file
source_module = source_file.replace('/', '.').replace('.py', '').replace('.js', '').replace('.ts', '')
# Check for import statements
if any(imp in content for imp in [f'import {source_module}', f'from {source_module}', f"require('{source_module}"]):
return True
except:
pass
return False
def build_dependency_graph(self, commits: List[Dict]) -> Dict[int, Set[int]]:
"""Build dependency graph between commits"""
graph = defaultdict(set)
for i, commit1 in enumerate(commits):
for j, commit2 in enumerate(commits):
if i != j and self.detect_dependencies(commit1, commit2):
# commit2 depends on commit1
graph[j].add(i)
self.log(f"Dependency: Commit {j+1} depends on Commit {i+1}")
return graph
def topological_sort(self, commits: List[Dict], dependencies: Dict[int, Set[int]]) -> List[int]:
"""Perform topological sort to respect dependencies"""
# Calculate in-degree for each node
in_degree = defaultdict(int)
for node in range(len(commits)):
in_degree[node] = len(dependencies[node])
# Queue of nodes with no dependencies
queue = deque([node for node in range(len(commits)) if in_degree[node] == 0])
result = []
while queue:
# Sort queue by priority (type priority)
queue_list = list(queue)
queue_list.sort(key=lambda x: TYPE_PRIORITY.get(commits[x]['type'], 99))
queue = deque(queue_list)
node = queue.popleft()
result.append(node)
# Update dependencies
for other_node in range(len(commits)):
if node in dependencies[other_node]:
dependencies[other_node].remove(node)
in_degree[other_node] -= 1
if in_degree[other_node] == 0:
queue.append(other_node)
# Check for cycles
if len(result) != len(commits):
print("Error: Circular dependency detected", file=sys.stderr)
sys.exit(1)
return result
def create_sequence(self, commits: List[Dict]) -> List[Dict]:
"""Create optimal commit sequence"""
self.log(f"Planning sequence for {len(commits)} commits")
# Build dependency graph
dependencies = self.build_dependency_graph(commits)
# Topological sort
order = self.topological_sort(commits, dependencies)
# Create ordered sequence
sequence = []
for idx, commit_idx in enumerate(order):
commit = commits[commit_idx].copy()
commit['order'] = idx + 1
commit['commit_id'] = commit_idx + 1
commit['original_id'] = commit_idx
# Determine when can execute
deps = dependencies[commit_idx]
if not deps:
commit['can_execute'] = 'now'
else:
dep_ids = [order.index(d) + 1 for d in deps]
commit['can_execute'] = f"after commit {min(dep_ids)}"
sequence.append(commit)
return sequence
def format_plan(self, sequence: List[Dict]) -> str:
"""Format sequence as readable plan"""
lines = []
lines.append("=" * 60)
lines.append("COMMIT SEQUENCE PLAN")
lines.append("=" * 60)
lines.append("")
lines.append(f"Execution Order: {len(sequence)} commits in sequence")
lines.append("")
for commit in sequence:
lines.append("" * 60)
lines.append(f"COMMIT {commit['order']}: {commit['type']}" +
(f"({commit.get('scope', '')})" if commit.get('scope') else ""))
lines.append(f"Files: {len(commit['files'])}")
lines.append(f"Can execute: {commit['can_execute']}")
lines.append("" * 60)
lines.append("")
# Message
lines.append("Message:")
lines.append(f" {commit['subject']}")
if commit.get('body'):
lines.append("")
for line in commit['body'].split('\n'):
lines.append(f" {line}")
lines.append("")
# Files to stage
lines.append("Files to stage:")
for file in commit['files']:
lines.append(f" git add {file}")
lines.append("")
# Commit command
commit_msg = commit['subject']
if commit.get('body'):
body = commit['body'].replace('"', '\\"')
commit_cmd = f'git commit -m "{commit_msg}" -m "{body}"'
else:
commit_cmd = f'git commit -m "{commit_msg}"'
lines.append("Command:")
lines.append(f" {commit_cmd}")
lines.append("")
lines.append("=" * 60)
lines.append(f"Total commits: {len(sequence)}")
lines.append(f"Total files: {sum(len(c['files']) for c in sequence)}")
lines.append("=" * 60)
return '\n'.join(lines)
def format_script(self, sequence: List[Dict]) -> str:
"""Format sequence as executable bash script"""
lines = [
"#!/bin/bash",
"# Atomic commit sequence",
f"# Generated: {subprocess.run(['date'], capture_output=True, text=True).stdout.strip()}",
f"# Total commits: {len(sequence)}",
"",
"set -e # Exit on error",
"",
'echo "🚀 Starting commit sequence..."',
""
]
for commit in sequence:
lines.append(f"# Commit {commit['order']}: {commit['type']}" +
(f"({commit.get('scope', '')})" if commit.get('scope') else ""))
lines.append('echo ""')
lines.append(f'echo "📝 Commit {commit["order"]}/{len(sequence)}: {commit["type"]}"')
# Stage files
for file in commit['files']:
lines.append(f'git add "{file}"')
# Commit
commit_msg = commit['subject']
if commit.get('body'):
body = commit['body'].replace('"', '\\"').replace('\n', ' ')
lines.append(f'git commit -m "{commit_msg}" -m "{body}"')
else:
lines.append(f'git commit -m "{commit_msg}"')
lines.append(f'echo "✅ Commit {commit["order"]} complete"')
lines.append("")
lines.append('echo ""')
lines.append('echo "🎉 All commits completed successfully!"')
lines.append(f'echo "Total commits: {len(sequence)}"')
lines.append(f'echo "Total files: {sum(len(c["files"]) for c in sequence)}"')
return '\n'.join(lines)
def main():
import argparse
parser = argparse.ArgumentParser(description='Create optimal commit sequence')
parser.add_argument('--input', help='Input JSON file (default: stdin)')
parser.add_argument('--output', choices=['plan', 'script', 'json'], default='plan',
help='Output format')
parser.add_argument('--verbose', action='store_true', help='Verbose output')
args = parser.parse_args()
planner = CommitPlanner(verbose=args.verbose)
# Load suggestions
commits = planner.load_suggestions(args.input)
if not commits:
print("No commit suggestions provided", file=sys.stderr)
sys.exit(1)
# Create sequence
sequence = planner.create_sequence(commits)
# Output
if args.output == 'json':
result = {
'sequence': sequence,
'summary': {
'total_commits': len(sequence),
'total_files': sum(len(c['files']) for c in sequence)
}
}
print(json.dumps(result, indent=2))
elif args.output == 'script':
print(planner.format_script(sequence))
else: # plan
print(planner.format_plan(sequence))
sys.exit(0)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,328 @@
#!/bin/bash
# Dependency Checker - Analyze file dependencies
#
# Purpose: Check for dependencies between files to inform commit ordering
# Version: 1.0.0
# Usage: ./dependency-checker.sh [files...]
# If no files specified, analyzes all changed files
# Returns:
# Exit 0: Success
# Exit 1: Error
# Exit 2: Invalid parameters
#
# Dependencies: git, bash 4.0+, grep
set -euo pipefail
VERBOSE=false
OUTPUT_FORMAT="text"
# Parse options
while [[ $# -gt 0 ]]; do
case "$1" in
--verbose)
VERBOSE=true
shift
;;
--format)
OUTPUT_FORMAT="${2:-text}"
shift 2
;;
*)
break
;;
esac
done
# Logging
log() {
if [[ "$VERBOSE" == "true" ]]; then
echo "[DEBUG] $*" >&2
fi
}
# Get changed files if not specified
FILES=()
if [[ $# -eq 0 ]]; then
log "Getting changed files from git..."
while IFS= read -r file; do
[[ -n "$file" ]] && FILES+=("$file")
done < <(git diff --cached --name-only; git diff --name-only)
else
FILES=("$@")
fi
# Remove duplicates
mapfile -t FILES < <(printf '%s\n' "${FILES[@]}" | sort -u)
if [[ ${#FILES[@]} -eq 0 ]]; then
echo "No files to analyze" >&2
exit 1
fi
log "Analyzing ${#FILES[@]} files for dependencies..."
# Dependency storage
declare -A dependencies
declare -A file_types
# Detect file type
detect_file_type() {
local file="$1"
case "$file" in
*.py)
echo "python"
;;
*.js|*.jsx)
echo "javascript"
;;
*.ts|*.tsx)
echo "typescript"
;;
*.go)
echo "go"
;;
*.java)
echo "java"
;;
*.rb)
echo "ruby"
;;
*.rs)
echo "rust"
;;
*.md|*.txt|*.rst)
echo "docs"
;;
*test*|*spec*)
echo "test"
;;
*)
echo "unknown"
;;
esac
}
# Extract imports from Python file
extract_python_imports() {
local file="$1"
local content
if [[ -f "$file" ]]; then
content=$(cat "$file")
else
# Try to get from git
content=$(git show :"$file" 2>/dev/null || echo "")
fi
# Match: import module, from module import
echo "$content" | grep -E "^(import|from) " | sed -E 's/^(import|from) ([a-zA-Z0-9_.]+).*/\2/' || true
}
# Extract imports from JavaScript/TypeScript
extract_js_imports() {
local file="$1"
local content
if [[ -f "$file" ]]; then
content=$(cat "$file")
else
content=$(git show :"$file" 2>/dev/null || echo "")
fi
# Match: import from, require()
{
echo "$content" | grep -oE "import .* from ['\"]([^'\"]+)['\"]" | sed -E "s/.*from ['\"](.*)['\"].*/\1/" || true
echo "$content" | grep -oE "require\(['\"]([^'\"]+)['\"]\)" | sed -E "s/.*require\(['\"]([^'\"]+)['\"]\).*/\1/" || true
} | sort -u
}
# Extract imports from Go
extract_go_imports() {
local file="$1"
local content
if [[ -f "$file" ]]; then
content=$(cat "$file")
else
content=$(git show :"$file" 2>/dev/null || echo "")
fi
# Match: import "module" or import ( "module" )
echo "$content" | grep -oE 'import +"[^"]+"' | sed -E 's/import +"([^"]+)".*/\1/' || true
echo "$content" | sed -n '/^import (/,/^)/p' | grep -oE '"[^"]+"' | tr -d '"' || true
}
# Convert import path to file path
import_to_file() {
local import_path="$1"
local file_type="$2"
case "$file_type" in
python)
# module.submodule -> module/submodule.py
echo "$import_path" | tr '.' '/' | sed 's|$|.py|'
;;
javascript|typescript)
# Handle relative imports
if [[ "$import_path" == ./* ]] || [[ "$import_path" == ../* ]]; then
echo "$import_path"
else
# node_modules - not a local file
echo ""
fi
;;
go)
# Package imports - check if local
if [[ "$import_path" == github.com/* ]]; then
echo ""
else
echo "$import_path"
fi
;;
*)
echo ""
;;
esac
}
# Find dependencies for each file
log "Extracting imports and dependencies..."
for file in "${FILES[@]}"; do
file_type=$(detect_file_type "$file")
file_types["$file"]="$file_type"
log " $file: type=$file_type"
case "$file_type" in
python)
imports=$(extract_python_imports "$file")
;;
javascript|typescript)
imports=$(extract_js_imports "$file")
;;
go)
imports=$(extract_go_imports "$file")
;;
*)
imports=""
;;
esac
if [[ -n "$imports" ]]; then
log " Imports:"
while IFS= read -r import_path; do
[[ -z "$import_path" ]] && continue
log " - $import_path"
# Convert import to file path
imported_file=$(import_to_file "$import_path" "$file_type")
# Check if imported file is in our file list
if [[ -n "$imported_file" ]]; then
for other_file in "${FILES[@]}"; do
if [[ "$other_file" == *"$imported_file"* ]]; then
# file depends on other_file
if [[ -z "${dependencies[$file]:-}" ]]; then
dependencies["$file"]="$other_file"
else
dependencies["$file"]="${dependencies[$file]},$other_file"
fi
log " Dependency: $file -> $other_file"
fi
done
fi
done <<< "$imports"
fi
done
# Detect test dependencies
log "Detecting test dependencies..."
for file in "${FILES[@]}"; do
if [[ "${file_types[$file]}" == "test" ]]; then
# Test file depends on implementation file
impl_file="${file//test/}"
impl_file="${impl_file//.test/}"
impl_file="${impl_file//.spec/}"
impl_file="${impl_file//tests\//}"
impl_file="${impl_file//spec\//}"
for other_file in "${FILES[@]}"; do
if [[ "$other_file" == *"$impl_file"* ]] && [[ "$other_file" != "$file" ]]; then
if [[ -z "${dependencies[$file]:-}" ]]; then
dependencies["$file"]="$other_file"
else
dependencies["$file"]="${dependencies[$file]},$other_file"
fi
log " Test dependency: $file -> $other_file"
fi
done
fi
done
# Output results
if [[ "$OUTPUT_FORMAT" == "json" ]]; then
echo "{"
echo " \"files\": ["
local first=true
for file in "${FILES[@]}"; do
if [[ "$first" == "true" ]]; then
first=false
else
echo ","
fi
echo -n " {\"file\": \"$file\", \"type\": \"${file_types[$file]}\""
if [[ -n "${dependencies[$file]:-}" ]]; then
IFS=',' read -ra deps <<< "${dependencies[$file]}"
echo -n ", \"depends_on\": ["
local first_dep=true
for dep in "${deps[@]}"; do
if [[ "$first_dep" == "true" ]]; then
first_dep=false
else
echo -n ", "
fi
echo -n "\"$dep\""
done
echo -n "]"
fi
echo -n "}"
done
echo ""
echo " ]"
echo "}"
else
echo "=== FILE DEPENDENCIES ==="
echo ""
echo "Files analyzed: ${#FILES[@]}"
echo ""
local has_dependencies=false
for file in "${FILES[@]}"; do
if [[ -n "${dependencies[$file]:-}" ]]; then
has_dependencies=true
echo "File: $file"
echo " Type: ${file_types[$file]}"
echo " Depends on:"
IFS=',' read -ra deps <<< "${dependencies[$file]}"
for dep in "${deps[@]}"; do
echo " - $dep"
done
echo ""
fi
done
if [[ "$has_dependencies" == "false" ]]; then
echo "No dependencies detected."
echo "All files can be committed independently."
else
echo "Recommendation:"
echo " Commit dependencies before dependent files."
echo " Group dependent files in same commit if they form atomic unit."
fi
fi
log "Dependency analysis complete"
exit 0

View File

@@ -0,0 +1,395 @@
#!/bin/bash
# File Grouper - Group changed files by type, scope, or feature
#
# Purpose: Group git changed files using different strategies
# Version: 1.0.0
# Usage: ./file-grouper.sh <strategy> [options]
# strategy: type|scope|feature
# options: --verbose, --format json|text
# Returns:
# Exit 0: Success
# Exit 1: No changes
# Exit 2: Invalid parameters
#
# Dependencies: git, bash 4.0+
set -euo pipefail
# Configuration
STRATEGY="${1:-type}"
VERBOSE=false
FORMAT="text"
# Parse arguments
shift || true
while [[ $# -gt 0 ]]; do
case "$1" in
--verbose)
VERBOSE=true
shift
;;
--format)
FORMAT="${2:-text}"
shift 2
;;
*)
echo "Unknown option: $1" >&2
exit 2
;;
esac
done
# Logging function
log() {
if [[ "$VERBOSE" == "true" ]]; then
echo "[DEBUG] $*" >&2
fi
}
# Get changed files
get_changed_files() {
local files=()
# Staged files
while IFS= read -r file; do
[[ -n "$file" ]] && files+=("$file")
done < <(git diff --cached --name-only)
# Unstaged files
while IFS= read -r file; do
[[ -n "$file" ]] && files+=("$file")
done < <(git diff --name-only)
# Remove duplicates
printf '%s\n' "${files[@]}" | sort -u
}
# Detect commit type from file
detect_type() {
local file="$1"
local diff
# Get diff for analysis
diff=$(git diff --cached "$file" 2>/dev/null || git diff "$file" 2>/dev/null || echo "")
# Documentation files
if [[ "$file" =~ \.(md|txt|rst|adoc)$ ]]; then
echo "docs"
return
fi
# Test files
if [[ "$file" =~ (test|spec|__tests__)/ ]] || [[ "$file" =~ \.(test|spec)\. ]]; then
echo "test"
return
fi
# CI/CD files
if [[ "$file" =~ \.github/|\.gitlab-ci|jenkins|\.circleci ]]; then
echo "ci"
return
fi
# Build files
if [[ "$file" =~ (package\.json|pom\.xml|build\.gradle|Makefile|CMakeLists\.txt)$ ]]; then
echo "build"
return
fi
# Analyze diff content
if [[ -z "$diff" ]]; then
echo "chore"
return
fi
# Check for new functionality
if echo "$diff" | grep -q "^+.*\(function\|class\|def\|const\|let\)"; then
if echo "$diff" | grep -iq "^+.*\(new\|add\|implement\|create\)"; then
echo "feat"
return
fi
fi
# Check for bug fixes
if echo "$diff" | grep -iq "^+.*\(fix\|bug\|error\|issue\|null\|undefined\)"; then
echo "fix"
return
fi
# Check for refactoring
if echo "$diff" | grep -iq "^+.*\(refactor\|rename\|move\|extract\)"; then
echo "refactor"
return
fi
# Check for performance
if echo "$diff" | grep -iq "^+.*\(performance\|optimize\|cache\|memoize\)"; then
echo "perf"
return
fi
# Default to chore
echo "chore"
}
# Extract scope from file path
extract_scope() {
local file="$1"
local scope
# Remove leading ./
file="${file#./}"
# Split path
IFS='/' read -ra parts <<< "$file"
# Skip common prefixes
for part in "${parts[@]}"; do
case "$part" in
src|lib|app|packages|tests|test|.)
continue
;;
*)
# Remove file extension
scope="${part%%.*}"
echo "$scope"
return
;;
esac
done
echo "root"
}
# Group by type
group_by_type() {
log "Grouping by type..."
declare -A groups
local files
mapfile -t files < <(get_changed_files)
if [[ ${#files[@]} -eq 0 ]]; then
echo "No changes detected" >&2
exit 1
fi
# Group files by type
for file in "${files[@]}"; do
local type
type=$(detect_type "$file")
log " $file -> type:$type"
if [[ -z "${groups[$type]:-}" ]]; then
groups[$type]="$file"
else
groups[$type]="${groups[$type]},$file"
fi
done
# Output results
if [[ "$FORMAT" == "json" ]]; then
echo "{"
echo " \"strategy\": \"type\","
echo " \"groups\": {"
local first=true
for type in "${!groups[@]}"; do
if [[ "$first" == "true" ]]; then
first=false
else
echo ","
fi
IFS=',' read -ra file_list <<< "${groups[$type]}"
echo -n " \"$type\": ["
local first_file=true
for f in "${file_list[@]}"; do
if [[ "$first_file" == "true" ]]; then
first_file=false
else
echo -n ", "
fi
echo -n "\"$f\""
done
echo -n "]"
done
echo ""
echo " }"
echo "}"
else
echo "=== FILE GROUPS (strategy: type) ==="
echo ""
local group_num=1
for type in "${!groups[@]}"; do
IFS=',' read -ra file_list <<< "${groups[$type]}"
echo "Group $group_num: $type (${#file_list[@]} files)"
for file in "${file_list[@]}"; do
echo " $file"
done
echo ""
((group_num++))
done
fi
}
# Group by scope
group_by_scope() {
log "Grouping by scope..."
declare -A groups
local files
mapfile -t files < <(get_changed_files)
if [[ ${#files[@]} -eq 0 ]]; then
echo "No changes detected" >&2
exit 1
fi
# Group files by scope
for file in "${files[@]}"; do
local scope
scope=$(extract_scope "$file")
log " $file -> scope:$scope"
if [[ -z "${groups[$scope]:-}" ]]; then
groups[$scope]="$file"
else
groups[$scope]="${groups[$scope]},$file"
fi
done
# Output results
if [[ "$FORMAT" == "json" ]]; then
echo "{"
echo " \"strategy\": \"scope\","
echo " \"groups\": {"
local first=true
for scope in "${!groups[@]}"; do
if [[ "$first" == "true" ]]; then
first=false
else
echo ","
fi
IFS=',' read -ra file_list <<< "${groups[$scope]}"
echo -n " \"$scope\": ["
local first_file=true
for f in "${file_list[@]}"; do
if [[ "$first_file" == "true" ]]; then
first_file=false
else
echo -n ", "
fi
echo -n "\"$f\""
done
echo -n "]"
done
echo ""
echo " }"
echo "}"
else
echo "=== FILE GROUPS (strategy: scope) ==="
echo ""
local group_num=1
for scope in "${!groups[@]}"; do
IFS=',' read -ra file_list <<< "${groups[$scope]}"
echo "Group $group_num: $scope (${#file_list[@]} files)"
for file in "${file_list[@]}"; do
echo " $file"
done
echo ""
((group_num++))
done
fi
}
# Group by feature (simplified - uses type and scope combination)
group_by_feature() {
log "Grouping by feature..."
declare -A groups
local files
mapfile -t files < <(get_changed_files)
if [[ ${#files[@]} -eq 0 ]]; then
echo "No changes detected" >&2
exit 1
fi
# Group files by type+scope combination
for file in "${files[@]}"; do
local type scope feature
type=$(detect_type "$file")
scope=$(extract_scope "$file")
feature="${type}_${scope}"
log " $file -> feature:$feature"
if [[ -z "${groups[$feature]:-}" ]]; then
groups[$feature]="$file"
else
groups[$feature]="${groups[$feature]},$file"
fi
done
# Output results
if [[ "$FORMAT" == "json" ]]; then
echo "{"
echo " \"strategy\": \"feature\","
echo " \"groups\": {"
local first=true
for feature in "${!groups[@]}"; do
if [[ "$first" == "true" ]]; then
first=false
else
echo ","
fi
IFS=',' read -ra file_list <<< "${groups[$feature]}"
echo -n " \"$feature\": ["
local first_file=true
for f in "${file_list[@]}"; do
if [[ "$first_file" == "true" ]]; then
first_file=false
else
echo -n ", "
fi
echo -n "\"$f\""
done
echo -n "]"
done
echo ""
echo " }"
echo "}"
else
echo "=== FILE GROUPS (strategy: feature) ==="
echo ""
local group_num=1
for feature in "${!groups[@]}"; do
IFS=',' read -ra file_list <<< "${groups[$feature]}"
IFS='_' read -ra feature_parts <<< "$feature"
local type="${feature_parts[0]}"
local scope="${feature_parts[1]}"
echo "Group $group_num: $type($scope) (${#file_list[@]} files)"
for file in "${file_list[@]}"; do
echo " $file"
done
echo ""
((group_num++))
done
fi
}
# Main execution
case "$STRATEGY" in
type)
group_by_type
;;
scope)
group_by_scope
;;
feature)
group_by_feature
;;
*)
echo "Invalid strategy: $STRATEGY" >&2
echo "Valid strategies: type, scope, feature" >&2
exit 2
;;
esac

View File

@@ -0,0 +1,283 @@
#!/usr/bin/env python3
"""
Split Analyzer - Determine if changes should be split
Purpose: Analyze git changes to determine if they should be split into multiple commits
Version: 1.0.0
Usage: ./split-analyzer.py [--verbose] [--threshold N]
Returns:
Exit 0: Should split
Exit 1: Already atomic
Exit 2: Error occurred
Dependencies: git, python3
"""
import sys
import subprocess
import re
import json
from collections import defaultdict
from typing import List, Dict, Tuple, Optional
# Conventional commit types
COMMIT_TYPES = ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'perf', 'ci', 'build']
class SplitAnalyzer:
def __init__(self, threshold: int = 10, verbose: bool = False):
self.threshold = threshold
self.verbose = verbose
self.files = []
self.types = defaultdict(list)
self.scopes = defaultdict(list)
self.concerns = []
def log(self, message: str):
"""Print message if verbose mode enabled"""
if self.verbose:
print(f"[DEBUG] {message}", file=sys.stderr)
def run_git_command(self, args: List[str]) -> str:
"""Execute git command and return output"""
try:
result = subprocess.run(
['git'] + args,
capture_output=True,
text=True,
check=True
)
return result.stdout
except subprocess.CalledProcessError as e:
print(f"Error running git command: {e}", file=sys.stderr)
sys.exit(2)
def get_changed_files(self) -> List[str]:
"""Get list of changed files (staged and unstaged)"""
# Staged files
staged = self.run_git_command(['diff', '--cached', '--name-only'])
# Unstaged files
unstaged = self.run_git_command(['diff', '--name-only'])
files = set()
files.update(filter(None, staged.split('\n')))
files.update(filter(None, unstaged.split('\n')))
return list(files)
def get_file_diff(self, file_path: str) -> str:
"""Get diff for a specific file"""
try:
# Try staged first
diff = self.run_git_command(['diff', '--cached', file_path])
if not diff:
# Try unstaged
diff = self.run_git_command(['diff', file_path])
return diff
except:
return ""
def detect_type_from_diff(self, file_path: str, diff: str) -> str:
"""Detect commit type from file path and diff content"""
# Documentation files
if any(file_path.endswith(ext) for ext in ['.md', '.txt', '.rst', '.adoc']):
return 'docs'
# Test files
if any(pattern in file_path for pattern in ['test/', 'tests/', 'spec/', '__tests__', '.test.', '.spec.']):
return 'test'
# CI/CD files
if any(pattern in file_path for pattern in ['.github/', '.gitlab-ci', 'jenkins', '.circleci']):
return 'ci'
# Build files
if any(file_path.endswith(ext) for ext in ['package.json', 'pom.xml', 'build.gradle', 'Makefile', 'CMakeLists.txt']):
return 'build'
# Analyze diff content
if not diff:
return 'chore'
# Look for new functionality
added_lines = [line for line in diff.split('\n') if line.startswith('+') and not line.startswith('+++')]
# Check for function/class additions (new features)
if any(keyword in ' '.join(added_lines) for keyword in ['function ', 'class ', 'def ', 'const ', 'let ', 'var ']):
if any(keyword in ' '.join(added_lines) for keyword in ['new ', 'add', 'implement', 'create']):
return 'feat'
# Check for bug fix patterns
if any(keyword in ' '.join(added_lines).lower() for keyword in ['fix', 'bug', 'error', 'issue', 'null', 'undefined']):
return 'fix'
# Check for refactoring
if any(keyword in ' '.join(added_lines).lower() for keyword in ['refactor', 'rename', 'move', 'extract']):
return 'refactor'
# Check for performance
if any(keyword in ' '.join(added_lines).lower() for keyword in ['performance', 'optimize', 'cache', 'memoize']):
return 'perf'
# Check for style changes (formatting only)
removed_lines = [line for line in diff.split('\n') if line.startswith('-') and not line.startswith('---')]
if len(added_lines) == len(removed_lines):
# Similar number of additions and deletions might indicate formatting
return 'style'
# Default to feat for new code, chore for modifications
if len(added_lines) > len(removed_lines) * 2:
return 'feat'
return 'chore'
def extract_scope_from_path(self, file_path: str) -> str:
"""Extract scope from file path"""
parts = file_path.split('/')
# Skip common prefixes
skip_prefixes = ['src', 'lib', 'app', 'packages', 'tests', 'test']
for part in parts:
if part not in skip_prefixes and part != '.' and part != '..':
# Remove file extension
scope = part.split('.')[0]
return scope
return 'root'
def detect_mixed_concerns(self) -> List[str]:
"""Detect mixed concerns in changes"""
concerns = []
# Check for feature + unrelated changes
has_feature = 'feat' in self.types
has_refactor = 'refactor' in self.types
has_style = 'style' in self.types
if has_feature and has_refactor:
concerns.append("Feature implementation mixed with refactoring")
if has_feature and has_style:
concerns.append("Feature implementation mixed with style changes")
# Check for test + implementation in separate modules
if 'test' in self.types:
test_scopes = set(self.scopes[scope] for scope in self.scopes if 'test' in scope)
impl_scopes = set(self.scopes[scope] for scope in self.scopes if 'test' not in scope)
if test_scopes != impl_scopes and len(impl_scopes) > 1:
concerns.append("Tests for multiple unrelated implementations")
return concerns
def analyze(self) -> Tuple[bool, str, Dict]:
"""Analyze changes and determine if should split"""
# Get changed files
self.files = self.get_changed_files()
self.log(f"Found {len(self.files)} changed files")
if not self.files:
return False, "No changes detected", {}
# Analyze each file
for file_path in self.files:
self.log(f"Analyzing: {file_path}")
diff = self.get_file_diff(file_path)
file_type = self.detect_type_from_diff(file_path, diff)
scope = self.extract_scope_from_path(file_path)
self.types[file_type].append(file_path)
self.scopes[scope].append(file_path)
self.log(f" Type: {file_type}, Scope: {scope}")
# Check splitting criteria
reasons = []
# Check 1: Multiple types
if len(self.types) > 1:
type_list = ', '.join(self.types.keys())
reasons.append(f"Multiple types detected: {type_list}")
self.log(f"SPLIT REASON: Multiple types: {type_list}")
# Check 2: Multiple scopes
if len(self.scopes) > 1:
scope_list = ', '.join(self.scopes.keys())
reasons.append(f"Multiple scopes detected: {scope_list}")
self.log(f"SPLIT REASON: Multiple scopes: {scope_list}")
# Check 3: Too many files
if len(self.files) > self.threshold:
reasons.append(f"Large change: {len(self.files)} files (threshold: {self.threshold})")
self.log(f"SPLIT REASON: Too many files: {len(self.files)} > {self.threshold}")
# Check 4: Mixed concerns
self.concerns = self.detect_mixed_concerns()
if self.concerns:
reasons.append(f"Mixed concerns: {'; '.join(self.concerns)}")
self.log(f"SPLIT REASON: Mixed concerns detected")
# Prepare detailed metrics
metrics = {
'file_count': len(self.files),
'types_detected': list(self.types.keys()),
'type_counts': {t: len(files) for t, files in self.types.items()},
'scopes_detected': list(self.scopes.keys()),
'scope_counts': {s: len(files) for s, files in self.scopes.items()},
'concerns': self.concerns,
'threshold': self.threshold
}
# Determine result
if reasons:
should_split = True
reason = '; '.join(reasons)
self.log(f"RECOMMENDATION: Should split - {reason}")
else:
should_split = False
reason = "Changes are atomic - single logical unit"
self.log(f"RECOMMENDATION: Already atomic")
return should_split, reason, metrics
def main():
import argparse
parser = argparse.ArgumentParser(description='Analyze if git changes should be split')
parser.add_argument('--verbose', action='store_true', help='Verbose output')
parser.add_argument('--threshold', type=int, default=10, help='File count threshold')
parser.add_argument('--json', action='store_true', help='Output JSON format')
args = parser.parse_args()
analyzer = SplitAnalyzer(threshold=args.threshold, verbose=args.verbose)
should_split, reason, metrics = analyzer.analyze()
if args.json:
# Output JSON format
result = {
'should_split': should_split,
'reason': reason,
'metrics': metrics,
'recommendation': 'split' if should_split else 'atomic'
}
print(json.dumps(result, indent=2))
else:
# Output human-readable format
print(f"Should split: {'YES' if should_split else 'NO'}")
print(f"Reason: {reason}")
print(f"\nMetrics:")
print(f" Files: {metrics['file_count']}")
print(f" Types: {', '.join(metrics['types_detected'])}")
print(f" Scopes: {', '.join(metrics['scopes_detected'])}")
if metrics['concerns']:
print(f" Concerns: {', '.join(metrics['concerns'])}")
# Exit code: 0 = should split, 1 = atomic
sys.exit(0 if should_split else 1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,138 @@
# Operation: Analyze Splitting
Determine if current changes should be split into multiple commits.
## Parameters from $ARGUMENTS
- `verbose:true|false` - Detailed analysis output (default: false)
- `threshold:number` - File count threshold for splitting (default: 10)
## Workflow
### Step 1: Get Current Changes
Invoke Bash to get git status and diff:
```bash
git status --porcelain
git diff --cached --stat
git diff --cached --numstat
```
Capture:
- List of modified files
- Number of files changed
- Additions/deletions per file
### Step 2: Run Split Analyzer
Execute the split analyzer script:
```bash
.claude/plugins/git-commit-assistant/commands/atomic-commit/.scripts/split-analyzer.py
```
The script analyzes:
1. **Type diversity**: Multiple commit types (feat, fix, docs, etc.)
2. **Scope diversity**: Multiple modules/components affected
3. **File count**: Too many files changed
4. **Concern separation**: Mixed concerns in changes
### Step 3: Analyze Results
Parse analyzer output to determine:
**Should Split if:**
- Multiple commit types detected (feat + fix + docs)
- Multiple scopes detected (auth + api + ui)
- File count exceeds threshold (>10 files)
- Mixed concerns detected (feature + unrelated refactoring)
**Keep Together if:**
- Single logical change
- All files serve same purpose
- Interdependent changes
- Reasonable file count (≤10)
### Step 4: Generate Recommendation
Create actionable recommendation:
**If should split:**
```
🔀 SPLIT RECOMMENDED
Reason: Multiple types detected (feat, fix, docs)
Files affected: 15
Detected types: feat (8 files), fix (5 files), docs (2 files)
Recommendation: Split into 3 commits
- Commit 1: feat changes (8 files)
- Commit 2: fix changes (5 files)
- Commit 3: docs changes (2 files)
Next steps:
1. Run: /atomic-commit group strategy:type
2. Review groupings
3. Run: /atomic-commit suggest
```
**If already atomic:**
```
✅ ATOMIC COMMIT
Changes represent single logical unit:
- Single type: feat
- Single scope: auth
- File count: 5 files
- Logically cohesive: OAuth implementation
No splitting needed. Proceed with commit.
```
## Output Format
Return structured analysis:
```yaml
should_split: true|false
reason: "Primary reason for recommendation"
metrics:
file_count: number
types_detected: [list]
scopes_detected: [list]
concerns: [list]
recommendation: "Action to take"
next_steps: [ordered list of commands]
```
## Error Handling
- **No changes**: "No staged or unstaged changes detected. Make changes first."
- **Script failure**: "Analysis failed. Check git repository status."
- **Invalid parameters**: "Invalid parameter. Use verbose:true or threshold:number"
## MCP Tool Integration
Use workspace-mcp for script execution:
- Spawn isolated workspace if needed
- Execute analyzer script
- Parse and return results
- Clean up temporary files
## Examples
**Example 1: Large feature**
```bash
/atomic-commit analyze
→ Should split: 15 files, multiple types
```
**Example 2: Bug fix**
```bash
/atomic-commit analyze verbose:true
→ Atomic: 3 files, single fix
```
**Example 3: Custom threshold**
```bash
/atomic-commit analyze threshold:5
→ Should split: 8 files exceeds threshold
```

View File

@@ -0,0 +1,352 @@
# Operation: Create Sequence
Generate executable commit sequence plan.
## Parameters from $ARGUMENTS
- `groups:string` - Group specifications (e.g., "feat:5,fix:2,docs:1")
- `output:plan|script` - Output format (default: plan)
- `auto_stage:true|false` - Auto-stage files (default: false)
## Workflow
### Step 1: Get Commit Suggestions
If not provided, invoke commit suggestion:
```bash
/atomic-commit suggest
```
Parse suggestions to get commit details.
### Step 2: Analyze Dependencies
Execute dependency checker:
```bash
.claude/plugins/git-commit-assistant/commands/atomic-commit/.scripts/dependency-checker.sh
```
Identify dependencies between commits:
- File dependencies (imports, references)
- Logical dependencies (feature order)
- Test dependencies (code → tests)
Build dependency graph:
```
feat(auth) → test(auth) → docs(auth)
fix(api) → test(api)
```
### Step 3: Determine Commit Order
Apply ordering rules:
**Priority 1: Dependencies**
- Commits with dependencies come first
- Tests after implementation
- Docs after features
**Priority 2: Type Order**
Standard order:
1. feat (features enable other changes)
2. fix (fixes should be applied early)
3. refactor (restructuring before additions)
4. perf (performance after stability)
5. test (tests after implementation)
6. docs (documentation last)
7. chore (housekeeping last)
**Priority 3: Scope Grouping**
- Related scopes together
- Independent scopes can be parallel
**Priority 4: Impact**
- High-impact changes first
- Low-risk changes can be later
### Step 4: Generate Sequence Plan
Create executable sequence:
```
📋 COMMIT SEQUENCE PLAN
Execution Order: 3 commits in sequence
┌─────────────────────────────────────────────┐
│ COMMIT 1: feat(auth) │
│ Files: 8 │
│ Dependencies: None │
│ Can execute: ✅ Now │
└─────────────────────────────────────────────┘
Message:
feat(auth): implement OAuth 2.0 authentication
Add complete OAuth 2.0 authentication flow with
support for multiple providers (GitHub, Google).
Files to stage:
git add src/auth/oauth.ts
git add src/auth/tokens.ts
git add src/auth/providers/github.ts
git add src/auth/providers/google.ts
git add src/config/oauth.config.ts
git add src/types/auth.types.ts
git add tests/auth/oauth.test.ts
git add tests/auth/tokens.test.ts
Command:
git commit -m "feat(auth): implement OAuth 2.0 authentication" -m "Add complete OAuth 2.0 authentication flow with support for multiple providers (GitHub, Google). Includes token management, refresh handling, and comprehensive test coverage."
─────────────────────────────────────────────
┌─────────────────────────────────────────────┐
│ COMMIT 2: fix(api) │
│ Files: 3 │
│ Dependencies: None │
│ Can execute: ✅ After commit 1 │
└─────────────────────────────────────────────┘
Message:
fix(api): handle null pointer in user endpoint
Fix null pointer exception when user profile
is incomplete.
Files to stage:
git add src/api/endpoints.ts
git add src/api/validation.ts
git add tests/api.test.ts
Command:
git commit -m "fix(api): handle null pointer in user endpoint" -m "Fix null pointer exception when user profile is incomplete. Add validation to check for required fields before access."
─────────────────────────────────────────────
┌─────────────────────────────────────────────┐
│ COMMIT 3: docs │
│ Files: 2 │
│ Dependencies: Commit 1 (feat(auth)) │
│ Can execute: ✅ After commit 1 │
└─────────────────────────────────────────────┘
Message:
docs: add OAuth authentication guide
Add comprehensive documentation for OAuth 2.0
authentication setup and usage.
Files to stage:
git add README.md
git add docs/authentication.md
Command:
git commit -m "docs: add OAuth authentication guide" -m "Add comprehensive documentation for OAuth 2.0 authentication setup and usage. Includes configuration examples and provider setup."
─────────────────────────────────────────────
Summary:
Total commits: 3
Total files: 13
Execution time: ~3 minutes
All dependencies resolved: ✅ Yes
```
### Step 5: Generate Script (if output:script)
Create executable bash script:
```bash
#!/bin/bash
# Atomic commit sequence
# Generated: 2025-10-13
# Total commits: 3
set -e # Exit on error
echo "🚀 Starting commit sequence..."
# Commit 1: feat(auth)
echo ""
echo "📝 Commit 1/3: feat(auth)"
git add src/auth/oauth.ts
git add src/auth/tokens.ts
git add src/auth/providers/github.ts
git add src/auth/providers/google.ts
git add src/config/oauth.config.ts
git add src/types/auth.types.ts
git add tests/auth/oauth.test.ts
git add tests/auth/tokens.test.ts
git commit -m "feat(auth): implement OAuth 2.0 authentication" -m "Add complete OAuth 2.0 authentication flow with support for multiple providers (GitHub, Google). Includes token management, refresh handling, and comprehensive test coverage."
echo "✅ Commit 1 complete"
# Commit 2: fix(api)
echo ""
echo "📝 Commit 2/3: fix(api)"
git add src/api/endpoints.ts
git add src/api/validation.ts
git add tests/api.test.ts
git commit -m "fix(api): handle null pointer in user endpoint" -m "Fix null pointer exception when user profile is incomplete. Add validation to check for required fields before access."
echo "✅ Commit 2 complete"
# Commit 3: docs
echo ""
echo "📝 Commit 3/3: docs"
git add README.md
git add docs/authentication.md
git commit -m "docs: add OAuth authentication guide" -m "Add comprehensive documentation for OAuth 2.0 authentication setup and usage. Includes configuration examples and provider setup."
echo "✅ Commit 3 complete"
echo ""
echo "🎉 All commits completed successfully!"
echo "Total commits: 3"
echo "Total files: 13"
```
### Step 6: Validate Sequence
Check sequence for issues:
- All files accounted for
- No file appears in multiple commits
- Dependencies resolved
- Logical ordering
- Commands are valid
### Step 7: Provide Execution Options
Offer execution methods:
**Option 1: Manual execution**
Copy-paste commands one by one
**Option 2: Script execution**
Save script and run: `bash commit-sequence.sh`
**Option 3: Interactive**
Execute with guidance: `/atomic-commit interactive`
**Option 4: Agent execution**
Let agent execute sequence
## Output Format
Return structured sequence:
```yaml
sequence:
- commit_id: 1
order: 1
type: feat
scope: auth
message:
subject: "Brief description"
body: "Detailed explanation"
files: [list]
dependencies: []
can_execute: "now|after:<id>"
commands:
stage: [git add commands]
commit: "git commit command"
- commit_id: 2
order: 2
...
summary:
total_commits: number
total_files: number
estimated_time: "minutes"
dependencies_resolved: true|false
execution:
manual: "Copy commands"
script: "Use generated script"
interactive: "/atomic-commit interactive"
agent: "Let agent execute"
script: "Bash script content (if output:script)"
```
## Error Handling
- **No suggestions**: "No commit suggestions. Run: /atomic-commit suggest"
- **Circular dependencies**: "Cannot resolve: circular dependency detected"
- **Invalid group spec**: "Invalid group specification: {spec}"
- **File conflicts**: "File appears in multiple commits: {file}"
## Dependency Analysis
The dependency checker identifies:
**Import dependencies:**
- File A imports from File B
- B must be committed before A
**Test dependencies:**
- Test file tests code file
- Code must be committed before tests
**Logical dependencies:**
- Feature depends on another feature
- Base feature first, dependent after
**Type dependencies:**
- Fixes may depend on features
- Docs depend on implementations
## Execution Planning
### Commit Planner Script
The commit planner creates optimal sequence:
```python
def create_sequence(suggestions, dependencies):
# Build dependency graph
graph = build_graph(suggestions, dependencies)
# Topological sort for dependency order
ordered = topological_sort(graph)
# Apply type priority within independent groups
prioritized = apply_type_priority(ordered)
# Group by scope for related commits
sequenced = group_by_scope(prioritized)
return sequenced
```
## Examples
**Example 1: Auto-generate plan**
```bash
/atomic-commit sequence
→ Creates sequence from current suggestions
```
**Example 2: Custom groups**
```bash
/atomic-commit sequence groups:"feat:5,fix:2,docs:1"
→ Creates sequence for specified groups
```
**Example 3: Generate script**
```bash
/atomic-commit sequence output:script
→ Outputs executable bash script
```
**Example 4: Auto-stage**
```bash
/atomic-commit sequence auto_stage:true
→ Automatically stages files during execution
```
## Integration Notes
This operation:
1. Uses results from `suggest-commits`
2. Requires `dependency-checker.sh` script
3. Uses `commit-planner.py` for optimization
4. Feeds into execution workflows
5. Can output multiple formats
The sequence ensures atomic commits in optimal order.

View File

@@ -0,0 +1,209 @@
# Operation: Group Files
Group related files together for atomic commits.
## Parameters from $ARGUMENTS
- `strategy:type|scope|feature` - Grouping strategy (default: type)
- `show:all|summary` - Output detail level (default: summary)
## Workflow
### Step 1: Get Changed Files
Invoke Bash to get file list:
```bash
git status --porcelain
git diff --cached --name-only
git diff --name-only
```
Capture complete list of changed files.
### Step 2: Execute File Grouper
Run the file grouper script with selected strategy:
```bash
.claude/plugins/git-commit-assistant/commands/atomic-commit/.scripts/file-grouper.sh <strategy>
```
### Step 3: Parse Grouping Results
Process grouper output based on strategy:
**Strategy: type**
Groups files by commit type:
- `feat`: New features or enhancements
- `fix`: Bug fixes
- `docs`: Documentation changes
- `style`: Formatting, whitespace
- `refactor`: Code restructuring
- `test`: Test additions or changes
- `chore`: Build, dependencies, tooling
**Strategy: scope**
Groups files by module/component:
- Extract scope from file path
- Group by common directory structure
- Identify module boundaries
- Example: `auth/`, `api/`, `ui/`, `utils/`
**Strategy: feature**
Groups files by related functionality:
- Analyze dependencies between files
- Group interdependent changes
- Identify feature boundaries
- Requires dependency analysis
### Step 4: Validate Groups
Check each group for atomicity:
- Single logical purpose
- Reasonable size (≤10 files per group)
- Clear scope boundary
- Independent from other groups
### Step 5: Generate Output
Create structured grouping report:
**Summary format:**
```
📦 FILE GROUPS (strategy: type)
Group 1: feat (8 files)
src/auth/oauth.ts
src/auth/tokens.ts
src/config/oauth.config.ts
...
Group 2: fix (3 files)
src/api/endpoints.ts
src/api/validation.ts
tests/api.test.ts
Group 3: docs (2 files)
README.md
docs/authentication.md
Total: 3 groups, 13 files
```
**Detailed format (show:all):**
```
📦 GROUP 1: feat (8 files)
Files:
✓ src/auth/oauth.ts (+145, -0)
✓ src/auth/tokens.ts (+78, -0)
✓ src/auth/providers/github.ts (+95, -0)
✓ src/auth/providers/google.ts (+92, -0)
✓ src/config/oauth.config.ts (+34, -0)
✓ src/types/auth.types.ts (+56, -0)
✓ tests/auth/oauth.test.ts (+123, -0)
✓ tests/auth/tokens.test.ts (+67, -0)
Scope: auth
Purpose: OAuth 2.0 implementation
Dependencies: None
Atomic: ✅ Yes
Suggested commit:
feat(auth): implement OAuth 2.0 authentication
```
## Output Format
Return structured groupings:
```yaml
strategy: type|scope|feature
groups:
- id: 1
type: feat|fix|docs|etc
scope: module_name
files: [list]
stats:
file_count: number
additions: number
deletions: number
atomic: true|false
suggested_message: "commit message"
summary:
total_groups: number
total_files: number
ready_for_commit: true|false
```
## Error Handling
- **No changes**: "No files to group. Make changes first."
- **Invalid strategy**: "Invalid strategy. Use: type, scope, or feature"
- **Grouping failed**: "Could not group files. Check git status."
- **Too many groups**: "Warning: 10+ groups detected. Consider broader grouping."
## Grouping Strategies Explained
### Type-based Grouping
Groups files by conventional commit type. Best for:
- Mixed-type changes
- Clear type boundaries
- Standard workflow
Algorithm:
1. Analyze diff content for each file
2. Detect commit type from changes
3. Group files of same type
4. Validate each group
### Scope-based Grouping
Groups files by module/component. Best for:
- Changes across multiple modules
- Modular codebase structure
- Component isolation
Algorithm:
1. Extract directory structure
2. Identify module boundaries
3. Group files by module
4. Validate scope coherence
### Feature-based Grouping
Groups files by related functionality. Best for:
- Complex feature implementation
- Interdependent changes
- Logical feature units
Algorithm:
1. Analyze file dependencies
2. Build dependency graph
3. Group connected components
4. Validate feature boundaries
## Examples
**Example 1: Type grouping**
```bash
/atomic-commit group strategy:type
→ Groups: feat (5), fix (2), docs (1)
```
**Example 2: Scope grouping**
```bash
/atomic-commit group strategy:scope show:all
→ Detailed groups by module
```
**Example 3: Feature grouping**
```bash
/atomic-commit group strategy:feature
→ Groups by related functionality
```
## Integration Notes
This operation is typically called:
1. After `analyze-splitting` recommends splitting
2. Before `suggest-commits` generates messages
3. As part of `interactive-split` workflow
Results feed into commit suggestion and sequence planning.

View File

@@ -0,0 +1,455 @@
# Operation: Interactive Split
Interactive guided workflow for splitting commits.
## Parameters from $ARGUMENTS
- `step:number` - Start at specific step (default: 1)
- `auto:true|false` - Auto-advance through steps (default: false)
## Workflow
This operation provides step-by-step guided experience through the entire atomic commit splitting process.
### Step 1: Analyze Current Changes
**Display:**
```
🔍 STEP 1: ANALYZE CHANGES
─────────────────────────────────────
Analyzing your current changes...
```
**Execute:**
```bash
/atomic-commit analyze verbose:true
```
**Parse results and show:**
```
Current status:
Files changed: 13
Types detected: feat, fix, docs
Scopes detected: auth, api
Recommendation: Should split
Analysis:
⚠️ Multiple types detected (feat, fix, docs)
⚠️ Multiple scopes detected (auth, api)
✅ File count manageable (13 files)
Recommendation:
Split into multiple atomic commits for:
- Easier code review
- Safer reverts
- Clearer history
```
**Prompt user:**
```
Options:
[1] Continue to grouping (recommended)
[2] Review analysis details
[3] Exit (keep as one commit)
Your choice:
```
### Step 2: Group Files
**Display:**
```
📦 STEP 2: GROUP FILES
─────────────────────────────────────
How would you like to group files?
```
**Show grouping strategies:**
```
Strategies:
[1] By type (feat, fix, docs)
Best for: Mixed-type changes
Groups: ~3 groups expected
[2] By scope (auth, api, docs)
Best for: Changes across modules
Groups: ~3 groups expected
[3] By feature (related functionality)
Best for: Complex feature work
Groups: ~2-4 groups expected
[4] Auto-select (recommended)
Let me choose the best strategy
Your choice:
```
**Execute grouping:**
```bash
/atomic-commit group strategy:<selected> show:all
```
**Show results:**
```
Groups created:
📦 Group 1: feat(auth) - 8 files
Purpose: OAuth 2.0 implementation
Files: src/auth/*, tests/auth/*
Atomic: ✅ Yes
📦 Group 2: fix(api) - 3 files
Purpose: Null pointer fix
Files: src/api/*, tests/api/*
Atomic: ✅ Yes
📦 Group 3: docs - 2 files
Purpose: Authentication guide
Files: README.md, docs/authentication.md
Atomic: ✅ Yes
All groups are atomic: ✅ Yes
```
**Prompt user:**
```
Options:
[1] Continue to commit suggestions
[2] Adjust grouping
[3] Try different strategy
[4] Back to step 1
Your choice:
```
### Step 3: Review Commit Suggestions
**Display:**
```
💬 STEP 3: COMMIT SUGGESTIONS
─────────────────────────────────────
Generating commit messages...
```
**Execute:**
```bash
/atomic-commit suggest format:conventional include_body:true
```
**Show suggestions with review interface:**
```
Commit 1 of 3:
┌─────────────────────────────────────────────┐
│ feat(auth): implement OAuth 2.0 auth │
│ │
│ Add complete OAuth 2.0 authentication flow │
│ with support for multiple providers │
│ (GitHub, Google). Includes token │
│ management, refresh handling, and │
│ comprehensive test coverage. │
│ │
│ Files: 8 │
│ Atomic: ✅ Yes │
└─────────────────────────────────────────────┘
Options for this commit:
[a] Accept as-is
[e] Edit message
[s] Skip this commit
[v] View files in commit
Your choice:
```
**If user edits, provide interface:**
```
Edit commit message:
Subject (max 50 chars):
> feat(auth): implement OAuth 2.0 authentication
Body (optional):
> Add complete OAuth 2.0 authentication flow with
> support for multiple providers (GitHub, Google).
> Includes token management, refresh handling, and
> comprehensive test coverage.
Footer (optional):
>
[s] Save [c] Cancel [r] Reset
```
**After reviewing all commits:**
```
All commits reviewed:
✅ Commit 1: feat(auth) - Accepted
✅ Commit 2: fix(api) - Edited
✅ Commit 3: docs - Accepted
Options:
[1] Continue to sequence planning
[2] Review commits again
[3] Back to grouping
Your choice:
```
### Step 4: Create Commit Sequence
**Display:**
```
📋 STEP 4: COMMIT SEQUENCE
─────────────────────────────────────
Creating execution plan...
```
**Execute:**
```bash
/atomic-commit sequence output:plan
```
**Show sequence:**
```
Commit Order:
1⃣ feat(auth) - 8 files
Dependencies: None
Can commit: ✅ Now
2⃣ fix(api) - 3 files
Dependencies: None
Can commit: ✅ After commit 1
3⃣ docs - 2 files
Dependencies: Commit 1 (documents auth)
Can commit: ✅ After commit 1
Execution time: ~3 minutes
All dependencies resolved: ✅ Yes
```
**Prompt user:**
```
Options:
[1] Show execution commands
[2] Generate script
[3] Execute with guidance (recommended)
[4] Let agent execute
[5] Back to step 3
Your choice:
```
### Step 5: Execute Commits
**If user chooses guided execution:**
```
🚀 STEP 5: EXECUTE COMMITS
─────────────────────────────────────
Executing commit 1 of 3...
┌─────────────────────────────────────────────┐
│ COMMIT 1: feat(auth) │
└─────────────────────────────────────────────┘
Staging files:
✅ src/auth/oauth.ts
✅ src/auth/tokens.ts
✅ src/auth/providers/github.ts
✅ src/auth/providers/google.ts
✅ src/config/oauth.config.ts
✅ src/types/auth.types.ts
✅ tests/auth/oauth.test.ts
✅ tests/auth/tokens.test.ts
Creating commit:
Message: feat(auth): implement OAuth 2.0 authentication
Files: 8
Status: ✅ Success
Commit created: a1b2c3d
─────────────────────────────────────────────
Proceed to commit 2?
[y] Yes, continue
[r] Review what was just committed
[p] Pause (save progress)
[a] Abort remaining commits
Your choice:
```
**Continue through all commits...**
**Final summary:**
```
✨ COMPLETE: ALL COMMITS CREATED
─────────────────────────────────────
Summary:
✅ Commit 1: feat(auth) - a1b2c3d
✅ Commit 2: fix(api) - b2c3d4e
✅ Commit 3: docs - c3d4e5f
Total commits: 3
Total files: 13
Time elapsed: 2m 45s
Your git history is now atomic! 🎉
Next steps:
• Review commits: git log -3
• Push commits: git push
• Create PR: gh pr create
```
## Progress Tracking
The interactive workflow maintains state:
```yaml
session:
step: 1-5
completed_steps: [list]
current_groups: [...]
current_suggestions: [...]
current_sequence: [...]
created_commits: [...]
can_resume: true|false
```
Users can:
- Pause at any step
- Resume from where they left off
- Go back to previous steps
- Skip steps if desired
## Navigation Commands
Throughout the interactive workflow:
- **[n]** Next
- **[b]** Back
- **[h]** Help
- **[q]** Quit
- **[r]** Restart
- **[s]** Status
## Error Handling
**During analysis:**
- No changes detected → Guide to make changes first
- Git errors → Provide troubleshooting steps
**During grouping:**
- Cannot group files → Suggest manual review
- Grouping unclear → Offer alternative strategies
**During suggestions:**
- Cannot generate message → Provide template
- User unsure → Offer examples and guidance
**During execution:**
- Staging fails → Show file status and retry
- Commit fails → Preserve work, show error
- Partial completion → Save progress, allow resume
## Auto Mode
When `auto:true` is specified:
- Automatically selects recommended options
- Shows results at each step
- Pauses for user confirmation at commit execution
- Useful for experienced users who trust recommendations
Example:
```bash
/atomic-commit interactive auto:true
→ Analyzes → Groups by best strategy → Suggests → Creates sequence → Waits for confirmation
```
## Resume Capability
If interrupted, users can resume:
```bash
/atomic-commit interactive step:3
→ Resumes from step 3 (commit suggestions)
```
Session data is preserved across invocations.
## Help System
At any point, user can type `[h]` for context-sensitive help:
```
📚 HELP - Step 2: Grouping
─────────────────────────────────────
Grouping strategies:
• Type: Groups by commit type (feat, fix, docs)
• Scope: Groups by module (auth, api, ui)
• Feature: Groups by related functionality
Tips:
• Choose "type" for mixed-type changes
• Choose "scope" for module-based work
• Choose "feature" for complex features
• Choose "auto" if unsure
Examples:
Type grouping: feat files | fix files | docs files
Scope grouping: auth files | api files | ui files
Press any key to continue...
```
## Output Format
The interactive workflow uses:
- Clear step indicators
- Progress tracking
- Visual separators
- Emoji for status
- Color coding (if supported)
- Consistent option menus
- Helpful prompts
## Examples
**Example 1: Full interactive workflow**
```bash
/atomic-commit interactive
→ Guides through all steps with prompts
```
**Example 2: Auto mode**
```bash
/atomic-commit interactive auto:true
→ Auto-selects best options, confirms before execution
```
**Example 3: Resume from step**
```bash
/atomic-commit interactive step:3
→ Resumes from commit suggestions step
```
## Integration with Agent
The commit-assistant agent can:
1. Recommend interactive mode for complex splits
2. Guide users through steps
3. Answer questions during workflow
4. Explain recommendations
5. Execute commits on behalf of user (with approval)
The interactive workflow provides the best user experience for learning atomic commit practices.

View File

@@ -0,0 +1,99 @@
---
description: Guide splitting large commits into atomic, focused commits
---
# Atomic Commit Skill
Help users create atomic commits - one logical change per commit.
## Available Operations
- **analyze** → Determine if changes should be split
- **group** → Group related files together
- **suggest** → Recommend commit breakdown
- **sequence** → Generate commit sequence plan
- **interactive** → Interactive splitting guidance
## Usage Examples
```bash
# Analyze if splitting needed
/atomic-commit analyze
# Group files by type and scope
/atomic-commit group strategy:type
# Suggest commit breakdown
/atomic-commit suggest
# Create commit sequence
/atomic-commit sequence groups:"feat:5,fix:2,docs:1"
# Interactive splitting
/atomic-commit interactive
```
## Atomic Commit Principles
**One logical change per commit:**
- ✅ Single type (all feat, or all fix)
- ✅ Single scope (all auth, or all api)
- ✅ Reasonable size (≤10 files)
- ✅ Logically cohesive
- ✅ Can be reverted independently
## Router Logic
Parse $ARGUMENTS to determine operation:
1. Extract first word as operation name
2. Parse remaining parameters as key:value pairs
3. Route to appropriate operation file
4. Handle errors gracefully
**Available operations:**
- `analyze` → Read `commands/atomic-commit/analyze-splitting.md`
- `group` → Read `commands/atomic-commit/group-files.md`
- `suggest` → Read `commands/atomic-commit/suggest-commits.md`
- `sequence` → Read `commands/atomic-commit/create-sequence.md`
- `interactive` → Read `commands/atomic-commit/interactive-split.md`
**Error Handling:**
- Unknown operation → List available operations with examples
- No changes detected → Prompt user to make changes first
- Already atomic → Confirm no split needed
**Base directory**: `commands/atomic-commit/`
**Current request**: $ARGUMENTS
### Processing Steps
1. Parse operation from $ARGUMENTS
2. Validate operation exists
3. Extract parameters
4. Read corresponding operation file
5. Execute operation with parameters
6. Return results with actionable guidance
### Example Flows
**Quick analysis:**
```
/atomic-commit analyze
→ Analyzes current changes
→ Returns split recommendation with reasoning
```
**Interactive workflow:**
```
/atomic-commit interactive
→ Guides step-by-step through splitting
→ Shows groupings, suggests commits, creates plan
```
**Custom grouping:**
```
/atomic-commit group strategy:scope
→ Groups files by module/scope
→ Returns groupings for review
```

View File

@@ -0,0 +1,272 @@
# Operation: Suggest Commits
Generate commit message suggestions for file groups.
## Parameters from $ARGUMENTS
- `groups:string` - Comma-separated group IDs to process (default: all)
- `format:conventional|simple` - Message format (default: conventional)
- `include_body:true|false` - Include commit body (default: true)
## Workflow
### Step 1: Get File Groups
If groups not provided, invoke file grouping:
```bash
/atomic-commit group strategy:type
```
Parse grouping results to identify distinct commit groups.
### Step 2: Analyze Each Group
For each file group:
1. Get file diffs
2. Analyze changes
3. Identify commit type
4. Determine scope
5. Extract key changes
Invoke Bash to get detailed diffs:
```bash
git diff --cached <files>
git diff <files>
```
### Step 3: Generate Commit Messages
For each group, create commit message following conventions:
**Conventional format:**
```
<type>(<scope>): <subject>
<body>
<footer>
```
**Components:**
- **type**: feat|fix|docs|style|refactor|test|chore
- **scope**: Module or component affected
- **subject**: Brief description (≤50 chars)
- **body**: Detailed explanation (optional)
- **footer**: Breaking changes, issue references (optional)
**Example:**
```
feat(auth): implement OAuth 2.0 authentication
Add OAuth 2.0 authentication flow with support for:
- GitHub provider
- Google provider
- Token management
- Refresh token handling
Includes comprehensive test coverage.
```
### Step 4: Validate Messages
Check each message for:
- Type correctness
- Scope accuracy
- Subject clarity
- Body completeness
- Footer requirements
Apply commit message best practices:
- Subject in imperative mood
- Subject ≤50 characters
- Body wrapped at 72 characters
- Clear explanation of "why"
- Reference related issues
### Step 5: Rank Suggestions
Order commits by:
1. **Dependency order**: Dependencies first
2. **Type priority**: feat → fix → refactor → docs → chore
3. **Scope cohesion**: Related scopes together
4. **Logical flow**: Natural progression
### Step 6: Generate Output
Create comprehensive suggestion report:
```
💬 COMMIT SUGGESTIONS
Commit 1 of 3: feat(auth) - 8 files
─────────────────────────────────────
feat(auth): implement OAuth 2.0 authentication
Add complete OAuth 2.0 authentication flow with
support for multiple providers (GitHub, Google).
Includes token management, refresh handling, and
comprehensive test coverage.
Files:
• src/auth/oauth.ts
• src/auth/tokens.ts
• src/auth/providers/github.ts
• src/auth/providers/google.ts
• src/config/oauth.config.ts
• src/types/auth.types.ts
• tests/auth/oauth.test.ts
• tests/auth/tokens.test.ts
Atomic: ✅ Yes
Ready: ✅ Can commit
─────────────────────────────────────
Commit 2 of 3: fix(api) - 3 files
─────────────────────────────────────
fix(api): handle null pointer in user endpoint
Fix null pointer exception when user profile is
incomplete. Add validation to check for required
fields before access.
Files:
• src/api/endpoints.ts
• src/api/validation.ts
• tests/api.test.ts
Atomic: ✅ Yes
Ready: ✅ Can commit
─────────────────────────────────────
Commit 3 of 3: docs - 2 files
─────────────────────────────────────
docs: add OAuth authentication guide
Add comprehensive documentation for OAuth 2.0
authentication setup and usage. Includes
configuration examples and provider setup.
Files:
• README.md
• docs/authentication.md
Atomic: ✅ Yes
Ready: ✅ Can commit
─────────────────────────────────────
Summary:
Total commits: 3
Total files: 13
All atomic: ✅ Yes
Ready to commit: ✅ Yes
Next steps:
1. Review suggestions above
2. Run: /atomic-commit sequence (to create plan)
3. Or manually stage and commit each group
```
## Output Format
Return structured suggestions:
```yaml
suggestions:
- commit_id: 1
type: feat|fix|docs|etc
scope: module_name
subject: "Brief description"
body: "Detailed explanation"
footer: "Breaking changes, refs"
files: [list]
stats:
file_count: number
additions: number
deletions: number
atomic: true|false
ready: true|false
dependencies: [commit_ids]
summary:
total_commits: number
total_files: number
all_atomic: true|false
ready_to_commit: true|false
next_steps: [ordered actions]
```
## Message Generation Rules
### Type Detection
- `feat`: New features, capabilities, enhancements
- `fix`: Bug fixes, error corrections
- `docs`: Documentation only
- `style`: Formatting, whitespace, semicolons
- `refactor`: Code restructuring without behavior change
- `test`: Test additions or modifications
- `chore`: Build, dependencies, tooling
- `perf`: Performance improvements
- `ci`: CI/CD configuration changes
### Scope Extraction
1. Analyze file paths for common prefix
2. Identify module name from structure
3. Use meaningful component names
4. Keep scopes consistent across project
### Subject Crafting
1. Use imperative mood: "add" not "added"
2. No capitalization of first letter
3. No period at the end
4. Be specific but concise
5. Maximum 50 characters
### Body Creation
1. Explain "why" not "what"
2. Provide context for changes
3. Wrap at 72 characters
4. Use bullet points for lists
5. Include relevant details
### Footer Guidelines
- **Breaking changes**: Start with "BREAKING CHANGE:"
- **Issue references**: "Fixes #123", "Closes #456"
- **Related issues**: "Related to #789"
## Error Handling
- **No groups**: "No file groups found. Run: /atomic-commit group"
- **Invalid group**: "Group ID not found: {id}"
- **Cannot analyze**: "Failed to analyze changes for group {id}"
- **Message generation failed**: "Could not generate message. Check diffs."
## Examples
**Example 1: All groups**
```bash
/atomic-commit suggest
→ Suggests commits for all detected groups
```
**Example 2: Specific groups**
```bash
/atomic-commit suggest groups:"1,3"
→ Suggests commits only for groups 1 and 3
```
**Example 3: Simple format**
```bash
/atomic-commit suggest format:simple include_body:false
→ Simple messages without detailed bodies
```
## Integration Notes
This operation:
1. Uses results from `group-files`
2. Feeds into `create-sequence`
3. Is part of `interactive-split` workflow
4. Can be used standalone for quick suggestions
Suggestions are not final - user can review and modify before committing.

View File

@@ -0,0 +1,186 @@
#!/usr/bin/env python3
# Script: atomicity-checker.py
# Purpose: Assess if changes form an atomic commit or should be split
# Author: Git Commit Assistant Plugin
# Version: 1.0.0
#
# Usage:
# git diff HEAD | ./atomicity-checker.py
#
# Returns:
# JSON: {"atomic": true/false, "reasoning": "...", "recommendations": [...]}
#
# Exit Codes:
# 0 - Success
# 1 - No input
# 2 - Analysis error
import sys
import re
import json
from collections import defaultdict
def analyze_atomicity(diff_content):
"""
Analyze if changes are atomic (single logical unit).
Criteria for atomic:
- Single type (all feat, or all fix, etc.)
- Single scope (all in one module)
- Logically cohesive
- Reasonable file count (<= 10)
"""
lines = diff_content.split('\n')
# Track changes
files = []
types_detected = set()
scopes_detected = set()
file_changes = defaultdict(lambda: {'additions': 0, 'deletions': 0})
current_file = None
for line in lines:
# Track files
if line.startswith('+++ '):
file_path = line[4:].strip()
if file_path != '/dev/null' and file_path.startswith('b/'):
file_path = file_path[2:]
files.append(file_path)
current_file = file_path
# Detect type from file
if '.test.' in file_path or '.spec.' in file_path:
types_detected.add('test')
elif file_path.endswith('.md'):
types_detected.add('docs')
elif 'package.json' in file_path or 'pom.xml' in file_path:
types_detected.add('build')
elif '.github/workflows' in file_path or '.gitlab-ci' in file_path:
types_detected.add('ci')
# Detect scope from path
match = re.match(r'src/([^/]+)/', file_path)
if match:
scopes_detected.add(match.group(1))
# Count line changes
if current_file:
if line.startswith('+') and not line.startswith('+++'):
file_changes[current_file]['additions'] += 1
elif line.startswith('-') and not line.startswith('---'):
file_changes[current_file]['deletions'] += 1
# Detect types from content
if line.startswith('+'):
if 'export function' in line or 'export class' in line:
types_detected.add('feat')
elif 'fix' in line.lower() or 'error' in line.lower():
types_detected.add('fix')
elif 'refactor' in line.lower() or 'rename' in line.lower():
types_detected.add('refactor')
# Calculate metrics
total_files = len(files)
total_additions = sum(f['additions'] for f in file_changes.values())
total_deletions = sum(f['deletions'] for f in file_changes.values())
total_changes = total_additions + total_deletions
num_types = len(types_detected)
num_scopes = len(scopes_detected)
# Atomicity checks
checks = {
'single_type': num_types <= 1,
'single_scope': num_scopes <= 1,
'reasonable_file_count': total_files <= 10,
'reasonable_change_size': total_changes <= 500,
'cohesive': num_types <= 1 and num_scopes <= 1
}
# Determine atomicity
is_atomic = all([
checks['single_type'] or num_types == 0,
checks['single_scope'] or num_scopes == 0,
checks['reasonable_file_count']
])
# Build reasoning
if is_atomic:
reasoning = f"Changes are atomic: {total_files} files, "
if num_types <= 1:
reasoning += f"single type ({list(types_detected)[0] if types_detected else 'unknown'}), "
if num_scopes <= 1:
reasoning += f"single scope ({list(scopes_detected)[0] if scopes_detected else 'root'}). "
reasoning += "Forms a cohesive logical unit."
else:
issues = []
if num_types > 1:
issues.append(f"multiple types ({', '.join(types_detected)})")
if num_scopes > 1:
issues.append(f"multiple scopes ({', '.join(list(scopes_detected)[:3])})")
if total_files > 10:
issues.append(f"many files ({total_files})")
reasoning = f"Changes are NOT atomic: {', '.join(issues)}. Should be split into focused commits."
# Generate recommendations if not atomic
recommendations = []
if not is_atomic:
if num_types > 1:
recommendations.append({
'strategy': 'Split by type',
'description': f"Create separate commits for each type: {', '.join(types_detected)}"
})
if num_scopes > 1:
recommendations.append({
'strategy': 'Split by scope',
'description': f"Create separate commits for each module: {', '.join(list(scopes_detected)[:3])}"
})
if total_files > 15:
recommendations.append({
'strategy': 'Split by feature',
'description': 'Break into smaller logical units (5-10 files per commit)'
})
return {
'atomic': is_atomic,
'reasoning': reasoning,
'checks': checks,
'metrics': {
'total_files': total_files,
'total_additions': total_additions,
'total_deletions': total_deletions,
'total_changes': total_changes,
'types_detected': list(types_detected),
'scopes_detected': list(scopes_detected),
'num_types': num_types,
'num_scopes': num_scopes
},
'recommendations': recommendations if not is_atomic else []
}
def main():
diff_content = sys.stdin.read()
if not diff_content or not diff_content.strip():
print(json.dumps({
'error': 'No diff content provided',
'atomic': None
}))
sys.exit(1)
try:
result = analyze_atomicity(diff_content)
print(json.dumps(result, indent=2))
sys.exit(0)
except Exception as e:
print(json.dumps({
'error': str(e),
'atomic': None
}))
sys.exit(2)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,103 @@
#!/bin/bash
# Script: git-diff-analyzer.sh
# Purpose: Parse git diff output for detailed file and line change analysis
# Author: Git Commit Assistant Plugin
# Version: 1.0.0
#
# Usage:
# git diff HEAD | ./git-diff-analyzer.sh
#
# Returns:
# JSON with file details, line counts, and change summaries
#
# Exit Codes:
# 0 - Success
# 1 - No input
# 2 - Analysis error
# Read diff from stdin
diff_content=$(cat)
if [ -z "$diff_content" ]; then
echo '{"error": "No diff content provided"}'
exit 1
fi
# Initialize counters
total_files=0
total_additions=0
total_deletions=0
declare -A file_stats
# Parse diff output
current_file=""
while IFS= read -r line; do
# File headers
if [[ "$line" =~ ^\+\+\+\ b/(.+)$ ]]; then
current_file="${BASH_REMATCH[1]}"
((total_files++))
file_stats["$current_file,additions"]=0
file_stats["$current_file,deletions"]=0
file_stats["$current_file,status"]="M"
# New file
elif [[ "$line" =~ ^\+\+\+\ b/(.+)$ ]] && [[ "$diff_content" == *"--- /dev/null"* ]]; then
file_stats["$current_file,status"]="A"
# Deleted file
elif [[ "$line" =~ ^---\ a/(.+)$ ]] && [[ "$diff_content" == *"+++ /dev/null"* ]]; then
current_file="${BASH_REMATCH[1]}"
file_stats["$current_file,status"]="D"
# Count additions
elif [[ "$line" =~ ^\+[^+] ]] && [ -n "$current_file" ]; then
((total_additions++))
((file_stats["$current_file,additions"]++))
# Count deletions
elif [[ "$line" =~ ^-[^-] ]] && [ -n "$current_file" ]; then
((total_deletions++))
((file_stats["$current_file,deletions"]++))
fi
done <<< "$diff_content"
# Build JSON output
echo "{"
echo " \"summary\": {"
echo " \"total_files\": $total_files,"
echo " \"total_additions\": $total_additions,"
echo " \"total_deletions\": $total_deletions,"
echo " \"net_change\": $((total_additions - total_deletions))"
echo " },"
echo " \"files\": ["
# Output file stats
first=true
for key in "${!file_stats[@]}"; do
if [[ "$key" == *",status" ]]; then
file="${key%,status}"
status="${file_stats[$key]}"
additions=${file_stats["$file,additions"]:-0}
deletions=${file_stats["$file,deletions"]:-0}
if [ "$first" = true ]; then
first=false
else
echo ","
fi
echo -n " {"
echo -n "\"file\": \"$file\", "
echo -n "\"status\": \"$status\", "
echo -n "\"additions\": $additions, "
echo -n "\"deletions\": $deletions, "
echo -n "\"net\": $((additions - deletions))"
echo -n "}"
fi
done
echo ""
echo " ]"
echo "}"
exit 0

View File

@@ -0,0 +1,131 @@
#!/bin/bash
# Script: scope-identifier.sh
# Purpose: Identify primary scope (module/component) from file paths
# Author: Git Commit Assistant Plugin
# Version: 1.0.0
#
# Usage:
# git diff HEAD --name-only | ./scope-identifier.sh
# ./scope-identifier.sh < files.txt
#
# Returns:
# JSON: {"scope": "auth", "confidence": "high", "affected_areas": {...}}
#
# Exit Codes:
# 0 - Success
# 1 - No input
# 2 - Analysis error
# Read file paths from stdin
files=()
while IFS= read -r line; do
files+=("$line")
done
if [ ${#files[@]} -eq 0 ]; then
echo '{"error": "No files provided", "scope": null}'
exit 1
fi
# Scope counters
declare -A scope_counts
declare -A scope_files
# Analyze each file path
for file in "${files[@]}"; do
# Skip empty lines
[ -z "$file" ] && continue
# Extract scope from path patterns
scope=""
# Pattern 1: src/<scope>/*
if [[ "$file" =~ ^src/([^/]+)/ ]]; then
scope="${BASH_REMATCH[1]}"
# Pattern 2: components/<Component>
elif [[ "$file" =~ components/([^/]+) ]]; then
# Convert PascalCase to kebab-case
component="${BASH_REMATCH[1]}"
scope=$(echo "$component" | sed 's/\([A-Z]\)/-\1/g' | tr '[:upper:]' '[:lower:]' | sed 's/^-//')
# Pattern 3: tests/<module>
elif [[ "$file" =~ tests?/([^/]+) ]]; then
scope="${BASH_REMATCH[1]}"
scope=$(echo "$scope" | sed 's/\.test.*$//' | sed 's/\.spec.*$//')
# Pattern 4: docs/*
elif [[ "$file" =~ ^docs?/ ]]; then
scope="docs"
# Pattern 5: .github/workflows
elif [[ "$file" =~ \.github/workflows ]]; then
scope="ci"
# Pattern 6: config files
elif [[ "$file" =~ (package\.json|tsconfig\.json|.*\.config\.(js|ts|json)) ]]; then
scope="config"
# Pattern 7: root README
elif [[ "$file" == "README.md" ]]; then
scope="docs"
fi
# Count scopes
if [ -n "$scope" ]; then
((scope_counts[$scope]++))
scope_files[$scope]="${scope_files[$scope]}$file\n"
fi
done
# Find primary scope (most files)
primary_scope=""
max_count=0
for scope in "${!scope_counts[@]}"; do
count=${scope_counts[$scope]}
if [ $count -gt $max_count ]; then
max_count=$count
primary_scope="$scope"
fi
done
# Determine confidence
confidence="low"
total_files=${#files[@]}
if [ -n "$primary_scope" ]; then
primary_percentage=$((max_count * 100 / total_files))
if [ $primary_percentage -ge 80 ]; then
confidence="high"
elif [ $primary_percentage -ge 50 ]; then
confidence="medium"
fi
fi
# Build affected areas JSON
affected_areas="{"
first=true
for scope in "${!scope_counts[@]}"; do
if [ "$first" = true ]; then
first=false
else
affected_areas+=","
fi
affected_areas+="\"$scope\":${scope_counts[$scope]}"
done
affected_areas+="}"
# Build reasoning
if [ -n "$primary_scope" ]; then
reasoning="Primary scope '$primary_scope' identified from $max_count of $total_files files ($primary_percentage%)."
else
reasoning="Unable to identify clear scope. Files span multiple unrelated areas."
fi
# Output JSON
cat <<EOF
{
"scope": ${primary_scope:+\"$primary_scope\"},
"confidence": "$confidence",
"reasoning": "$reasoning",
"affected_areas": $affected_areas,
"total_files": $total_files,
"primary_file_count": $max_count,
"primary_percentage": ${primary_percentage:-0}
}
EOF
exit 0

View File

@@ -0,0 +1,246 @@
#!/usr/bin/env python3
# Script: type-detector.py
# Purpose: Detect conventional commit type from git diff analysis
# Author: Git Commit Assistant Plugin
# Version: 1.0.0
#
# Usage:
# git diff HEAD | ./type-detector.py
# ./type-detector.py < diff.txt
#
# Returns:
# JSON: {"type": "feat", "confidence": "high", "reasoning": "..."}
#
# Exit Codes:
# 0 - Success
# 1 - No input provided
# 2 - Analysis error
import sys
import re
import json
def detect_type_from_diff(diff_content):
"""
Detect commit type using priority-based decision tree.
Priority order:
1. feat - new files/functions
2. fix - bug fixes/error handling
3. docs - documentation only
4. refactor - code restructuring
5. style - formatting only
6. test - test files only
7. build - dependencies
8. ci - CI/CD configs
9. perf - performance
10. chore - other
"""
lines = diff_content.split('\n')
# Indicators
indicators = {
'new_files': 0,
'new_exports': 0,
'bug_keywords': 0,
'error_handling': 0,
'docs_only': True,
'test_only': True,
'formatting_only': True,
'build_files': 0,
'ci_files': 0,
'perf_keywords': 0,
'refactor_keywords': 0
}
changed_files = []
for line in lines:
# Track changed files
if line.startswith('+++') or line.startswith('---'):
file_path = line[4:].strip()
if file_path != '/dev/null':
changed_files.append(file_path)
# New file indicator
if line.startswith('+++ ') and '/dev/null' not in line:
if line.startswith('+++ b/'):
indicators['new_files'] += 1
# New exports (feat indicator)
if line.startswith('+') and ('export function' in line or 'export class' in line or 'export const' in line):
indicators['new_exports'] += 1
# Bug fix keywords
if line.startswith('+') and any(kw in line.lower() for kw in ['fix', 'resolve', 'correct', 'handle error']):
indicators['bug_keywords'] += 1
# Error handling (fix indicator)
if line.startswith('+') and ('try {' in line or 'catch' in line or 'if (! in line or 'throw' in line):
indicators['error_handling'] += 1
# Check if only docs changed
if line.startswith('+++') and not line.endswith('.md') and not '# ' in line:
if '/dev/null' not in line:
indicators['docs_only'] = False
# Check if only tests changed
if line.startswith('+++'):
if not ('.test.' in line or '.spec.' in line or '_test' in line):
if '/dev/null' not in line:
indicators['test_only'] = False
# Check if only formatting
if line.startswith('+') and len(line.strip()) > 1:
stripped = line[1:].strip()
if stripped and not stripped.isspace():
indicators['formatting_only'] = False
# Build files (package.json, etc.)
if 'package.json' in line or 'pom.xml' in line or 'build.gradle' in line:
indicators['build_files'] += 1
# CI files
if '.github/workflows' in line or '.gitlab-ci' in line or 'Jenkinsfile' in line:
indicators['ci_files'] += 1
# Performance keywords
if line.startswith('+') and any(kw in line.lower() for kw in ['optimize', 'cache', 'memoize', 'performance']):
indicators['perf_keywords'] += 1
# Refactor keywords
if line.startswith('+') and any(kw in line.lower() for kw in ['extract', 'rename', 'simplify', 'reorganize']):
indicators['refactor_keywords'] += 1
# Decision tree
# 1. Check for feat
if indicators['new_files'] > 0 or indicators['new_exports'] > 2:
return {
'type': 'feat',
'confidence': 'high' if indicators['new_files'] > 0 else 'medium',
'reasoning': f"New files ({indicators['new_files']}) or new exports ({indicators['new_exports']}) detected. Indicates new feature.",
'indicators': {
'new_files': indicators['new_files'],
'new_exports': indicators['new_exports']
}
}
# 2. Check for fix
if indicators['error_handling'] > 2 or indicators['bug_keywords'] > 1:
return {
'type': 'fix',
'confidence': 'high',
'reasoning': f"Error handling ({indicators['error_handling']}) or bug fix keywords ({indicators['bug_keywords']}) found. Indicates bug fix.",
'indicators': {
'error_handling': indicators['error_handling'],
'bug_keywords': indicators['bug_keywords']
}
}
# 3. Check for docs
if indicators['docs_only']:
return {
'type': 'docs',
'confidence': 'high',
'reasoning': "Only documentation files (.md) changed. Pure documentation update.",
'indicators': {}
}
# 4. Check for style
if indicators['formatting_only']:
return {
'type': 'style',
'confidence': 'high',
'reasoning': "Only formatting/whitespace changes detected. No logic changes.",
'indicators': {}
}
# 5. Check for test
if indicators['test_only']:
return {
'type': 'test',
'confidence': 'high',
'reasoning': "Only test files changed. Test additions or updates.",
'indicators': {}
}
# 6. Check for build
if indicators['build_files'] > 0:
return {
'type': 'build',
'confidence': 'high',
'reasoning': f"Build files ({indicators['build_files']}) changed. Dependency or build system updates.",
'indicators': {
'build_files': indicators['build_files']
}
}
# 7. Check for ci
if indicators['ci_files'] > 0:
return {
'type': 'ci',
'confidence': 'high',
'reasoning': f"CI/CD configuration files ({indicators['ci_files']}) changed.",
'indicators': {
'ci_files': indicators['ci_files']
}
}
# 8. Check for perf
if indicators['perf_keywords'] > 2:
return {
'type': 'perf',
'confidence': 'medium',
'reasoning': f"Performance-related keywords ({indicators['perf_keywords']}) found.",
'indicators': {
'perf_keywords': indicators['perf_keywords']
}
}
# 9. Check for refactor
if indicators['refactor_keywords'] > 2:
return {
'type': 'refactor',
'confidence': 'medium',
'reasoning': f"Refactoring keywords ({indicators['refactor_keywords']}) found.",
'indicators': {
'refactor_keywords': indicators['refactor_keywords']
}
}
# 10. Default to chore
return {
'type': 'chore',
'confidence': 'low',
'reasoning': "Changes don't match specific patterns. Defaulting to chore.",
'indicators': {}
}
def main():
# Read diff from stdin
diff_content = sys.stdin.read()
if not diff_content or not diff_content.strip():
print(json.dumps({
'error': 'No diff content provided',
'type': None,
'confidence': None
}))
sys.exit(1)
try:
result = detect_type_from_diff(diff_content)
print(json.dumps(result, indent=2))
sys.exit(0)
except Exception as e:
print(json.dumps({
'error': str(e),
'type': None,
'confidence': None
}))
sys.exit(2)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,196 @@
---
description: Perform comprehensive analysis of git changes including type, scope, and atomicity
---
# Operation: Analyze Changes
Perform full analysis of git repository changes to understand the nature, scope, commit type, and whether changes are atomic.
## Parameters from $ARGUMENTS
No parameters required. Analyzes all current changes.
## Workflow
### Step 1: Verify Git Repository
```bash
git rev-parse --git-dir 2>/dev/null
```
If this fails:
- Return error: "Not a git repository. Run 'git init' to initialize."
- Exit operation
### Step 2: Check for Changes
```bash
git status --short
```
If no changes:
- Return: "No changes to analyze. Working tree is clean."
- Exit operation
### Step 3: Gather Change Information
Run git commands to collect comprehensive data:
```bash
# Get detailed diff
git diff HEAD
# Get status summary
git status --short
# Get diff statistics
git diff --stat HEAD
# Count changed files
git status --short | wc -l
```
### Step 4: Invoke Analysis Scripts
Execute utility scripts to analyze different aspects:
**Type Detection:**
```bash
# Run type detector script
.scripts/type-detector.py
```
This script analyzes the diff output and returns the primary commit type (feat, fix, docs, etc.)
**Scope Identification:**
```bash
# Run scope identifier
.scripts/scope-identifier.sh
```
This script identifies the primary module/component affected
**Atomicity Assessment:**
```bash
# Run atomicity checker
.scripts/atomicity-checker.py
```
This script determines if changes should be split into multiple commits
**Diff Analysis:**
```bash
# Run git diff analyzer
.scripts/git-diff-analyzer.sh
```
This script parses diff output for detailed file and line change information
### Step 5: Compile Analysis Report
Format comprehensive analysis:
```
COMMIT ANALYSIS REPORT
═══════════════════════════════════════════════
CHANGE SUMMARY:
───────────────────────────────────────────────
Files Changed: X files
Insertions: +XXX lines
Deletions: -XXX lines
Net Change: ±XXX lines
COMMIT TYPE DETECTION:
───────────────────────────────────────────────
Primary Type: <feat|fix|docs|refactor|style|test|chore|perf|ci>
Confidence: <High|Medium|Low>
Reasoning: <explanation of why this type was chosen>
Alternative Types: <if applicable>
SCOPE IDENTIFICATION:
───────────────────────────────────────────────
Primary Scope: <module/component name>
Affected Areas: <list of all affected areas>
Scope Confidence: <High|Medium|Low>
ATOMICITY ASSESSMENT:
───────────────────────────────────────────────
Status: <Atomic|Should Split>
Reasoning: <explanation>
If Should Split:
Recommended Splits:
1. <type>(<scope>): <description> - X files
2. <type>(<scope>): <description> - X files
...
FILE BREAKDOWN:
───────────────────────────────────────────────
<list files grouped by type/scope>
New Files: <count>
- <file1>
- <file2>
Modified Files: <count>
- <file1> (+XX -YY)
- <file2> (+XX -YY)
Deleted Files: <count>
- <file1>
RECOMMENDATIONS:
───────────────────────────────────────────────
- <recommendation 1>
- <recommendation 2>
- <recommendation 3>
═══════════════════════════════════════════════
```
## Output Format
Return structured analysis with:
- Change statistics
- Detected commit type with confidence level
- Identified scope
- Atomicity assessment
- File breakdown
- Specific recommendations
## Error Handling
**Not a git repository:**
```
ERROR: Not a git repository
Initialize with: git init
```
**No changes to analyze:**
```
NO CHANGES
Working tree is clean. Make changes before analyzing.
```
**Git command fails:**
```
ERROR: Git command failed
<error message>
Ensure git is properly configured.
```
## Integration with Agent
The commit-assistant agent uses this operation to:
1. Understand change nature before suggesting commits
2. Determine if changes are atomic or need splitting
3. Identify appropriate commit type and scope
4. Provide context for message generation
## Usage Example
```bash
# Agent workflow:
# User: "analyze my changes"
# Agent invokes: analyze-changes operation
# Operation returns comprehensive report
# Agent presents findings to user
```

View File

@@ -0,0 +1,363 @@
---
description: Assess if changes are atomic (single logical change) or should be split into multiple commits
---
# Operation: Assess Atomicity
Determine if changes form an atomic commit (single logical change) or should be split into multiple focused commits.
## Parameters from $ARGUMENTS
No parameters required. Analyzes all current changes.
## Workflow
### Step 1: Gather Change Data
```bash
git status --short
git diff HEAD --stat
git diff HEAD
```
### Step 2: Run Atomicity Analysis
Execute atomicity checker script:
```bash
git diff HEAD | .scripts/atomicity-checker.py
```
The script analyzes:
- Number of files changed
- Types of changes (feat, fix, docs, etc.)
- Scopes affected
- Interdependencies
- Logical cohesion
### Step 3: Atomicity Criteria
**Atomic Commit** (single commit appropriate):
- All changes serve ONE logical purpose
- All files relate to the same feature/fix
- Changes are interdependent
- Same type throughout (all feat, or all fix)
- Same scope throughout (all auth, or all api)
- Can be reverted as a complete unit
- Tests pass after commit
**Non-Atomic** (should split):
- Multiple distinct types (feat + fix + docs)
- Multiple unrelated scopes (auth + api + ui)
- Mixing concerns (feature + refactoring + bug fix)
- Too many files (10+ unrelated files)
- Multiple stories being told
### Step 4: Splitting Recommendations
If non-atomic, suggest how to split:
**By Type:**
```
Split into commits by change type:
1. feat(auth): OAuth implementation (5 files)
2. test(auth): OAuth tests (2 files)
3. docs: authentication guide (1 file)
```
**By Scope:**
```
Split into commits by module:
1. feat(auth): authentication system (6 files)
2. feat(api): user API endpoints (4 files)
3. feat(ui): login components (3 files)
```
**By Feature:**
```
Split into commits by logical unit:
1. feat(payments): Stripe integration (8 files)
2. feat(payments): PayPal integration (6 files)
3. test(payments): payment tests (4 files)
```
### Step 5: Format Atomicity Assessment
```
ATOMICITY ASSESSMENT
═══════════════════════════════════════════════
STATUS: <ATOMIC | SHOULD SPLIT>
ANALYSIS:
───────────────────────────────────────────────
Files Changed: X
Lines Changed: +XXX -XXX
Type Diversity: <Single | Multiple>
Scope Diversity: <Single | Multiple>
Logical Cohesion: <High | Medium | Low>
ATOMICITY CHECKS:
───────────────────────────────────────────────
✓ Single logical purpose
✓ Related files only
✓ Same change type
✓ Same scope
✓ Interdependent changes
✓ Complete unit
✓ Can be reverted independently
OR
✗ Multiple purposes detected
✗ Unrelated files mixed
✗ Multiple change types
✗ Multiple scopes
✗ Independent changes
✗ Incomplete without splits
REASONING:
───────────────────────────────────────────────
<detailed explanation>
If ATOMIC:
All changes implement [specific feature/fix].
Files work together and depend on each other.
Single commit tells a clear, focused story.
If SHOULD SPLIT:
Changes address multiple concerns:
1. <concern 1>
2. <concern 2>
3. <concern 3>
Each concern should be a separate commit.
SPLITTING RECOMMENDATIONS:
───────────────────────────────────────────────
<if should split>
Recommended Commits:
Commit 1: <type>(<scope>): <description>
Files: X files
Purpose: <specific purpose>
Files:
- file1.js
- file2.js
Lines: +XX -YY
Commit 2: <type>(<scope>): <description>
Files: X files
Purpose: <specific purpose>
Files:
- file3.js
- file4.js
Lines: +XX -YY
BENEFITS OF SPLITTING:
───────────────────────────────────────────────
<if should split>
- Better code review (focused changes)
- Easier to revert individual features
- Clearer git history
- Better bisecting for bugs
- Easier cherry-picking
RECOMMENDATION:
───────────────────────────────────────────────
<specific actionable recommendation>
═══════════════════════════════════════════════
```
## Detailed Atomicity Rules
### Rule 1: Single Purpose Test
**Question:** Can you describe ALL changes in one sentence?
**Atomic Examples:**
- "Add OAuth authentication"
- "Fix null pointer in user endpoint"
- "Refactor date utility functions"
**Non-Atomic Examples:**
- "Add OAuth and fix login bug and update README"
- "Implement payment and refactor utils and add tests"
### Rule 2: Type Consistency
**Atomic:**
- All files are "feat" type
- All files are "fix" type
- All files are "refactor" type
**Non-Atomic:**
- Mix of feat + fix
- Mix of refactor + feat
- Mix of docs + code changes (exception: docs can accompany code)
### Rule 3: Scope Consistency
**Atomic:**
- All changes in "auth" module
- All changes in "api" module
- All changes in "ui/components"
**Non-Atomic:**
- Changes in auth + api + ui
- Changes in multiple unrelated modules
### Rule 4: Revert Independence
**Test:** If reverted, does it break unrelated functionality?
**Atomic:**
- Can be reverted without breaking other features
- Forms a complete, self-contained unit
**Non-Atomic:**
- Reverting breaks unrelated features
- Mixes independent changes
### Rule 5: Review Simplicity
**Atomic:**
- Reviewer can focus on one logical change
- Clear what and why
- Single story
**Non-Atomic:**
- Reviewer must context-switch between multiple concerns
- Multiple stories mixed together
## File Count Guidelines
**Generally Atomic:**
- 1-5 files: Usually focused
- 5-10 files: Check cohesion carefully
- 10-15 files: Likely needs splitting unless tightly coupled
**Usually Non-Atomic:**
- 15+ files: Almost always should split
- 20+ files: Definitely split
**Exceptions:**
- Large refactoring may touch many files atomically
- Package updates may affect many files atomically
- Renaming across project may touch many files atomically
## Edge Cases
### Case 1: Feature + Tests
**Atomic?** Usually YES
- Tests directly validate the feature code
- They tell the same story
- Can be reverted together
**Example:**
```
feat(auth): implement OAuth authentication
- 5 implementation files
- 2 test files
→ ATOMIC (7 files, same feature)
```
### Case 2: Feature + Documentation
**Atomic?** DEPENDS
- If docs describe the feature: YES
- If docs are unrelated updates: NO
**Example:**
```
feat(auth): add OAuth support
- 5 auth files
- 1 auth documentation file
→ ATOMIC (documents the feature)
vs.
feat(auth): add OAuth support
- 5 auth files
- README general update
→ NON-ATOMIC (split docs)
```
### Case 3: Refactoring + New Feature
**Atomic?** NO
- Refactoring should be separate
- Makes review harder
- Mixing concerns
**Example:**
```
Changes:
- Refactor utils (3 files)
- Add new payment feature (5 files)
→ NON-ATOMIC
Split:
1. refactor(utils): simplify utility functions
2. feat(payments): add Stripe integration
```
### Case 4: Multiple Small Fixes
**Atomic?** DEPENDS
- If all in same module: MAYBE
- If unrelated: NO
**Example:**
```
fix(auth): resolve three auth-related bugs
- All in auth module
- All are fixes
- Related to each other
→ ATOMIC
vs.
- Fix auth bug
- Fix API bug
- Fix UI bug
→ NON-ATOMIC (different scopes)
```
## Output Format
Return:
- Atomicity status (ATOMIC or SHOULD SPLIT)
- Detailed analysis and reasoning
- Atomicity checks (passed/failed)
- Splitting recommendations if needed
- Benefits explanation
- Specific recommendation
## Error Handling
**No changes:**
```
NO CHANGES TO ASSESS
Working tree is clean.
```
**Single file change:**
```
ATOMIC (Single File)
One file changed: <filename>
Automatically atomic.
```
## Integration with Agent
The commit-assistant agent uses this operation to:
1. Determine if /commit or /commit-split should be used
2. Warn users about non-atomic changes
3. Provide splitting guidance
4. Educate users on atomic commit benefits
## Usage Example
```bash
# Agent checks atomicity before committing
# User: "commit my changes"
# Agent: Invokes assess-atomicity
# Operation: Returns "SHOULD SPLIT"
# Agent: "Your changes should be split. Run /commit-review"
```

View File

@@ -0,0 +1,341 @@
---
description: Detect the appropriate conventional commit type based on git diff analysis
---
# Operation: Detect Commit Type
Analyze git changes to determine the conventional commit type (feat, fix, docs, refactor, style, test, chore, perf, ci).
## Parameters from $ARGUMENTS
No parameters required. Analyzes current git diff.
## Workflow
### Step 1: Verify Git Repository and Changes
```bash
git rev-parse --git-dir 2>/dev/null
git diff HEAD
```
If no repository or no changes, exit with appropriate message.
### Step 2: Run Type Detection Algorithm
Execute the type detector script with git diff output:
```bash
git diff HEAD | .scripts/type-detector.py
```
The script implements the decision tree algorithm:
### Detection Algorithm (Implemented in type-detector.py)
```
Priority Order (check in sequence):
1. Check for new files or new exports
→ IF new files OR new functions/classes/exports
→ RETURN "feat"
2. Check for bug fixes
→ IF error handling OR bug keywords (fix, resolve, correct)
→ RETURN "fix"
3. Check for docs-only changes
→ IF only .md files OR only comments changed
→ RETURN "docs"
4. Check for code restructuring
→ IF code moved/renamed but no behavior change
→ RETURN "refactor"
5. Check for style changes
→ IF only whitespace OR formatting changed
→ RETURN "style"
6. Check for test changes
→ IF only test files changed
→ RETURN "test"
7. Check for dependency changes
→ IF package.json OR build files changed
→ RETURN "build"
8. Check for CI changes
→ IF .github/workflows OR CI configs changed
→ RETURN "ci"
9. Check for performance improvements
→ IF optimization keywords OR caching added
→ RETURN "perf"
10. Default
→ RETURN "chore"
```
### Step 3: Analyze Confidence Level
Determine confidence in the detected type:
**High Confidence:**
- Only one type detected
- Clear indicators present
- No ambiguity
**Medium Confidence:**
- Multiple possible types
- Mixed indicators
- Requires human judgment
**Low Confidence:**
- Unclear changes
- Multiple types with equal weight
- Recommend manual review
### Step 4: Identify Alternative Types
If changes could fit multiple types, list alternatives:
```
Primary: feat (High confidence)
Alternatives:
- test (if test files are significant)
- docs (if documentation is substantial)
```
### Step 5: Format Detection Result
Return structured type detection:
```
COMMIT TYPE DETECTION
═══════════════════════════════════════════════
DETECTED TYPE: <type>
CONFIDENCE: <High|Medium|Low>
REASONING:
───────────────────────────────────────────────
<detailed explanation of why this type was chosen>
Key Indicators:
- <indicator 1>
- <indicator 2>
- <indicator 3>
FILE ANALYSIS:
───────────────────────────────────────────────
New Files: X (suggests feat)
Bug Fixes: X (suggests fix)
Documentation: X (suggests docs)
Refactoring: X (suggests refactor)
Formatting: X (suggests style)
Tests: X (suggests test)
Dependencies: X (suggests build/chore)
CI/CD: X (suggests ci)
ALTERNATIVE TYPES:
───────────────────────────────────────────────
<if applicable>
- <type>: <reasoning>
RECOMMENDATION:
───────────────────────────────────────────────
<specific recommendation based on analysis>
═══════════════════════════════════════════════
```
## Type Detection Rules
### feat (Feature)
**Indicators:**
- New files created
- New functions/classes exported
- New functionality added
- New components/modules
- Keywords: "add", "implement", "introduce", "create"
**Example Patterns:**
```diff
+export function newFeature() {
+export class NewComponent {
+++ new-file.js
```
### fix (Bug Fix)
**Indicators:**
- Error handling added
- Null/undefined checks
- Validation corrections
- Bug keywords in code
- Keywords: "fix", "resolve", "correct", "handle"
**Example Patterns:**
```diff
+if (!value) throw new Error
+try { ... } catch
-return null
+return value || default
```
### docs (Documentation)
**Indicators:**
- Only .md files changed
- Only comments changed
- JSDoc additions
- README updates
- No code logic changes
**Example Patterns:**
```diff
+++ README.md
+// This function does...
+/** @param {string} name */
```
### refactor (Code Restructuring)
**Indicators:**
- Code moved/reorganized
- Variables/functions renamed
- Logic extracted
- No behavior change
- Keywords: "extract", "rename", "simplify", "reorganize"
**Example Patterns:**
```diff
-function oldName() {
+function newName() {
-inline code block
+extractedFunction()
```
### style (Formatting)
**Indicators:**
- Only whitespace changes
- Indentation fixes
- Semicolon additions/removals
- Code formatting
- No logic changes
**Example Patterns:**
```diff
- const x = 1
+ const x = 1;
-function(a,b) {
+function(a, b) {
```
### test (Tests)
**Indicators:**
- Only test files changed (.test.js, .spec.js, _test.py)
- Test additions/updates
- Mock/fixture changes
- Keywords: "describe", "it", "test", "expect"
**Example Patterns:**
```diff
+++ tests/feature.test.js
+describe('Feature', () => {
+it('should work', () => {
```
### build (Build System)
**Indicators:**
- package.json changes
- Dependency updates
- Build configuration
- Build scripts
**Example Patterns:**
```diff
+++ package.json
+"dependencies": {
+ "new-lib": "^1.0.0"
```
### ci (CI/CD)
**Indicators:**
- .github/workflows changes
- .gitlab-ci.yml changes
- CI configuration
- Deployment scripts
**Example Patterns:**
```diff
+++ .github/workflows/test.yml
+++ .gitlab-ci.yml
```
### perf (Performance)
**Indicators:**
- Optimization changes
- Caching implementation
- Algorithm improvements
- Keywords: "optimize", "cache", "performance", "faster"
**Example Patterns:**
```diff
+const cache = new Map()
+if (cache.has(key)) return cache.get(key)
-O(n²) algorithm
+O(n log n) algorithm
```
### chore (Other)
**Indicators:**
- Maintenance tasks
- Tooling configuration
- Repository housekeeping
- Doesn't fit other types
## Output Format
Return:
- Detected type
- Confidence level
- Detailed reasoning
- Key indicators found
- Alternative types (if any)
- Specific recommendation
## Error Handling
**No changes:**
```
NO CHANGES TO ANALYZE
Working tree is clean.
```
**Ambiguous changes:**
```
AMBIGUOUS TYPE DETECTION
Multiple types detected with equal weight.
Manual review recommended.
Detected types:
- feat (40%)
- refactor (35%)
- test (25%)
```
## Integration with Agent
The commit-assistant agent uses this operation to:
1. Automatically determine commit type
2. Validate user-provided types
3. Suggest alternative types if ambiguous
4. Guide users in type selection
## Usage Example
```bash
# Agent detects type before generating message
# User: "commit these changes"
# Agent: Invokes detect-type
# Operation: Returns "feat" with high confidence
# Agent: Generates message with "feat" type
```

View File

@@ -0,0 +1,327 @@
---
description: Get detailed file change statistics for commit analysis
---
# Operation: File Statistics
Retrieve detailed statistics about file changes including counts, line changes, and file types.
## Parameters from $ARGUMENTS
Optional parameters:
- `format:json|text` - Output format (default: text)
- `detailed:true|false` - Include per-file breakdown (default: false)
## Workflow
### Step 1: Verify Repository
```bash
git rev-parse --git-dir 2>/dev/null
```
### Step 2: Collect File Statistics
```bash
# Get file status
git status --short
# Get statistics
git diff --stat HEAD
# Get detailed stats per file
git diff --numstat HEAD
# Count file types
git diff --name-only HEAD | wc -l
```
### Step 3: Categorize Files
Group files by status and type:
**By Status:**
- New files (A)
- Modified files (M)
- Deleted files (D)
- Renamed files (R)
**By Type:**
- Source code (.js, .ts, .py, .java, etc.)
- Tests (.test.js, .spec.ts, _test.py, etc.)
- Documentation (.md, .txt)
- Configuration (.json, .yml, .toml, .config.js)
- Styles (.css, .scss, .less)
### Step 4: Calculate Metrics
```
METRICS:
- Total files changed
- Total insertions (lines added)
- Total deletions (lines removed)
- Net change (insertions - deletions)
- Largest files (by lines changed)
- Files by type distribution
- New vs Modified vs Deleted ratio
```
### Step 5: Format Statistics Report
**Text Format (default):**
```
FILE STATISTICS
═══════════════════════════════════════════════
OVERVIEW:
───────────────────────────────────────────────
Total Files: X
Insertions: +XXX lines
Deletions: -XXX lines
Net Change: ±XXX lines
STATUS BREAKDOWN:
───────────────────────────────────────────────
New Files: X
Modified Files: X
Deleted Files: X
Renamed Files: X
FILE TYPE BREAKDOWN:
───────────────────────────────────────────────
Source Code: X files (+XXX -XXX lines)
Tests: X files (+XXX -XXX lines)
Documentation: X files (+XXX -XXX lines)
Configuration: X files (+XXX -XXX lines)
Styles: X files (+XXX -XXX lines)
Other: X files (+XXX -XXX lines)
TOP CHANGED FILES:
───────────────────────────────────────────────
1. <filename> (+XXX -XXX) = XXX lines
2. <filename> (+XXX -XXX) = XXX lines
3. <filename> (+XXX -XXX) = XXX lines
4. <filename> (+XXX -XXX) = XXX lines
5. <filename> (+XXX -XXX) = XXX lines
<if detailed:true>
PER-FILE BREAKDOWN:
───────────────────────────────────────────────
<status> <filename>
Insertions: +XXX
Deletions: -XXX
Net: ±XXX
<status> <filename>
Insertions: +XXX
Deletions: -XXX
Net: ±XXX
═══════════════════════════════════════════════
```
**JSON Format (if format:json):**
```json
{
"overview": {
"total_files": X,
"insertions": XXX,
"deletions": XXX,
"net_change": XXX
},
"status_breakdown": {
"new": X,
"modified": X,
"deleted": X,
"renamed": X
},
"type_breakdown": {
"source": {
"count": X,
"insertions": XXX,
"deletions": XXX
},
"tests": {
"count": X,
"insertions": XXX,
"deletions": XXX
},
"docs": {
"count": X,
"insertions": XXX,
"deletions": XXX
},
"config": {
"count": X,
"insertions": XXX,
"deletions": XXX
}
},
"top_files": [
{
"path": "<filename>",
"insertions": XXX,
"deletions": XXX,
"net": XXX
}
],
"files": [
{
"path": "<filename>",
"status": "<status>",
"insertions": XXX,
"deletions": XXX,
"net": XXX,
"type": "<file-type>"
}
]
}
```
## File Type Detection
**Source Code:**
```
.js, .jsx, .ts, .tsx (JavaScript/TypeScript)
.py (Python)
.java (Java)
.go (Go)
.rs (Rust)
.rb (Ruby)
.php (PHP)
.c, .cpp, .h (C/C++)
.swift (Swift)
.kt (Kotlin)
```
**Tests:**
```
.test.js, .spec.js
.test.ts, .spec.ts
_test.py, test_*.py
.test.jsx, .spec.tsx
```
**Documentation:**
```
.md (Markdown)
.txt (Text)
.rst (reStructuredText)
.adoc (AsciiDoc)
```
**Configuration:**
```
.json, .yml, .yaml, .toml
.config.js, .config.ts
.env, .env.*
.editorconfig, .gitignore
package.json, tsconfig.json
```
**Styles:**
```
.css, .scss, .sass, .less
```
## Statistical Insights
Provide insights based on stats:
**Large Changes:**
```
INSIGHT: Large change detected
You've changed 500+ lines across 15 files.
Consider splitting into smaller commits.
```
**Many New Files:**
```
INSIGHT: Many new files added
8 new files suggest a new feature (feat).
```
**Deletion Heavy:**
```
INSIGHT: More deletions than insertions
Significant code removal suggests refactoring or cleanup.
```
**Test Heavy:**
```
INSIGHT: High test coverage
40% of changes are in test files.
Good practice to include tests with features.
```
**Documentation Heavy:**
```
INSIGHT: Documentation focused
60% of changes are documentation.
Consider "docs" commit type.
```
## Usage Examples
**Basic stats:**
```bash
/commit-analysis file-stats
```
**JSON output:**
```bash
/commit-analysis file-stats format:json
```
**Detailed breakdown:**
```bash
/commit-analysis file-stats detailed:true
```
**JSON with details:**
```bash
/commit-analysis file-stats format:json detailed:true
```
## Output Format
Return:
- Overview statistics
- Status breakdown
- File type breakdown
- Top changed files
- Per-file details (if detailed:true)
- Statistical insights
- Format as specified (text or JSON)
## Error Handling
**No changes:**
```
NO CHANGES
Working tree is clean. No statistics to display.
```
**Invalid format parameter:**
```
ERROR: Invalid format
Valid formats: text, json
```
## Integration with Agent
The commit-assistant agent uses this operation to:
1. Understand change magnitude
2. Identify dominant change types
3. Provide context for commit decisions
4. Generate statistics for reports
## Usage Example
```bash
# Agent gathers stats before analysis
# User: "how big are my changes?"
# Agent: Invokes file-stats
# Operation: Returns comprehensive statistics
# Agent: "You've changed 15 files with 500+ lines"
```

View File

@@ -0,0 +1,231 @@
---
description: Identify the primary module or component affected by changes for commit scope
---
# Operation: Identify Scope
Analyze git changes to determine the appropriate scope (module/component/area) for the conventional commit message.
## Parameters from $ARGUMENTS
No parameters required. Analyzes current changes.
## Workflow
### Step 1: Verify Repository and Changes
```bash
git rev-parse --git-dir 2>/dev/null
git status --short
```
### Step 2: Analyze File Paths
Execute scope identifier script:
```bash
git diff HEAD --name-only | .scripts/scope-identifier.sh
```
The script analyzes file paths to identify affected modules.
### Step 3: Scope Detection Algorithm
**Directory-Based Scope:**
```
src/auth/* → scope: auth
src/api/* → scope: api
src/components/* → scope: components (or specific component)
src/utils/* → scope: utils
src/database/* → scope: database
tests/* → scope: test (or module being tested)
docs/* → scope: docs
```
**Component-Based Scope:**
```
src/components/LoginForm.js → scope: login-form
src/components/UserProfile/* → scope: user-profile
src/api/users.js → scope: users or user-api
src/auth/oauth.js → scope: oauth or auth
```
**Multiple Scopes:**
If multiple distinct scopes:
```
Primary: auth (5 files)
Secondary: api (2 files)
Recommendation: Split into separate commits or use broader scope
```
### Step 4: Determine Scope Specificity
**Specific Scope** (preferred):
- Targets single module/component
- Example: "auth", "user-profile", "payment-api"
**Broad Scope**:
- Multiple modules affected
- Example: "api", "components", "core"
**No Scope** (optional):
- Changes are too diverse
- Use no scope in message
### Step 5: Format Scope Report
```
SCOPE IDENTIFICATION
═══════════════════════════════════════════════
PRIMARY SCOPE: <scope-name>
CONFIDENCE: <High|Medium|Low>
REASONING:
───────────────────────────────────────────────
<explanation of scope selection>
AFFECTED AREAS:
───────────────────────────────────────────────
<scope>: X files
- path/to/file1.js
- path/to/file2.js
<other-scope>: X files (if applicable)
- path/to/file3.js
FILE PATH ANALYSIS:
───────────────────────────────────────────────
src/auth/*: 5 files → scope: auth
src/api/*: 2 files → scope: api
tests/*: 3 files → scope based on tested module
RECOMMENDATION:
───────────────────────────────────────────────
Use scope: "<recommended-scope>"
Alternative: <if multiple scopes>
- Split into commits: auth (5 files), api (2 files)
- Use broader scope: "backend"
- Use no scope (if too diverse)
═══════════════════════════════════════════════
```
## Scope Naming Conventions
**Format:**
- Lowercase
- Hyphen-separated (kebab-case)
- Concise (1-3 words)
- Specific but not too granular
**Good Examples:**
```
auth
user-api
login-form
payment-processing
database
ci-pipeline
docs
```
**Bad Examples:**
```
src/auth/oauth (too specific, includes path)
Authentication (not lowercase)
user_api (use hyphens, not underscores)
the-entire-authentication-module-system (too long)
```
## Common Scopes by Project Type
### Web Application:
```
auth, api, components, ui, pages, routing, state, utils, config, hooks
```
### Backend API:
```
api, auth, database, middleware, routes, controllers, services, models, validation
```
### Library/Package:
```
core, utils, types, cli, docs, build, test
```
### Full Stack:
```
frontend, backend, api, database, auth, ci, docs
```
## Multiple Scope Handling
**Case 1: Closely Related Scopes**
```
Changes in: auth/oauth.js, auth/providers.js, auth/middleware.js
Recommendation: scope: "auth"
```
**Case 2: Distinct Scopes**
```
Changes in: auth/* (5 files), api/* (3 files)
Recommendation: Split commits
- Commit 1: feat(auth): implement OAuth
- Commit 2: feat(api): add user endpoints
```
**Case 3: Very Broad Changes**
```
Changes in: 15 different directories
Recommendation: Use broad scope or no scope
- feat(core): major refactoring
- feat: implement new architecture
```
## Output Format
Return:
- Primary scope name
- Confidence level
- Reasoning for scope selection
- List of affected areas
- Alternative recommendations if applicable
## Error Handling
**No changes:**
```
NO CHANGES
Cannot identify scope from empty diff.
```
**Unclear scope:**
```
SCOPE UNCLEAR
Changes span multiple unrelated areas.
Recommendations:
- Split into focused commits
- Use broader scope like "core" or "backend"
- Use no scope
```
## Integration with Agent
The commit-assistant agent uses this operation to:
1. Determine scope for commit messages
2. Validate user-provided scopes
3. Suggest scope splitting if needed
4. Guide users in scope selection
## Usage Example
```bash
# Agent identifies scope automatically
# User: "commit my auth changes"
# Agent: Invokes identify-scope
# Operation: Returns "auth" with high confidence
# Agent: Uses in message "feat(auth): ..."
```

View File

@@ -0,0 +1,65 @@
---
description: Analyze git changes to understand nature, scope, and commit type for intelligent message generation
---
# Commit Analysis Skill - Change Analysis and Type Detection
Intelligent analysis of git changes to determine commit type, scope, and atomicity for semantic commit message generation.
## Operations
- **analyze** - Full analysis (type, scope, atomicity)
- **detect-type** - Determine commit type (feat, fix, docs, etc.)
- **identify-scope** - Identify affected module/component
- **assess-atomicity** - Check if changes should be split
- **file-stats** - Get file change statistics
## Router Logic
Parse $ARGUMENTS to determine which operation to perform:
1. Extract operation from first word of $ARGUMENTS
2. Extract remaining arguments as operation parameters
3. Route to appropriate instruction file:
- "analyze" → Read `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-analysis/analyze-changes.md`
- "detect-type" → Read `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-analysis/detect-type.md`
- "identify-scope" → Read `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-analysis/identify-scope.md`
- "assess-atomicity" → Read `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-analysis/assess-atomicity.md`
- "file-stats" → Read `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-analysis/file-stats.md`
4. Execute instructions with parameters
5. Return structured analysis results
## Error Handling
- If operation is unknown, list available operations
- If parameters are missing, show required format
- If not a git repository, return clear error message
- If no changes to analyze, inform user
## Usage Examples
```bash
# Full analysis of current changes
/commit-analysis analyze
# Detect commit type only
/commit-analysis detect-type
# Identify affected scope
/commit-analysis identify-scope
# Check if changes should be split
/commit-analysis assess-atomicity
# Get file statistics
/commit-analysis file-stats
```
---
**Base directory:** `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-analysis`
**Current request:** $ARGUMENTS
Parse operation and route to appropriate instruction file.

View File

@@ -0,0 +1,259 @@
#!/usr/bin/env bash
################################################################################
# Amend Safety Checker Script
#
# Purpose: Check if it's safe to amend the last commit
# Version: 1.0.0
# Usage: ./amend-safety.sh
# Returns: JSON with safety analysis
# Exit Codes:
# 0 = Safe to amend
# 1 = Unsafe to amend
# 2 = Warning (proceed with caution)
################################################################################
set -euo pipefail
################################################################################
# Check if commit is pushed to remote
################################################################################
check_not_pushed() {
local status="fail"
local message=""
# Check if we have upstream tracking
if ! git rev-parse --abbrev-ref --symbolic-full-name @{upstream} &>/dev/null; then
status="pass"
message="No upstream branch (commit is local only)"
else
# Check if HEAD commit exists on upstream
local upstream_branch
upstream_branch=$(git rev-parse --abbrev-ref --symbolic-full-name @{upstream})
# Get commits that are local only
local local_commits
local_commits=$(git log "$upstream_branch"..HEAD --oneline 2>/dev/null || echo "")
if [[ -n "$local_commits" ]]; then
status="pass"
message="Commit not pushed to $upstream_branch"
else
status="fail"
message="Commit already pushed to $upstream_branch"
fi
fi
echo "{\"status\": \"$status\", \"message\": \"$message\"}"
}
################################################################################
# Check if current user is the commit author
################################################################################
check_same_author() {
local status="fail"
local message=""
# Get current user email
local current_user
current_user=$(git config user.email)
# Get last commit author email
local commit_author
commit_author=$(git log -1 --format='%ae')
if [[ "$current_user" == "$commit_author" ]]; then
status="pass"
message="You are the commit author"
else
status="fail"
message="Different author: $commit_author (you are $current_user)"
fi
echo "{\"status\": \"$status\", \"message\": \"$message\"}"
}
################################################################################
# Check if on a safe branch (not main/master)
################################################################################
check_safe_branch() {
local status="pass"
local message=""
# Get current branch
local branch
branch=$(git branch --show-current)
# List of protected branches
local protected_branches=("main" "master" "develop" "production" "release")
for protected in "${protected_branches[@]}"; do
if [[ "$branch" == "$protected" ]]; then
status="warn"
message="On protected branch: $branch (amending discouraged)"
break
fi
done
if [[ "$status" == "pass" ]]; then
message="On feature branch: $branch"
fi
echo "{\"status\": \"$status\", \"message\": \"$message\"}"
}
################################################################################
# Check if collaborators might have this commit
################################################################################
check_collaborators() {
local status="pass"
local message="Solo work on branch"
# This is a heuristic check - actual collaboration is hard to detect
# We check if:
# 1. Remote branch exists
# 2. There are other commits on the remote not in local
if git rev-parse --abbrev-ref --symbolic-full-name @{upstream} &>/dev/null; then
local upstream_branch
upstream_branch=$(git rev-parse --abbrev-ref --symbolic-full-name @{upstream})
# Check if there are remote commits we don't have
local remote_commits
remote_commits=$(git log HEAD.."$upstream_branch" --oneline 2>/dev/null || echo "")
if [[ -n "$remote_commits" ]]; then
status="warn"
message="Remote has commits you don't have - possible collaboration"
else
# Check if branch is shared (exists on remote)
local remote_name
remote_name=$(echo "$upstream_branch" | cut -d/ -f1)
local branch_name
branch_name=$(git branch --show-current)
if git ls-remote --heads "$remote_name" "$branch_name" | grep -q "$branch_name"; then
status="warn"
message="Branch exists on remote - collaborators may have pulled"
fi
fi
fi
echo "{\"status\": \"$status\", \"message\": \"$message\"}"
}
################################################################################
# Determine overall recommendation
################################################################################
determine_recommendation() {
local not_pushed="$1"
local same_author="$2"
local safe_branch="$3"
local collaborators="$4"
# UNSAFE conditions (critical failures)
if [[ "$not_pushed" == "fail" ]] || [[ "$same_author" == "fail" ]]; then
echo "unsafe"
return
fi
# WARNING conditions (proceed with caution)
if [[ "$safe_branch" == "warn" ]] || [[ "$collaborators" == "warn" ]]; then
echo "warning"
return
fi
# SAFE (all checks pass)
echo "safe"
}
################################################################################
# Main execution
################################################################################
main() {
# Verify we're in a git repository
if ! git rev-parse --git-dir &>/dev/null; then
echo "{\"error\": \"Not a git repository\"}"
exit 2
fi
# Check if there are any commits
if ! git rev-parse HEAD &>/dev/null 2>&1; then
echo "{\"error\": \"No commits to amend (empty repository)\"}"
exit 2
fi
# Run all checks
local not_pushed_result
local same_author_result
local safe_branch_result
local collaborators_result
not_pushed_result=$(check_not_pushed)
same_author_result=$(check_same_author)
safe_branch_result=$(check_safe_branch)
collaborators_result=$(check_collaborators)
# Extract status values for recommendation
local not_pushed_status
local same_author_status
local safe_branch_status
local collaborators_status
not_pushed_status=$(echo "$not_pushed_result" | grep -o '"status": "[^"]*"' | cut -d'"' -f4)
same_author_status=$(echo "$same_author_result" | grep -o '"status": "[^"]*"' | cut -d'"' -f4)
safe_branch_status=$(echo "$safe_branch_result" | grep -o '"status": "[^"]*"' | cut -d'"' -f4)
collaborators_status=$(echo "$collaborators_result" | grep -o '"status": "[^"]*"' | cut -d'"' -f4)
# Determine overall recommendation
local recommendation
recommendation=$(determine_recommendation "$not_pushed_status" "$same_author_status" "$safe_branch_status" "$collaborators_status")
# Determine safe boolean
local safe="false"
if [[ "$recommendation" == "safe" ]]; then
safe="true"
fi
# Get commit info
local commit_sha
local commit_author
local branch
commit_sha=$(git rev-parse --short HEAD)
commit_author=$(git log -1 --format='%an <%ae>')
branch=$(git branch --show-current)
# Build JSON output
cat <<EOF
{
"safe": $safe,
"recommendation": "$recommendation",
"commit": "$commit_sha",
"author": "$commit_author",
"branch": "$branch",
"checks": {
"not_pushed": $not_pushed_result,
"same_author": $same_author_result,
"safe_branch": $safe_branch_result,
"collaborators": $collaborators_result
}
}
EOF
# Exit with appropriate code
case "$recommendation" in
safe)
exit 0
;;
warning)
exit 2
;;
unsafe)
exit 1
;;
esac
}
# Execute main function
main "$@"

View File

@@ -0,0 +1,345 @@
#!/usr/bin/env python3
################################################################################
# Commit Reviewer Script
#
# Purpose: Analyze commit quality including message, changes, and atomicity
# Version: 1.0.0
# Usage: ./commit-reviewer.py <commit-sha>
# Returns: JSON with comprehensive quality analysis
# Exit Codes:
# 0 = Success
# 1 = Commit not found
# 2 = Script execution error
################################################################################
import sys
import json
import subprocess
import re
from typing import Dict, List, Tuple, Any
################################################################################
# Git operations
################################################################################
def git_command(args: List[str]) -> str:
"""Execute git command and return output"""
try:
result = subprocess.run(
['git'] + args,
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except subprocess.CalledProcessError as e:
return ""
def commit_exists(sha: str) -> bool:
"""Check if commit exists"""
result = git_command(['rev-parse', '--verify', sha])
return bool(result)
def get_commit_info(sha: str) -> Dict[str, str]:
"""Get commit metadata"""
return {
'sha': git_command(['rev-parse', sha]),
'author': git_command(['log', '-1', '--format=%an <%ae>', sha]),
'date': git_command(['log', '-1', '--format=%ad', '--date=short', sha]),
'subject': git_command(['log', '-1', '--format=%s', sha]),
'body': git_command(['log', '-1', '--format=%b', sha]),
}
def get_commit_stats(sha: str) -> Dict[str, int]:
"""Get commit statistics"""
stats_raw = git_command(['show', '--stat', '--format=', sha])
files_changed = 0
insertions = 0
deletions = 0
test_files = 0
doc_files = 0
for line in stats_raw.split('\n'):
if '|' in line:
files_changed += 1
filename = line.split('|')[0].strip()
# Count test files
if 'test' in filename.lower() or 'spec' in filename.lower():
test_files += 1
# Count doc files
if filename.endswith('.md') or 'doc' in filename.lower():
doc_files += 1
# Parse summary line: "5 files changed, 234 insertions(+), 12 deletions(-)"
if 'insertion' in line:
match = re.search(r'(\d+) insertion', line)
if match:
insertions = int(match.group(1))
if 'deletion' in line:
match = re.search(r'(\d+) deletion', line)
if match:
deletions = int(match.group(1))
return {
'files_changed': files_changed,
'insertions': insertions,
'deletions': deletions,
'test_files': test_files,
'doc_files': doc_files,
}
################################################################################
# Message analysis
################################################################################
def analyze_message(subject: str, body: str) -> Dict[str, Any]:
"""Analyze commit message quality"""
# Check conventional commits format
conventional_pattern = r'^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9\-]+\))?: .+'
is_conventional = bool(re.match(conventional_pattern, subject, re.IGNORECASE))
# Extract type and scope if conventional
commit_type = None
commit_scope = None
if is_conventional:
match = re.match(r'^([a-z]+)(?:\(([a-z0-9\-]+)\))?: ', subject, re.IGNORECASE)
if match:
commit_type = match.group(1).lower()
commit_scope = match.group(2) if match.group(2) else None
# Check subject length
subject_length = len(subject)
subject_ok = subject_length <= 50
# Check imperative mood (basic heuristics)
imperative_verbs = ['add', 'fix', 'update', 'remove', 'refactor', 'improve', 'implement']
past_tense_patterns = ['added', 'fixed', 'updated', 'removed', 'refactored', 'improved', 'implemented']
subject_lower = subject.lower()
uses_imperative = any(subject_lower.startswith(verb) for verb in imperative_verbs)
uses_past_tense = any(pattern in subject_lower for pattern in past_tense_patterns)
# Check if body exists and is useful
has_body = bool(body.strip())
body_lines = body.strip().split('\n') if has_body else []
body_line_count = len([line for line in body_lines if line.strip()])
# Body quality assessment
has_explanation = body_line_count > 2
uses_bullets = any(line.strip().startswith(('-', '*', '')) for line in body_lines)
return {
'subject': subject,
'body': body if has_body else None,
'subject_length': subject_length,
'subject_ok': subject_ok,
'has_body': has_body,
'body_line_count': body_line_count,
'conventional': is_conventional,
'type': commit_type,
'scope': commit_scope,
'imperative': uses_imperative and not uses_past_tense,
'explanation': has_explanation,
'uses_bullets': uses_bullets,
}
################################################################################
# Atomicity analysis
################################################################################
def analyze_atomicity(sha: str, stats: Dict[str, int]) -> Dict[str, Any]:
"""Analyze if commit is atomic"""
# Get changed files
changed_files = git_command(['show', '--name-only', '--format=', sha]).split('\n')
changed_files = [f for f in changed_files if f.strip()]
# Analyze file types
file_types = set()
scopes = set()
for filepath in changed_files:
# Determine file type
if 'test' in filepath.lower() or 'spec' in filepath.lower():
file_types.add('test')
elif filepath.endswith('.md') or 'doc' in filepath.lower():
file_types.add('docs')
elif any(filepath.endswith(ext) for ext in ['.js', '.ts', '.py', '.go', '.rs', '.java']):
file_types.add('code')
elif any(filepath.endswith(ext) for ext in ['.json', '.yaml', '.yml', '.toml']):
file_types.add('config')
# Determine scope from path
parts = filepath.split('/')
if len(parts) > 1:
scopes.add(parts[0])
# Check for multiple types (excluding test + code as acceptable)
suspicious_type_mix = False
if 'docs' in file_types and 'code' in file_types:
suspicious_type_mix = True
if 'config' in file_types and len(file_types) > 2:
suspicious_type_mix = True
# Check for multiple scopes
multiple_scopes = len(scopes) > 2
# Size check (too large likely non-atomic)
too_large = stats['files_changed'] > 15 or stats['insertions'] > 500
# Determine atomicity
is_atomic = not (suspicious_type_mix or multiple_scopes or too_large)
issues = []
if suspicious_type_mix:
issues.append(f"Mixes {' and '.join(file_types)}")
if multiple_scopes:
issues.append(f"Affects multiple scopes: {', '.join(sorted(scopes))}")
if too_large:
issues.append(f"Large commit: {stats['files_changed']} files")
return {
'atomic': is_atomic,
'file_types': sorted(file_types),
'scopes': sorted(scopes),
'issues': issues,
}
################################################################################
# Quality scoring
################################################################################
def calculate_score(message: Dict[str, Any], stats: Dict[str, int], atomicity: Dict[str, Any]) -> Tuple[int, str, List[str]]:
"""Calculate overall quality score (0-100)"""
score = 100
issues = []
# Message quality (40 points)
if not message['conventional']:
score -= 10
issues.append("Not using conventional commits format")
if not message['subject_ok']:
score -= 5
issues.append(f"Subject too long ({message['subject_length']} chars, should be ≤50)")
if not message['imperative']:
score -= 5
issues.append("Subject not in imperative mood")
if not message['has_body'] and stats['files_changed'] > 3:
score -= 10
issues.append("No commit body explaining changes")
elif message['has_body'] and not message['explanation']:
score -= 5
issues.append("Commit body too brief")
# Atomicity (30 points)
if not atomicity['atomic']:
score -= 20
issues.extend(atomicity['issues'])
# Test coverage (20 points)
has_code_changes = 'code' in atomicity['file_types']
has_test_changes = stats['test_files'] > 0
if has_code_changes and not has_test_changes and stats['insertions'] > 100:
score -= 15
issues.append("No tests included for significant code changes")
elif has_code_changes and not has_test_changes:
score -= 5
issues.append("No tests included")
# Size appropriateness (10 points)
if stats['files_changed'] > 20:
score -= 5
issues.append(f"Very large commit ({stats['files_changed']} files)")
if stats['insertions'] > 1000:
score -= 5
issues.append(f"Very large changeset ({stats['insertions']} insertions)")
# Determine quality level
if score >= 90:
quality = "excellent"
elif score >= 70:
quality = "good"
elif score >= 50:
quality = "fair"
else:
quality = "poor"
return max(0, score), quality, issues
################################################################################
# Main execution
################################################################################
def main():
if len(sys.argv) < 2:
print(json.dumps({"error": "Usage: commit-reviewer.py <commit-sha>"}))
sys.exit(2)
commit_sha = sys.argv[1]
# Check if git repository
if not git_command(['rev-parse', '--git-dir']):
print(json.dumps({"error": "Not a git repository"}))
sys.exit(2)
# Check if commit exists
if not commit_exists(commit_sha):
print(json.dumps({"error": f"Commit not found: {commit_sha}"}))
sys.exit(1)
# Gather commit information
info = get_commit_info(commit_sha)
stats = get_commit_stats(commit_sha)
message_analysis = analyze_message(info['subject'], info['body'])
atomicity_analysis = analyze_atomicity(commit_sha, stats)
# Calculate quality score
score, quality, issues = calculate_score(message_analysis, stats, atomicity_analysis)
# Build output
output = {
'commit': info['sha'][:8],
'author': info['author'],
'date': info['date'],
'message': {
'subject': message_analysis['subject'],
'body': message_analysis['body'],
'subject_length': message_analysis['subject_length'],
'has_body': message_analysis['has_body'],
'conventional': message_analysis['conventional'],
'type': message_analysis['type'],
'scope': message_analysis['scope'],
},
'changes': {
'files_changed': stats['files_changed'],
'insertions': stats['insertions'],
'deletions': stats['deletions'],
'test_files': stats['test_files'],
'doc_files': stats['doc_files'],
},
'quality': {
'atomic': atomicity_analysis['atomic'],
'message_quality': quality,
'test_coverage': stats['test_files'] > 0,
'issues': issues,
},
'score': score,
}
print(json.dumps(output, indent=2))
sys.exit(0)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,322 @@
#!/usr/bin/env bash
################################################################################
# Pre-Commit Validation Script
#
# Purpose: Run comprehensive pre-commit checks to ensure code quality
# Version: 1.0.0
# Usage: ./pre-commit-check.sh [quick:true|false]
# Returns: JSON with validation results
# Exit Codes:
# 0 = All checks passed
# 1 = One or more checks failed
# 2 = Script execution error
################################################################################
set -euo pipefail
# Default to full validation
QUICK_MODE="${1:-quick:false}"
QUICK_MODE="${QUICK_MODE#quick:}"
# Initialize results
OVERALL_STATUS="pass"
declare -A RESULTS
################################################################################
# Check if tests pass
################################################################################
check_tests() {
local status="skip"
local message="Tests skipped in quick mode"
if [[ "$QUICK_MODE" != "true" ]]; then
# Detect test framework and run tests
if [[ -f "package.json" ]] && grep -q "\"test\":" package.json; then
if npm test &>/dev/null; then
status="pass"
message="All tests passed"
else
status="fail"
message="Tests failing"
OVERALL_STATUS="fail"
fi
elif [[ -f "pytest.ini" ]] || [[ -f "setup.py" ]]; then
if python -m pytest &>/dev/null; then
status="pass"
message="All tests passed"
else
status="fail"
message="Tests failing"
OVERALL_STATUS="fail"
fi
elif [[ -f "Cargo.toml" ]]; then
if cargo test &>/dev/null; then
status="pass"
message="All tests passed"
else
status="fail"
message="Tests failing"
OVERALL_STATUS="fail"
fi
elif [[ -f "go.mod" ]]; then
if go test ./... &>/dev/null; then
status="pass"
message="All tests passed"
else
status="fail"
message="Tests failing"
OVERALL_STATUS="fail"
fi
else
status="skip"
message="No test framework detected"
fi
fi
echo "{\"status\": \"$status\", \"message\": \"$message\"}"
}
################################################################################
# Check if lint passes
################################################################################
check_lint() {
local status="skip"
local message="Linting skipped in quick mode"
if [[ "$QUICK_MODE" != "true" ]]; then
# Detect linter and run
if [[ -f "package.json" ]] && (grep -q "eslint" package.json || [[ -f ".eslintrc.json" ]]); then
if npx eslint . --max-warnings 0 &>/dev/null; then
status="pass"
message="Linting passed"
else
status="fail"
message="Linting errors found"
OVERALL_STATUS="fail"
fi
elif command -v pylint &>/dev/null; then
if pylint $(git diff --cached --name-only --diff-filter=ACM | grep '\.py$') &>/dev/null; then
status="pass"
message="Linting passed"
else
status="fail"
message="Linting errors found"
OVERALL_STATUS="fail"
fi
elif command -v clippy &>/dev/null; then
if cargo clippy -- -D warnings &>/dev/null; then
status="pass"
message="Linting passed"
else
status="fail"
message="Linting errors found"
OVERALL_STATUS="fail"
fi
else
status="skip"
message="No linter detected"
fi
fi
echo "{\"status\": \"$status\", \"message\": \"$message\"}"
}
################################################################################
# Check for debug code in staged files
################################################################################
check_debug_code() {
local count=0
local locations=()
# Get staged diff
local diff_output
diff_output=$(git diff --cached)
# Search for debug patterns in added lines only
local debug_patterns=(
'console\.log'
'console\.debug'
'console\.error'
'debugger'
'print\('
'println!'
'pdb\.set_trace'
'binding\.pry'
'byebug'
'Debug\.Log'
)
for pattern in "${debug_patterns[@]}"; do
while IFS= read -r line; do
if [[ -n "$line" ]]; then
((count++))
locations+=("\"$line\"")
fi
done < <(echo "$diff_output" | grep "^+" | grep -v "^+++" | grep -E "$pattern" || true)
done
local status="pass"
local message="No debug code found"
if [[ $count -gt 0 ]]; then
status="fail"
message="Found $count debug statement(s)"
OVERALL_STATUS="fail"
fi
# Format locations array
local locations_json="[]"
if [[ ${#locations[@]} -gt 0 ]]; then
locations_json="[$(IFS=,; echo "${locations[*]}")]"
fi
echo "{\"status\": \"$status\", \"message\": \"$message\", \"count\": $count, \"locations\": $locations_json}"
}
################################################################################
# Check for TODOs in staged files
################################################################################
check_todos() {
local count=0
local locations=()
# Get staged diff
local diff_output
diff_output=$(git diff --cached)
# Search for TODO patterns in added lines only
local todo_patterns=(
'TODO'
'FIXME'
'XXX'
'HACK'
)
for pattern in "${todo_patterns[@]}"; do
while IFS= read -r line; do
if [[ -n "$line" ]]; then
((count++))
locations+=("\"$line\"")
fi
done < <(echo "$diff_output" | grep "^+" | grep -v "^+++" | grep -E "$pattern" || true)
done
local status="pass"
local message="No TODOs in staged code"
if [[ $count -gt 0 ]]; then
status="warn"
message="Found $count TODO/FIXME comment(s)"
# TODOs are warning, not failure (project decision)
fi
# Format locations array
local locations_json="[]"
if [[ ${#locations[@]} -gt 0 ]]; then
locations_json="[$(IFS=,; echo "${locations[*]}")]"
fi
echo "{\"status\": \"$status\", \"message\": \"$message\", \"count\": $count, \"locations\": $locations_json}"
}
################################################################################
# Check for merge conflict markers
################################################################################
check_merge_markers() {
local count=0
local locations=()
# Get staged files
local staged_files
staged_files=$(git diff --cached --name-only --diff-filter=ACM || true)
if [[ -n "$staged_files" ]]; then
# Search for conflict markers in staged files
while IFS= read -r file; do
if [[ -f "$file" ]]; then
local markers
markers=$(grep -n -E '^(<<<<<<<|=======|>>>>>>>)' "$file" || true)
if [[ -n "$markers" ]]; then
while IFS= read -r marker; do
((count++))
locations+=("\"$file:$marker\"")
done <<< "$markers"
fi
fi
done <<< "$staged_files"
fi
local status="pass"
local message="No merge markers found"
if [[ $count -gt 0 ]]; then
status="fail"
message="Found $count merge conflict marker(s)"
OVERALL_STATUS="fail"
fi
# Format locations array
local locations_json="[]"
if [[ ${#locations[@]} -gt 0 ]]; then
locations_json="[$(IFS=,; echo "${locations[*]}")]"
fi
echo "{\"status\": \"$status\", \"message\": \"$message\", \"count\": $count, \"locations\": $locations_json}"
}
################################################################################
# Main execution
################################################################################
main() {
# Verify we're in a git repository
if ! git rev-parse --git-dir &>/dev/null; then
echo "{\"error\": \"Not a git repository\"}"
exit 2
fi
# Check if there are staged changes
if ! git diff --cached --quiet 2>/dev/null; then
: # Has staged changes, continue
else
echo "{\"error\": \"No staged changes to validate\"}"
exit 2
fi
# Run all checks
local tests_result
local lint_result
local debug_result
local todos_result
local markers_result
tests_result=$(check_tests)
lint_result=$(check_lint)
debug_result=$(check_debug_code)
todos_result=$(check_todos)
markers_result=$(check_merge_markers)
# Build JSON output
cat <<EOF
{
"status": "$OVERALL_STATUS",
"quick_mode": $([[ "$QUICK_MODE" == "true" ]] && echo "true" || echo "false"),
"checks": {
"tests": $tests_result,
"lint": $lint_result,
"debug_code": $debug_result,
"todos": $todos_result,
"merge_markers": $markers_result
}
}
EOF
# Exit with appropriate code
if [[ "$OVERALL_STATUS" == "fail" ]]; then
exit 1
else
exit 0
fi
}
# Execute main function
main "$@"

View File

@@ -0,0 +1,259 @@
#!/usr/bin/env bash
################################################################################
# Revert Helper Script
#
# Purpose: Generate proper revert commit message and analyze safety
# Version: 1.0.0
# Usage: ./revert-helper.sh <commit-sha>
# Returns: JSON with revert information
# Exit Codes:
# 0 = Success
# 1 = Commit not found
# 2 = Script execution error
################################################################################
set -euo pipefail
################################################################################
# Parse conventional commit message
################################################################################
parse_commit_message() {
local subject="$1"
local type=""
local scope=""
local description=""
# Try to match conventional format: type(scope): description
if [[ "$subject" =~ ^([a-z]+)(\([a-z0-9\-]+\)):[[:space:]](.+)$ ]]; then
type="${BASH_REMATCH[1]}"
scope="${BASH_REMATCH[2]}" # includes parentheses
scope="${scope#(}" # remove leading (
scope="${scope%)}" # remove trailing )
description="${BASH_REMATCH[3]}"
# Try to match without scope: type: description
elif [[ "$subject" =~ ^([a-z]+):[[:space:]](.+)$ ]]; then
type="${BASH_REMATCH[1]}"
scope=""
description="${BASH_REMATCH[2]}"
else
# Non-conventional format
type=""
scope=""
description="$subject"
fi
echo "$type|$scope|$description"
}
################################################################################
# Generate revert commit message
################################################################################
generate_revert_message() {
local commit_sha="$1"
local original_subject="$2"
# Parse original message
local parsed
parsed=$(parse_commit_message "$original_subject")
local type
local scope
local description
IFS='|' read -r type scope description <<< "$parsed"
# Build revert message
local revert_subject
if [[ -n "$type" ]]; then
# Conventional format
if [[ -n "$scope" ]]; then
revert_subject="revert: $type($scope): $description"
else
revert_subject="revert: $type: $description"
fi
else
# Non-conventional format
revert_subject="revert: $original_subject"
fi
# Build full message (subject + body + footer)
local revert_message
revert_message=$(cat <<EOF
$revert_subject
This reverts commit $commit_sha.
Reason: [Provide reason for revert here]
EOF
)
echo "$revert_message"
}
################################################################################
# Analyze revert safety
################################################################################
analyze_revert_safety() {
local commit_sha="$1"
local safe_to_revert="true"
local warnings=()
# Check for dependent commits (commits that touch same files after this one)
local files_changed
files_changed=$(git show --name-only --format= "$commit_sha")
local dependent_count=0
local dependent_commits=()
if [[ -n "$files_changed" ]]; then
# Get commits after this one
local later_commits
later_commits=$(git log "$commit_sha"..HEAD --oneline --format='%h %s' || echo "")
if [[ -n "$later_commits" ]]; then
while IFS= read -r commit_line; do
local later_sha
later_sha=$(echo "$commit_line" | awk '{print $1}')
# Check if any files overlap
local later_files
later_files=$(git show --name-only --format= "$later_sha" 2>/dev/null || echo "")
# Check for file overlap
while IFS= read -r file; do
if [[ -n "$file" ]] && echo "$files_changed" | grep -qxF "$file"; then
dependent_commits+=("$commit_line")
((dependent_count++))
break
fi
done <<< "$later_files"
done <<< "$later_commits"
if [[ $dependent_count -gt 0 ]]; then
safe_to_revert="false"
warnings+=("\"$dependent_count commit(s) depend on this change\"")
fi
fi
fi
# Check if files still exist (if deleted, might be harder to revert)
local deleted_files=0
while IFS= read -r file; do
if [[ -n "$file" ]] && [[ ! -f "$file" ]]; then
((deleted_files++))
fi
done <<< "$files_changed"
if [[ $deleted_files -gt 0 ]]; then
warnings+=("\"$deleted_files file(s) from commit no longer exist\"")
fi
# Check for potential merge conflicts (files modified since commit)
local modified_files=0
while IFS= read -r file; do
if [[ -n "$file" ]] && [[ -f "$file" ]]; then
# Check if file has been modified since this commit
local file_changed
file_changed=$(git log "$commit_sha"..HEAD --oneline -- "$file" | wc -l)
if [[ $file_changed -gt 0 ]]; then
((modified_files++))
fi
fi
done <<< "$files_changed"
if [[ $modified_files -gt 0 ]]; then
warnings+=("\"$modified_files file(s) modified since commit - potential conflicts\"")
fi
# Format warnings array
local warnings_json="[]"
if [[ ${#warnings[@]} -gt 0 ]]; then
warnings_json="[$(IFS=,; echo "${warnings[*]}")]"
fi
echo "{\"safe_to_revert\": $safe_to_revert, \"warnings\": $warnings_json, \"dependent_count\": $dependent_count}"
}
################################################################################
# Main execution
################################################################################
main() {
# Check arguments
if [[ $# -lt 1 ]]; then
echo "{\"error\": \"Usage: revert-helper.sh <commit-sha>\"}"
exit 2
fi
local commit_sha="$1"
# Verify we're in a git repository
if ! git rev-parse --git-dir &>/dev/null; then
echo "{\"error\": \"Not a git repository\"}"
exit 2
fi
# Verify commit exists
if ! git rev-parse --verify "$commit_sha" &>/dev/null 2>&1; then
echo "{\"error\": \"Commit not found: $commit_sha\"}"
exit 1
fi
# Get full commit SHA
local full_sha
full_sha=$(git rev-parse "$commit_sha")
local short_sha
short_sha=$(git rev-parse --short "$commit_sha")
# Get commit information
local original_subject
local original_author
local commit_date
local files_affected
original_subject=$(git log -1 --format='%s' "$commit_sha")
original_author=$(git log -1 --format='%an <%ae>' "$commit_sha")
commit_date=$(git log -1 --format='%ad' --date=short "$commit_sha")
files_affected=$(git show --name-only --format= "$commit_sha" | wc -l)
# Parse commit type and scope
local parsed
parsed=$(parse_commit_message "$original_subject")
local type
local scope
local description
IFS='|' read -r type scope description <<< "$parsed"
# Generate revert message
local revert_message
revert_message=$(generate_revert_message "$short_sha" "$original_subject")
# Analyze safety
local safety_analysis
safety_analysis=$(analyze_revert_safety "$commit_sha")
# Build JSON output
cat <<EOF
{
"commit": "$short_sha",
"full_sha": "$full_sha",
"original_message": "$original_subject",
"original_author": "$original_author",
"commit_date": "$commit_date",
"type": ${type:+\"$type\"},
"scope": ${scope:+\"$scope\"},
"files_affected": $files_affected,
"revert_message": $(echo "$revert_message" | jq -Rs .),
"safety": $safety_analysis
}
EOF
exit 0
}
# Execute main function
main "$@"

View File

@@ -0,0 +1,255 @@
# Commit Best Practices Skill - Usage Examples
## Quick Reference
```bash
# Pre-commit validation (full)
/commit-best-practices check-pre-commit
# Pre-commit validation (quick - skip tests/lint)
/commit-best-practices check-pre-commit quick:true
# Review last commit
/commit-best-practices review-commit
# Review specific commit
/commit-best-practices review-commit commit:abc123
# Check if safe to amend
/commit-best-practices amend-guidance
# Amend with force (bypass safety)
/commit-best-practices amend-guidance force:true
# Revert guidance for commit
/commit-best-practices revert-guidance commit:abc123
# Workflow tips (all)
/commit-best-practices workflow-tips
# Workflow tips (specific focus)
/commit-best-practices workflow-tips focus:commit
/commit-best-practices workflow-tips focus:branch
/commit-best-practices workflow-tips focus:merge
```
## Workflow Example 1: Safe Commit Flow
```
Developer: "I want to commit my changes"
Agent:
1. Runs: /commit-best-practices check-pre-commit
2. IF checks fail:
- Shows: "Tests failing, debug code found"
- Blocks commit
- Provides guidance to fix
3. IF checks pass:
- Continues with commit workflow
- Generates message
- Creates commit
- Reviews commit quality
```
## Workflow Example 2: Amend Safety Check
```
Developer: "I need to amend my last commit"
Agent:
1. Runs: /commit-best-practices amend-guidance
2. Checks:
- Not pushed to remote? ✓
- Same author? ✓
- Not on main/master? ✓
3. Result: SAFE - provides amend instructions
OR if unsafe:
3. Result: UNSAFE - commit already pushed
- Shows alternatives (new commit, revert)
- Blocks amend
```
## Workflow Example 3: Commit Review & Improvement
```
Developer commits code
Agent (automatically):
1. Runs: /commit-best-practices review-commit
2. Analyzes:
- Message quality: GOOD
- Atomicity: ✓ single feature
- Tests: ⚠ missing
- Score: 72/100
3. Suggests:
- "Good commit! Consider adding unit tests."
- "Safe to push with minor note."
```
## Script Output Examples
### pre-commit-check.sh
```json
{
"status": "fail",
"quick_mode": false,
"checks": {
"tests": {"status": "fail", "message": "Tests failing"},
"lint": {"status": "pass", "message": "Linting passed"},
"debug_code": {"status": "fail", "count": 3, "locations": ["src/auth.js:42: console.log(user)"]},
"todos": {"status": "warn", "count": 1, "locations": ["src/auth.js:56: TODO: refactor"]},
"merge_markers": {"status": "pass", "count": 0, "locations": []}
}
}
```
### commit-reviewer.py
```json
{
"commit": "abc123",
"author": "John Doe <john@example.com>",
"date": "2025-10-13",
"message": {
"subject": "feat(auth): add OAuth authentication",
"subject_length": 38,
"conventional": true,
"type": "feat",
"scope": "auth"
},
"changes": {
"files_changed": 5,
"insertions": 234,
"deletions": 12,
"test_files": 1
},
"quality": {
"atomic": true,
"message_quality": "excellent",
"test_coverage": true,
"issues": []
},
"score": 95
}
```
### amend-safety.sh
```json
{
"safe": true,
"recommendation": "safe",
"commit": "abc123",
"author": "John Doe <john@example.com>",
"branch": "feature/oauth",
"checks": {
"not_pushed": {"status": "pass", "message": "Commit not pushed to origin/feature/oauth"},
"same_author": {"status": "pass", "message": "You are the commit author"},
"safe_branch": {"status": "pass", "message": "On feature branch: feature/oauth"},
"collaborators": {"status": "pass", "message": "Solo work on branch"}
}
}
```
### revert-helper.sh
```json
{
"commit": "abc123",
"original_message": "feat(auth): add OAuth authentication",
"type": "feat",
"scope": "auth",
"files_affected": 5,
"revert_message": "revert: feat(auth): add OAuth authentication\n\nThis reverts commit abc123.\n\nReason: [Provide reason here]",
"safety": {
"safe_to_revert": true,
"warnings": [],
"dependent_count": 0
}
}
```
## Integration with Other Skills
```
/commit-best-practices check-pre-commit
↓ (if pass)
/commit-analysis analyze
/message-generation complete-message
/commit (create commit)
/commit-best-practices review-commit
↓ (if score < 70)
/commit-best-practices amend-guidance
```
## Best Practices Enforced
1. **Tests must pass** - No commits with failing tests
2. **No debug code** - No console.log, debugger in commits
3. **No TODOs** - Fix or remove before committing
4. **No merge markers** - Resolve conflicts fully
5. **Atomic commits** - One logical change per commit
6. **Quality messages** - Conventional commits format
7. **Safe amends** - Only amend unpushed commits
8. **Proper reverts** - Use revert for shared history
## Common Scenarios
### Scenario 1: Debug Code Found
```
Pre-commit check fails:
❌ Debug code: 3 instances found
- src/auth.js:42: console.log(user)
- src/api.js:18: debugger statement
Action: Remove debug code, re-stage files, retry
```
### Scenario 2: Tests Failing
```
Pre-commit check fails:
❌ Tests: 2 failing
- test/auth.test.js: OAuth flow test
Action: Fix tests, verify passing, retry
```
### Scenario 3: Unsafe Amend
```
Amend safety check:
❌ UNSAFE: Commit already pushed to remote
Alternatives:
1. Create new commit (recommended)
2. Use git revert (if undoing)
3. Coordinate with team before force push
```
### Scenario 4: Non-Atomic Commit
```
Commit review:
Score: 58/100
⚠ Non-atomic: Mixes feat + fix + docs
Recommendation:
Split into 3 commits:
1. feat(auth): OAuth implementation
2. fix(api): null pointer handling
3. docs: authentication guide
Use: /commit-split
```
---
This skill ensures high-quality commits through automated validation, intelligent review, and guided workflows.

View File

@@ -0,0 +1,373 @@
# Operation: Safe Commit Amend Guidance
Guide users through safely amending commits with comprehensive safety checks.
## Parameters from $ARGUMENTS
- **force** (optional): Skip safety checks (default: false) - USE WITH EXTREME CAUTION
Parse as: `amend-guidance force:true` or `amend-guidance`
## Amend Safety Workflow
### Step 1: Run Amend Safety Check
Execute safety validation script:
```bash
./.claude/commands/commit-best-practices/.scripts/amend-safety.sh
```
The script checks:
1. Commit not pushed to remote
2. Same author (current user matches commit author)
3. Not on protected branch (main/master)
4. No collaborators have pulled it
Returns:
```json
{
"safe": true|false,
"checks": {
"not_pushed": {"status": "pass|fail", "message": "..."},
"same_author": {"status": "pass|fail", "message": "..."},
"safe_branch": {"status": "pass|warn", "message": "..."},
"collaborators": {"status": "pass|warn", "message": "..."}
},
"recommendation": "safe|warning|unsafe"
}
```
### Step 2: Evaluate Safety Status
**SAFE (All checks pass):**
```
✅ SAFE TO AMEND
Last commit: abc123
Author: John Doe <john@example.com> (you)
Branch: feature/oauth-implementation
Status: Not pushed to remote
All safety checks passed:
✅ Commit not pushed to remote
✅ You are the author
✅ Not on main/master branch
✅ No collaborators have this commit
It is SAFE to amend this commit.
```
**WARNING (Minor concerns):**
```
⚠️ AMEND WITH CAUTION
Last commit: abc123
Author: John Doe <john@example.com> (you)
Branch: main
Status: Not pushed to remote
Safety checks:
✅ Commit not pushed to remote
✅ You are the author
⚠️ WARNING: On main/master branch
✅ No collaborators have this commit
Amending on main/master is discouraged but technically safe if not pushed.
Recommendation: Create new commit instead of amending.
```
**UNSAFE (Critical issues):**
```
❌ UNSAFE TO AMEND
Last commit: abc123
Author: Jane Smith <jane@example.com> (NOT you)
Branch: feature/oauth
Status: Pushed to origin/feature/oauth
Safety violations:
❌ CRITICAL: Commit already pushed to remote
❌ CRITICAL: Different author (Jane Smith, not you)
✅ Safe branch (not main/master)
⚠️ WARNING: Other developers may have pulled this
DO NOT AMEND THIS COMMIT!
Amending will:
1. Rewrite git history
2. Break other developers' work
3. Require force push (dangerous)
4. Cause merge conflicts for collaborators
Use: git revert (to undo changes safely)
Or: Create new commit (to add fixes)
```
### Step 3: Provide Amend Instructions
**If SAFE, show how to amend:**
```
How to Amend Commit
===================
Current commit message:
---
feat(auth): add OAuth authentication
Implement OAuth2 flow for Google and GitHub
---
Option 1: Amend with staged changes
-------------------------------------
1. Make your changes to files
2. Stage changes: git add <files>
3. Amend commit: git commit --amend
This opens editor to modify message if needed.
Option 2: Amend message only
-----------------------------
git commit --amend
Opens editor to change commit message.
No file changes included.
Option 3: Amend with new message (no editor)
---------------------------------------------
git commit --amend -m "new message here"
Direct message change without editor.
After amending:
---------------
Review commit: /commit-best-practices review-commit
Verify changes: git show HEAD
Continue work or push: git push origin <branch>
Note: If you already pushed, you'll need: git push --force-with-lease
(Only if you're certain no one else has pulled your changes!)
```
**If UNSAFE, show alternatives:**
```
Safe Alternatives to Amending
==============================
Since amending is unsafe, use these alternatives:
Option 1: Create New Commit (Recommended)
------------------------------------------
Make your changes and commit normally:
1. Make changes to files
2. Stage: git add <files>
3. Commit: git commit -m "fix: address review feedback"
This preserves history and is safe for collaborators.
Option 2: Revert Previous Commit
---------------------------------
If the commit is wrong, revert it:
1. Revert: git revert HEAD
2. Make correct changes
3. Commit: git commit -m "correct implementation"
This creates two new commits (revert + fix).
Option 3: Interactive Rebase (Advanced)
----------------------------------------
Only if you're experienced and coordinate with team:
1. git rebase -i HEAD~2
2. Mark commit as 'edit'
3. Make changes
4. git rebase --continue
⚠️ WARNING: Requires force push, coordinate with team!
Best Practice:
--------------
When in doubt, create a new commit. It's always safer.
```
### Step 4: Force Mode Handling
If user specified `force:true`:
```
⚠️ FORCE MODE ENABLED
You've bypassed safety checks. This is DANGEROUS!
Proceeding with amend despite warnings.
Current commit: abc123
Branch: main
Status: Pushed to remote
You are responsible for:
1. Coordinating with team before force push
2. Ensuring no one has pulled this commit
3. Handling any conflicts that arise
4. Notifying team of force push
Force push command (if you must):
git push --force-with-lease origin <branch>
Better alternatives:
- Create new commit instead
- Use git revert for pushed commits
- Coordinate with team before rewriting history
Proceed with extreme caution!
```
## Safety Rules Summary
### ✅ SAFE to amend if ALL true:
1. **Not pushed to remote**
- `git log @{upstream}..HEAD` shows commit
- Commit only exists locally
2. **Same author**
- `git config user.email` matches commit author
- You made the commit
3. **Not on protected branch**
- Not on main/master
- Or: on feature branch
4. **No collaborators affected**
- Solo work on branch
- Or: coordinated with team
### ❌ NEVER amend if ANY true:
1. **Already pushed to remote**
- Commit exists on origin
- Others may have pulled it
2. **Different author**
- Someone else made the commit
- Not your work to modify
3. **Collaborators working on same branch**
- Team has pulled your commit
- Would break their work
4. **On shared/protected branch**
- main/master branch
- Production/release branches
## When Amend is Useful
**Good use cases:**
- Fix typo in commit message (before push)
- Add forgotten file to commit (before push)
- Improve commit message clarity (before push)
- Reformat message to match conventions (before push)
**Bad use cases:**
- Fixing bug in pushed commit (use new commit)
- Changing commit after code review started (confusing)
- Modifying shared branch commits (breaks collaboration)
- Rewriting public history (dangerous)
## Output Format
```
Amend Safety Check
==================
Commit: <sha>
Author: <name> <email> [you|NOT you]
Branch: <branch>
Status: [Local only|Pushed to <remote>]
SAFETY CHECKS:
[✅|⚠️|❌] Commit not pushed
[✅|⚠️|❌] Same author
[✅|⚠️|❌] Safe branch
[✅|⚠️|❌] No collaborators affected
VERDICT: [SAFE|WARNING|UNSAFE]
[Detailed explanation]
[If SAFE: Amend instructions]
[If UNSAFE: Safe alternatives]
```
## Error Handling
**No commits to amend:**
```
ERROR: No commits to amend
This is the first commit (no parent)
Use: git commit (to create first commit)
```
**Not a git repository:**
```
ERROR: Not a git repository
Run: git init (to initialize)
```
**Script execution error:**
```
ERROR: Amend safety check script failed
Check: .claude/commands/commit-best-practices/.scripts/amend-safety.sh exists
Verify: Script is executable
```
## Integration with Agent
When user says "amend my commit" or "fix my commit":
1. Agent MUST run safety check FIRST
2. If unsafe, BLOCK amend and show alternatives
3. If safe, provide clear amend instructions
4. Never allow unsafe amend without explicit force flag
## Git Commands Reference
```bash
# Check if commit is pushed
git log @{upstream}..HEAD
# Check commit author
git log -1 --format='%an %ae'
# Check current branch
git branch --show-current
# Check remote tracking
git rev-parse --abbrev-ref --symbolic-full-name @{upstream}
# Amend commit (opens editor)
git commit --amend
# Amend with message (no editor)
git commit --amend -m "new message"
# Amend without changing message
git commit --amend --no-edit
# Force push if needed (DANGEROUS)
git push --force-with-lease origin <branch>
```
## Best Practices
1. **Default to safe**: Always check before amending
2. **Never force without reason**: Force flag is dangerous
3. **Prefer new commits**: When in doubt, commit new changes
4. **Communicate**: Tell team before force pushing
5. **Use revert**: For pushed commits, revert instead of amend
Safe git practices prevent broken collaboration and lost work.

View File

@@ -0,0 +1,214 @@
# Operation: Pre-Commit Validation
Validate repository state and changes before committing to ensure quality standards.
## Parameters from $ARGUMENTS
- **quick** (optional): `true` for fast checks only, `false` for full validation (default: false)
Parse as: `check-pre-commit quick:true` or `check-pre-commit`
## Pre-Commit Validation Workflow
### Step 1: Repository State Check
Verify git repository is valid and has changes:
```bash
# Check if git repository
if ! git rev-parse --git-dir >/dev/null 2>&1; then
ERROR: "Not a git repository"
exit 1
fi
# Check for changes to commit
git status --short
```
If no changes, inform user: "Working tree clean. No changes to commit."
### Step 2: Run Pre-Commit Checks Script
Execute comprehensive validation:
```bash
cd $(git rev-parse --show-toplevel)
./.claude/commands/commit-best-practices/.scripts/pre-commit-check.sh quick:${quick:-false}
```
The script returns JSON:
```json
{
"status": "pass|fail",
"checks": {
"tests": {"status": "pass|fail|skip", "message": "..."},
"lint": {"status": "pass|fail|skip", "message": "..."},
"debug_code": {"status": "pass|fail", "count": 0, "locations": []},
"todos": {"status": "pass|warn", "count": 0, "locations": []},
"merge_markers": {"status": "pass|fail", "count": 0, "locations": []}
}
}
```
### Step 3: Parse Results
**If status = "pass"**:
```
✅ Pre-commit validation passed!
All checks completed successfully:
✅ Tests: passed
✅ Lint: passed
✅ Debug code: none found
✅ TODOs: none in committed code
✅ Merge markers: none found
Safe to commit. Proceed with: /commit
```
**If status = "fail"**:
```
❌ Pre-commit validation failed!
Issues found:
❌ Tests: 2 failing
- test/auth.test.js: OAuth flow test
- test/api.test.js: null pointer test
❌ Debug code: 3 instances found
- src/auth.js:42: console.log(user)
- src/api.js:18: debugger statement
- src/utils.js:91: print(response)
❌ TODOs: 1 found in staged code
- src/auth.js:56: TODO: refactor this
Cannot commit until issues are resolved.
Actions to take:
1. Fix failing tests
2. Remove debug statements (console.log, debugger, print)
3. Resolve or remove TODOs in staged files
4. Run: /commit-best-practices check-pre-commit (to re-validate)
```
### Step 4: Provide Guidance
Based on failures, provide specific remediation:
**Tests Failing:**
```
To fix tests:
1. Run tests locally: npm test (or pytest, cargo test, etc.)
2. Review failures and fix issues
3. Verify all tests pass
4. Re-run validation
```
**Debug Code Found:**
```
To remove debug code:
1. Search for: console.log, debugger, print(, pdb.
2. Remove or comment out debug statements
3. Consider using proper logging instead
4. Re-stage files: git add <file>
```
**TODOs in Committed Code:**
```
TODOs found in staged code:
1. Either: Fix the TODO items now
2. Or: Unstage files with TODOs (git reset HEAD <file>)
3. Or: Remove TODO comments temporarily
Best practice: Don't commit TODOs to main/master
```
**Merge Markers Found:**
```
Merge conflict markers detected:
- <<<<<<<
- =======
- >>>>>>>
Actions:
1. Resolve merge conflicts completely
2. Remove all conflict markers
3. Test merged code
4. Re-stage resolved files
```
## Output Format
Provide clear, actionable feedback:
```
Pre-Commit Validation Report
============================
Status: [PASS|FAIL]
Checks Performed:
[✅|❌] Tests: [result]
[✅|❌] Lint: [result]
[✅|❌] Debug Code: [result]
[✅|❌] TODOs: [result]
[✅|❌] Merge Markers: [result]
[If FAIL: Detailed issue list with file locations]
[If FAIL: Remediation steps]
[If PASS: "Safe to commit" confirmation]
```
## Quick Mode
If `quick:true`:
- Skip test execution (assume tests run in CI)
- Skip lint execution (assume linter runs separately)
- Only check: debug code, TODOs, merge markers
- Much faster (~1 second vs ~30 seconds)
Use quick mode for rapid iteration during development.
## Error Handling
**Not a git repository:**
```
ERROR: Not a git repository
Run: git init (to initialize)
Or: cd to correct directory
```
**No changes to validate:**
```
INFO: Working tree clean
No changes staged for commit
Use: git add <files> (to stage changes)
```
**Script execution error:**
```
ERROR: Pre-commit check script failed
Check: .claude/commands/commit-best-practices/.scripts/pre-commit-check.sh exists
Verify: Script is executable (chmod +x)
```
## Integration with Agent
When user says "commit my changes":
1. Agent MUST run this check FIRST
2. If check fails, BLOCK commit and provide guidance
3. If check passes, proceed with commit workflow
4. Never allow commit with failing validation (unless user explicitly forces)
## Best Practices Enforced
1. **Tests must pass** - Failing tests = broken code
2. **No debug code** - console.log, debugger not for production
3. **No committed TODOs** - Fix or remove before commit
4. **No merge markers** - Resolve conflicts completely
5. **Lint compliance** - Follow project style
These checks ensure high code quality and prevent common mistakes.

View File

@@ -0,0 +1,419 @@
# Operation: Commit Revert Guidance
Guide users through safely reverting commits with proper formatting and workflow.
## Parameters from $ARGUMENTS
- **commit** (required): Commit SHA to revert
Parse as: `revert-guidance commit:abc123`
## Revert Workflow
### Step 1: Validate Commit
Verify commit exists and get details:
```bash
# Check if commit exists
if ! git rev-parse --verify ${commit} >/dev/null 2>&1; then
ERROR: "Commit not found: ${commit}"
exit 1
fi
# Get commit details
commit_sha=$(git rev-parse ${commit})
commit_subject=$(git log -1 --format='%s' ${commit_sha})
commit_author=$(git log -1 --format='%an <%ae>' ${commit_sha})
commit_date=$(git log -1 --format='%ad' ${commit_sha})
```
### Step 2: Run Revert Helper Script
Generate proper revert commit message:
```bash
./.claude/commands/commit-best-practices/.scripts/revert-helper.sh "${commit_sha}"
```
Returns:
```json
{
"commit": "abc123...",
"original_message": "feat(auth): add OAuth authentication",
"revert_message": "revert: feat(auth): add OAuth authentication\n\nThis reverts commit abc123.\n\nReason: OAuth implementation incompatible with current auth system",
"type": "feat",
"scope": "auth",
"files_affected": 5,
"safe_to_revert": true,
"warnings": []
}
```
### Step 3: Analyze Revert Safety
Check if revert will cause issues:
**Safe to revert:**
```
✅ Safe to revert
Commit: abc123
Original: feat(auth): add OAuth authentication
Author: John Doe
Date: 2025-10-10
Analysis:
✅ No dependent commits found
✅ No merge conflicts expected
✅ Files still exist
✅ Clean revert possible
This commit can be safely reverted.
```
**Potential issues:**
```
⚠️ Revert with caution
Commit: abc123
Original: feat(auth): add OAuth authentication
Author: John Doe
Date: 2025-10-10
Warnings:
⚠️ 3 commits depend on this change
- def456: feat(auth): add OAuth providers
- ghi789: fix(auth): OAuth token refresh
- jkl012: docs: OAuth setup guide
⚠️ Potential merge conflicts
- src/auth/oauth.js: modified in later commits
- src/config/auth.js: modified in later commits
Reverting will require:
1. Resolving merge conflicts
2. Possibly reverting dependent commits first
3. Updating or removing affected features
Consider: Revert dependent commits in reverse order.
```
### Step 4: Generate Revert Message
Follow proper revert commit message format:
```
Conventional Commits Revert Format:
-----------------------------------
revert: <original-type>(<original-scope>): <original-subject>
This reverts commit <sha>.
Reason: <explanation-of-why>
[Optional: Additional context]
[Optional: BREAKING CHANGE if revert breaks functionality]
[Optional: Issue references]
Example:
-----------------------------------
revert: feat(auth): add OAuth authentication
This reverts commit abc123def456789.
Reason: OAuth implementation incompatible with existing SAML
authentication system. Caused authentication failures for
enterprise users.
Need to redesign OAuth to work alongside SAML before
reintroducing.
BREAKING CHANGE: OAuth authentication temporarily removed.
Users must use username/password authentication.
Refs: #456
```
### Step 5: Provide Revert Instructions
**Simple revert (no conflicts):**
```
How to Revert Commit
====================
Commit to revert: abc123
Original message: feat(auth): add OAuth authentication
Generated revert message:
---
revert: feat(auth): add OAuth authentication
This reverts commit abc123.
Reason: [Provide reason here]
---
Option 1: Auto-revert with Git (Recommended)
---------------------------------------------
git revert abc123
This will:
1. Create revert commit automatically
2. Open editor for message (pre-filled)
3. Add reason for revert
4. Save and close
Option 2: Manual revert
-----------------------
1. git revert --no-commit abc123
2. Review changes: git diff --cached
3. Adjust if needed
4. git commit (use message above)
After reverting:
----------------
1. Test that functionality is restored
2. Verify no broken dependencies
3. Push: git push origin <branch>
The revert commit preserves history while undoing changes.
```
**Complex revert (conflicts or dependencies):**
```
Complex Revert Required
=======================
Commit to revert: abc123
Original: feat(auth): add OAuth authentication
Dependencies: 3 commits depend on this
Revert Strategy:
----------------
Step 1: Revert dependent commits FIRST (newest to oldest)
git revert jkl012 # docs: OAuth setup guide
git revert ghi789 # fix(auth): OAuth token refresh
git revert def456 # feat(auth): add OAuth providers
Step 2: Revert original commit
git revert abc123 # feat(auth): add OAuth authentication
Step 3: Handle conflicts
If conflicts occur:
1. git status (see conflicted files)
2. Edit files to resolve conflicts
3. git add <resolved-files>
4. git revert --continue
Step 4: Test thoroughly
- Run full test suite
- Verify auth system works
- Check for broken functionality
Alternative: Revert in single commit
-------------------------------------
If you want one revert commit for all:
1. git revert --no-commit jkl012
2. git revert --no-commit ghi789
3. git revert --no-commit def456
4. git revert --no-commit abc123
5. Resolve any conflicts
6. git commit -m "revert: OAuth authentication feature
This reverts commits abc123, def456, ghi789, jkl012.
Reason: OAuth incompatible with SAML system."
This creates a single revert commit for multiple changes.
```
### Step 6: Post-Revert Checklist
After reverting, verify:
```
Post-Revert Checklist
=====================
□ Revert commit created successfully
git log -1 (verify revert commit exists)
□ Changes actually reverted
git diff <commit>^..<commit> (should be inverse of original)
□ Tests pass
npm test (or your test command)
□ No broken functionality
Test affected features manually
□ Documentation updated
Update docs if feature was documented
□ Team notified
Inform team of revert and reason
□ Issue tracker updated
Comment on related issues
□ Ready to push
git push origin <branch>
```
## Revert vs Reset vs Amend
**Use `git revert` when:**
- ✅ Commit already pushed to remote
- ✅ Working on shared branch
- ✅ Need to preserve history
- ✅ Undoing specific commit in middle of history
**Use `git reset` when:**
- ⚠️ Commit not yet pushed (local only)
- ⚠️ Want to completely remove commit
- ⚠️ Rewriting personal branch history
- ⚠️ No one else has the commit
**Use `git commit --amend` when:**
- ⚠️ Fixing most recent commit only
- ⚠️ Commit not yet pushed
- ⚠️ Small typo or forgotten file
**Decision tree:**
```
Has commit been pushed?
├─ Yes → Use git revert (safe for shared history)
└─ No → Is it the most recent commit?
├─ Yes → Use git commit --amend (if minor fix)
└─ No → Use git reset (if complete removal needed)
```
## Revert Message Examples
**Example 1: Bug found**
```
revert: fix(api): add null check in user endpoint
This reverts commit abc123.
Reason: Fix introduced regression causing authentication
failures for legacy clients. Null check logic incorrect.
Will reimplement with proper validation after adding tests.
Fixes: #789
```
**Example 2: Performance issue**
```
revert: perf(search): implement new search algorithm
This reverts commit def456.
Reason: New algorithm causes 300% increase in database
load under production traffic. Needs optimization before
redeployment.
Keeping old algorithm until performance issues resolved.
```
**Example 3: Breaking change**
```
revert: feat(api): change authentication endpoint format
This reverts commit ghi789.
Reason: Breaking change deployed without proper migration
period. Mobile app version 1.x incompatible with new format.
BREAKING CHANGE: Reverting API format to v1 for backwards
compatibility. Will reintroduce in v2 with migration path.
Refs: #234, #235, #236 (user reports)
```
## Output Format
```
Revert Guidance Report
======================
Commit to Revert: <sha>
Original Message: <message>
Author: <name>
Date: <date>
REVERT SAFETY:
[✅|⚠️] No dependent commits
[✅|⚠️] No merge conflicts expected
[✅|⚠️] Files still exist
GENERATED REVERT MESSAGE:
---
<revert-message-content>
---
REVERT INSTRUCTIONS:
[Step-by-step instructions]
POST-REVERT CHECKLIST:
[Verification steps]
```
## Error Handling
**Commit not found:**
```
ERROR: Commit not found: abc123
Check: git log (to see available commits)
Verify: Commit hash is correct (7+ characters)
```
**Already reverted:**
```
WARNING: This commit may already be reverted
Found revert commit: xyz789
Message: "revert: feat(auth): add OAuth authentication"
If this is a duplicate revert, it may cause issues.
Check: git log (to verify)
```
**Merge commit:**
```
WARNING: This is a merge commit
Merge commits require special handling:
git revert -m 1 abc123
-m 1: Keep changes from first parent
-m 2: Keep changes from second parent
Determine which parent to keep based on your branch strategy.
```
## Integration with Agent
When user says "revert my commit" or "undo commit abc123":
1. Agent identifies commit to revert
2. Runs safety analysis
3. Generates proper revert message
4. Provides step-by-step instructions
5. Offers post-revert verification checklist
## Best Practices
1. **Always provide reason**: Explain WHY reverting in message
2. **Reference original commit**: Include SHA in revert message
3. **Test after revert**: Ensure functionality restored
4. **Notify team**: Communicate reverts to collaborators
5. **Update issues**: Comment on related issue tracker items
6. **Preserve history**: Use revert, not reset, for shared branches
Proper reverts maintain clear history and facilitate collaboration.

View File

@@ -0,0 +1,347 @@
# Operation: Review Commit Quality
Analyze a commit's quality including message, changes, atomicity, and completeness.
## Parameters from $ARGUMENTS
- **commit** (optional): Commit SHA or reference (default: HEAD)
Parse as: `review-commit commit:abc123` or `review-commit` (defaults to HEAD)
## Commit Review Workflow
### Step 1: Validate Commit Exists
```bash
# Check if commit exists
if ! git rev-parse --verify ${commit:-HEAD} >/dev/null 2>&1; then
ERROR: "Commit not found: ${commit}"
exit 1
fi
# Get commit hash
commit_sha=$(git rev-parse ${commit:-HEAD})
```
### Step 2: Run Commit Reviewer Script
Execute comprehensive analysis:
```bash
./.claude/commands/commit-best-practices/.scripts/commit-reviewer.py "${commit_sha}"
```
The script returns JSON:
```json
{
"commit": "abc123...",
"author": "John Doe <john@example.com>",
"date": "2025-10-13",
"message": {
"subject": "feat(auth): add OAuth authentication",
"body": "- Implement OAuth2 flow\n- Add providers",
"subject_length": 38,
"has_body": true,
"conventional": true,
"type": "feat",
"scope": "auth"
},
"changes": {
"files_changed": 5,
"insertions": 234,
"deletions": 12,
"test_files": 1,
"doc_files": 0
},
"quality": {
"atomic": true,
"message_quality": "excellent",
"test_coverage": true,
"issues": []
},
"score": 95
}
```
### Step 3: Analyze Message Quality
**Check Subject Line:**
- Length ≤ 50 characters ✅
- Imperative mood ("add" not "added") ✅
- Conventional commits format ✅
- Clear and descriptive ✅
**Check Body (if present):**
- Wrapped at 72 characters ✅
- Explains WHY (not just what) ✅
- Uses bullet points for clarity ✅
- Blank line after subject ✅
**Check Footer (if present):**
- Breaking changes noted ✅
- Issue references included ✅
### Step 4: Analyze Changes
**Atomicity Check:**
```
Atomic commit = Single logical change
Check for:
❌ Multiple types mixed (feat + fix)
❌ Multiple scopes mixed (auth + api + docs)
❌ Unrelated changes bundled together
Example ATOMIC:
feat(auth): add OAuth authentication
- 5 files, all auth-related
- Single feature implementation
Example NON-ATOMIC:
feat: add OAuth and fix null pointer
- Mixing feat + fix
- Should be 2 commits
```
**Completeness Check:**
```
Complete commit includes:
✅ Implementation code
✅ Tests for new code
✅ Documentation if needed
✅ No missing pieces
Incomplete examples:
❌ New feature without tests
❌ API change without docs
❌ Partial implementation
```
### Step 5: Generate Review Report
**Excellent Commit (Score 90-100):**
```
✅ EXCELLENT COMMIT
Commit: abc123 (HEAD)
Author: John Doe
Date: 2025-10-13
Message Quality: EXCELLENT
✅ Subject: "feat(auth): add OAuth authentication" (38 chars)
✅ Conventional commits format
✅ Descriptive body with bullet points
✅ Proper formatting
Changes:
✅ Atomic: Single feature (OAuth authentication)
✅ Complete: Implementation + tests
✅ Files: 5 changed (+234 -12 lines)
✅ Test coverage included
Score: 95/100
This commit follows all best practices. Safe to push!
```
**Good Commit (Score 70-89):**
```
✅ GOOD COMMIT (Minor improvements possible)
Commit: abc123 (HEAD)
Author: John Doe
Date: 2025-10-13
Message Quality: GOOD
✅ Subject: "feat(auth): add OAuth authentication" (38 chars)
✅ Conventional commits format
⚠️ Body: Could be more detailed (explains WHAT but not WHY)
Changes:
✅ Atomic: Single feature
⚠️ Test coverage: Only integration tests (unit tests missing)
✅ Files: 5 changed (+234 -12 lines)
Score: 82/100
Suggestions:
1. Add more context in commit body about WHY this change
2. Consider adding unit tests for edge cases
Still a good commit. Safe to push with minor notes.
```
**Needs Improvement (Score 50-69):**
```
⚠️ NEEDS IMPROVEMENT
Commit: abc123 (HEAD)
Author: John Doe
Date: 2025-10-13
Message Quality: FAIR
⚠️ Subject: "add oauth" (9 chars - too short)
❌ Not conventional commits format (missing type/scope)
❌ No body explaining changes
Changes:
⚠️ Atomicity: Questionable (mixes auth + API changes)
❌ Test coverage: No tests included
✅ Files: 8 changed (+312 -45 lines)
Score: 58/100
Issues to address:
1. Improve commit message: "feat(auth): add OAuth authentication"
2. Add commit body explaining implementation
3. Add tests for new OAuth functionality
4. Consider splitting auth changes from API changes
Recommendation: Amend or rewrite this commit before pushing.
```
**Poor Commit (Score < 50):**
```
❌ POOR COMMIT - Should be rewritten
Commit: abc123 (HEAD)
Author: John Doe
Date: 2025-10-13
Message Quality: POOR
❌ Subject: "stuff" (5 chars - meaningless)
❌ No type, no scope, no clarity
❌ No body, no context
Changes:
❌ Non-atomic: Multiple unrelated changes
- Auth system
- API refactoring
- Documentation
- Bug fixes (3 different issues)
❌ No tests
❌ Files: 23 changed (+1,234 -567 lines)
Score: 28/100
Critical issues:
1. Commit message is meaningless ("stuff")
2. Bundles 4+ unrelated changes together
3. No tests for significant code changes
4. Too large (23 files)
Action required: Reset and split into 4+ atomic commits with proper messages.
Use: /commit-split (to split into atomic commits)
```
### Step 6: Provide Actionable Guidance
Based on score, recommend action:
**Score ≥ 90**: Safe to push as-is
**Score 70-89**: Safe to push, minor suggestions noted
**Score 50-69**: Amend recommended before pushing
**Score < 50**: Rewrite required, do not push
## Amend Guidance
If commit needs improvement and is safe to amend:
```
To amend this commit:
1. Make improvements (add tests, update message, etc.)
2. Stage changes: git add <files>
3. Amend commit: git commit --amend
4. Review again: /commit-best-practices review-commit
Note: Only amend if commit not yet pushed to remote!
Check: /commit-best-practices amend-guidance
```
## Split Guidance
If commit is non-atomic:
```
This commit should be split into multiple commits:
Detected separate concerns:
1. feat(auth): OAuth implementation (5 files)
2. fix(api): null pointer handling (2 files)
3. docs: authentication guide (1 file)
Use: /commit-split (for interactive splitting)
Or: git reset HEAD~1 (to undo and recommit properly)
```
## Output Format
```
Commit Review Report
===================
Commit: <sha> (<ref>)
Author: <name> <email>
Date: <date>
MESSAGE QUALITY: [EXCELLENT|GOOD|FAIR|POOR]
[✅|⚠️|❌] Subject: "<subject>" (<length> chars)
[✅|⚠️|❌] Format: [Conventional|Non-conventional]
[✅|⚠️|❌] Body: [Present|Missing]
[✅|⚠️|❌] Clarity: [Clear|Vague]
CHANGES:
[✅|⚠️|❌] Atomic: [Yes|Questionable|No]
[✅|⚠️|❌] Complete: [Yes|Partial]
[✅|⚠️|❌] Tests: [Included|Missing]
[✅|⚠️|❌] Size: <files> files (+<ins> -<del> lines)
SCORE: <score>/100
[Issues list if any]
[Recommendations]
VERDICT: [Safe to push|Amend recommended|Rewrite required]
```
## Error Handling
**Commit not found:**
```
ERROR: Commit not found: abc123
Check: git log (to see available commits)
```
**Not a git repository:**
```
ERROR: Not a git repository
Run: git init (to initialize)
```
**Script execution error:**
```
ERROR: Commit reviewer script failed
Check: .claude/commands/commit-best-practices/.scripts/commit-reviewer.py exists
Verify: Script is executable
```
## Integration with Agent
After user creates a commit:
1. Agent automatically runs review (unless disabled)
2. If score < 70, suggest improvements
3. If non-atomic, suggest splitting
4. If excellent (≥90), congratulate and suggest push
## Best Practices Enforced
1. **Meaningful messages** - Clear, descriptive commit messages
2. **Conventional format** - type(scope): description
3. **Atomic commits** - One logical change per commit
4. **Complete commits** - Include tests and docs
5. **Proper formatting** - Subject ≤50 chars, body wrapped at 72
High-quality commits make git history useful for debugging, code review, and collaboration.

View File

@@ -0,0 +1,78 @@
---
description: Enforce git commit best practices and workflow guidance
---
# Commit Best Practices Skill Router
You are orchestrating commit best practices validation and workflow guidance operations.
## Parse Request
Examine `$ARGUMENTS` to determine the operation and parameters:
**Format**: `<operation> [parameters]`
## Available Operations
1. **check-pre-commit** - Validate repository state before committing
- Runs tests, lint checks, detects debug code
- Validates no TODOs, no merge markers
- Format: `check-pre-commit [quick:true|false]`
2. **review-commit** - Review most recent commit quality
- Analyzes commit message and changes
- Checks atomicity and completeness
- Format: `review-commit [commit:HEAD|<sha>]`
3. **amend-guidance** - Guide safe commit amending
- Checks if safe to amend (not pushed, same author)
- Provides amend instructions
- Format: `amend-guidance [force:true|false]`
4. **revert-guidance** - Help with commit reverts
- Generates proper revert commit message
- Provides revert instructions
- Format: `revert-guidance commit:<sha>`
5. **workflow-tips** - Complete git workflow guidance
- Best practices overview
- Branch management tips
- Format: `workflow-tips [focus:commit|branch|merge]`
## Routing Logic
```
Parse first word from $ARGUMENTS:
"check-pre-commit" → Read commands/commit-best-practices/check-pre-commit.md
"review-commit" → Read commands/commit-best-practices/review-commit.md
"amend-guidance" → Read commands/commit-best-practices/amend-guidance.md
"revert-guidance" → Read commands/commit-best-practices/revert-guidance.md
"workflow-tips" → Read commands/commit-best-practices/workflow-tips.md
(unknown) → Show error and list available operations
```
## Base Directory
**Location**: `.claude/commands/commit-best-practices/`
## Error Handling
If operation is not recognized:
```
ERROR: Unknown operation: <operation>
Available operations:
- check-pre-commit [quick:true|false]
- review-commit [commit:HEAD|<sha>]
- amend-guidance [force:true|false]
- revert-guidance commit:<sha>
- workflow-tips [focus:commit|branch|merge]
Example: /commit-best-practices check-pre-commit quick:true
```
## Request Context
**User Request**: `$ARGUMENTS`
Now route to the appropriate operation file and execute its instructions with the provided parameters.

View File

@@ -0,0 +1,607 @@
# Operation: Git Workflow Tips and Best Practices
Comprehensive git workflow guidance covering commits, branches, merging, and collaboration.
## Parameters from $ARGUMENTS
- **focus** (optional): Specific area (commit|branch|merge|all) (default: all)
Parse as: `workflow-tips focus:commit` or `workflow-tips`
## Workflow Guidance
Route to specific section based on focus parameter:
### Focus: Commit Workflow
**Complete commit workflow best practices:**
```
Git Commit Workflow Best Practices
===================================
1. BEFORE YOU CODE
------------------
□ Create feature branch
git checkout -b feature/your-feature-name
□ Pull latest changes
git pull origin main
□ Verify clean state
git status
2. WHILE CODING
---------------
□ Commit frequently (atomic commits)
- After each logical change
- Not too small (typo fixes can be bundled)
- Not too large (avoid 1000+ line commits)
□ Test before committing
npm test (or your test command)
□ Keep commits focused
- One concern per commit
- Don't mix features and fixes
- Don't mix multiple scopes
3. BEFORE COMMITTING
--------------------
□ Review your changes
git diff (see what changed)
git status (see staged/unstaged)
□ Stage selectively
git add <specific-files>
(Don't blindly: git add .)
□ Run pre-commit checks
/commit-best-practices check-pre-commit
□ Remove debug code
- console.log, print statements
- debugger statements
- Temporary test code
4. CREATING COMMIT
------------------
□ Use conventional commits format
<type>(<scope>): <subject>
□ Write clear subject (≤50 chars)
- Imperative mood: "add" not "added"
- Lowercase after type
- No period at end
□ Add descriptive body (if needed)
- Explain WHY, not what
- Wrap at 72 characters
- Use bullet points
□ Include footer (if applicable)
- BREAKING CHANGE: description
- Closes #123
5. AFTER COMMITTING
-------------------
□ Review commit
/commit-best-practices review-commit
git show HEAD
□ Verify tests still pass
npm test
□ Push to remote
git push origin <branch>
□ Create pull request (if ready)
gh pr create
6. COMMON MISTAKES TO AVOID
---------------------------
❌ "WIP" or "work in progress" commits
→ Finish work before committing
❌ "Fix" or "update" without context
→ Be specific: "fix(auth): null pointer in OAuth"
❌ Committing all changes at once
→ Split into atomic commits
❌ Forgetting to add tests
→ Tests should accompany code changes
❌ Committing node_modules or .env files
→ Use .gitignore properly
❌ Force pushing to shared branches
→ Only force push on personal branches
7. COMMIT CHECKLIST
-------------------
Before every commit, verify:
□ Changes are atomic (single logical change)
□ Tests pass
□ No debug code
□ No TODOs in committed code
□ Message follows conventions
□ Includes tests (if new functionality)
□ Documentation updated (if needed)
Use: /commit (agent handles checklist automatically)
```
### Focus: Branch Workflow
**Branch management best practices:**
```
Git Branch Workflow Best Practices
===================================
1. BRANCH NAMING CONVENTIONS
-----------------------------
Follow consistent naming:
Feature branches:
feature/oauth-authentication
feature/user-profile-page
Bug fixes:
fix/null-pointer-in-auth
fix/memory-leak-in-parser
Hotfixes:
hotfix/security-vulnerability
hotfix/production-crash
Experiments:
experiment/new-algorithm
experiment/performance-optimization
Pattern: <type>/<brief-description>
Use: lowercase-with-hyphens
2. BRANCH LIFECYCLE
-------------------
Create → Develop → Test → Merge → Delete
Create:
git checkout -b feature/my-feature
(Always branch from main/develop)
Develop:
- Commit frequently
- Push regularly
- Keep branch updated
Test:
- Run full test suite
- Manual testing
- Code review
Merge:
- Create pull request
- Address review feedback
- Merge to main
Delete:
git branch -d feature/my-feature
git push origin --delete feature/my-feature
3. KEEPING BRANCHES UPDATED
---------------------------
Regularly sync with main branch:
Option 1: Rebase (cleaner history)
git checkout feature/my-feature
git fetch origin
git rebase origin/main
Option 2: Merge (preserves history)
git checkout feature/my-feature
git merge origin/main
Recommendation: Use rebase for feature branches,
merge for shared branches.
4. BRANCH PROTECTION RULES
---------------------------
Protect important branches:
Main/Master:
- Require pull request
- Require code review
- Require passing tests
- Block force push
- Block deletion
Develop:
- Require pull request
- Require passing tests
- Allow force push with lease
Feature:
- No restrictions
- Personal branches
5. LONG-RUNNING BRANCHES
-------------------------
If branch lives more than a few days:
□ Sync with main frequently (daily)
□ Keep commits atomic and clean
□ Push to remote regularly
□ Communicate with team
□ Consider splitting into smaller branches
6. BRANCH STRATEGIES
--------------------
Choose strategy based on team size:
Git Flow (large teams):
- main: production
- develop: integration
- feature/*: new features
- release/*: release prep
- hotfix/*: urgent fixes
GitHub Flow (small teams):
- main: production
- feature/*: all work
(Simple and effective)
Trunk-Based (continuous deployment):
- main: always deployable
- Short-lived feature branches
- Feature flags for incomplete work
7. COMMON BRANCH MISTAKES
--------------------------
❌ Working directly on main
→ Always use feature branches
❌ Branches that live for weeks
→ Split into smaller branches
❌ Not syncing with main regularly
→ Causes massive merge conflicts
❌ Unclear branch names (branch1, temp)
→ Use descriptive names
❌ Leaving merged branches
→ Delete after merging
❌ Force pushing shared branches
→ Only force push personal branches
```
### Focus: Merge Workflow
**Merge and pull request best practices:**
```
Git Merge Workflow Best Practices
==================================
1. BEFORE CREATING PULL REQUEST
-------------------------------
□ All commits are atomic
/commit-review (check quality)
□ Branch is up-to-date
git fetch origin
git rebase origin/main
□ All tests pass
npm test (full suite)
□ No merge conflicts
Resolve before creating PR
□ Code is reviewed locally
Self-review your changes
2. CREATING PULL REQUEST
------------------------
Good PR title:
feat(auth): Add OAuth authentication support
Good PR description:
## Summary
Implements OAuth 2.0 authentication for Google and GitHub.
## Changes
- OAuth configuration system
- Provider implementations (Google, GitHub)
- Token refresh mechanism
- Middleware integration
## Testing
- Unit tests: 15 added
- Integration tests: 3 added
- Manual testing: OAuth flows verified
## Screenshots (if UI)
[Include screenshots]
Closes #123
3. CODE REVIEW PROCESS
----------------------
As author:
□ Respond to all comments
□ Make requested changes
□ Push new commits (don't amend!)
□ Re-request review when ready
□ Be open to feedback
As reviewer:
□ Review code thoroughly
□ Check tests are included
□ Verify documentation updated
□ Test locally if possible
□ Be constructive in feedback
4. MERGE STRATEGIES
-------------------
Choose appropriate merge strategy:
Merge Commit (default):
git merge --no-ff feature/my-feature
- Preserves complete history
- Shows where feature was merged
- Creates merge commit
Use for: Significant features
Squash Merge:
git merge --squash feature/my-feature
- Combines all commits into one
- Clean linear history
- Loses individual commit detail
Use for: Small features, many WIP commits
Rebase Merge:
git rebase main
git merge --ff-only
- Linear history
- No merge commit
- Clean git log
Use for: Personal branches, small changes
5. RESOLVING MERGE CONFLICTS
-----------------------------
When conflicts occur:
Step 1: Identify conflicts
git status (shows conflicted files)
Step 2: Open conflicted file
Conflict markers:
<<<<<<< HEAD
Your changes
=======
Their changes
>>>>>>> feature/branch
Step 3: Resolve conflict
- Choose one version, or
- Combine both versions, or
- Write new solution
Step 4: Remove conflict markers
Delete <<<<<<<, =======, >>>>>>>
Step 5: Mark as resolved
git add <resolved-file>
Step 6: Complete merge
git merge --continue
(or git rebase --continue)
Step 7: Test thoroughly
npm test
Manual testing
6. AFTER MERGING
----------------
□ Delete feature branch
git branch -d feature/my-feature
git push origin --delete feature/my-feature
□ Verify merge on main
git checkout main
git pull origin main
git log (verify commit appears)
□ Run tests on main
npm test
□ Deploy if applicable
(Follow deployment process)
□ Update issue tracker
Close related issues
□ Notify stakeholders
Team notification of merge
7. MERGE BEST PRACTICES
------------------------
✅ Merge frequently (avoid long-lived branches)
✅ Require code review before merge
✅ Ensure tests pass before merge
✅ Delete branches after merge
✅ Use meaningful merge commit messages
✅ Squash "fix review comments" commits
❌ Don't merge failing tests
❌ Don't merge without review
❌ Don't merge with unresolved conflicts
❌ Don't merge incomplete features to main
❌ Don't forget to pull after merge
```
### Focus: All (Complete Workflow)
When `focus:all` or no focus specified, provide comprehensive overview:
```
Complete Git Workflow Guide
============================
Quick Reference for Git Best Practices
DAILY WORKFLOW
--------------
1. Start day: git pull origin main
2. Create branch: git checkout -b feature/name
3. Code & commit frequently (atomic commits)
4. Test before each commit
5. Push regularly: git push origin feature/name
6. Create PR when ready
7. Address review feedback
8. Merge and delete branch
9. Pull updated main
COMMIT GUIDELINES
-----------------
Format: <type>(<scope>): <subject>
Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
Subject: ≤50 chars, imperative, lowercase, no period
Body: Wrap at 72 chars, explain WHY
Footer: BREAKING CHANGE, Closes #123
BRANCH STRATEGY
---------------
- main: production-ready code
- feature/*: new features
- fix/*: bug fixes
- hotfix/*: urgent production fixes
Keep branches short-lived (< 1 week)
Sync with main daily
Delete after merging
MERGE PROCESS
-------------
1. Rebase on main: git rebase origin/main
2. Resolve conflicts
3. All tests pass
4. Code review approved
5. Merge to main
6. Delete feature branch
TOOLS TO USE
------------
/commit - Create commit with agent assistance
/commit-review - Review commit quality
/commit-split - Split non-atomic commits
/commit-best-practices check-pre-commit - Validate before commit
/commit-best-practices review-commit - Review commit after creation
/commit-best-practices amend-guidance - Safe amend help
/commit-best-practices revert-guidance - Revert help
COMMON COMMANDS
---------------
git status # Check status
git diff # See changes
git add <files> # Stage changes
git commit # Create commit
git push origin <branch> # Push to remote
git pull origin main # Get latest main
git log --oneline -10 # Recent commits
git show HEAD # Show last commit
EMERGENCY PROCEDURES
--------------------
Committed to wrong branch:
git reset --soft HEAD~1
git checkout correct-branch
git commit
Pushed wrong commit:
git revert HEAD
git push
Need to undo local changes:
git checkout -- <file>
(or git restore <file>)
Accidentally deleted branch:
git reflog
git checkout -b branch-name <sha>
For more detailed guidance on specific areas:
/commit-best-practices workflow-tips focus:commit
/commit-best-practices workflow-tips focus:branch
/commit-best-practices workflow-tips focus:merge
```
## Output Format
Provide guidance formatted with:
- Clear section headers
- Checkboxes for actionable items
- Code examples with syntax highlighting
- Visual indicators (✅ ❌ ⚠️)
- Step-by-step instructions
- Common pitfalls and solutions
## Error Handling
**Invalid focus parameter:**
```
ERROR: Invalid focus: invalid-focus
Valid options: commit, branch, merge, all
Example: /commit-best-practices workflow-tips focus:commit
```
## Integration with Agent
Proactively suggest workflow tips when:
- User commits to main branch directly (suggest feature branch)
- Branch hasn't been synced in 3+ days (suggest rebase)
- PR has many commits (suggest squash)
- User attempts unsafe operation (provide guidance)
## Best Practices Summary
**The Golden Rules:**
1. **Commit often** - Atomic, focused commits
2. **Test always** - Before every commit
3. **Branch per feature** - Isolate work
4. **Sync regularly** - Stay updated with main
5. **Review thoroughly** - All code reviewed
6. **Merge cleanly** - No conflicts, passing tests
7. **Delete branches** - After merging
8. **Communicate** - Keep team informed
**Tools make it easier:**
Use `/commit` and related skills to automate best practices and ensure consistent, high-quality commits.
These workflows ensure clean history, facilitate collaboration, and make debugging easier when issues arise.

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env bash
# ================================================================
# Script: changes-detector.sh
# Purpose: Check for changes to commit (staged, unstaged, untracked)
# Version: 1.0.0
# Usage: ./changes-detector.sh
# Returns: JSON with change counts
# Exit Codes:
# 0 = Success (with or without changes)
# 1 = Not a git repository
# 2 = Script error
# ================================================================
set -euo pipefail
# Function to output JSON
output_json() {
local has_changes=$1
local staged=$2
local unstaged=$3
local untracked=$4
local total=$5
cat <<EOF
{
"has_changes": $has_changes,
"staged_count": $staged,
"unstaged_count": $unstaged,
"untracked_count": $untracked,
"total_changes": $total,
"checked_at": "$(date -Iseconds)"
}
EOF
}
# Main logic
main() {
# Verify we're in a git repository
if ! git rev-parse --git-dir &>/dev/null; then
output_json false 0 0 0 0
exit 1
fi
# Count staged changes (added to index)
STAGED_COUNT=$(git diff --cached --numstat | wc -l)
# Count unstaged changes (modified but not staged)
UNSTAGED_COUNT=$(git diff --numstat | wc -l)
# Count untracked files
UNTRACKED_COUNT=$(git ls-files --others --exclude-standard | wc -l)
# Total changes
TOTAL=$((STAGED_COUNT + UNSTAGED_COUNT + UNTRACKED_COUNT))
# Determine if there are any changes
if [ "$TOTAL" -gt 0 ]; then
HAS_CHANGES=true
else
HAS_CHANGES=false
fi
# Output JSON
output_json "$HAS_CHANGES" "$STAGED_COUNT" "$UNSTAGED_COUNT" "$UNTRACKED_COUNT" "$TOTAL"
exit 0
}
# Run main function
main "$@"

View File

@@ -0,0 +1,173 @@
#!/usr/bin/env python3
"""
================================================================
Script: conflict-detector.py
Purpose: Detect and report merge conflicts
Version: 1.0.0
Usage: ./conflict-detector.py
Returns: JSON with conflict information
Exit Codes:
0 = Success (conflicts may or may not exist)
1 = Not a git repository
2 = Script error
================================================================
"""
import json
import subprocess
import sys
from datetime import datetime
from pathlib import Path
def run_git_command(command):
"""Run a git command and return output."""
try:
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
check=False
)
return result.returncode, result.stdout.strip(), result.stderr.strip()
except Exception as e:
return -1, "", str(e)
def check_repo_validity():
"""Check if current directory is a git repository."""
returncode, _, _ = run_git_command("git rev-parse --git-dir")
return returncode == 0
def get_conflicted_files():
"""Get list of files with merge conflicts."""
# Files with conflicts show up with 'U' status (unmerged)
returncode, stdout, _ = run_git_command("git ls-files -u")
if returncode != 0 or not stdout:
return []
# Extract unique filenames (git ls-files -u shows each stage)
conflicted_files = set()
for line in stdout.split('\n'):
if line.strip():
# Format: <mode> <object> <stage> <filename>
parts = line.split('\t')
if len(parts) > 1:
filename = parts[1]
conflicted_files.add(filename)
return sorted(conflicted_files)
def check_merge_in_progress():
"""Check if a merge operation is in progress."""
git_dir_code, git_dir, _ = run_git_command("git rev-parse --git-dir")
if git_dir_code != 0:
return False, None
git_dir_path = Path(git_dir)
# Check for various merge/rebase states
if (git_dir_path / "MERGE_HEAD").exists():
return True, "merge"
elif (git_dir_path / "REBASE_HEAD").exists():
return True, "rebase"
elif (git_dir_path / "CHERRY_PICK_HEAD").exists():
return True, "cherry-pick"
elif (git_dir_path / "REVERT_HEAD").exists():
return True, "revert"
return False, None
def get_conflict_details(files):
"""Get detailed information about conflicts in each file."""
details = []
for filepath in files:
try:
# Count conflict markers in file
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
conflict_count = content.count('<<<<<<<')
details.append({
"file": filepath,
"conflict_regions": conflict_count
})
except Exception:
# If can't read file, just include filename
details.append({
"file": filepath,
"conflict_regions": 0
})
return details
def main():
"""Main execution function."""
# Check if in git repository
if not check_repo_validity():
result = {
"has_conflicts": False,
"conflict_count": 0,
"conflicted_files": [],
"merge_in_progress": False,
"operation_type": None,
"error": "not a git repository",
"checked_at": datetime.now().isoformat()
}
print(json.dumps(result, indent=2))
sys.exit(1)
# Get conflicted files
conflicted_files = get_conflicted_files()
conflict_count = len(conflicted_files)
has_conflicts = conflict_count > 0
# Check merge status
merge_in_progress, operation_type = check_merge_in_progress()
# Get detailed conflict information
conflict_details = []
if has_conflicts:
conflict_details = get_conflict_details(conflicted_files)
# Build result
result = {
"has_conflicts": has_conflicts,
"conflict_count": conflict_count,
"conflicted_files": conflicted_files,
"conflict_details": conflict_details,
"merge_in_progress": merge_in_progress,
"operation_type": operation_type,
"error": "",
"checked_at": datetime.now().isoformat()
}
# Output JSON
print(json.dumps(result, indent=2))
sys.exit(0)
if __name__ == "__main__":
try:
main()
except Exception as e:
# Handle unexpected errors
result = {
"has_conflicts": False,
"conflict_count": 0,
"conflicted_files": [],
"merge_in_progress": False,
"operation_type": None,
"error": f"script error: {str(e)}",
"checked_at": datetime.now().isoformat()
}
print(json.dumps(result, indent=2))
sys.exit(2)

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# ================================================================
# Script: repo-checker.sh
# Purpose: Verify git repository validity
# Version: 1.0.0
# Usage: ./repo-checker.sh
# Returns: JSON with repository status
# Exit Codes:
# 0 = Valid repository
# 1 = Not a repository
# 2 = Script error
# ================================================================
set -euo pipefail
# Function to output JSON
output_json() {
local is_repo=$1
local git_dir=$2
local error=$3
cat <<EOF
{
"is_repo": $is_repo,
"git_dir": $git_dir,
"error": "$error",
"checked_at": "$(date -Iseconds)"
}
EOF
}
# Main logic
main() {
# Check if git is installed
if ! command -v git &>/dev/null; then
output_json false "null" "git not installed"
exit 2
fi
# Try to get git directory
if GIT_DIR=$(git rev-parse --git-dir 2>/dev/null); then
# Valid repository
ABSOLUTE_GIT_DIR=$(cd "$GIT_DIR" && pwd)
output_json true "\"$ABSOLUTE_GIT_DIR\"" ""
exit 0
else
# Not a repository
output_json false "null" "not a git repository"
exit 1
fi
}
# Run main function
main "$@"

View File

@@ -0,0 +1,183 @@
#!/usr/bin/env bash
# ================================================================
# Script: state-analyzer.sh
# Purpose: Analyze repository state (HEAD, branch, remote status)
# Version: 1.0.0
# Usage: ./state-analyzer.sh
# Returns: JSON with repository state information
# Exit Codes:
# 0 = Success
# 1 = Not a git repository
# 2 = Script error
# ================================================================
set -euo pipefail
# Function to check HEAD state
check_head_state() {
if git symbolic-ref HEAD &>/dev/null; then
echo "attached"
else
echo "detached"
fi
}
# Function to get current branch
get_current_branch() {
local branch
branch=$(git branch --show-current 2>/dev/null)
if [ -z "$branch" ]; then
echo "null"
else
echo "\"$branch\""
fi
}
# Function to get current commit SHA
get_current_commit() {
git rev-parse --short HEAD 2>/dev/null || echo "unknown"
}
# Function to check remote status
check_remote_status() {
# Check if remote exists
if ! git remote &>/dev/null || [ -z "$(git remote)" ]; then
echo "no_remote"
return
fi
# Check if branch tracks remote
if ! git rev-parse --abbrev-ref @{upstream} &>/dev/null; then
echo "no_upstream"
return
fi
# Compare with upstream
local local_commit remote_commit
local_commit=$(git rev-parse HEAD 2>/dev/null)
remote_commit=$(git rev-parse @{upstream} 2>/dev/null)
if [ "$local_commit" = "$remote_commit" ]; then
echo "up_to_date"
else
# Check ahead/behind
local ahead behind
ahead=$(git rev-list --count @{upstream}..HEAD 2>/dev/null || echo "0")
behind=$(git rev-list --count HEAD..@{upstream} 2>/dev/null || echo "0")
if [ "$ahead" -gt 0 ] && [ "$behind" -gt 0 ]; then
echo "diverged"
elif [ "$ahead" -gt 0 ]; then
echo "ahead"
elif [ "$behind" -gt 0 ]; then
echo "behind"
else
echo "up_to_date"
fi
fi
}
# Function to get ahead/behind counts
get_ahead_behind_counts() {
if ! git rev-parse --abbrev-ref @{upstream} &>/dev/null; then
echo "0" "0"
return
fi
local ahead behind
ahead=$(git rev-list --count @{upstream}..HEAD 2>/dev/null || echo "0")
behind=$(git rev-list --count HEAD..@{upstream} 2>/dev/null || echo "0")
echo "$ahead" "$behind"
}
# Function to get remote name
get_remote_name() {
local remote
remote=$(git remote 2>/dev/null | head -1)
if [ -z "$remote" ]; then
echo "null"
else
echo "\"$remote\""
fi
}
# Function to get remote URL
get_remote_url() {
local remote_name
remote_name=$(git remote 2>/dev/null | head -1)
if [ -z "$remote_name" ]; then
echo "null"
return
fi
local url
url=$(git remote get-url "$remote_name" 2>/dev/null)
if [ -z "$url" ]; then
echo "null"
else
echo "\"$url\""
fi
}
# Function to check if working tree is clean
check_working_tree() {
if git diff-index --quiet HEAD -- 2>/dev/null; then
echo "clean"
else
echo "dirty"
fi
}
# Main function
main() {
# Verify we're in a git repository
if ! git rev-parse --git-dir &>/dev/null; then
cat <<EOF
{
"error": "not a git repository",
"is_repo": false
}
EOF
exit 1
fi
# Collect all state information
local head_state current_branch current_commit remote_status
local ahead behind remote_name remote_url working_tree
head_state=$(check_head_state)
current_branch=$(get_current_branch)
current_commit=$(get_current_commit)
remote_status=$(check_remote_status)
read -r ahead behind < <(get_ahead_behind_counts)
remote_name=$(get_remote_name)
remote_url=$(get_remote_url)
working_tree=$(check_working_tree)
# Output JSON
cat <<EOF
{
"is_repo": true,
"head_state": "$head_state",
"current_branch": $current_branch,
"current_commit": "$current_commit",
"remote_status": "$remote_status",
"ahead_by": $ahead,
"behind_by": $behind,
"remote_name": $remote_name,
"remote_url": $remote_url,
"working_tree": "$working_tree",
"checked_at": "$(date -Iseconds)"
}
EOF
exit 0
}
# Run main function
main "$@"

View File

@@ -0,0 +1,466 @@
# Operation: Diagnose Issues
Comprehensive git repository issue diagnosis.
## Purpose
Run all diagnostic checks and provide a complete health report of the repository state, identifying any issues that could prevent commits or other git operations.
## Parameters
None required - runs complete diagnostic suite.
## Workflow
### 1. Run All Diagnostic Scripts
Execute all diagnostic utilities in parallel:
#### Check Repository Validity
```bash
REPO_RESULT=$(/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-error-handling/.scripts/repo-checker.sh)
```
#### Check for Changes
```bash
CHANGES_RESULT=$(/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-error-handling/.scripts/changes-detector.sh)
```
#### Check for Conflicts
```bash
CONFLICTS_RESULT=$(/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-error-handling/.scripts/conflict-detector.py)
```
#### Check Repository State
```bash
STATE_RESULT=$(/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-error-handling/.scripts/state-analyzer.sh)
```
### 2. Check Git Configuration
Verify git is properly configured:
```bash
# User name
USER_NAME=$(git config user.name)
USER_EMAIL=$(git config user.email)
# Verify both are set
if [ -z "$USER_NAME" ] || [ -z "$USER_EMAIL" ]; then
CONFIG_STATUS="missing"
else
CONFIG_STATUS="configured"
fi
```
### 3. Check Remote Status
Verify remote connectivity and status:
```bash
# Check if remote exists
git remote -v
# Check remote connection (if exists)
git ls-remote --exit-code origin &>/dev/null
REMOTE_STATUS=$?
# Check if ahead/behind remote
git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null
```
### 4. Aggregate Results
Combine all diagnostic results into comprehensive report.
### 5. Present Comprehensive Diagnosis
#### Example Output Format
```
GIT REPOSITORY DIAGNOSIS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Repository Information:
━━━━━━━━━━━━━━━━━━━━━
Location: /home/user/project
Branch: feature-auth
Repository: Valid ✅
Repository State:
━━━━━━━━━━━━━━━━
HEAD State: Attached ✅
Current Branch: feature-auth ✅
Remote Status: Up to date ✅
Changes Status:
━━━━━━━━━━━━━━━
Has Changes: Yes ✅
Staged Files: 3
Unstaged Files: 2
Untracked Files: 1
Total Changes: 6
Conflicts:
━━━━━━━━━━
Merge Conflicts: None ✅
Conflict Count: 0
Configuration:
━━━━━━━━━━━━━━
User Name: John Doe ✅
User Email: john@example.com ✅
Git Version: 2.39.0 ✅
Remote:
━━━━━━━
Remote Name: origin ✅
Remote URL: github.com/user/repo ✅
Connection: Reachable ✅
Ahead: 2 commits
Behind: 0 commits
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Overall Status: HEALTHY ✅
Summary:
✅ Repository is valid and properly configured
✅ Git configuration complete
✅ No merge conflicts
✅ Changes ready to commit
✅ Remote connection working
You can proceed with git operations.
Next Steps:
1. Review changes: git status
2. Commit changes: git commit
3. Push to remote: git push
```
#### Example with Issues
```
GIT REPOSITORY DIAGNOSIS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Repository Information:
━━━━━━━━━━━━━━━━━━━━━
Location: /home/user/project
Branch: (detached HEAD)
Repository: Valid ✅
Repository State:
━━━━━━━━━━━━━━━━
HEAD State: Detached ⚠️
Current Branch: (none)
Remote Status: Cannot determine
Changes Status:
━━━━━━━━━━━━━━━
Has Changes: Yes ✅
Staged Files: 0
Unstaged Files: 5
Untracked Files: 2
Total Changes: 7
Conflicts:
━━━━━━━━━━
Merge Conflicts: YES ❌
Conflicted Files: 2
- src/auth/oauth.js
- src/api/users.js
Configuration:
━━━━━━━━━━━━━━
User Name: (not set) ❌
User Email: (not set) ❌
Git Version: 2.39.0 ✅
Remote:
━━━━━━━
Remote Name: origin ✅
Remote URL: github.com/user/repo ✅
Connection: Failed ❌
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Overall Status: ISSUES DETECTED ⚠️
Issues Found: 4
Priority: HIGH ❌
CRITICAL ISSUES:
━━━━━━━━━━━━━━━━
1. Merge Conflicts Present ❌
Impact: Cannot commit until resolved
Files affected: 2
Action: /commit-error-handling handle-conflicts
2. Git Configuration Missing ❌
Impact: Cannot commit without user.name and user.email
Action: Configure git:
git config user.name "Your Name"
git config user.email "your@email.com"
WARNINGS:
━━━━━━━━━
3. Detached HEAD State ⚠️
Impact: New commits won't be on a branch
Action: /commit-error-handling handle-detached-head
4. Remote Connection Failed ⚠️
Impact: Cannot push changes
Possible causes: Network issues, authentication
Action: Check network and credentials
RECOMMENDED RESOLUTION ORDER:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. First: Resolve merge conflicts
→ /commit-error-handling handle-conflicts
2. Then: Configure git user
→ git config user.name "Your Name"
→ git config user.email "your@email.com"
3. Then: Fix detached HEAD
→ /commit-error-handling handle-detached-head
4. Finally: Test remote connection
→ git fetch origin
After resolving all issues, run:
/commit-error-handling diagnose-issues
```
### 6. Provide Specific Recommendations
Based on findings, provide targeted guidance:
#### If no issues found
```
✅ ALL CHECKS PASSED
Your repository is in good health.
Ready for:
- Committing changes
- Pushing to remote
- Branching
- Merging
- All git operations
Proceed with your intended git operation.
```
#### If issues found
Priority-ordered resolution plan:
```
RESOLUTION PLAN
━━━━━━━━━━━━━━━
CRITICAL (must fix first):
1. Issue: Merge conflicts
Command: /commit-error-handling handle-conflicts
Estimated time: 10-30 minutes
2. Issue: Git config missing
Command: git config user.name "Your Name"
git config user.email "your@email.com"
Estimated time: 1 minute
HIGH (should fix):
3. Issue: Detached HEAD
Command: /commit-error-handling handle-detached-head
Estimated time: 2 minutes
MEDIUM (can fix later):
4. Issue: Remote connection
Check network, verify credentials
Estimated time: 5 minutes
```
### 7. Export Detailed Report
Optionally save full diagnostic report:
```bash
# Save to file
cat > git-diagnosis.txt <<EOF
[Full diagnosis output]
EOF
echo "Full report saved to: git-diagnosis.txt"
```
## Diagnostic Categories
### 1. Repository Validity
- Is this a git repository?
- Is .git directory valid?
- Is repository corrupted?
### 2. Repository State
- HEAD attached or detached?
- Current branch name
- Clean or dirty working tree?
### 3. Changes Detection
- Staged changes count
- Unstaged changes count
- Untracked files count
- Total changes
### 4. Conflicts
- Any merge conflicts?
- Conflicted file list
- Merge/rebase in progress?
### 5. Configuration
- user.name set?
- user.email set?
- Other critical config
### 6. Remote Status
- Remote configured?
- Remote reachable?
- Ahead/behind status
- Push/pull needed?
### 7. Branch Status
- On a branch?
- Branch tracking remote?
- Up to date with remote?
## Error Handling
### If critical git command fails
```
Unable to run git commands.
Possible causes:
- Git not installed
- Current directory has permission issues
- Repository is corrupted
Actions:
1. Verify git is installed:
git --version
2. Check current directory:
pwd
ls -la
3. Try from different directory
```
### If partial diagnosis succeeds
```
Partial diagnosis completed.
Completed checks:
✅ Repository validity
✅ Changes detection
❌ Remote status (failed)
❌ Branch status (failed)
Showing results from successful checks...
Note: Some checks failed. This may indicate:
- Network issues
- Permission problems
- Repository corruption
```
## Output Format
The diagnosis always provides:
1. **Executive Summary** - Overall status at a glance
2. **Detailed Sections** - Each diagnostic category
3. **Issue List** - All problems found, prioritized
4. **Resolution Plan** - Ordered steps to fix issues
5. **Next Actions** - Specific commands to run
6. **Success Indicators** - How to verify fixes
Use visual indicators:
- ✅ Pass
- ❌ Fail
- ⚠️ Warning
- Info
## Success Indicators
After diagnosis:
- User understands complete repository state
- All issues identified and prioritized
- Clear action plan provided
- User knows exact commands to run
- Estimated effort/time provided
## Integration with Other Operations
The diagnosis operation orchestrates all error handling:
```
If repository invalid:
→ Route to: handle-no-repo
If no changes:
→ Route to: handle-no-changes
If conflicts found:
→ Route to: handle-conflicts
If detached HEAD:
→ Route to: handle-detached-head
If all pass:
→ Ready to proceed with commit workflow
```
## Usage Patterns
### Pre-commit Check
```bash
# Before attempting commit
/commit-error-handling diagnose-issues
# If all clear:
/commit-analysis analyze
/message-generation complete-message
```
### Troubleshooting
```bash
# When git operations fail
/commit-error-handling diagnose-issues
# Follow recommended resolution order
```
### Repository Health Check
```bash
# Periodic verification
/commit-error-handling diagnose-issues
# Ensure repository is in good state
```
## Related Operations
- Comprehensive entry point that may route to any other operation
- **handle-no-repo** - If repository invalid
- **handle-no-changes** - If no changes detected
- **handle-conflicts** - If conflicts found
- **handle-detached-head** - If HEAD detached
- After fixes, re-run **diagnose-issues** to verify

View File

@@ -0,0 +1,310 @@
# Operation: Handle Merge Conflicts
Detect and guide resolution of merge conflicts.
## Purpose
When merge conflicts are present, provide clear detection, explanation, and step-by-step resolution guidance.
## Parameters
None required - automatic detection and analysis.
## Workflow
### 1. Detect Conflicts
Execute the conflict detector script:
```bash
/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-error-handling/.scripts/conflict-detector.py
```
This will return JSON:
```json
{
"has_conflicts": true,
"conflict_count": 3,
"conflicted_files": [
"src/auth/oauth.js",
"src/api/users.js",
"README.md"
],
"merge_in_progress": true
}
```
### 2. Analyze Conflict Context
Determine the merge situation:
```bash
# Check merge state
git status
# View merge information
git log --merge --oneline -5
# Check which operation caused conflicts
ls -la .git/MERGE_HEAD 2>/dev/null && echo "Merge in progress"
ls -la .git/REBASE_HEAD 2>/dev/null && echo "Rebase in progress"
ls -la .git/CHERRY_PICK_HEAD 2>/dev/null && echo "Cherry-pick in progress"
```
### 3. Present Conflict Report
#### High-Level Overview
```
MERGE CONFLICTS DETECTED
━━━━━━━━━━━━━━━━━━━━━━━━━━
You have unresolved merge conflicts.
Conflict Summary:
━━━━━━━━━━━━━━━━
Conflicted files: 3
Operation: merge
Current branch: feature-branch
Merging from: main
Conflicted Files:
━━━━━━━━━━━━━━━━
1. src/auth/oauth.js
2. src/api/users.js
3. README.md
```
### 4. Provide Resolution Guidance
#### Step-by-Step Resolution Process
```
RESOLUTION STEPS
━━━━━━━━━━━━━━━━━━
Step 1: Understand Conflict Markers
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Conflicts look like this in files:
<<<<<<< HEAD (your changes)
your code here
=======
their code here
>>>>>>> branch-name (incoming changes)
Step 2: Open Each Conflicted File
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Open in your editor:
- src/auth/oauth.js
- src/api/users.js
- README.md
Step 3: Resolve Each Conflict
━━━━━━━━━━━━━━━━━━━━━━━━━━━
For each conflict, decide:
A) Keep your changes (HEAD):
Remove markers and keep your version
B) Keep their changes:
Remove markers and keep their version
C) Keep both (merge manually):
Combine both versions intelligently
Remove all conflict markers
D) Rewrite completely:
Replace with new code that integrates both
Important: Remove ALL markers (<<<<<<, =======, >>>>>>>)
Step 4: Mark as Resolved
━━━━━━━━━━━━━━━━━━━━━━━
After editing each file:
git add src/auth/oauth.js
git add src/api/users.js
git add README.md
Step 5: Complete the Merge
━━━━━━━━━━━━━━━━━━━━━━━
After resolving all conflicts:
git commit
This will create a merge commit.
Git will suggest a merge message - accept or customize it.
Step 6: Verify Resolution
━━━━━━━━━━━━━━━━━━━━━━━
git status # Should show no conflicts
git log --oneline -1 # See merge commit
```
### 5. Provide Abort Option
```
ALTERNATIVE: Abort the Merge
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
If you want to cancel and start over:
git merge --abort
This will:
- Undo the merge attempt
- Return to state before merge
- No changes will be committed
After aborting, you can:
- Prepare your branch better
- Try the merge again later
- Use a different merge strategy
```
### 6. Show Conflict Details Per File
For each conflicted file, provide analysis:
```
File: src/auth/oauth.js
━━━━━━━━━━━━━━━━━━━━━━━
Conflict regions: 2
Region 1 (lines 45-52):
Your changes: Added new OAuth provider
Their changes: Refactored existing providers
Suggestion: Keep both, integrate new provider into refactored code
Region 2 (lines 89-94):
Your changes: New error handling
Their changes: Different error handling
Suggestion: Merge both approaches, keep comprehensive handling
Quick view:
git diff src/auth/oauth.js
```
### 7. Provide Merge Tool Suggestions
```
MERGE TOOLS
━━━━━━━━━━━
For complex conflicts, use a merge tool:
1. Built-in tool:
git mergetool
2. VS Code:
code src/auth/oauth.js
(Look for conflict highlighting)
3. Diff tool:
git diff --merge
4. Compare with branches:
git show :1:src/auth/oauth.js # common ancestor
git show :2:src/auth/oauth.js # your version
git show :3:src/auth/oauth.js # their version
```
## Error Handling
### If no conflicts detected
```
No conflicts detected.
Checking git status...
[show git status output]
If you expected conflicts:
- Conflicts may have been auto-resolved
- Check git log for merge commits
- Run: git log --merge
```
### If conflicts already resolved
```
Conflicts were already resolved.
Remaining actions:
1. Verify all changes are correct:
git diff --cached
2. Complete the merge:
git commit
3. Or abort if incorrect:
git merge --abort
```
### If in middle of rebase
```
Rebase in progress (not merge).
Different resolution process:
1. Resolve conflicts in files
2. Stage resolved files:
git add <file>
3. Continue rebase:
git rebase --continue
Or abort:
git rebase --abort
```
## Output Format
Always provide:
1. **Conflict summary** - Count and list of files
2. **Context** - What operation caused conflicts
3. **Step-by-step guidance** - Clear resolution steps
4. **Per-file analysis** - Specific conflict details
5. **Verification steps** - How to confirm resolution
6. **Abort option** - How to cancel safely
## Success Indicators
After resolution:
- No conflict markers remain in files
- `git status` shows no conflicts
- All changes are staged
- User can complete merge commit
- Tests still pass (if applicable)
## Best Practices Guidance
```
CONFLICT RESOLUTION BEST PRACTICES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Don't rush - Understand both versions first
2. Test after resolving - Ensure code still works
3. Ask for help - If conflict is complex, consult team
4. Keep context - Review what both branches were trying to do
5. Document - If resolution is non-obvious, explain in commit
When in doubt:
- git merge --abort and ask for help
- Don't commit broken code
- Review changes carefully
```
## Related Operations
- Before resolving, run **diagnose-issues** for full context
- After resolving, run **commit-analysis/analyze-changes** to verify
- Use **commit-best-practices/review-commit** before pushing merge

View File

@@ -0,0 +1,366 @@
# Operation: Handle Detached HEAD State
Handle detached HEAD state and provide solutions.
## Purpose
When git is in "detached HEAD" state, explain what this means and provide clear options for resolution.
## Parameters
None required - automatic detection and guidance.
## Workflow
### 1. Detect Detached HEAD State
Check HEAD status:
```bash
# Check if HEAD is detached
git symbolic-ref HEAD 2>/dev/null || echo "detached"
# Get current commit
git rev-parse --short HEAD
# Check if any branch points here
git branch --contains HEAD
```
### 2. Analyze Context
Determine how user got into detached HEAD:
```bash
# Check reflog for recent operations
git reflog -10
# Check recent checkouts
git reflog | grep "checkout:" | head -5
# See what branches exist
git branch -a
```
Common causes:
- Checked out a specific commit SHA
- Checked out a tag
- During rebase or bisect operations
- After certain git operations that move HEAD
### 3. Explain Detached HEAD State
#### Clear Explanation
```
DETACHED HEAD STATE
━━━━━━━━━━━━━━━━━━━
Current state:
HEAD is at: abc1234
Branch: (none - detached HEAD)
What This Means:
━━━━━━━━━━━━━━━━
You are not on any branch. You're directly on commit abc1234.
Why This Matters:
━━━━━━━━━━━━━━━━
- New commits you make won't be on any branch
- When you switch branches, these commits become hard to find
- You could lose work if you don't create a branch
This is OK for:
✅ Viewing old commits
✅ Testing code at a specific point
✅ Temporary exploration
This is NOT OK for:
❌ Making new commits you want to keep
❌ Starting new work
❌ Continuing development
```
### 4. Provide Resolution Options
#### Option A: Create New Branch Here
```
SOLUTION A: Create a New Branch Here
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
If you want to keep working from this commit:
1. Create and switch to new branch:
git checkout -b new-branch-name
OR (two steps):
git branch new-branch-name
git checkout new-branch-name
2. Verify:
git status
# Should show "On branch new-branch-name"
3. Continue working:
Make commits as normal
They'll be on the new branch
Example:
git checkout -b fix-issue-123
```
#### Option B: Return to a Branch
```
SOLUTION B: Return to Your Branch
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
If you were just exploring and want to go back:
1. Switch to your branch:
git checkout main
OR:
git checkout <your-branch-name>
2. Verify:
git status
# Should show "On branch main"
Available branches:
[List branches from git branch -a]
Recent branch:
git checkout - # Goes to previous branch
```
#### Option C: Attach HEAD to Existing Branch
```
SOLUTION C: Attach HEAD to Existing Branch
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
If a branch already points to this commit:
Current commit: abc1234
Branches at this commit:
[List from git branch --contains]
1. Check out that branch:
git checkout <branch-name>
2. Verify:
git branch
# Should show * next to branch name
```
#### Option D: Keep Commits Made in Detached HEAD
If user already made commits in detached HEAD:
```
SOLUTION D: Preserve Commits Made in Detached HEAD
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
You made commits while in detached HEAD.
Current situation:
- HEAD at: abc1234
- Commits made: 3
- These commits are not on any branch!
To save them:
1. Create branch from current position:
git checkout -b save-my-work
2. Verify commits are there:
git log --oneline -5
Your commits are now safe on "save-my-work" branch.
Next steps:
- Continue work on this branch, OR
- Merge into another branch:
git checkout main
git merge save-my-work
```
### 5. Show Visual Diagram
```
VISUAL EXPLANATION
━━━━━━━━━━━━━━━━━━
Normal State (on a branch):
━━━━━━━━━━━━━━━━━━━━━━━━━
main → [commit 1] → [commit 2] → [commit 3]
HEAD
HEAD points to branch "main"
Branch "main" points to commit 3
Detached HEAD State:
━━━━━━━━━━━━━━━━━━━━
main → [commit 1] → [commit 2] → [commit 3]
[commit X] ← HEAD (detached)
HEAD points directly to commit X
No branch points to commit X
Making Commits in Detached HEAD:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
main → [commit 1] → [commit 2] → [commit 3]
[commit X] → [commit Y] ← HEAD (detached)
If you checkout main now, commit Y becomes orphaned!
Solution - Create Branch:
━━━━━━━━━━━━━━━━━━━━━━━━
main → [commit 1] → [commit 2] → [commit 3]
new-branch → [commit X] → [commit Y] ← HEAD
Now commits are safe on "new-branch"
```
### 6. Interactive Decision Support
Guide user to choose:
```
What would you like to do?
A) Create a new branch here and continue working
→ Choose this if you want to keep this commit as a starting point
B) Return to a branch (discard position)
→ Choose this if you were just exploring
C) Attach to an existing branch
→ Choose this if a branch already points here
D) Save commits I made while detached
→ Choose this if you already made commits
E) Not sure - need more explanation
Please respond with A, B, C, D, or E.
```
## Error Handling
### If not in detached HEAD
```
Not in detached HEAD state.
Current state:
Branch: main
HEAD: abc1234
You're on a normal branch. No action needed.
```
### If during special operation
```
Detached HEAD during rebase.
This is temporary and expected.
Actions:
- Complete the rebase: git rebase --continue
- OR abort: git rebase --abort
After completing/aborting, HEAD will reattach automatically.
```
### If commits will be lost
```
⚠️ WARNING: Orphaned Commits Detected
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
You made 3 commits in detached HEAD:
- abc1234 "commit message 1"
- def5678 "commit message 2"
- ghi9012 "commit message 3"
If you switch branches now, these will be lost!
REQUIRED ACTION: Create a branch first
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
git checkout -b save-my-commits
Then you can safely work with these commits.
```
## Output Format
Always provide:
1. **Clear state description** - What detached HEAD means
2. **Current situation** - What commit, how user got here
3. **Multiple solutions** - All applicable options
4. **Visual aids** - Diagrams if helpful
5. **Specific commands** - Exact steps to resolve
6. **Warnings** - If commits could be lost
## Success Indicators
After resolution:
- HEAD is attached to a branch
- `git branch` shows current branch with *
- `git status` shows "On branch <name>"
- User understands what happened
- No commits were lost
## Best Practices Guidance
```
AVOIDING DETACHED HEAD
━━━━━━━━━━━━━━━━━━━━━━
Prevention tips:
1. Use branches for work:
git checkout -b feature-name
(not: git checkout abc1234)
2. If checking out commits for inspection:
Remember you're in detached HEAD
Don't make commits you want to keep
3. If you need to work on an old commit:
git checkout -b fix-branch abc1234
(Creates branch at that commit)
4. Use tags for reference points:
git tag v1.0 abc1234
git checkout tags/v1.0
(Still detached, but purpose is clear)
Remember:
Detached HEAD is a valid state for temporary inspection,
but always create a branch before making commits.
```
## Related Operations
- After resolving, run **diagnose-issues** to verify state
- Before creating branches, check with **history-analysis** for naming conventions
- After attaching HEAD, continue with normal commit workflow

View File

@@ -0,0 +1,220 @@
# Operation: Handle No Changes Error
Handle "nothing to commit, working tree clean" errors.
## Purpose
When attempting to commit with no staged or unstaged changes, guide users to understand why and what to do next.
## Parameters
None required - detection is automatic.
## Workflow
### 1. Verify Changes Status
Execute the changes detector script:
```bash
/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-error-handling/.scripts/changes-detector.sh
```
This will return JSON:
```json
{
"has_changes": false,
"staged_count": 0,
"unstaged_count": 0,
"untracked_count": 0,
"total_changes": 0
}
```
### 2. Analyze Git Status
Run comprehensive status check:
```bash
git status --porcelain
git status
```
Determine:
- Are files modified but not saved?
- Are all changes already committed?
- Are changes in a different directory?
- Are files ignored by .gitignore?
### 3. Provide Context-Specific Guidance
#### Scenario A: All Changes Already Committed
```
NO CHANGES TO COMMIT
━━━━━━━━━━━━━━━━━━━━
Your working tree is clean.
Current Status:
✅ All changes are already committed
✅ No modified files
✅ No untracked files
This means:
- All your changes have been saved to git
- Nothing new to commit
Next Steps:
1. Make some changes to files
2. Create new files
3. Then commit again
Or if you're done:
- Push your commits: git push
- View history: git log --oneline -5
```
#### Scenario B: Files Modified But Not Saved
```
NO CHANGES TO COMMIT
━━━━━━━━━━━━━━━━━━━━
Possible Reason: Files Not Saved
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Git sees no changes because:
- You edited files but didn't save them
- Changes are in your editor buffer
Actions:
1. Save all files in your editor (Ctrl+S or Cmd+S)
2. Check git status again:
git status
3. If files appear, stage and commit:
git add .
git commit
```
#### Scenario C: Wrong Directory
```
NO CHANGES TO COMMIT
━━━━━━━━━━━━━━━━━━━━
Possible Reason: Wrong Directory
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Current directory: /path/to/current
Changes might be elsewhere.
Actions:
1. Verify you're in the right place:
pwd
ls
2. Navigate to project root:
cd /path/to/project
3. Check status there:
git status
```
#### Scenario D: Files Ignored
```
NO CHANGES TO COMMIT
━━━━━━━━━━━━━━━━━━━━
Possible Reason: Files Ignored by .gitignore
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Git might be ignoring your files.
Check:
1. View ignored patterns:
cat .gitignore
2. Check if file is ignored:
git check-ignore -v <filename>
3. Force add ignored files (if needed):
git add -f <filename>
Note: Be careful adding ignored files - they're usually
ignored for good reason (node_modules, .env, etc.)
```
### 4. Detect Edge Cases
Check for:
- **Unstaged changes in subdirectories**
```bash
git status --porcelain
```
- **Changes to ignored files only**
```bash
git status --ignored
```
- **Accidentally reset changes**
```bash
git reflog -5
```
### 5. Interactive Verification
Guide user to verify:
```
Let's verify your changes:
1. List all files in directory:
ls -la
2. Check git status:
git status
3. Check recent history:
git log --oneline -3
Do you see the files you expected to commit?
```
## Error Handling
### If git status fails
```
Unable to check git status.
Ensure you're in a git repository.
Run: /commit-error-handling handle-no-repo
```
### If permissions issues
```
Permission denied reading files.
Check file permissions: ls -la
```
## Output Format
Always provide:
1. **Clear status** - What git sees
2. **Explanation** - Why there are no changes
3. **Likely causes** - Ordered by probability
4. **Specific actions** - Commands to verify/fix
5. **Next steps** - What to do after resolution
## Success Indicators
After user follows guidance:
- User understands why there were no changes
- Changes appear in `git status`
- User can proceed with commit
- Or user understands work is already committed
## Related Operations
- Run **diagnose-issues** for comprehensive check
- After making changes, verify with **commit-analysis/analyze-changes**

View File

@@ -0,0 +1,161 @@
# Operation: Handle No Repository Error
Detect and resolve "not a git repository" errors.
## Purpose
When git commands fail with `fatal: not a git repository (or any of the parent directories): .git`, guide users to resolve the issue.
## Parameters
None required - detection is automatic.
## Workflow
### 1. Verify Repository Status
Execute the repository checker script:
```bash
/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/commit-error-handling/.scripts/repo-checker.sh
```
This will return JSON:
```json
{
"is_repo": false,
"git_dir": null,
"error": "not a git repository"
}
```
### 2. Analyze Context
Check the current directory:
```bash
pwd
ls -la
```
Determine if:
- User is in the wrong directory
- Repository was never initialized
- .git directory was deleted
- User needs to clone a repository
### 3. Provide Solutions
Present clear, actionable solutions based on the scenario:
#### Scenario A: Need to Initialize New Repository
```
ERROR: Not a Git Repository
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Current directory: /path/to/directory
This is not a git repository.
SOLUTION 1: Initialize a New Repository
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
If you want to start version control here:
1. Initialize git:
git init
2. Add files:
git add .
3. Create first commit:
git commit -m "Initial commit"
4. (Optional) Connect to remote:
git remote add origin <url>
git push -u origin main
```
#### Scenario B: Wrong Directory
```
SOLUTION 2: Navigate to Your Repository
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
If your git repository is elsewhere:
1. Find your repository:
find ~ -type d -name ".git" 2>/dev/null
2. Navigate to it:
cd /path/to/your/repo
3. Try your command again
```
#### Scenario C: Clone Existing Repository
```
SOLUTION 3: Clone an Existing Repository
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
If you need to clone a remote repository:
1. Clone the repository:
git clone <repository-url>
2. Navigate into it:
cd <repository-name>
3. Verify:
git status
```
### 4. Interactive Guidance
If context is unclear, ask clarifying questions:
```
What would you like to do?
A) Initialize a new git repository here
B) Navigate to an existing repository
C) Clone a repository from a URL
D) Not sure, need more help
Please respond with A, B, C, or D.
```
## Error Handling
### If pwd fails
```
Unable to determine current directory.
Please manually navigate to your git repository.
```
### If user has no permissions
```
Permission denied: Cannot initialize repository here.
Try a directory where you have write permissions.
```
## Output Format
Always provide:
1. **Clear error description** - What's wrong
2. **Context** - Current directory and state
3. **Multiple solutions** - Ordered by likelihood
4. **Specific commands** - Copy-pasteable
5. **Next steps** - What to do after resolution
## Success Indicators
After user follows guidance:
- `git status` works without errors
- User can proceed with git operations
- `.git` directory exists and is valid
## Related Operations
- After resolution, run **diagnose-issues** to verify full repository health
- Before committing, run **handle-no-changes** to ensure there are changes

View File

@@ -0,0 +1,96 @@
---
description: Handle git errors and edge cases gracefully
---
# Git Commit Error Handling
You are a git error diagnosis and resolution specialist. Your role is to detect, diagnose, and provide clear guidance for resolving common git issues that prevent successful commits.
## Operation Router
Parse the operation from $ARGUMENTS and route to the appropriate handler:
**Available Operations:**
1. **handle-no-repo** - Not a git repository error
- Usage: `handle-no-repo`
- Detects and resolves "not a git repository" errors
2. **handle-no-changes** - Working tree clean error
- Usage: `handle-no-changes`
- Handles "nothing to commit, working tree clean" errors
3. **handle-conflicts** - Merge conflicts present
- Usage: `handle-conflicts`
- Detects and guides resolution of merge conflicts
4. **handle-detached-head** - Detached HEAD state
- Usage: `handle-detached-head`
- Handles detached HEAD state and provides solutions
5. **diagnose-issues** - Comprehensive git issue diagnosis
- Usage: `diagnose-issues`
- Runs all checks and provides complete diagnosis
## Routing Logic
```
Extract first word from $ARGUMENTS as operation
IF operation = "handle-no-repo":
Read .claude/commands/commit-error-handling/handle-no-repo.md
Execute instructions
ELSE IF operation = "handle-no-changes":
Read .claude/commands/commit-error-handling/handle-no-changes.md
Execute instructions
ELSE IF operation = "handle-conflicts":
Read .claude/commands/commit-error-handling/handle-conflicts.md
Execute instructions
ELSE IF operation = "handle-detached-head":
Read .claude/commands/commit-error-handling/handle-detached-head.md
Execute instructions
ELSE IF operation = "diagnose-issues":
Read .claude/commands/commit-error-handling/diagnose-issues.md
Execute instructions
ELSE:
Show error:
"Unknown operation: {operation}
Available operations:
- handle-no-repo
- handle-no-changes
- handle-conflicts
- handle-detached-head
- diagnose-issues
Usage: /commit-error-handling <operation>"
```
## Error Handling Philosophy
1. **Detect Early** - Identify issues before attempting operations
2. **Clear Messages** - Explain what's wrong in plain language
3. **Actionable Solutions** - Provide specific commands to fix
4. **Safe Guidance** - Never suggest destructive operations without warnings
5. **Educational** - Help users understand the underlying issue
## Integration Points
This skill is typically invoked:
- **Before commit operations** - Validate repository state
- **On commit failures** - Diagnose why commit failed
- **Interactive guidance** - Help users resolve git issues
- **Agent workflows** - Automated error detection and recovery
## Base Directory
All operation files are located in: `.claude/commands/commit-error-handling/`
## Request
Process: $ARGUMENTS

686
commands/commit-review.md Normal file
View File

@@ -0,0 +1,686 @@
---
description: Review staged changes and recommend commit strategy for atomic commits
allowed-tools: Bash(git:*)
---
# Commit Review - Analyze Changes for Atomic Commits
Review your staged or unstaged changes and receive recommendations on whether to create a single commit or split into multiple atomic commits.
## Overview
The `/commit-review` command provides comprehensive change analysis by orchestrating multiple analytical skills to determine whether your changes should be committed as one atomic commit or split into multiple focused commits.
### Skills Orchestrated
This command coordinates:
1. **commit-error-handling** - Repository state validation
- Operation: `/commit-error-handling diagnose-issues`
- Ensures repository is ready for analysis
2. **commit-analysis** - Multi-dimensional change analysis
- Operation: `/commit-analysis analyze`
- Detects: types, scopes, file changes, atomicity
- Provides: Comprehensive change breakdown
3. **atomic-commit** - Splitting analysis
- Operations:
- `/atomic-commit analyze` - Determine if should split
- `/atomic-commit group strategy:type` - Group files by type
- `/atomic-commit suggest` - Generate commit breakdown
- Provides: Detailed splitting recommendations
4. **message-generation** - Preview commit messages
- Operation: `/message-generation complete`
- Generates: Suggested messages for each commit group
5. **history-analysis** (optional) - Project convention awareness
- Operation: `/history-analysis analyze-style`
- Ensures: Recommendations match project style
### Workflow Integration
After `/commit-review` shows recommendations:
**If single commit recommended**:
→ Use `/commit` to create the commit
**If split recommended**:
→ Use `/commit-split` for interactive splitting
→ OR use `/atomic-commit interactive` for granular control
→ OR manually commit each group
### For Granular Control
Analyze specific aspects independently:
- `/commit-analysis detect-type` - Just classify change types
- `/commit-analysis assess-atomicity` - Just check atomicity
- `/atomic-commit group` - Just group files
- `/atomic-commit suggest` - Just get commit suggestions
## Usage
```bash
/commit-review # Review all changes (staged and unstaged)
```
## What is an Atomic Commit?
An atomic commit is a single, focused commit that:
- Contains one logical change
- Can be reverted independently without breaking other functionality
- Has all tests passing after the commit
- Is self-contained and complete
- Has a clear, single purpose
**Good Atomic Commits:**
- Add authentication feature (all auth files together)
- Fix login bug (only the fix, not other changes)
- Update dependencies (package updates together)
**Bad Non-Atomic Commits:**
- Add feature + fix unrelated bug + update docs
- Refactor + add new functionality
- Mix of formatting changes and logic changes
## Complete Analysis Workflow
```
User: /commit-review
┌────────────────────────────────┐
│ 1. Validate Repository │
│ /commit-error-handling │
│ diagnose-issues │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ 2. Analyze All Changes │
│ /commit-analysis analyze │
│ - Detect types │
│ - Identify scopes │
│ - Count files/lines │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ 3. Assess Atomicity │
│ /atomic-commit analyze │
└────────────┬───────────────────┘
┌─────┴──────┐
│ Should │
│ Split? │
└─────┬──────┘
┌────────┴────────┐
│ │
YES NO
↓ ↓
┌───────────────┐ ┌──────────────────┐
│ 4a. Group │ │ 4b. Recommend │
│ Files │ │ Single Commit │
│ /atomic- │ │ │
│ commit group │ │ → Use /commit │
└───────┬───────┘ └──────────────────┘
┌───────────────────┐
│ 5a. Generate │
│ Suggestions │
│ /atomic-commit │
│ suggest │
│ │
│ For each group: │
│ /message- │
│ generation │
│ complete │
└─────────┬─────────┘
┌─────────────────────────────┐
│ 6a. Present Plan │
│ - Commit breakdown │
│ - Suggested messages │
│ - File groups │
│ │
│ → Use /commit-split │
└─────────────────────────────┘
```
## Process
### Step 1: Repository Validation
**Orchestrates**: `/commit-error-handling diagnose-issues`
Before analyzing changes, verify:
- Valid git repository
- Changes present (staged or unstaged)
- No merge conflicts blocking analysis
- Repository not in problematic state
This prevents analysis of invalid states.
**For detailed diagnostics**: `/commit-error-handling diagnose-issues`
```bash
git rev-parse --git-dir 2>/dev/null
git status --short
```
If not a git repo, show error and exit.
### Step 2: Analyze All Changes
**Orchestrates**: `/commit-analysis analyze`
Performs multi-dimensional analysis:
**File-Level Analysis**:
- Type detection (feat, fix, docs, refactor, test, chore, style, perf, ci)
- Scope identification (auth, api, ui, database, etc.)
- Line change statistics (+/- counts)
**Change-Level Analysis**:
- Total files modified
- Insertions and deletions
- Change complexity assessment
**Atomicity Assessment**:
- Single concern vs. multiple concerns
- Related vs. unrelated changes
- Interdependencies between files
**Grouping Strategy**:
- Type-based grouping (all feat together)
- Scope-based grouping (all auth together)
- Feature-based grouping (implementation + tests)
Check both staged and unstaged changes:
```bash
# Get status
git status --short
# Get staged changes
git diff --cached --stat
git diff --cached
# Get unstaged changes
git diff --stat
git diff
# Count files by status
git status --short | wc -l
```
**Type Detection Rules**:
Analyze the diff output to categorize each file by its primary change type:
1. **feat** (New Feature):
- New files being added
- New functions/classes/exports
- New functionality in existing files
- Look for: `export function`, `export class`, `export const`, new files
2. **fix** (Bug Fix):
- Error handling additions
- Null/undefined checks
- Validation fixes
- Look for: `if (!`, `try/catch`, `throw`, `error`, `null`, `undefined`
3. **refactor** (Code Restructuring):
- Variable/function renaming
- Code extraction or reorganization
- Look for: similar logic moved, renamed identifiers
4. **docs** (Documentation):
- README.md changes, documentation files (*.md), JSDoc additions
- Look for: file extensions `.md`, comment blocks
5. **style** (Formatting):
- Whitespace changes only, indentation fixes
- Look for: only whitespace diffs, no logic changes
6. **test** (Tests):
- Test file changes (*.test.js, *.spec.js, *_test.py)
- Look for: `test(`, `describe(`, `it(`, `expect(`, file patterns
7. **chore** (Build/Config):
- package.json dependencies, build configuration
- Look for: package.json, *.config.js, .github/workflows
8. **perf** (Performance):
- Optimization changes, caching implementations
- Look for: `cache`, `memoize`, performance-related comments
9. **ci** (CI/CD):
- GitHub Actions workflows, GitLab CI configs
- Look for: .github/workflows, .gitlab-ci.yml
**For granular analysis**:
- `/commit-analysis detect-type file:path` - Type for specific file
- `/commit-analysis identify-scope file:path` - Scope for specific file
- `/commit-analysis file-stats` - Just statistics
### Step 3: Determine Split Strategy
**Orchestrates**: `/atomic-commit analyze`
Evaluates whether changes should be split:
**Split Criteria**:
- Multiple types present (feat + fix + docs)
- Multiple scopes (auth + api + ui)
- Too many files (10+ of different types)
- Mixed concerns (feature + unrelated refactor)
**Keep Together Criteria**:
- Single type and scope
- All files serve same purpose
- Changes are interdependent
- Reasonable file count (≤10)
**Atomicity Scoring**:
- atomic=true (single commit) → Proceed to `/commit`
- atomic=false (split needed) → Continue to grouping
**For direct splitting analysis**: `/atomic-commit analyze`
### Step 4: Group Related Files
**Orchestrates**: `/atomic-commit group strategy:type`
Groups files into logical commit groups:
**Grouping Strategies**:
1. **Type-Based** (default):
- feat files → Commit 1
- fix files → Commit 2
- docs files → Commit 3
- test files → Commit 4
2. **Scope-Based**:
- auth files → Commit 1
- api files → Commit 2
- ui files → Commit 3
3. **Feature-Based**:
- Implementation + Tests → Commit 1
- Documentation → Commit 2
Create a mapping of files to their change types:
```
feat:
- src/auth/oauth.js
- src/auth/providers.js
- src/auth/middleware.js
test:
- tests/auth.test.js
- tests/oauth.test.js
docs:
- README.md
refactor:
- src/utils.js
- src/helpers.js
```
**For custom grouping**:
- `/atomic-commit group strategy:scope` - Group by module
- `/atomic-commit group strategy:feature` - Group by feature
### Step 5: Generate Commit Recommendations
**Orchestrates**:
- `/atomic-commit suggest` - Create commit breakdown
- `/message-generation complete` (for each group) - Generate messages
For each group, generates:
- **Commit message**: Conventional commits format
- **File list**: All files in the group
- **Change summary**: What the commit does
- **Line statistics**: Insertions/deletions
**Message Generation Uses**:
- Detected type (feat, fix, etc.)
- Identified scope (auth, api, etc.)
- Change analysis (what was actually done)
- Project conventions (from history-analysis)
**For custom messages**:
- `/message-generation subject` - Just subject line
- `/message-generation validate` - Check message format
### Step 6: Present Analysis Results
## Analysis Output Format
### Split Recommendation
When multiple concerns are detected:
```
COMMIT REVIEW ANALYSIS
═══════════════════════════════════════════════
Data from: /commit-analysis analyze
/atomic-commit analyze
RECOMMENDATION: SPLIT INTO ATOMIC COMMITS
Your changes cover multiple concerns:
⚠ Multiple types: feat, test, refactor, docs
⚠ Multiple scopes or unrelated changes
⚠ Better split for clear history
──────────────────────────────────────────────
CURRENT CHANGES SUMMARY:
Data from: /commit-analysis analyze
────────────────────────────────────────────
Total Files: 12
Insertions: 847 (+)
Deletions: 234 (-)
CHANGE TYPE BREAKDOWN:
────────────────────────────────────────────
FEAT (5 files) - New authentication system
M src/auth/oauth.js (+156 lines)
M src/auth/providers.js (+89 lines)
M src/auth/middleware.js (+73 lines)
M src/auth/config.js (+45 lines)
M src/auth/index.js (+23 lines)
TEST (2 files) - Authentication tests
M tests/auth.test.js (+142 lines)
M tests/oauth.test.js (+98 lines)
REFACTOR (3 files) - Code cleanup
M src/utils.js (+45 -67 lines)
M src/helpers.js (+34 -52 lines)
M src/validators.js (+12 -8 lines)
DOCS (2 files) - Documentation updates
M README.md (+156 -42 lines)
M docs/authentication.md (+74 lines)
──────────────────────────────────────────────
SPLITTING PLAN
Generated by: /atomic-commit suggest
Messages from: /message-generation complete
Commit 1 (feat): 5 files, 386 lines
feat(auth): implement OAuth authentication system
- Add OAuth2 authentication flow
- Implement provider support for Google and GitHub
- Add authentication middleware
- Include configuration management
Files:
src/auth/oauth.js
src/auth/providers.js
src/auth/middleware.js
src/auth/config.js
src/auth/index.js
────────────────────────────────────────────
Commit 2 (test): 2 files, 240 lines
test(auth): add comprehensive OAuth authentication tests
- Add unit tests for OAuth flow
- Add integration tests for providers
- Achieve 95% coverage for auth module
Files:
tests/auth.test.js
tests/oauth.test.js
────────────────────────────────────────────
Commit 3 (refactor): 3 files, 24 lines
refactor: simplify utility functions and improve validation
- Simplify date/time utilities
- Improve helper function clarity
- Enhance validator logic
Files:
src/utils.js
src/helpers.js
src/validators.js
────────────────────────────────────────────
Commit 4 (docs): 2 files, 188 lines
docs: add OAuth authentication guide
- Update README with authentication setup
- Add detailed authentication documentation
- Include usage examples
Files:
README.md
docs/authentication.md
──────────────────────────────────────────────
NEXT STEPS:
→ Interactive splitting: /commit-split
→ Granular control: /atomic-commit interactive
→ Manual commits: Use /commit for each group
WHY SPLIT COMMITS?
Atomic commits provide:
✓ Better history browsing (each commit tells a story)
✓ Easier code review (reviewers see logical changes)
✓ Safer reverts (revert one feature without breaking others)
✓ Clearer blame/annotate (understand why changes were made)
✓ Better bisecting (find bugs by commit)
═══════════════════════════════════════════════
```
### Single Commit Recommendation
When changes form a cohesive unit:
```
COMMIT REVIEW ANALYSIS
═══════════════════════════════════════════════
Data from: /commit-analysis analyze
RECOMMENDATION: SINGLE ATOMIC COMMIT ✓
Your changes form a cohesive unit:
✓ Single type: feat
✓ Single scope: auth
✓ Logical unit: OAuth authentication
✓ Reasonable size: 5 files, 386 lines
──────────────────────────────────────────────
CURRENT CHANGES SUMMARY:
Data from: /commit-analysis analyze
────────────────────────────────────────────
Total Files: 5
Insertions: 386 (+)
Deletions: 0 (-)
CHANGE TYPE BREAKDOWN:
────────────────────────────────────────────
FEAT (5 files) - Complete authentication feature
M src/auth/oauth.js
M src/auth/providers.js
M src/auth/middleware.js
M src/auth/config.js
M src/auth/index.js
──────────────────────────────────────────────
SUGGESTED COMMIT MESSAGE
Generated by: /message-generation complete
feat(auth): implement OAuth authentication system
- Add OAuth2 authentication flow
- Implement provider support for Google and GitHub
- Add authentication middleware
- Include configuration management
──────────────────────────────────────────────
ATOMICITY CHECK: PASSED
✓ All files relate to a single feature (auth)
✓ Changes are cohesive and interdependent
✓ Single commit tells a clear story
✓ Can be reverted as a complete unit
NEXT STEPS:
→ Create commit: /commit
→ Pre-validate: /commit-best-practices check-pre-commit
═══════════════════════════════════════════════
```
## Error Handling
**Not a git repository:**
```
ERROR: Not a git repository
Run: git init
```
**No changes to review:**
```
NO CHANGES TO REVIEW
Your working tree is clean.
Make some changes, then run /commit-review again.
```
**All changes already committed:**
```
ALL CHANGES COMMITTED
Great job! Your working tree is clean.
Run git log to see your commits.
```
## Tips for Atomic Commits
**DO:**
- Group related changes together
- Keep features separate from fixes
- Keep refactoring separate from new features
- Include tests with the code they test (optional)
- Make each commit independently buildable
**DON'T:**
- Mix formatting changes with logic changes
- Combine multiple features in one commit
- Include unrelated bug fixes with features
- Make "WIP" or "misc changes" commits
- Bundle everything at the end of the day
## Examples
**Example 1: Mixed changes (needs splitting)**
```bash
/commit-review
# Shows: feat (3 files), fix (2 files), docs (1 file)
# Recommends: Split into 3 commits
# Next: /commit-split
```
**Example 2: Clean atomic change**
```bash
/commit-review
# Shows: fix (2 files) - all related to login bug
# Recommends: Single commit is appropriate
# Next: /commit
```
**Example 3: Large feature**
```bash
/commit-review
# Shows: feat (15 files) - authentication system
# Recommends: Consider splitting by module/component
# Next: /commit-split or careful manual commits
```
## Related Skills and Commands
### Analytical Skills Used
- **commit-analysis**: `/commit-analysis` - Detailed change analysis
- Operations: analyze, detect-type, identify-scope, assess-atomicity, file-stats
- Purpose: Multi-dimensional change analysis and type/scope detection
- **atomic-commit**: `/atomic-commit` - Splitting analysis and grouping
- Operations: analyze, group, suggest, sequence, interactive
- Purpose: Determine if changes should split and generate commit groups
- **history-analysis**: `/history-analysis` - Project convention analysis
- Operations: analyze-style, recent-patterns, common-scopes
- Purpose: Learn project conventions to inform recommendations
### Execution Skills Referenced
- **message-generation**: `/message-generation` - Message creation
- Operations: complete, subject, body, validate
- Purpose: Generate conventional commit messages for each group
- **commit-best-practices**: `/commit-best-practices` - Validation
- Operations: check-pre-commit, workflow-tips, validate-message
- Purpose: Ensure commits follow best practices
- **commit-error-handling**: `/commit-error-handling` - Error diagnosis
- Operations: diagnose-issues, recover, clean-state
- Purpose: Validate repository state before analysis
### Related Commands
- `/commit` - Create single atomic commit
- Use when: Review recommends single commit
- Purpose: Execute the commit with generated message
- `/commit-split` - Interactively split into atomic commits
- Use when: Review recommends splitting
- Purpose: Interactive file-by-file commit creation
### Workflow Paths
```
/commit-review (analysis)
├─ Single commit? → /commit (execution)
└─ Multiple commits? → /commit-split (execution)
OR /atomic-commit interactive (granular control)
```
### For Power Users
Access granular operations directly:
**Analysis Only**:
- `/commit-analysis detect-type` - Classify file types
- `/commit-analysis identify-scope` - Extract scopes
- `/commit-analysis assess-atomicity` - Check if should split
**Grouping Only**:
- `/atomic-commit group strategy:type` - Group by type
- `/atomic-commit group strategy:scope` - Group by scope
- `/atomic-commit group strategy:feature` - Group by feature
**Message Generation Only**:
- `/message-generation complete` - Generate full message
- `/message-generation subject` - Just subject line
- `/message-generation validate message:"text"` - Validate format
### Documentation References
- **Atomic Commits Guide**: See `/commit-best-practices workflow-tips`
- **Skill Architecture**: See `.claude/docs/SKILL_ARCHITECTURE.md`
- **Agent Reference**: See `agents/commit-assistant.md`
- **Command List**: See plugin README.md for all available commands

835
commands/commit-split.md Normal file
View File

@@ -0,0 +1,835 @@
---
description: Split large commits into atomic commits through interactive guidance
allowed-tools: Bash(git:*)
---
# Commit Split - Interactive Atomic Commit Creation
## Overview
The `/commit-split` command provides an interactive workflow that orchestrates multiple skills to help you split large commits into focused, atomic commits. Each atomic commit represents one logical change that can be independently reviewed, tested, and reverted.
### Skills Orchestrated
This command coordinates a complete execution workflow:
1. **commit-error-handling** - Pre-flight validation
- Operation: `/commit-error-handling diagnose-issues`
- Ensures: Repository ready for splitting
2. **commit-analysis** - Change understanding
- Operation: `/commit-analysis analyze`
- Provides: Type, scope, and file analysis for grouping
3. **atomic-commit** - Core splitting logic (PRIMARY)
- Operation: `/atomic-commit interactive`
- Provides: File grouping, suggestions, sequence generation
- This skill contains the core splitting algorithms
4. **message-generation** - Message creation
- Operation: `/message-generation complete` (per commit)
- Generates: Conventional commit message for each group
5. **commit-best-practices** - Quality assurance
- Operation: `/commit-best-practices check-pre-commit` (optional)
- Operation: `/commit-best-practices review-commit` (after each)
- Validates: Tests pass, quality maintained
6. **history-analysis** - Project awareness
- Operation: `/history-analysis analyze-style` (cached)
- Ensures: Messages match project conventions
### Interactive Workflow
The `/commit-split` command provides a guided experience:
```
1. Analyze changes → Show plan
2. Group files → Present groups
3. For each group:
- Show files and changes
- Generate message
- Get user approval
- Create commit
- Review quality
4. Final summary
```
### Alternative Workflows
**For more control**:
- `/atomic-commit interactive` - Same workflow, skill-level access
- `/atomic-commit sequence` - Generate script to execute
- Manual: Use `/commit` for each group after reviewing
**For just analysis**:
- `/commit-review` - Analysis without execution
- `/atomic-commit suggest` - Get suggestions only
## Usage
```bash
/commit-split # Start interactive commit splitting
```
## What This Does
This command helps you:
1. Validate repository state and analyze all changes
2. Group related files by change type and scope
3. Generate conventional commit messages for each group
4. Create multiple focused commits interactively
5. Ensure each commit is atomic and well-documented
## Complete Interactive Workflow
```
User: /commit-split
┌─────────────────────────────────┐
│ Pre-Flight & Analysis │
│ /commit-error-handling │
│ /commit-analysis │
└────────────┬────────────────────┘
┌─────────────────────────────────┐
│ Generate Splitting Plan │
│ /atomic-commit suggest │
│ → groups files │
│ → calls /message-generation │
│ for each group │
└────────────┬────────────────────┘
┌─────────────────────────────────┐
│ Present Plan & Confirm │
│ User reviews: │
│ - N commits │
│ - Files in each │
│ - Suggested messages │
└────────────┬────────────────────┘
┌────────┐
│ Ready? │
└───┬────┘
┌──────┴──────┐
NO YES
↓ ↓
[Abort] ┌──────────────┐
│ For Each │
│ Group (1-N) │
└──────┬───────┘
┌─────────────────────────────┐
│ Show Group Details │
│ - Files │
│ - Message │
│ - Changes │
└──────────┬──────────────────┘
┌─────────────────────────────┐
│ Get User Action │
│ 1. Create │
│ 2. Edit │
│ 3. Skip │
│ 4. Abort │
└──────────┬──────────────────┘
┌──────┴──────┐
Create Skip/Abort
↓ ↓
┌──────────────────┐ │
│ Pre-Commit Check │ │
│ (optional) │ │
│ /commit-best- │ │
│ practices │ │
└────────┬─────────┘ │
↓ │
┌──────────────────┐ │
│ git add <files> │ │
│ git commit -m "" │ │
└────────┬─────────┘ │
↓ │
┌──────────────────┐ │
│ Post-Commit │ │
│ Review │ │
│ /commit-best- │ │
│ practices │ │
└────────┬─────────┘ │
↓ │
┌──────────────────┐ │
│ Show Progress │ │
│ Continue? │ │
└────────┬─────────┘ │
│ │
└──────────────┘
┌─────────────────────────────┐
│ Final Summary │
│ - List commits created │
│ - Quality scores │
│ - Remaining changes │
│ - Verification commands │
└─────────────────────────────┘
```
## Process
### Step 1: Validate Repository and Analyze Changes
**Orchestrates**:
1. `/commit-error-handling diagnose-issues` - Validate state
2. `/commit-analysis analyze` - Understand changes
**Pre-Flight Checks**:
```bash
# Verify git repository
git rev-parse --git-dir 2>/dev/null
```
If not a git repo, show error and exit.
**Change Analysis**:
```bash
# Get all changes
git status --short
# Get detailed diff
git diff HEAD
# Count changes
git status --short | wc -l
```
**Analysis Output**:
- Total files and line changes
- Type detection for each file
- Scope identification
- Initial grouping strategy
This provides the data for intelligent splitting.
**Direct access**:
- `/commit-error-handling diagnose-issues` - Just validation
- `/commit-analysis analyze` - Just analysis
If no changes, show message and exit.
### Step 2: Generate Splitting Plan
**Orchestrates**: `/atomic-commit suggest`
This operation:
1. **Groups related files**:
- Strategy: Type-based (feat, fix, docs, test)
- Alternative strategies: scope-based, feature-based
2. **Generates commit messages** for each group:
- Uses: `/message-generation complete`
- Format: Conventional commits
- Style: Matches project conventions (from `/history-analysis`)
3. **Creates execution plan**:
- Commit order (dependencies respected)
- File → commit mapping
- Estimated changes per commit
**Plan Structure**:
```
Group 1: feat(auth) - 5 files, +386 lines
feat(auth): implement OAuth authentication system
Group 2: test(auth) - 2 files, +240 lines
test(auth): add comprehensive OAuth tests
Group 3: docs - 1 file, +128 lines
docs: add OAuth authentication guide
```
**Direct access**:
- `/atomic-commit group` - Just grouping
- `/atomic-commit suggest` - Suggestions only
- `/atomic-commit sequence` - Executable script
### Step 3: Present Splitting Plan
Shows the complete plan generated by skill orchestration:
```
INTERACTIVE COMMIT SPLITTING
═════════════════════════════════════════════
I've analyzed your changes and identified 4 logical
commits to create. I'll guide you through each one.
SPLITTING PLAN:
────────────────────────────────────────────
📦 Commit 1: Authentication Feature
Type: feat(auth)
Files: 5 files
Lines: +386 -0
📦 Commit 2: Authentication Tests
Type: test(auth)
Files: 2 files
Lines: +240 -0
📦 Commit 3: Utility Refactoring
Type: refactor
Files: 2 files
Lines: +79 -119
📦 Commit 4: Documentation Updates
Type: docs
Files: 2 files
Lines: +230 -42
════════════════════════════════════════════
Let's create these commits one by one.
You'll review each before it's created.
Ready to start? (y/n)
```
User reviews and confirms before any commits are created.
This transparency allows users to understand the splitting strategy before execution.
### Step 4: Create Commits Iteratively
For each commit group, orchestrates:
**Per-Commit Workflow**:
```
For each group (1 to N):
1. Show group details
2. Present generated message
(from /message-generation complete)
3. Get user action:
- Create with message
- Edit message
- Skip group
- Abort
4. If creating:
a. Stage files: git add <files>
b. Validate (optional):
/commit-best-practices check-pre-commit
c. Create commit: git commit -m "message"
d. Review quality:
/commit-best-practices review-commit
5. Show progress
6. Continue to next group
```
**User Control Points**:
- Review each commit before creation
- Edit any generated message
- Skip groups to commit separately
- Abort if plan needs adjustment
**Quality Assurance** (optional, configurable):
- Pre-commit checks before each commit
- Post-commit review after each commit
- Rollback guidance if issues found
#### Detailed Commit Creation Loop
**For each group:**
1. **Show Group Details**
```
═══════════════════════════════════════════
COMMIT 1 of 4: Authentication Feature
═══════════════════════════════════════════
FILES TO COMMIT (5 files):
────────────────────────────────────────────
M src/auth/oauth.js +156
M src/auth/providers.js +89
M src/auth/middleware.js +73
M src/auth/config.js +45
M src/auth/index.js +23
CHANGES SUMMARY:
────────────────────────────────────────────
- Implements OAuth2 authentication flow
- Adds provider support (Google, GitHub)
- Includes authentication middleware
- Adds configuration management
- Exports unified auth interface
PROPOSED COMMIT MESSAGE:
────────────────────────────────────────────
feat(auth): implement OAuth authentication system
- Add OAuth2 authentication flow
- Implement provider support for Google and GitHub
- Add authentication middleware
- Include configuration management
════════════════════════════════════════════
What would you like to do?
1. Create commit with this message (recommended)
2. Edit the message
3. Skip this group
4. Abort splitting
Enter choice (1/2/3/4):
```
2. **Handle User Choice**
**Choice 1: Create commit**
```bash
# Stage only the files in this group
git add src/auth/oauth.js src/auth/providers.js src/auth/middleware.js src/auth/config.js src/auth/index.js
# Create the commit
git commit -m "feat(auth): implement OAuth authentication system
- Add OAuth2 authentication flow
- Implement provider support for Google and GitHub
- Add authentication middleware
- Include configuration management"
# Show success
echo "✅ Commit 1 created successfully"
git log -1 --oneline
```
**Choice 2: Edit message**
Prompt user for new message:
```
Enter your commit message:
(Use conventional commits format: <type>(<scope>): <subject>)
> _
```
Then create commit with user's message.
**Choice 3: Skip**
Skip this group, move to next.
**Choice 4: Abort**
Exit without creating any more commits. Already created commits remain.
3. **Show Progress**
After each commit:
```
PROGRESS: 1 of 4 commits created
✅ Commit 1: feat(auth) - abc1234
Remaining commits:
📦 Commit 2: test(auth) - 2 files
📦 Commit 3: refactor - 2 files
📦 Commit 4: docs - 2 files
Continue to next commit? (y/n)
```
### Step 5: Complete and Verify
After all commits created:
**Final Validation**:
- List all created commits
- Show remaining uncommitted changes (if any)
- Provide git commands to verify:
- `git log --oneline -N`
- `git log -p -N`
**Quality Report**:
For each commit created:
- Commit hash
- Message
- Quality score (from `/commit-best-practices review-commit`)
**Next Steps Guidance**:
- Run full test suite
- Push commits
- Create pull request
- Use `/commit-best-practices revert-guidance` if needed
**Final Summary Display**:
```
═══════════════════════════════════════════
COMMIT SPLITTING COMPLETE
═══════════════════════════════════════════
CREATED COMMITS:
────────────────────────────────────────────
✅ abc1234 - feat(auth): implement OAuth authentication system
✅ def5678 - test(auth): add comprehensive OAuth tests
✅ ghi9012 - refactor: simplify utility functions
✅ jkl3456 - docs: add OAuth authentication guide
SUMMARY:
────────────────────────────────────────────
Total commits created: 4
Files committed: 12
Remaining changes: 0
═══════════════════════════════════════════
VERIFY YOUR COMMITS:
────────────────────────────────────────────
git log --oneline -4
git log -p -4
NEXT STEPS:
────────────────────────────────────────────
✓ Run tests to verify everything works
✓ Push commits: git push
✓ Create pull request if needed
═══════════════════════════════════════════
Great job creating atomic commits!
Your git history is now clean and logical.
```
### Alternative: If Skipped Groups
If user skipped some groups, show remaining files:
```
REMAINING UNCOMMITTED CHANGES:
────────────────────────────────────────────
M src/utils.js
M README.md
These files were skipped during splitting.
Run /commit to commit them, or /commit-split to try again.
```
## Smart Grouping Strategies
The `/atomic-commit` skill implements multiple grouping strategies:
### Strategy 1: Type-Based Grouping
Group by change type first:
- All `feat` changes together (if same scope)
- All `fix` changes together
- All `refactor` changes together
- All `docs` changes together
- All `test` changes together
### Strategy 2: Scope-Based Grouping
Group by module/component:
- All `auth` changes together
- All `api` changes together
- All `ui` changes together
### Strategy 3: Feature-Based Grouping
Group by feature completion:
- Implementation + Tests together
- Feature code + Documentation together
**Recommendation**: Use Type-Based for most cases, Scope-Based for large features.
**Direct access**: `/atomic-commit group strategy:<type|scope|feature>`
## Commit Message Generation
For each group, the `/message-generation` skill generates a message:
**Template:**
```
<type>(<scope>): <subject>
<body>
```
**Subject Generation:**
- Analyze primary change in the files
- Use active voice, imperative mood
- Keep under 50 characters
- Example: "implement OAuth authentication system"
**Body Generation (bullet points):**
- List major changes in the group
- Keep bullets concise (under 72 chars)
- Focus on what and why, not how
- Example:
```
- Add OAuth2 authentication flow
- Implement provider support for Google and GitHub
- Include authentication middleware
```
**Direct access**: `/message-generation complete files:"<files>" type:<type> scope:<scope>`
## Edge Cases
### Case 1: Single File, Multiple Concerns
If one file has multiple unrelated changes:
```
WARNING: Complex Changes Detected
────────────────────────────────────────────
File: src/app.js
This file contains multiple types of changes:
- New feature: OAuth integration
- Bug fix: Memory leak fix
- Refactor: Code cleanup
RECOMMENDATION:
Use git add -p to stage changes interactively,
or manually split the file changes.
Cannot automatically split this file.
```
### Case 2: Interdependent Changes
If changes must stay together:
```
NOTE: Interdependent Changes
────────────────────────────────────────────
These files must be committed together:
- src/auth/oauth.js (defines function)
- src/auth/index.js (exports function)
Keeping them in the same commit: Commit 1
```
### Case 3: Very Large Number of Files
If 20+ files:
```
LARGE CHANGE SET DETECTED
────────────────────────────────────────────
You have 47 files changed.
This is a lot! Consider:
1. Splitting by module/feature first
2. Creating multiple PRs
3. Committing incrementally as you work
Would you like me to try splitting anyway? (y/n)
```
## Error Handling
**Not a git repository:**
```
ERROR: Not a git repository
Run: git init
(Detected by /commit-error-handling diagnose-issues)
```
**No changes to split:**
```
NO CHANGES TO SPLIT
Your working tree is clean.
Make some changes first, then run /commit-split.
```
**Commit creation failed:**
```
ERROR: Failed to create commit
<git error message>
This may happen if:
- Git user.name or user.email not configured
- Repository is in conflicted state
- Pre-commit hooks failed
Fix the issue and try again.
Use /commit-error-handling diagnose-issues for detailed diagnostics.
```
## Granular Control Options
### Option 1: Use atomic-commit Skill Directly
The `/commit-split` command is a wrapper around `/atomic-commit interactive`.
For equivalent functionality with more control:
```bash
/atomic-commit interactive
```
**Additional atomic-commit operations**:
- `/atomic-commit analyze` - Just check if should split
- `/atomic-commit group strategy:type` - Just group files
- `/atomic-commit suggest` - Just get suggestions
- `/atomic-commit sequence output:script` - Generate bash script
### Option 2: Generate Script and Execute
Generate an executable script:
```bash
/atomic-commit sequence output:script > split-commits.sh
chmod +x split-commits.sh
./split-commits.sh
```
This gives you a script you can review and modify before execution.
### Option 3: Manual with Guidance
Get the plan without execution:
```bash
/commit-review # Get analysis and recommendations
/atomic-commit suggest # Get specific suggestions
```
Then manually:
```bash
git add file1 file2 file3
/commit "feat(auth): add OAuth"
git add file4 file5
/commit "test(auth): add tests"
```
### Option 4: Analysis Only
Just understand your changes without committing:
```bash
/commit-review # Full analysis
/commit-analysis analyze # Just change analysis
/atomic-commit analyze # Just atomicity check
```
## Tips
**Before Splitting:**
- Run `/commit-review` first to see the splitting plan
- Ensure all changes are related to work you want to commit
- Consider using `git stash` for truly unrelated changes
**During Splitting:**
- Review each proposed message carefully
- Edit messages if the generated ones aren't perfect
- Skip groups if you want to commit them separately
- Don't worry about making mistakes - commits can be amended
**After Splitting:**
- Run tests to verify everything still works
- Review commits with `git log -p`
- Use `git commit --amend` if you need to fix the last commit
- Push all commits together or create a PR
## Examples
**Example 1: Feature Development**
```
Files changed: 8
Groups: 3
- feat(payment): 5 files
- test(payment): 2 files
- docs: 1 file
Result: 3 clean atomic commits
```
**Example 2: Bug Fix with Refactoring**
```
Files changed: 4
Groups: 2
- fix(api): 2 files (the actual fix)
- refactor: 2 files (cleanup done while fixing)
Result: 2 separate commits (fix can be cherry-picked)
```
**Example 3: Mixed Changes**
```
Files changed: 15
Groups: 5
- feat(auth): 6 files
- feat(profile): 4 files
- fix(api): 2 files
- test: 2 files
- docs: 1 file
Result: 5 focused commits
```
## Best Practices
1. **Keep It Simple**: Don't overthink grouping
2. **Test Between Commits**: Each commit should work
3. **Review Messages**: Generated messages are suggestions
4. **Be Consistent**: Follow your team's commit conventions
5. **Document Why**: Use commit body for complex changes
6. **One Story Per Commit**: Each commit tells one story
## Related Skills and Commands
### Core Skill: atomic-commit
The `/commit-split` command is powered by the atomic-commit skill:
**Full Skill Access**: `/atomic-commit`
- `analyze` - Determine if should split
- `group` - Group related files
- `suggest` - Generate commit breakdown
- `sequence` - Create execution plan
- `interactive` - Full guided workflow (what /commit-split uses)
### Supporting Skills
**Analysis**:
- `/commit-analysis` - Change type/scope detection
- `/commit-error-handling` - Repository validation
**Message Generation**:
- `/message-generation` - Conventional commit messages
- `/history-analysis` - Project convention learning
**Quality Assurance**:
- `/commit-best-practices` - Validation and review
### Related Commands
**Analysis Phase**:
- `/commit-review` - Analyze and get recommendations (no execution)
**Execution Phase**:
- `/commit-split` - Interactive splitting (this command)
- `/commit` - Single atomic commit
### Complete Workflow
```
1. Analysis: /commit-review
↓ If split needed
2. Execution: /commit-split
↓ Creates N atomic commits
3. Verify: /commit-best-practices review-commit
```
### Documentation
- **Atomic Commits Guide**: `.claude/commands/commit-best-practices/workflow-tips.md`
- **Splitting Strategies**: `.claude/commands/atomic-commit/` skill documentation
- **Skill Architecture**: `.claude/docs/SKILL_ARCHITECTURE.md`
- **Agent Reference**: `.claude/agents/commit-assistant.md`

655
commands/commit.md Normal file
View File

@@ -0,0 +1,655 @@
---
description: Create git commit with intelligent message generation using conventional commits format
argument-hint: [message]
allowed-tools: Bash(git:*)
---
# Intelligent Git Commit Assistant
Create well-formatted git commits with optional automatic message generation following conventional commits standards.
## Overview
The `/commit` command provides a simplified workflow that orchestrates multiple specialized skills to create intelligent, well-formatted commits following conventional commits standards.
### Skills Orchestrated
This command coordinates these granular skills:
1. **commit-error-handling** - Validates git repository state and checks for issues
- Operation: `/commit-error-handling diagnose-issues`
- Purpose: Ensure repository is in a clean state before committing
2. **commit-analysis** - Analyzes changes to determine type, scope, and atomicity
- Operation: `/commit-analysis analyze`
- Purpose: Understand what changes are being committed
3. **history-analysis** (optional) - Learns project commit conventions
- Operation: `/history-analysis analyze-style`
- Purpose: Match team's existing commit style
4. **commit-best-practices** - Validates commit readiness
- Operation: `/commit-best-practices check-pre-commit`
- Purpose: Ensure tests pass, no debug code, lint clean
5. **message-generation** - Generates conventional commit messages
- Operation: `/message-generation complete`
- Purpose: Create well-formatted conventional commit message
6. **atomic-commit** (conditional) - Suggests splitting if needed
- Operation: `/atomic-commit analyze`
- Purpose: Determine if changes should be split into multiple commits
### For Granular Control
If you need precise control over individual operations:
- Use `/commit-error-handling` for error diagnosis
- Use `/commit-analysis` for change analysis
- Use `/message-generation` for message generation
- Use `/atomic-commit` for commit splitting
- Use `/commit-best-practices` for validation
- Use `/history-analysis` for project conventions
See the commit-assistant agent for complete skill documentation.
## Usage
```bash
/commit # Analyze changes and generate commit message
/commit "feat: add login" # Use provided message directly
```
## Complete Workflow
```
User: /commit [optional message]
┌────────────────────────────────┐
│ 1. Error Check │
│ /commit-error-handling │
│ diagnose-issues │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ 2. Analyze Changes │
│ /commit-analysis analyze │
└────────────┬───────────────────┘
┌─────┴──────┐
│ Atomic? │
└─────┬──────┘
┌────────┴────────┐
│ NO │ YES
↓ ↓
┌───────────────┐ ┌──────────────────┐
│ Suggest Split │ │ Continue │
│ /atomic-commit│ │ │
│ analyze │ │ │
└───────┬───────┘ └────────┬─────────┘
│ │
└───────────────────┘
┌────────────────────────────────┐
│ 3. Learn Project Style │
│ /history-analysis (optional) │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ 4. Pre-Commit Check │
│ /commit-best-practices │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ 5. Generate Message │
│ /message-generation complete │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ 6. Present & Confirm │
│ User reviews message │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ 7. Create Commit │
│ git add . && git commit │
└────────────┬───────────────────┘
┌────────────────────────────────┐
│ 8. Post-Commit Review │
│ /commit-best-practices │
│ review-commit │
└────────────────────────────────┘
```
## Process
### Step 1: Verify Git Repository and Check for Issues
**Orchestrates**: `/commit-error-handling diagnose-issues`
This operation checks:
- Repository validity (is this a git repo?)
- Changes present (are there files to commit?)
- No merge conflicts
- Not in detached HEAD state
- Git configuration present
Internally executes:
```bash
git rev-parse --git-dir 2>/dev/null
git status --short
git status --porcelain
git config user.name
git config user.email
```
If issues are found, the operation provides specific guidance:
**Not a git repository:**
```
ERROR: Not a git repository
This directory is not initialized as a git repository.
Initialize one with: git init
```
**No changes to commit:**
```
No changes to commit
Working tree is clean. Make some changes first, then run /commit again.
```
**For detailed error handling**: Use `/commit-error-handling` skill directly with operations:
- `/commit-error-handling handle-no-repo` - Not a repository error
- `/commit-error-handling handle-no-changes` - No changes error
- `/commit-error-handling handle-conflicts` - Merge conflict guidance
- `/commit-error-handling handle-detached-head` - Detached HEAD handling
### Step 2: Check for Atomic Commit Opportunity
**Orchestrates**: `/atomic-commit analyze`
Before proceeding with message generation, check if the changes should be split into multiple atomic commits.
This operation analyzes:
- Number of files changed
- Logical groupings of changes
- Mixed concerns (e.g., feature + refactor + tests)
If changes should be split, provide guidance:
```
RECOMMENDATION: Split into atomic commits
Your changes involve multiple concerns:
1. Feature: New authentication module (3 files)
2. Refactor: Code cleanup in utils (2 files)
3. Tests: Test coverage for auth (1 file)
Consider using /atomic-commit interactive to split these into 3 commits.
```
**For granular atomic commit analysis**:
- `/atomic-commit group` - Group related files together
- `/atomic-commit suggest` - Recommend commit breakdown
- `/atomic-commit sequence` - Generate commit execution plan
- `/atomic-commit interactive` - Step-by-step guided splitting
### Step 3: Analyze Changes
**Orchestrates**: `/commit-analysis analyze`
If user provided a message ($ARGUMENTS is not empty), skip to Step 5 and use their message.
If no message provided, this operation analyzes your changes to determine:
- **Type**: feat, fix, docs, style, refactor, test, chore, perf, ci
- **Scope**: Primary module/component affected (auth, api, ui, etc.)
- **Atomicity**: Whether changes should be in one commit or split
- **Files**: What files are being modified
The analysis uses git commands:
```bash
# Get detailed diff
git diff HEAD
# Get status for summary
git status --short
# Get recent commits for style reference
git log --oneline -10
```
**For granular analysis**: Use these operations directly:
- `/commit-analysis detect-type` - Just determine type
- `/commit-analysis identify-scope` - Just identify scope
- `/commit-analysis assess-atomicity` - Just check if should split
- `/commit-analysis file-stats` - Get file change statistics
### Step 4: Learn Project Conventions (Optional)
**Orchestrates**: `/history-analysis analyze-style` (optional, caches results)
This operation (run periodically, results cached):
1. **Learns project conventions**
- Analyzes recent 50-100 commits
- Detects conventional commits usage
- Identifies common scopes
- Determines average subject length
- Identifies team patterns
The analysis helps ensure generated messages match your project's existing style.
**For custom history analysis**:
- `/history-analysis detect-patterns` - Identify project conventions
- `/history-analysis extract-scopes` - Discover commonly used scopes
- `/history-analysis suggest-conventions` - Recommend conventions
- `/history-analysis learn-project` - Full project learning
### Step 5: Pre-Commit Validation (Recommended)
**Orchestrates**: `/commit-best-practices check-pre-commit`
This operation validates:
- Tests pass (npm test, pytest, cargo test, go test)
- Lint passes (eslint, pylint, clippy)
- No debug code (console.log, debugger, print, pdb)
- No TODO/FIXME in new code
- No merge conflict markers
If validation fails, commit is blocked with specific guidance:
```
PRE-COMMIT VALIDATION FAILED
Issues found:
Tests: 2 tests failing in auth.test.js
Lint: 3 errors in src/auth/oauth.js
Debug: Found console.log in src/utils/debug.js
Fix these issues before committing.
Run /commit-best-practices check-pre-commit for details.
```
**For validation control**:
- Enable/disable via configuration
- Run manually: `/commit-best-practices check-pre-commit`
- Skip with: `/commit --skip-validation` (not recommended)
### Step 6: Generate Commit Message
**Orchestrates**: `/message-generation complete`
Based on the analysis from Steps 3-4, this operation generates a conventional commit message following this format:
```
<type>(<scope>): <subject>
<body (optional)>
<footer (optional)>
```
The generated message matches your project's existing style from history analysis.
**Commit Types (prioritize by analyzing changes):**
- **feat**: New features, new functions, new capabilities
- Detects: New files, new functions, new exports, new components
- Example: "feat(auth): add OAuth2 authentication support"
- **fix**: Bug fixes, error corrections, issue resolutions
- Detects: Error handling, null checks, validation fixes, condition fixes
- Example: "fix(api): resolve null pointer in user endpoint"
- **docs**: Documentation only changes
- Detects: Changes only in README, *.md files, comments, JSDoc
- Example: "docs(readme): update installation instructions"
- **style**: Formatting, whitespace, no code logic change
- Detects: Indentation, semicolons, whitespace, code formatting
- Example: "style: apply prettier formatting to components"
- **refactor**: Code restructuring without behavior change
- Detects: Renamed variables, extracted functions, reorganized code
- Example: "refactor(utils): simplify date formatting logic"
- **test**: Adding or updating tests
- Detects: Changes in test files, spec files, test utilities
- Example: "test(auth): add unit tests for login flow"
- **chore**: Build, dependencies, tooling, configuration
- Detects: package.json, build configs, CI configs, dependencies
- Example: "chore: update dependencies to latest versions"
- **perf**: Performance improvements
- Detects: Optimization, caching, algorithm improvements
- Example: "perf(render): optimize component re-rendering"
- **ci**: CI/CD configuration changes
- Detects: .github/workflows, .gitlab-ci.yml, CI configs
- Example: "ci: add automated testing workflow"
**Scope Determination:**
- Identify the primary module, component, or area affected
- Use kebab-case for scope names
- Examples: auth, api, utils, components, config, database
**Subject Guidelines:**
- 50 characters or less
- Present tense, imperative mood (add, not added or adds)
- No period at the end
- Lowercase after the colon
**Body (include if changes are complex):**
- Explain what and why, not how
- Wrap at 72 characters
- Separate from subject with blank line
- Use bullet points for multiple items
**For custom message generation**:
- `/message-generation subject` - Generate subject line only
- `/message-generation body` - Generate body only
- `/message-generation footer` - Add footer with breaking changes/issues
- `/message-generation validate` - Validate existing message format
**Analysis Example:**
If git diff shows:
```diff
+++ src/auth/oauth.js
+export function authenticateWithOAuth(provider) {
+ // OAuth implementation
+}
+++ src/auth/providers.js
+export const providers = {
+ google: { ... },
+ github: { ... }
+}
+++ tests/auth.test.js
+describe('OAuth authentication', () => {
```
Generate message:
```
feat(auth): add OAuth authentication support
- Implement OAuth2 authentication flow
- Add support for Google and GitHub providers
- Include comprehensive unit tests
```
### Step 7: Present Message and Confirm
If message was generated, present it to the user:
```
PROPOSED COMMIT MESSAGE:
─────────────────────────────────────────────
feat(auth): add OAuth authentication support
- Implement OAuth2 authentication flow
- Add support for Google and GitHub providers
- Include comprehensive unit tests
─────────────────────────────────────────────
FILES TO BE COMMITTED:
M src/auth/oauth.js
M src/auth/providers.js
M tests/auth.test.js
Would you like to:
1. Use this message (y)
2. Edit the message (e)
3. Provide your own message (m)
4. Cancel (n)
```
**Handle user response:**
- If user says "y" or "yes" or approves: proceed to commit
- If user says "e" or "edit": tell them to run `/commit "edited message here"`
- If user says "m" or "custom": tell them to run `/commit "your custom message"`
- If user says "n" or "no" or "cancel": abort
### Step 8: Create Commit
Stage all changes and create the commit:
```bash
# Stage all changes
git add .
# Create commit with message
git commit -m "<the message>"
```
### Step 9: Post-Commit Review
**Orchestrates**: `/commit-best-practices review-commit`
After successful commit, this operation:
- Analyzes commit quality (0-100 score)
- Validates conventional commits format
- Checks atomicity
- Provides improvement suggestions
Show detailed information:
```bash
# Get commit info
git log -1 --stat
git log -1 --format="%H"
```
Format output with quality review:
```
COMMIT SUCCESSFUL
─────────────────────────────────────────────
Hash: abc1234def5678
Message: feat(auth): add OAuth authentication support
FILES CHANGED: 5 files
INSERTIONS: 247 (+)
DELETIONS: 63 (-)
─────────────────────────────────────────────
COMMIT QUALITY REVIEW (Score: 95/100)
Format: Excellent - follows conventional commits
Atomicity: Good - focused on single feature
Message: Clear and descriptive
Suggestions:
Consider adding issue reference in footer (e.g., "Closes #123")
─────────────────────────────────────────────
NEXT STEPS:
Run /commit again to check for remaining uncommitted changes
Run git push to push this commit to remote
```
**For detailed review**:
- `/commit-best-practices review-commit` - Full analysis
- `/commit-best-practices amend-guidance` - Safe amend help
- `/commit-best-practices revert-guidance` - Help with commit reverts
### Step 10: Check for Remaining Changes
After committing, check if there are more uncommitted changes:
```bash
git status --short
```
If there are more changes, add a tip:
```
TIP: You have 3 more modified files.
Run /commit again to commit them.
```
## Error Handling
**Orchestrates**: `/commit-error-handling` skill for all error scenarios
**Not a git repository:**
```
ERROR: Not a git repository
Initialize with: git init
```
Use: `/commit-error-handling handle-no-repo` for detailed guidance
**No changes to commit:**
```
Nothing to commit, working tree clean
```
Use: `/commit-error-handling handle-no-changes` for suggestions
**Merge conflicts:**
```
ERROR: Merge conflicts detected
Resolve conflicts before committing
```
Use: `/commit-error-handling handle-conflicts` for conflict resolution guidance
**Detached HEAD state:**
```
ERROR: Detached HEAD state
Cannot commit in detached HEAD
```
Use: `/commit-error-handling handle-detached-head` for recovery steps
**Commit failed:**
```
ERROR: Commit failed
<show git error message>
Common fixes:
- Ensure you have staged changes
- Check git configuration (git config user.name/user.email)
- Verify repository is not in a conflicted state
```
Use: `/commit-error-handling diagnose-issues` for comprehensive diagnosis
## Examples
**Example 1: Auto-generate message**
```bash
/commit
# Analyzes changes, generates "feat(api): add user authentication endpoint"
# Prompts for confirmation, creates commit
```
**Example 2: Custom message**
```bash
/commit "fix: resolve login timeout issue"
# Uses provided message directly
```
**Example 3: Complex feature**
```bash
/commit
# Generates:
# feat(payments): integrate Stripe payment processing
#
# - Add Stripe SDK integration
# - Implement payment flow for subscriptions
# - Add webhook handlers for payment events
# - Include error handling and retry logic
```
## Tips
- Let the assistant analyze changes for accurate commit types
- Use conventional commits format for consistency
- Create atomic commits (one logical change per commit)
- Run `/atomic-commit analyze` first to check if you should split commits
- Run `/commit-best-practices check-pre-commit` to validate before committing
- Use `/history-analysis analyze-style` to learn project conventions
- Include scope for better commit history navigation
## Conventional Commits Quick Reference
**Format:** `<type>(<scope>): <subject>`
**Types:** feat, fix, docs, style, refactor, test, chore, perf, ci
**Good Examples:**
- feat(auth): add two-factor authentication
- fix(api): handle null response in user endpoint
- docs(readme): add installation instructions
- refactor(utils): extract validation logic to separate module
- test(auth): add integration tests for OAuth flow
**Bad Examples:**
- update files (vague, no type)
- fixed bug (no scope, not descriptive)
- WIP (work in progress, not descriptive)
- asdf (meaningless)
## Related Skills and Commands
### Granular Skills
For precise control over individual operations:
- **Error Handling**: `/commit-error-handling` - Diagnose and resolve git issues
- `/commit-error-handling diagnose-issues` - Comprehensive diagnosis
- `/commit-error-handling handle-no-repo` - Repository initialization
- `/commit-error-handling handle-no-changes` - No changes guidance
- `/commit-error-handling handle-conflicts` - Merge conflict resolution
- `/commit-error-handling handle-detached-head` - Detached HEAD recovery
- **Analysis**: `/commit-analysis` - Detailed change analysis
- `/commit-analysis analyze` - Full comprehensive analysis
- `/commit-analysis detect-type` - Determine commit type
- `/commit-analysis identify-scope` - Identify affected module
- `/commit-analysis assess-atomicity` - Check if should split
- `/commit-analysis file-stats` - Get file change statistics
- **Message Generation**: `/message-generation` - Create/validate messages
- `/message-generation complete` - Generate full message
- `/message-generation subject` - Create subject line only
- `/message-generation body` - Compose commit body
- `/message-generation footer` - Add footer with issues
- `/message-generation validate` - Validate message format
- **Atomic Commits**: `/atomic-commit` - Split large commits
- `/atomic-commit analyze` - Determine if should split
- `/atomic-commit group` - Group related files
- `/atomic-commit suggest` - Recommend commit breakdown
- `/atomic-commit sequence` - Generate commit plan
- `/atomic-commit interactive` - Guided splitting
- **Best Practices**: `/commit-best-practices` - Validate and review
- `/commit-best-practices check-pre-commit` - Validate readiness
- `/commit-best-practices review-commit` - Analyze quality
- `/commit-best-practices amend-guidance` - Safe amend help
- `/commit-best-practices revert-guidance` - Revert help
- `/commit-best-practices workflow-tips` - Complete workflow guidance
- **History Analysis**: `/history-analysis` - Learn project conventions
- `/history-analysis analyze-style` - Learn from recent commits
- `/history-analysis detect-patterns` - Identify conventions
- `/history-analysis extract-scopes` - Discover common scopes
- `/history-analysis suggest-conventions` - Recommend conventions
- `/history-analysis learn-project` - Full project learning
### Related High-Level Commands
Other simplified workflows:
- `/commit-review` - Analyze changes and get splitting recommendations
- `/commit-split` - Interactively split large commits into atomic commits
### Documentation References
- **Conventional Commits**: https://www.conventionalcommits.org/
- **SKILL_ARCHITECTURE.md**: Pattern documentation for skill orchestration
- **Agent Documentation**: See `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/agents/commit-assistant.md` for complete skill reference
- **Plugin Repository**: https://github.com/your-repo/git-commit-assistant

View File

@@ -0,0 +1,408 @@
#!/usr/bin/env python3
"""
============================================================================
SCRIPT: convention-recommender.py
PURPOSE: Generate project-specific commit convention recommendations
VERSION: 1.0.0
USAGE: ./convention-recommender.py --count N --branch BRANCH [--priority LEVEL]
RETURNS: JSON format with prioritized recommendations
EXIT CODES:
0 - Success
1 - Not a git repository
2 - No commit history
3 - Analysis failed
DEPENDENCIES: git, python3, style-analyzer.sh, pattern-detector.py, scope-extractor.sh
============================================================================
"""
import subprocess
import sys
import json
import argparse
import os
from typing import Dict, List, Tuple
def run_script(script_path: str, args: List[str]) -> Tuple[int, str]:
"""Execute a script and return exit code and output."""
try:
cmd = [script_path] + args
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=False,
cwd=os.path.dirname(script_path)
)
return result.returncode, result.stdout.strip()
except Exception as e:
return 1, json.dumps({'error': str(e)})
def gather_analysis_data(count: int, branch: str, scripts_dir: str) -> Dict:
"""Gather all analysis data from other scripts."""
data = {}
# Run style-analyzer.sh
style_script = os.path.join(scripts_dir, 'style-analyzer.sh')
code, output = run_script(style_script, [str(count), branch])
if code == 0:
try:
data['style'] = json.loads(output)
except json.JSONDecodeError:
data['style'] = {}
# Run pattern-detector.py
pattern_script = os.path.join(scripts_dir, 'pattern-detector.py')
code, output = run_script(pattern_script, [
'--count', str(count),
'--branch', branch
])
if code == 0:
try:
data['patterns'] = json.loads(output)
except json.JSONDecodeError:
data['patterns'] = {}
# Run scope-extractor.sh
scope_script = os.path.join(scripts_dir, 'scope-extractor.sh')
code, output = run_script(scope_script, [
'--count', str(count),
'--branch', branch,
'--min-frequency', '2'
])
if code == 0:
try:
data['scopes'] = json.loads(output)
except json.JSONDecodeError:
data['scopes'] = {}
return data
def generate_recommendations(data: Dict, priority_filter: str) -> Dict:
"""Generate prioritized recommendations based on analysis data."""
recommendations = {
'high_priority': [],
'medium_priority': [],
'low_priority': []
}
style = data.get('style', {}).get('project_style', {})
patterns = data.get('patterns', {})
scopes = data.get('scopes', {})
# HIGH PRIORITY RECOMMENDATIONS
# 1. Conventional commits adoption
conv_pct = style.get('conventional_commits_percentage', 0)
if conv_pct < 50:
recommendations['high_priority'].append({
'id': 1,
'title': 'Adopt Conventional Commits Format',
'status': 'needs_improvement',
'current_usage': conv_pct,
'target_usage': 80,
'action': 'Migrate to conventional commits format: <type>(<scope>): <subject>',
'benefit': 'Enables automated changelog, semantic versioning, and better git history',
'priority': 'high',
'examples': [
'feat(auth): implement OAuth2 authentication',
'fix(api): handle null pointer in user endpoint',
'docs: update API documentation'
]
})
elif conv_pct < 80:
recommendations['medium_priority'].append({
'id': 1,
'title': 'Increase Conventional Commits Usage',
'status': 'moderate',
'current_usage': conv_pct,
'target_usage': 90,
'action': 'Encourage team to use conventional commits consistently',
'benefit': 'Better consistency and tooling support',
'priority': 'medium'
})
else:
recommendations['high_priority'].append({
'id': 1,
'title': 'Continue Using Conventional Commits',
'status': 'good',
'current_usage': conv_pct,
'target_usage': 90,
'action': 'Maintain current practice',
'benefit': 'Already well-adopted, enables automation',
'priority': 'high'
})
# 2. Subject line length
avg_length = style.get('average_subject_length', 0)
if avg_length > 60:
recommendations['high_priority'].append({
'id': 2,
'title': 'Reduce Subject Line Length',
'status': 'needs_improvement',
'current_value': avg_length,
'target_value': 50,
'action': 'Keep subject lines under 50 characters',
'benefit': 'Better readability in git log, GitHub UI, and terminal',
'priority': 'high'
})
elif avg_length > 50:
recommendations['medium_priority'].append({
'id': 2,
'title': 'Optimize Subject Line Length',
'status': 'moderate',
'current_value': avg_length,
'target_value': 50,
'action': 'Aim for concise subject lines (under 50 chars)',
'priority': 'medium'
})
# 3. Imperative mood
imperative_pct = style.get('imperative_mood_percentage', 0)
if imperative_pct < 80:
recommendations['high_priority'].append({
'id': 3,
'title': 'Use Imperative Mood Consistently',
'status': 'needs_improvement',
'current_usage': imperative_pct,
'target_usage': 90,
'action': 'Use imperative mood: "add" not "added", "fix" not "fixed"',
'benefit': 'Clearer, more professional commit messages',
'priority': 'high',
'examples': [
'✓ add user authentication',
'✗ added user authentication',
'✓ fix null pointer exception',
'✗ fixed null pointer exception'
]
})
# MEDIUM PRIORITY RECOMMENDATIONS
# 4. Body usage
body_pct = style.get('has_body_percentage', 0)
if body_pct < 50:
recommendations['medium_priority'].append({
'id': 4,
'title': 'Increase Body Usage for Complex Changes',
'status': 'low',
'current_usage': body_pct,
'target_usage': 50,
'action': 'Add commit body for non-trivial changes (>3 files, complex logic)',
'benefit': 'Better context for code review and future reference',
'priority': 'medium',
'when_to_use': [
'Multiple files changed (>3)',
'Complex logic modifications',
'Breaking changes',
'Security-related changes'
]
})
# 5. Issue references
issue_pct = style.get('references_issues_percentage', 0)
if issue_pct > 50:
recommendations['medium_priority'].append({
'id': 5,
'title': 'Continue Issue Referencing Practice',
'status': 'good',
'current_usage': issue_pct,
'action': 'Maintain consistent issue references',
'benefit': 'Excellent traceability between commits and issues',
'priority': 'medium'
})
elif issue_pct > 25:
recommendations['medium_priority'].append({
'id': 5,
'title': 'Increase Issue References',
'status': 'moderate',
'current_usage': issue_pct,
'target_usage': 60,
'action': 'Reference related issues: "Closes #123", "Fixes #456", "Refs #789"',
'benefit': 'Better traceability',
'priority': 'medium'
})
# LOW PRIORITY RECOMMENDATIONS
# 6. Scope standardization
scope_count = scopes.get('total_scopes', 0)
if scope_count > 0:
top_scopes = scopes.get('scopes', [])[:5]
scope_names = [s['name'] for s in top_scopes]
recommendations['medium_priority'].append({
'id': 6,
'title': 'Use Standard Project Scopes',
'status': 'good',
'action': f'Use these common scopes: {", ".join(scope_names)}',
'benefit': 'Consistent scope usage across team',
'priority': 'medium',
'scopes': scope_names
})
# 7. Co-author attribution
recommendations['low_priority'].append({
'id': 7,
'title': 'Consider Co-Author Attribution',
'status': 'optional',
'action': 'Add co-authors for pair programming: Co-authored-by: Name <email>',
'benefit': 'Team recognition and contribution tracking',
'priority': 'low',
'example': 'Co-authored-by: Jane Doe <jane@example.com>'
})
# 8. Breaking change documentation
recommendations['low_priority'].append({
'id': 8,
'title': 'Document Breaking Changes',
'status': 'important',
'action': 'Use BREAKING CHANGE: footer when applicable',
'benefit': 'Clear communication of breaking changes for semantic versioning',
'priority': 'low',
'example': 'BREAKING CHANGE: API now requires OAuth tokens instead of API keys'
})
# Filter by priority if specified
if priority_filter and priority_filter != 'all':
priority_key = f'{priority_filter}_priority'
filtered = {priority_key: recommendations.get(priority_key, [])}
return filtered
return recommendations
def generate_style_guide(data: Dict) -> Dict:
"""Generate project-specific style guide."""
style = data.get('style', {}).get('project_style', {})
scopes_data = data.get('scopes', {})
# Extract common types
common_types = style.get('common_types', [])
types_sorted = sorted(common_types, key=lambda x: x.get('count', 0), reverse=True)
# Extract common scopes
scopes = scopes_data.get('scopes', [])[:10]
# Build style guide
return {
'format': '<type>(<scope>): <subject>',
'max_subject_length': 50,
'body_wrap': 72,
'types': [
{
'name': t.get('type', 'unknown'),
'percentage': t.get('percentage', 0),
'description': get_type_description(t.get('type', 'unknown'))
}
for t in types_sorted
],
'scopes': [
{
'name': s.get('name', 'unknown'),
'percentage': s.get('percentage', 0),
'description': s.get('description', 'Unknown'),
'category': s.get('category', 'other')
}
for s in scopes
],
'rules': [
'Use imperative mood ("add" not "added")',
'Capitalize first letter of subject',
'No period at end of subject line',
'Use lowercase for scopes',
'Wrap body at 72 characters',
'Separate body and footer with blank line',
'Use bullet points in body',
'Reference issues when applicable'
]
}
def get_type_description(type_name: str) -> str:
"""Get description for commit type."""
descriptions = {
'feat': 'New features',
'fix': 'Bug fixes',
'docs': 'Documentation changes',
'style': 'Formatting changes (no code change)',
'refactor': 'Code restructuring',
'perf': 'Performance improvements',
'test': 'Test additions/updates',
'build': 'Build system changes',
'ci': 'CI/CD changes',
'chore': 'Maintenance tasks',
'revert': 'Revert previous commit'
}
return descriptions.get(type_name, 'Other changes')
def calculate_confidence(data: Dict) -> str:
"""Calculate confidence level of recommendations."""
commits_analyzed = data.get('style', {}).get('project_style', {}).get('commits_analyzed', 0)
if commits_analyzed >= 100:
return 'high'
elif commits_analyzed >= 50:
return 'medium'
elif commits_analyzed >= 20:
return 'low'
else:
return 'very_low'
def main():
parser = argparse.ArgumentParser(description='Generate convention recommendations')
parser.add_argument('--count', type=int, default=50, help='Number of commits to analyze')
parser.add_argument('--branch', default='HEAD', help='Branch to analyze')
parser.add_argument('--priority', choices=['high', 'medium', 'low', 'all'], default='all',
help='Filter by priority level')
args = parser.parse_args()
# Get scripts directory
scripts_dir = os.path.dirname(os.path.abspath(__file__))
# Gather analysis data
data = gather_analysis_data(args.count, args.branch, scripts_dir)
if not data:
print(json.dumps({'error': 'Failed to gather analysis data'}), file=sys.stderr)
sys.exit(3)
# Generate recommendations
recommendations = generate_recommendations(data, args.priority)
# Generate style guide
style_guide = generate_style_guide(data)
# Calculate confidence
confidence = calculate_confidence(data)
# Calculate consistency score
consistency_score = data.get('patterns', {}).get('consistency_score', 0)
# Build output
output = {
'commits_analyzed': data.get('style', {}).get('project_style', {}).get('commits_analyzed', 0),
'branch': args.branch,
'consistency_score': consistency_score,
'confidence': confidence,
'recommendations': recommendations,
'style_guide': style_guide,
'automation': {
'commitlint': True,
'changelog_generator': 'standard-version',
'semantic_release': True
}
}
# Output JSON
print(json.dumps(output, indent=2))
sys.exit(0)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,421 @@
#!/usr/bin/env python3
"""
============================================================================
SCRIPT: pattern-detector.py
PURPOSE: Detect commit message patterns and conventions from git history
VERSION: 1.0.0
USAGE: ./pattern-detector.py --count N --branch BRANCH [--detailed]
RETURNS: JSON format with pattern detection results
EXIT CODES:
0 - Success
1 - Not a git repository
2 - No commit history
3 - Git command failed
DEPENDENCIES: git, python3
============================================================================
"""
import subprocess
import sys
import json
import re
import argparse
from collections import defaultdict
from typing import Dict, List, Tuple
def run_git_command(cmd: List[str]) -> Tuple[int, str]:
"""Execute git command and return exit code and output."""
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=False
)
return result.returncode, result.stdout.strip()
except Exception as e:
return 1, str(e)
def is_git_repository() -> bool:
"""Check if current directory is a git repository."""
code, _ = run_git_command(['git', 'rev-parse', '--git-dir'])
return code == 0
def has_commits() -> bool:
"""Check if repository has any commits."""
code, _ = run_git_command(['git', 'log', '-1'])
return code == 0
def get_commits(count: int, branch: str) -> List[Dict[str, str]]:
"""Fetch commit messages from git log."""
code, output = run_git_command([
'git', 'log',
f'-{count}',
branch,
'--format=%H%n%s%n%b%n---COMMIT_SEPARATOR---'
])
if code != 0:
return []
commits = []
lines = output.split('\n')
i = 0
while i < len(lines):
if i + 1 >= len(lines):
break
commit_hash = lines[i]
subject = lines[i + 1] if i + 1 < len(lines) else ""
# Find body (lines until separator)
body_lines = []
i += 2
while i < len(lines) and lines[i] != '---COMMIT_SEPARATOR---':
if lines[i].strip(): # Skip empty lines at start
body_lines.append(lines[i])
i += 1
body = '\n'.join(body_lines).strip()
commits.append({
'hash': commit_hash,
'subject': subject,
'body': body,
'full': subject + '\n\n' + body if body else subject
})
i += 1 # Skip separator
return commits
def is_conventional_commit(subject: str) -> bool:
"""Check if commit follows conventional commits format."""
pattern = r'^[a-z]+(\([^)]+\))?: .+'
return bool(re.match(pattern, subject))
def has_prefix(subject: str) -> bool:
"""Check if commit has prefix format like [PREFIX]."""
pattern = r'^\[[^\]]+\]'
return bool(re.match(pattern, subject))
def has_tag(subject: str) -> bool:
"""Check if commit starts with tag like #tag."""
return subject.startswith('#')
def is_imperative_mood(subject: str) -> bool:
"""
Check if subject uses imperative mood.
Simple heuristic: starts with common imperative verbs.
"""
# Extract first word after type/scope if conventional
words = subject.lower()
if ':' in words:
words = words.split(':', 1)[1].strip()
# Common imperative verbs and their non-imperative forms to avoid
imperative_verbs = [
'add', 'fix', 'update', 'remove', 'delete', 'create', 'implement',
'change', 'improve', 'optimize', 'refactor', 'enhance', 'correct',
'resolve', 'merge', 'bump', 'revert', 'document', 'upgrade',
'downgrade', 'rename', 'move', 'replace', 'extract', 'simplify'
]
# Non-imperative indicators
non_imperative = ['added', 'fixed', 'updated', 'removed', 'deleted',
'created', 'implemented', 'changed', 'improved',
'adding', 'fixing', 'updating']
first_word = words.split()[0] if words.split() else ""
if first_word in non_imperative:
return False
return first_word in imperative_verbs
def is_capitalized(subject: str) -> bool:
"""Check if subject is properly capitalized."""
# Extract text after type/scope if conventional
text = subject
if ':' in text:
text = text.split(':', 1)[1].strip()
return text[0].isupper() if text else False
def has_no_period_end(subject: str) -> bool:
"""Check if subject doesn't end with period."""
return not subject.endswith('.')
def has_blank_line_before_body(full_message: str) -> bool:
"""Check if there's a blank line between subject and body."""
lines = full_message.split('\n')
if len(lines) < 3:
return True # No body or only one line body
# Check if second line is empty
return lines[1].strip() == ''
def is_body_wrapped(body: str, max_width: int = 72) -> bool:
"""Check if body lines are wrapped at max_width."""
if not body:
return True
lines = body.split('\n')
for line in lines:
# Allow bullet points and URLs to exceed limit
if line.strip().startswith(('-', '*', '', 'http://', 'https://')):
continue
if len(line) > max_width:
return False
return True
def has_footer(full_message: str) -> bool:
"""Check if commit has footer (BREAKING CHANGE, issue refs, etc.)."""
footer_patterns = [
r'BREAKING CHANGE:',
r'Closes #\d+',
r'Fixes #\d+',
r'Refs #\d+',
r'Co-authored-by:',
r'Signed-off-by:'
]
for pattern in footer_patterns:
if re.search(pattern, full_message):
return True
return False
def references_issues(full_message: str) -> bool:
"""Check if commit references issues."""
pattern = r'#\d+|[Cc]loses|[Ff]ixes|[Rr]efs'
return bool(re.search(pattern, full_message))
def mentions_breaking(full_message: str) -> bool:
"""Check if commit mentions breaking changes."""
return 'BREAKING CHANGE:' in full_message or 'BREAKING-CHANGE:' in full_message
def has_co_authors(full_message: str) -> bool:
"""Check if commit has co-authors."""
return 'Co-authored-by:' in full_message
def is_signed_off(full_message: str) -> bool:
"""Check if commit is signed off."""
return 'Signed-off-by:' in full_message
def includes_rationale(body: str) -> bool:
"""Check if body includes rationale (why/because/to/for)."""
if not body:
return False
words = ['because', 'to ', 'for ', 'why', 'since', 'as ', 'in order to']
body_lower = body.lower()
return any(word in body_lower for word in words)
def mentions_impact(body: str) -> bool:
"""Check if body mentions impact."""
if not body:
return False
words = ['affect', 'impact', 'change', 'improve', 'break', 'fix']
body_lower = body.lower()
return any(word in body_lower for word in words)
def analyze_patterns(commits: List[Dict[str, str]]) -> Dict:
"""Analyze commit patterns and return results."""
total = len(commits)
# Initialize counters
patterns = {
'format': defaultdict(int),
'conventions': defaultdict(int),
'content': defaultdict(int)
}
# Count commits with bodies (for calculations)
commits_with_body = 0
for commit in commits:
subject = commit['subject']
body = commit['body']
full = commit['full']
# Format patterns
if is_conventional_commit(subject):
patterns['format']['conventional_commits'] += 1
elif has_prefix(subject):
patterns['format']['prefixed'] += 1
elif has_tag(subject):
patterns['format']['tagged'] += 1
else:
patterns['format']['simple_subject'] += 1
# Convention patterns
if is_imperative_mood(subject):
patterns['conventions']['imperative_mood'] += 1
if is_capitalized(subject):
patterns['conventions']['capitalized_subject'] += 1
if has_no_period_end(subject):
patterns['conventions']['no_period_end'] += 1
if body:
commits_with_body += 1
if has_blank_line_before_body(full):
patterns['conventions']['blank_line_before_body'] += 1
if is_body_wrapped(body):
patterns['conventions']['wrapped_body'] += 1
if has_footer(full):
patterns['conventions']['has_footer'] += 1
# Content patterns
if references_issues(full):
patterns['content']['references_issues'] += 1
if mentions_breaking(full):
patterns['content']['mentions_breaking'] += 1
if has_co_authors(full):
patterns['content']['has_co_authors'] += 1
if is_signed_off(full):
patterns['content']['signed_off'] += 1
if includes_rationale(body):
patterns['content']['includes_rationale'] += 1
if mentions_impact(body):
patterns['content']['mentions_impact'] += 1
# Calculate percentages and strength
def calc_percentage(count, denominator=total):
return round((count / denominator * 100), 1) if denominator > 0 else 0
def get_strength(percentage):
if percentage >= 95:
return "perfect"
elif percentage >= 80:
return "strong"
elif percentage >= 65:
return "dominant"
elif percentage >= 45:
return "common"
elif percentage >= 25:
return "moderate"
elif percentage >= 10:
return "occasional"
elif percentage >= 1:
return "rare"
else:
return "absent"
# Build results
results = {
'format': {},
'conventions': {},
'content': {}
}
for category, counters in patterns.items():
for pattern_name, count in counters.items():
# Use commits_with_body as denominator for body-specific patterns
if pattern_name in ['blank_line_before_body', 'wrapped_body']:
denominator = commits_with_body
else:
denominator = total
percentage = calc_percentage(count, denominator)
results[category][pattern_name] = {
'count': count,
'percentage': percentage,
'strength': get_strength(percentage)
}
# Calculate consistency score
# Weight: format(40), conventions(40), content(20)
format_score = results['format'].get('conventional_commits', {}).get('percentage', 0)
convention_scores = [
results['conventions'].get('imperative_mood', {}).get('percentage', 0),
results['conventions'].get('capitalized_subject', {}).get('percentage', 0),
results['conventions'].get('no_period_end', {}).get('percentage', 0)
]
avg_convention = sum(convention_scores) / len(convention_scores) if convention_scores else 0
content_scores = [
results['content'].get('references_issues', {}).get('percentage', 0),
results['content'].get('includes_rationale', {}).get('percentage', 0)
]
avg_content = sum(content_scores) / len(content_scores) if content_scores else 0
consistency_score = int(format_score * 0.4 + avg_convention * 0.4 + avg_content * 0.2)
# Determine dominant pattern
format_patterns = results['format']
dominant_pattern = max(format_patterns.items(), key=lambda x: x[1]['count'])[0] if format_patterns else "unknown"
return {
'commits_analyzed': total,
'patterns': results,
'consistency_score': consistency_score,
'dominant_pattern': dominant_pattern
}
def main():
parser = argparse.ArgumentParser(description='Detect commit message patterns')
parser.add_argument('--count', type=int, default=50, help='Number of commits to analyze')
parser.add_argument('--branch', default='HEAD', help='Branch to analyze')
parser.add_argument('--detailed', action='store_true', help='Include detailed breakdown')
args = parser.parse_args()
# Validate git repository
if not is_git_repository():
print(json.dumps({'error': 'Not in a git repository'}), file=sys.stderr)
sys.exit(1)
if not has_commits():
print(json.dumps({'error': 'No commit history found'}), file=sys.stderr)
sys.exit(2)
# Fetch commits
commits = get_commits(args.count, args.branch)
if not commits:
print(json.dumps({'error': 'Failed to fetch commits'}), file=sys.stderr)
sys.exit(3)
# Analyze patterns
results = analyze_patterns(commits)
results['branch'] = args.branch
results['detailed'] = args.detailed
# Output JSON
print(json.dumps(results, indent=2))
sys.exit(0)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,230 @@
#!/bin/bash
# ============================================================================
# SCRIPT: scope-extractor.sh
# PURPOSE: Extract and analyze scopes from commit messages
# VERSION: 1.0.0
# USAGE: ./scope-extractor.sh --count N --branch BRANCH [--min-frequency N]
# RETURNS: JSON format with scope analysis results
# EXIT CODES:
# 0 - Success
# 1 - Not a git repository
# 2 - No commit history
# 3 - Git command failed
# DEPENDENCIES: git, jq (optional)
# ============================================================================
# Parse command line arguments
COUNT=50
BRANCH="HEAD"
MIN_FREQUENCY=2
while [[ $# -gt 0 ]]; do
case $1 in
--count)
COUNT="$2"
shift 2
;;
--branch)
BRANCH="$2"
shift 2
;;
--min-frequency)
MIN_FREQUENCY="$2"
shift 2
;;
*)
shift
;;
esac
done
# Validate git repository
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo '{"error": "Not in a git repository"}' >&2
exit 1
fi
# Check if commits exist
if ! git log -1 >/dev/null 2>&1; then
echo '{"error": "No commit history found"}' >&2
exit 2
fi
# Get commit subjects
SUBJECTS=$(git log --format='%s' -"$COUNT" "$BRANCH" 2>/dev/null)
if [ $? -ne 0 ]; then
echo '{"error": "Failed to fetch git log"}' >&2
exit 3
fi
# Create temporary files for processing
TEMP_SCOPES=$(mktemp)
TEMP_RESULTS=$(mktemp)
# Clean up temp files on exit
trap "rm -f $TEMP_SCOPES $TEMP_RESULTS" EXIT
# Extract scopes from conventional commit format: type(scope): subject
# Also handle nested scopes: type(parent/child): subject
# And multi-scopes: type(scope1,scope2): subject
TOTAL_COMMITS=$(echo "$SUBJECTS" | wc -l)
SCOPED_COMMITS=0
while IFS= read -r subject; do
[ -z "$subject" ] && continue
# Match conventional commit with scope: type(scope): subject
if echo "$subject" | grep -qE '^[a-z]+\([^)]+\): '; then
((SCOPED_COMMITS++))
# Extract scope(s) - everything between parentheses
SCOPE_PART=$(echo "$subject" | sed -E 's/^[a-z]+\(([^)]+)\): .*/\1/')
# Handle multiple scopes (comma or space separated)
# Split by comma and/or space
echo "$SCOPE_PART" | tr ',' '\n' | tr ' ' '\n' | while read -r scope; do
scope=$(echo "$scope" | xargs) # Trim whitespace
[ -n "$scope" ] && echo "$scope" >> "$TEMP_SCOPES"
done
fi
done <<< "$SUBJECTS"
# Count scope frequencies
if [ ! -s "$TEMP_SCOPES" ]; then
# No scopes found
cat << EOF
{
"total_scopes": 0,
"scoped_commits": 0,
"scoped_percentage": 0,
"scopes": [],
"message": "No scopes detected in commit history"
}
EOF
exit 0
fi
# Sort and count unique scopes
SCOPE_COUNTS=$(sort "$TEMP_SCOPES" | uniq -c | sort -rn)
# Calculate scoped percentage
SCOPED_PCT=$(echo "scale=1; $SCOPED_COMMITS * 100 / $TOTAL_COMMITS" | bc -l)
# Build JSON output
echo "{" > "$TEMP_RESULTS"
echo " \"commits_analyzed\": $TOTAL_COMMITS," >> "$TEMP_RESULTS"
echo " \"scoped_commits\": $SCOPED_COMMITS," >> "$TEMP_RESULTS"
echo " \"scoped_percentage\": $SCOPED_PCT," >> "$TEMP_RESULTS"
echo " \"branch\": \"$BRANCH\"," >> "$TEMP_RESULTS"
# Count unique scopes
UNIQUE_SCOPES=$(echo "$SCOPE_COUNTS" | wc -l)
echo " \"total_scopes\": $UNIQUE_SCOPES," >> "$TEMP_RESULTS"
# Build scopes array
echo " \"scopes\": [" >> "$TEMP_RESULTS"
FIRST=true
while read -r count scope; do
# Skip if below min frequency
[ "$count" -lt "$MIN_FREQUENCY" ] && continue
# Calculate percentage
PCT=$(echo "scale=1; $count * 100 / $SCOPED_COMMITS" | bc -l)
# Determine if hierarchical (contains /)
HIERARCHY="null"
PARENT=""
CHILD=""
if echo "$scope" | grep -q '/'; then
PARENT=$(echo "$scope" | cut -d'/' -f1)
CHILD=$(echo "$scope" | cut -d'/' -f2)
HIERARCHY="\"$PARENT/$CHILD\""
fi
# Categorize scope
CATEGORY="other"
DESCRIPTION="Other"
case "$scope" in
auth|security|login|oauth|session)
CATEGORY="feature"
DESCRIPTION="Authentication and authorization"
;;
api|endpoint|backend|server|middleware)
CATEGORY="backend"
DESCRIPTION="API and backend services"
;;
ui|component|style|frontend|view)
CATEGORY="ui"
DESCRIPTION="User interface"
;;
db|database|schema|migration|query)
CATEGORY="backend"
DESCRIPTION="Database operations"
;;
docs|readme|changelog|guide)
CATEGORY="documentation"
DESCRIPTION="Documentation"
;;
test|e2e|unit|integration|spec)
CATEGORY="testing"
DESCRIPTION="Testing"
;;
ci|cd|deploy|docker|k8s|pipeline)
CATEGORY="infrastructure"
DESCRIPTION="Infrastructure and deployment"
;;
config|settings|env)
CATEGORY="configuration"
DESCRIPTION="Configuration"
;;
core|utils|lib|common)
CATEGORY="core"
DESCRIPTION="Core functionality"
;;
esac
# Check if active (used in recent commits - last 10)
RECENT_USAGE=$(git log --format='%s' -10 "$BRANCH" 2>/dev/null | grep -c "($scope)" || echo "0")
ACTIVE="true"
[ "$RECENT_USAGE" -eq 0 ] && ACTIVE="false"
# Get example commits
EXAMPLES=$(git log --format='%s' --grep="($scope)" -3 "$BRANCH" 2>/dev/null | \
awk '{printf "\"%s\",", $0}' | sed 's/,$//')
[ -z "$EXAMPLES" ] && EXAMPLES=""
# Add comma if not first
if [ "$FIRST" = true ]; then
FIRST=false
else
echo " ," >> "$TEMP_RESULTS"
fi
# Write scope entry
cat << EOF >> "$TEMP_RESULTS"
{
"name": "$scope",
"count": $count,
"percentage": $PCT,
"category": "$CATEGORY",
"description": "$DESCRIPTION",
"hierarchy": $HIERARCHY,
"active": $ACTIVE,
"recent_usage": $RECENT_USAGE,
"examples": [$EXAMPLES]
}
EOF
done <<< "$SCOPE_COUNTS"
echo " ]" >> "$TEMP_RESULTS"
echo "}" >> "$TEMP_RESULTS"
# Output result
cat "$TEMP_RESULTS"
exit 0

View File

@@ -0,0 +1,232 @@
#!/bin/bash
# ============================================================================
# SCRIPT: style-analyzer.sh
# PURPOSE: Analyze git commit history for style patterns and conventions
# VERSION: 1.0.0
# USAGE: ./style-analyzer.sh [count] [branch]
# RETURNS: JSON format with style analysis results
# EXIT CODES:
# 0 - Success
# 1 - Not a git repository
# 2 - No commit history
# 3 - Git command failed
# DEPENDENCIES: git, jq (optional for pretty JSON)
# ============================================================================
# Default parameters
COUNT="${1:-50}"
BRANCH="${2:-HEAD}"
# Validate git repository
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo '{"error": "Not in a git repository"}' >&2
exit 1
fi
# Check if commits exist
if ! git log -1 >/dev/null 2>&1; then
echo '{"error": "No commit history found"}' >&2
exit 2
fi
# Get commit subjects
SUBJECTS=$(git log --format='%s' -"$COUNT" "$BRANCH" 2>/dev/null)
if [ $? -ne 0 ]; then
echo '{"error": "Failed to fetch git log"}' >&2
exit 3
fi
# Get full commit messages (subject + body)
FULL_MESSAGES=$(git log --format='%B%n---COMMIT_SEPARATOR---' -"$COUNT" "$BRANCH" 2>/dev/null)
# Count total commits analyzed
TOTAL_COMMITS=$(echo "$SUBJECTS" | wc -l)
# Initialize counters
CONVENTIONAL_COUNT=0
IMPERATIVE_COUNT=0
HAS_BODY_COUNT=0
REFERENCES_ISSUES_COUNT=0
CAPITALIZED_COUNT=0
NO_PERIOD_COUNT=0
# Arrays for type and scope counting
declare -A TYPE_COUNT
declare -A SCOPE_COUNT
# Calculate subject line lengths
LENGTHS=""
TOTAL_LENGTH=0
# Process each subject line
while IFS= read -r subject; do
[ -z "$subject" ] && continue
# Length analysis
LENGTH=${#subject}
LENGTHS="$LENGTHS $LENGTH"
TOTAL_LENGTH=$((TOTAL_LENGTH + LENGTH))
# Check conventional commits format: type(scope): subject
if echo "$subject" | grep -qE '^[a-z]+(\([^)]+\))?: '; then
((CONVENTIONAL_COUNT++))
# Extract type
TYPE=$(echo "$subject" | sed -E 's/^([a-z]+)(\([^)]+\))?: .*/\1/')
TYPE_COUNT[$TYPE]=$((${TYPE_COUNT[$TYPE]:-0} + 1))
# Extract scope if present
if echo "$subject" | grep -qE '^[a-z]+\([^)]+\): '; then
SCOPE=$(echo "$subject" | sed -E 's/^[a-z]+\(([^)]+)\): .*/\1/')
SCOPE_COUNT[$SCOPE]=$((${SCOPE_COUNT[$SCOPE]:-0} + 1))
fi
fi
# Check imperative mood (simple heuristic - starts with verb in base form)
# Common imperative verbs
if echo "$subject" | grep -qiE '^(add|fix|update|remove|refactor|implement|create|delete|change|improve|optimize|enhance|correct|resolve|merge|bump|revert|feat|docs|style|test|chore|perf|build|ci)[:(]'; then
((IMPERATIVE_COUNT++))
fi
# Check capitalization (first letter after type/scope)
if echo "$subject" | grep -qE ': [A-Z]'; then
((CAPITALIZED_COUNT++))
fi
# Check no period at end
if ! echo "$subject" | grep -qE '\.$'; then
((NO_PERIOD_COUNT++))
fi
done <<< "$SUBJECTS"
# Analyze full messages for body and issue references
COMMIT_NUM=0
while IFS= read -r line; do
if [ "$line" = "---COMMIT_SEPARATOR---" ]; then
if [ $HAS_BODY = true ]; then
((HAS_BODY_COUNT++))
fi
if [ $HAS_ISSUE_REF = true ]; then
((REFERENCES_ISSUES_COUNT++))
fi
HAS_BODY=false
HAS_ISSUE_REF=false
IN_SUBJECT=true
COMMIT_NUM=$((COMMIT_NUM + 1))
continue
fi
if [ "$IN_SUBJECT" = true ]; then
IN_SUBJECT=false
continue
fi
# Check if has body (non-empty line after subject)
if [ -n "$line" ] && [ "$line" != "" ]; then
HAS_BODY=true
fi
# Check for issue references
if echo "$line" | grep -qE '#[0-9]+|[Cc]loses|[Ff]ixes|[Rr]efs'; then
HAS_ISSUE_REF=true
fi
done <<< "$FULL_MESSAGES"
# Calculate average subject length
if [ $TOTAL_COMMITS -gt 0 ]; then
AVG_LENGTH=$((TOTAL_LENGTH / TOTAL_COMMITS))
else
AVG_LENGTH=0
fi
# Calculate standard deviation (simplified)
SUM_SQUARED_DIFF=0
for length in $LENGTHS; do
DIFF=$((length - AVG_LENGTH))
SUM_SQUARED_DIFF=$((SUM_SQUARED_DIFF + DIFF * DIFF))
done
if [ $TOTAL_COMMITS -gt 0 ]; then
STDDEV=$(echo "scale=1; sqrt($SUM_SQUARED_DIFF / $TOTAL_COMMITS)" | bc -l 2>/dev/null || echo "0")
else
STDDEV=0
fi
# Calculate percentages
calc_percentage() {
if [ $TOTAL_COMMITS -gt 0 ]; then
echo "scale=1; $1 * 100 / $TOTAL_COMMITS" | bc -l
else
echo "0"
fi
}
CONVENTIONAL_PCT=$(calc_percentage $CONVENTIONAL_COUNT)
IMPERATIVE_PCT=$(calc_percentage $IMPERATIVE_COUNT)
HAS_BODY_PCT=$(calc_percentage $HAS_BODY_COUNT)
REFERENCES_ISSUES_PCT=$(calc_percentage $REFERENCES_ISSUES_COUNT)
CAPITALIZED_PCT=$(calc_percentage $CAPITALIZED_COUNT)
NO_PERIOD_PCT=$(calc_percentage $NO_PERIOD_COUNT)
# Build common_types JSON array
COMMON_TYPES_JSON="["
FIRST=true
for type in "${!TYPE_COUNT[@]}"; do
count=${TYPE_COUNT[$type]}
pct=$(calc_percentage $count)
if [ "$FIRST" = true ]; then
FIRST=false
else
COMMON_TYPES_JSON="$COMMON_TYPES_JSON,"
fi
COMMON_TYPES_JSON="$COMMON_TYPES_JSON{\"type\":\"$type\",\"count\":$count,\"percentage\":$pct}"
done
COMMON_TYPES_JSON="$COMMON_TYPES_JSON]"
# Build common_scopes JSON array
COMMON_SCOPES_JSON="["
FIRST=true
for scope in "${!SCOPE_COUNT[@]}"; do
count=${SCOPE_COUNT[$scope]}
pct=$(calc_percentage $count)
if [ "$FIRST" = true ]; then
FIRST=false
else
COMMON_SCOPES_JSON="$COMMON_SCOPES_JSON,"
fi
COMMON_SCOPES_JSON="$COMMON_SCOPES_JSON{\"scope\":\"$scope\",\"count\":$count,\"percentage\":$pct}"
done
COMMON_SCOPES_JSON="$COMMON_SCOPES_JSON]"
# Get sample commits (first 3)
SAMPLE_COMMITS=$(git log --format='%s' -3 "$BRANCH" 2>/dev/null | awk '{printf "\"%s\",", $0}' | sed 's/,$//')
# Calculate consistency score (weighted average)
# Weights: conventional(30), imperative(25), capitalized(15), no_period(15), body(10), issues(5)
CONSISTENCY_SCORE=$(echo "scale=0; ($CONVENTIONAL_PCT * 0.3 + $IMPERATIVE_PCT * 0.25 + $CAPITALIZED_PCT * 0.15 + $NO_PERIOD_PCT * 0.15 + $HAS_BODY_PCT * 0.1 + $REFERENCES_ISSUES_PCT * 0.05)" | bc -l)
# Output JSON
cat << EOF
{
"project_style": {
"commits_analyzed": $TOTAL_COMMITS,
"branch": "$BRANCH",
"uses_conventional_commits": $([ $CONVENTIONAL_COUNT -gt $((TOTAL_COMMITS / 2)) ] && echo "true" || echo "false"),
"conventional_commits_percentage": $CONVENTIONAL_PCT,
"average_subject_length": $AVG_LENGTH,
"subject_length_stddev": $STDDEV,
"common_types": $COMMON_TYPES_JSON,
"common_scopes": $COMMON_SCOPES_JSON,
"imperative_mood_percentage": $IMPERATIVE_PCT,
"capitalized_subject_percentage": $CAPITALIZED_PCT,
"no_period_end_percentage": $NO_PERIOD_PCT,
"has_body_percentage": $HAS_BODY_PCT,
"references_issues_percentage": $REFERENCES_ISSUES_PCT,
"consistency_score": $CONSISTENCY_SCORE,
"sample_commits": [$SAMPLE_COMMITS]
}
}
EOF
exit 0

View File

@@ -0,0 +1,245 @@
# Operation: Analyze Commit Style
**Purpose:** Analyze recent commit history to learn the project's commit message style, conventions, and patterns.
## Parameters
From `$ARGUMENTS` (after operation name):
- `count:N` - Number of commits to analyze (default: 50)
- `branch:name` - Branch to analyze (default: current branch)
- `format:json|text` - Output format (default: text)
## Workflow
### 1. Validate Repository
```bash
# Check if in git repository
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "Error: Not in a git repository"
exit 1
fi
# Check if has commits
if ! git log -1 >/dev/null 2>&1; then
echo "Error: No commit history found"
exit 1
fi
```
### 2. Execute Style Analysis Script
Invoke the style-analyzer.sh utility script:
```bash
./.scripts/style-analyzer.sh <count> <branch>
```
The script will:
- Fetch recent commits from git log
- Analyze commit message formats
- Calculate statistics (average length, type distribution, etc.)
- Detect conventional commits usage
- Identify common patterns
### 3. Process Analysis Results
The script returns JSON output with:
```json
{
"project_style": {
"uses_conventional_commits": true,
"conventional_commits_percentage": 87,
"average_subject_length": 47,
"subject_length_stddev": 8,
"common_types": [
{"type": "feat", "count": 45, "percentage": 35.4},
{"type": "fix", "count": 38, "percentage": 29.9},
{"type": "docs", "count": 20, "percentage": 15.7}
],
"common_scopes": [
{"scope": "auth", "count": 23, "percentage": 18.1},
{"scope": "api", "count": 19, "percentage": 14.9},
{"scope": "ui", "count": 15, "percentage": 11.8}
],
"imperative_mood_percentage": 92,
"has_body_percentage": 34,
"references_issues_percentage": 67,
"consistency_score": 85,
"sample_commits": [
"feat(auth): implement OAuth2 authentication",
"fix(api): handle null pointer in user endpoint",
"docs: update API documentation"
]
}
}
```
### 4. Generate Recommendations
Based on analysis results, provide:
**Style Recommendations:**
- Conventional commits adherence level
- Recommended message format
- Typical subject line length
- Body usage patterns
- Issue reference patterns
**Type Recommendations:**
- Most commonly used types
- Type usage frequency
- Recommended types for new commits
**Scope Recommendations:**
- Commonly used scopes
- Scope naming patterns
- When to use which scope
**Consistency Recommendations:**
- Areas of good consistency
- Areas needing improvement
- Specific guidance for better consistency
### 5. Format Output
**Text Format (default):**
```
Git Commit Style Analysis
=========================
Commits Analyzed: 50
Branch: main
Conventional Commits: 87% (43/50)
Consistency Score: 85/100
Subject Lines:
Average Length: 47 characters
Recommended: Keep under 50 characters
Common Types:
1. feat - 35.4% (45 commits) - New features
2. fix - 29.9% (38 commits) - Bug fixes
3. docs - 15.7% (20 commits) - Documentation
Common Scopes:
1. auth - 18.1% (23 commits) - Authentication/authorization
2. api - 14.9% (19 commits) - API endpoints
3. ui - 11.8% (15 commits) - User interface
Patterns Detected:
✓ Uses imperative mood (92%)
✓ References issues frequently (67%)
○ Body usage moderate (34%)
Recommendations:
• Continue using conventional commits format
• Consider 'auth' scope for authentication changes
• Keep subject lines under 50 characters
• Use imperative mood (e.g., "add" not "added")
• Reference issues when applicable (#123)
Sample Commits (project style):
feat(auth): implement OAuth2 authentication
fix(api): handle null pointer in user endpoint
docs: update API documentation
```
**JSON Format:**
```json
{
"analysis_type": "commit_style",
"commits_analyzed": 50,
"branch": "main",
"results": { ... },
"recommendations": [ ... ],
"confidence": "high"
}
```
## Error Handling
**No git repository:**
- Error: "Not in a git repository. Run this command from within a git project."
- Exit code: 1
**No commits:**
- Error: "No commit history found. This appears to be a new repository."
- Exit code: 1
**Branch doesn't exist:**
- Error: "Branch 'branch-name' not found. Check branch name and try again."
- Exit code: 1
**Insufficient commits:**
- Warning: "Only X commits found. Analysis may be less accurate."
- Proceed with available commits
**Git command fails:**
- Error: "Git command failed: {error message}"
- Provide troubleshooting steps
## Integration Usage
**By commit-assistant agent:**
```
User requests: "commit my changes"
→ Agent invokes: /history-analysis analyze-style
→ Learns: Project uses conventional commits, common scope: "auth"
→ Agent generates message matching project style
```
**By message-generation skill:**
```
Before generating message:
→ Invoke: /history-analysis analyze-style format:json
→ Extract: common_types, common_scopes, average_length
→ Use in: message generation to match project conventions
```
## Output Examples
**High Consistency Project (score: 95):**
```
✓ Excellent consistency across commits
✓ Strong conventional commits adherence (98%)
✓ Clear scope usage patterns
→ Continue current practices
```
**Medium Consistency Project (score: 65):**
```
○ Moderate consistency
○ Mixed conventional commits usage (64%)
○ Inconsistent scope patterns
→ Recommend adopting conventional commits
→ Define standard scopes for the project
```
**Low Consistency Project (score: 35):**
```
✗ Low consistency across commits
✗ Minimal conventional commits usage (12%)
✗ No clear patterns
→ Consider establishing commit message guidelines
→ Define project-specific conventions
→ Use this tool to enforce standards
```
## Success Criteria
Operation succeeds when:
- [x] Git repository validated
- [x] Commits successfully analyzed
- [x] Statistics calculated accurately
- [x] Patterns detected correctly
- [x] Recommendations generated
- [x] Output formatted properly
- [x] Results match project reality
## Performance
- **Analysis Time:** ~1-2 seconds for 50 commits
- **Memory Usage:** Minimal (processes line-by-line)
- **Git Operations:** Read-only, no modifications

View File

@@ -0,0 +1,302 @@
# Operation: Detect Commit Patterns
**Purpose:** Identify project-specific commit message patterns, conventions, and formatting standards from commit history.
## Parameters
From `$ARGUMENTS` (after operation name):
- `count:N` - Number of commits to analyze (default: 50)
- `branch:name` - Branch to analyze (default: current branch)
- `format:json|text` - Output format (default: text)
- `detailed:true|false` - Include detailed pattern breakdown (default: false)
## Workflow
### 1. Validate Repository
```bash
# Verify git repository
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "Error: Not in a git repository"
exit 1
fi
```
### 2. Execute Pattern Detection
Invoke the pattern-detector.py utility script:
```bash
./.scripts/pattern-detector.py --count <count> --branch <branch> [--detailed]
```
The script analyzes:
- **Format Patterns:** Message structure and formatting
- **Convention Patterns:** Conventional commits, semantic versioning references
- **Content Patterns:** Imperative mood, capitalization, punctuation
- **Metadata Patterns:** Issue references, co-authors, sign-offs
- **Body Patterns:** Bullet points, wrapping, section structure
### 3. Pattern Categories Analyzed
**A. Format Patterns**
```python
patterns = {
'conventional_commits': 0, # type(scope): subject
'simple_subject': 0, # Just a subject line
'prefixed': 0, # [PREFIX] subject
'tagged': 0, # #tag subject
'other': 0
}
```
**B. Convention Patterns**
```python
conventions = {
'imperative_mood': 0, # "add" vs "added"
'capitalized_subject': 0, # First letter capitalized
'no_period_end': 0, # No period at end
'blank_line_before_body': 0, # Proper body separation
'wrapped_body': 0, # 72-char wrap
'has_footer': 0 # Breaking changes, issues
}
```
**C. Content Patterns**
```python
content = {
'references_issues': 0, # #123, Closes #456
'mentions_breaking': 0, # BREAKING CHANGE:
'has_co_authors': 0, # Co-authored-by:
'signed_off': 0, # Signed-off-by:
'includes_rationale': 0, # "because", "to", "for"
'mentions_impact': 0 # "affects", "impacts", "changes"
}
```
### 4. Pattern Analysis Algorithm
The pattern-detector.py script implements:
```python
def analyze_commit_patterns(commits):
"""
Analyze commit messages for patterns.
Returns pattern frequencies and confidence scores.
"""
patterns = initialize_pattern_counters()
for commit in commits:
# Parse commit structure
subject, body, footer = parse_commit(commit)
# Detect format pattern
if is_conventional_commit(subject):
patterns['format']['conventional_commits'] += 1
elif has_prefix(subject):
patterns['format']['prefixed'] += 1
# ... more checks
# Detect conventions
if is_imperative_mood(subject):
patterns['conventions']['imperative_mood'] += 1
if is_capitalized(subject):
patterns['conventions']['capitalized_subject'] += 1
# ... more checks
# Detect content patterns
if references_issues(commit):
patterns['content']['references_issues'] += 1
# ... more checks
# Calculate percentages
return calculate_pattern_percentages(patterns, len(commits))
```
### 5. Output Structure
**Text Format (default):**
```
Commit Pattern Analysis
=======================
Commits Analyzed: 50
Branch: main
FORMAT PATTERNS
---------------
Conventional Commits: 87% (44/50) ✓ DOMINANT
Example: feat(auth): implement OAuth2
Simple Subject: 10% (5/50)
Example: Update documentation
Prefixed: 3% (1/50)
Example: [HOTFIX] Fix critical bug
CONVENTION PATTERNS
-------------------
Imperative Mood: 92% (46/50) ✓ STRONG
Capitalized Subject: 94% (47/50) ✓ STRONG
No Period at End: 88% (44/50) ✓ STRONG
Blank Line Before Body: 100% (17/17) ✓ PERFECT
Body Wrapped at 72: 94% (16/17) ✓ STRONG
Has Footer: 26% (13/50) ○ MODERATE
CONTENT PATTERNS
----------------
References Issues: 67% (34/50) ✓ COMMON
Mentions Breaking: 8% (4/50) ○ OCCASIONAL
Has Co-Authors: 2% (1/50) ✗ RARE
Signed-Off: 12% (6/50) ○ OCCASIONAL
Includes Rationale: 45% (23/50) ○ MODERATE
Mentions Impact: 31% (16/50) ○ MODERATE
DETECTED CONVENTIONS
--------------------
✓ Project uses conventional commits format
✓ Strong imperative mood usage
✓ Consistent capitalization and punctuation
✓ Frequent issue references
○ Moderate footer usage
○ Occasional breaking change mentions
PATTERN CONSISTENCY
-------------------
Overall Score: 85/100 (GOOD)
Format: High (87% conventional)
Conventions: High (90%+ adherence)
Content: Moderate (varied usage)
RECOMMENDATIONS
---------------
• Continue using conventional commits format
• Maintain imperative mood in subject lines
• Consider more consistent footer usage
• Document rationale in commit bodies when complex
```
**JSON Format:**
```json
{
"analysis_type": "pattern_detection",
"commits_analyzed": 50,
"branch": "main",
"patterns": {
"format": {
"conventional_commits": {"count": 44, "percentage": 87, "strength": "dominant"},
"simple_subject": {"count": 5, "percentage": 10, "strength": "rare"},
"prefixed": {"count": 1, "percentage": 3, "strength": "rare"}
},
"conventions": {
"imperative_mood": {"count": 46, "percentage": 92, "strength": "strong"},
"capitalized_subject": {"count": 47, "percentage": 94, "strength": "strong"},
"no_period_end": {"count": 44, "percentage": 88, "strength": "strong"}
},
"content": {
"references_issues": {"count": 34, "percentage": 67, "strength": "common"},
"mentions_breaking": {"count": 4, "percentage": 8, "strength": "occasional"}
}
},
"consistency_score": 85,
"dominant_pattern": "conventional_commits",
"recommendations": [
"Continue using conventional commits format",
"Maintain imperative mood in subject lines"
]
}
```
### 6. Pattern Strength Classification
```
PERFECT: 95-100% - Universal usage
STRONG: 80-94% - Very consistent
DOMINANT: 65-79% - Clear preference
COMMON: 45-64% - Regular usage
MODERATE: 25-44% - Occasional usage
OCCASIONAL: 10-24% - Infrequent usage
RARE: 1-9% - Seldom used
ABSENT: 0% - Not used
```
### 7. Detailed Pattern Breakdown
When `detailed:true` is specified, include:
**Per-Pattern Examples:**
```
IMPERATIVE MOOD (92%)
✓ "Add user authentication"
✓ "Fix null pointer exception"
✓ "Update API documentation"
✗ "Added new feature"
✗ "Updated dependencies"
```
**Timeline Analysis:**
```
Pattern Evolution (most recent 10 commits):
Conventional Commits: 10/10 (100%) - Improving ↑
Imperative Mood: 9/10 (90%) - Stable →
Issue References: 8/10 (80%) - Improving ↑
```
## Error Handling
**No git repository:**
- Return: "Error: Not in a git repository"
- Exit code: 1
**No commits:**
- Return: "Error: No commit history to analyze"
- Exit code: 1
**Insufficient commits:**
- Warning: "Only X commits available (requested Y)"
- Proceed with available commits
**Pattern detection fails:**
- Return partial results with warning
- Indicate which patterns couldn't be detected
## Integration Usage
**By commit-assistant agent:**
```
Agent: Determine project conventions
→ Invoke: /history-analysis detect-patterns
→ Learn: Project uses conventional commits (87%)
→ Apply: Use conventional format for new commits
```
**By message-generation skill:**
```
Before generating:
→ Detect dominant patterns
→ Extract format preferences
→ Match project conventions
```
**By commit-best-practices skill:**
```
When reviewing commits:
→ Compare against detected patterns
→ Flag deviations from project norms
→ Suggest consistency improvements
```
## Success Criteria
Operation succeeds when:
- [x] All pattern categories analyzed
- [x] Frequencies calculated accurately
- [x] Strength classifications assigned
- [x] Consistency score computed
- [x] Dominant pattern identified
- [x] Recommendations generated
- [x] Output formatted correctly
## Performance
- **Analysis Time:** ~2-3 seconds for 50 commits
- **Memory Usage:** Low (streaming analysis)
- **Accuracy:** High (>95% pattern detection accuracy)

View File

@@ -0,0 +1,378 @@
# Operation: Extract Scopes
**Purpose:** Discover and analyze commonly used scopes in commit messages to understand project structure and component organization.
## Parameters
From `$ARGUMENTS` (after operation name):
- `count:N` - Number of commits to analyze (default: 50)
- `branch:name` - Branch to analyze (default: current branch)
- `format:json|text` - Output format (default: text)
- `min_frequency:N` - Minimum occurrences to include (default: 2)
- `top:N` - Show only top N scopes (default: 20)
## Workflow
### 1. Validate Repository
```bash
# Check git repository
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "Error: Not in a git repository"
exit 1
fi
```
### 2. Execute Scope Extraction
Invoke the scope-extractor.sh utility script:
```bash
./.scripts/scope-extractor.sh --count <count> --branch <branch> --min-frequency <min>
```
The script will:
- Parse commit messages
- Extract scopes using regex patterns
- Count scope frequencies
- Analyze scope naming patterns
- Categorize scopes by type
### 3. Scope Extraction Algorithm
**Pattern Matching:**
```bash
# Primary pattern: type(scope): subject
^[a-z]+\(([^)]+)\):
# Examples:
feat(auth): add OAuth → scope: "auth"
fix(api/users): handle null → scope: "api/users"
docs(readme): update guide → scope: "readme"
```
**Nested Scope Handling:**
```bash
# Hierarchical scopes
api/endpoints → parent: "api", child: "endpoints"
ui/components → parent: "ui", child: "components"
core/auth → parent: "core", child: "auth"
```
**Multi-Scope Handling:**
```bash
# Comma-separated scopes
feat(api,docs): add endpoint → scopes: ["api", "docs"]
fix(ui, ux): button alignment → scopes: ["ui", "ux"]
```
### 4. Scope Analysis Categories
**A. Frequency Analysis**
```json
{
"scope": "auth",
"count": 23,
"percentage": 18.1,
"first_seen": "2024-01-15",
"last_seen": "2024-03-10",
"active": true
}
```
**B. Scope Hierarchy**
```json
{
"api": {
"count": 45,
"children": {
"endpoints": 12,
"middleware": 8,
"validation": 6
}
}
}
```
**C. Scope Categories**
```
Architecture: core, utils, config
Features: auth, payment, search
UI: ui, components, styles
Backend: api, db, server
Infrastructure: ci, docker, deploy
Documentation: docs, readme, changelog
Testing: test, e2e, unit
```
### 5. Output Structure
**Text Format (default):**
```
Scope Extraction Analysis
=========================
Commits Analyzed: 50
Branch: main
Scopes Found: 15 unique
Total Scoped Commits: 44/50 (88%)
TOP SCOPES BY FREQUENCY
-----------------------
1. auth 23 (18.1%) ████████████████████
└─ Authentication and authorization
2. api 19 (14.9%) ████████████████
├─ endpoints 7
├─ middleware 5
└─ validation 3
3. ui 15 (11.8%) █████████████
├─ components 8
└─ styles 4
4. db 12 (9.4%) ██████████
└─ Database operations
5. docs 11 (8.7%) █████████
└─ Documentation
6. core 8 (6.3%) ███████
└─ Core functionality
7. config 7 (5.5%) ██████
└─ Configuration
8. test 6 (4.7%) █████
└─ Testing
9. ci 5 (3.9%) ████
└─ CI/CD pipelines
10. deploy 4 (3.1%) ███
└─ Deployment
SCOPE CATEGORIES
----------------
Features (45%): auth, payment, search
Backend (32%): api, db, server
UI (19%): ui, components, styles
Infrastructure (12%): ci, docker, deploy
Documentation (8%): docs, readme
SCOPE PATTERNS
--------------
Naming Style: lowercase, hyphen-separated
Hierarchy: Uses / for nested scopes (api/endpoints)
Multi-Scope: Occasional (3% of commits)
Active Scopes: 12/15 (used in last 10 commits)
Deprecated: payment-v1, legacy-api
RECOMMENDATIONS
---------------
• Use 'auth' for authentication/authorization changes
• Use 'api' for backend API modifications
• Use 'ui' for user interface changes
• Consider hierarchical scopes for complex modules (api/endpoints)
• Active scopes: auth, api, ui, db, docs, core, config
• Avoid deprecated: payment-v1, legacy-api
RECENT SCOPE USAGE (last 10 commits)
------------------------------------
auth ████ (4 commits)
api ███ (3 commits)
ui ██ (2 commits)
docs █ (1 commit)
```
**JSON Format:**
```json
{
"analysis_type": "scope_extraction",
"commits_analyzed": 50,
"branch": "main",
"statistics": {
"total_scopes": 15,
"scoped_commits": 44,
"scoped_percentage": 88,
"active_scopes": 12,
"deprecated_scopes": 2
},
"scopes": [
{
"name": "auth",
"count": 23,
"percentage": 18.1,
"category": "feature",
"description": "Authentication and authorization",
"hierarchy": null,
"first_seen": "2024-01-15",
"last_seen": "2024-03-10",
"active": true,
"recent_usage": 4,
"examples": [
"feat(auth): implement OAuth2",
"fix(auth): session timeout",
"refactor(auth): simplify middleware"
]
},
{
"name": "api",
"count": 19,
"percentage": 14.9,
"category": "backend",
"description": "API endpoints and logic",
"hierarchy": {
"endpoints": 7,
"middleware": 5,
"validation": 3
},
"active": true,
"recent_usage": 3
}
],
"categories": {
"feature": {"count": 45, "percentage": 45},
"backend": {"count": 32, "percentage": 32},
"ui": {"count": 19, "percentage": 19}
},
"patterns": {
"naming_style": "lowercase_hyphen",
"uses_hierarchy": true,
"uses_multi_scope": true,
"multi_scope_percentage": 3
},
"recommendations": [
"Use 'auth' for authentication/authorization changes",
"Use 'api' for backend API modifications",
"Consider hierarchical scopes for complex modules"
]
}
```
### 6. Scope Categorization Logic
**Category Detection:**
```python
def categorize_scope(scope_name, usage_patterns):
"""Categorize scope based on name and usage"""
# Authentication/Authorization
if scope_name in ['auth', 'security', 'login', 'oauth']:
return 'feature', 'Authentication'
# API/Backend
if scope_name in ['api', 'endpoint', 'backend', 'server']:
return 'backend', 'API and backend'
# UI/Frontend
if scope_name in ['ui', 'component', 'style', 'frontend']:
return 'ui', 'User interface'
# Database
if scope_name in ['db', 'database', 'schema', 'migration']:
return 'backend', 'Database'
# Infrastructure
if scope_name in ['ci', 'cd', 'deploy', 'docker', 'k8s']:
return 'infrastructure', 'Infrastructure'
# Documentation
if scope_name in ['docs', 'readme', 'changelog']:
return 'documentation', 'Documentation'
# Testing
if scope_name in ['test', 'e2e', 'unit', 'integration']:
return 'testing', 'Testing'
# Default
return 'other', 'Other'
```
### 7. Trend Analysis
**Activity Tracking:**
```
Scope Activity Timeline (last 30 days):
auth: ████████████████ (very active)
api: ████████████ (active)
ui: ████████ (moderate)
db: ████ (low)
test: ██ (occasional)
docs: █ (rare)
```
**Deprecated Scope Detection:**
```python
def is_deprecated(scope):
"""Detect if scope is deprecated"""
# Not used in last 20 commits
if scope.last_seen_commit_index > 20:
return True
# Explicitly marked as deprecated
if 'legacy' in scope.name or 'v1' in scope.name:
return True
return False
```
## Error Handling
**No scopes found:**
- Warning: "No scopes detected in commit history"
- Suggestion: "Project may not use conventional commits format"
- Return: Empty scope list with explanation
**Minimal scopes:**
- Warning: "Only X scopes found with min_frequency threshold"
- Suggestion: "Lower min_frequency or analyze more commits"
**Malformed scopes:**
- Skip invalid entries
- Log: "Skipped N malformed scope entries"
- Continue with valid scopes
## Integration Usage
**By commit-assistant agent:**
```
User modifies auth files:
→ Invoke: /history-analysis extract-scopes
→ Find: 'auth' is common scope (18.1%)
→ Suggest: Use 'auth' scope for this commit
```
**By message-generation skill:**
```
Generating commit message:
→ Extract scopes from history
→ Check if current files match known scopes
→ Auto-suggest appropriate scope
```
**By identify-scope operation:**
```
Unknown scope needed:
→ Check extracted scopes
→ Find similar/related scopes
→ Recommend best match
```
## Success Criteria
Operation succeeds when:
- [x] All scopes extracted from commits
- [x] Frequencies calculated correctly
- [x] Hierarchies identified
- [x] Categories assigned accurately
- [x] Active/deprecated status determined
- [x] Recommendations generated
- [x] Output formatted properly
## Performance
- **Extraction Time:** ~1-2 seconds for 50 commits
- **Regex Matching:** ~0.02ms per commit
- **Memory:** Low (scope hash table only)

View File

@@ -0,0 +1,586 @@
# Operation: Learn Project
**Purpose:** Comprehensive project commit pattern learning - analyze all aspects of commit history to provide complete understanding of project conventions.
## Parameters
From `$ARGUMENTS` (after operation name):
- `count:N` - Number of commits to analyze (default: 100)
- `branch:name` - Branch to analyze (default: current branch)
- `format:json|text` - Output format (default: text)
- `save:path` - Save results to file (optional)
- `full:true|false` - Include detailed breakdown (default: false)
## Workflow
### 1. Validate Repository
```bash
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "Error: Not in a git repository"
exit 1
fi
if ! git log -1 >/dev/null 2>&1; then
echo "Error: No commit history found"
exit 1
fi
```
### 2. Execute Comprehensive Analysis
This operation orchestrates all other history-analysis operations for complete project learning:
**Phase 1: Style Analysis**
```bash
echo "Phase 1/4: Analyzing commit style..."
./.scripts/style-analyzer.sh <count> <branch>
```
**Phase 2: Pattern Detection**
```bash
echo "Phase 2/4: Detecting conventions..."
./.scripts/pattern-detector.py --count <count> --branch <branch> --detailed
```
**Phase 3: Scope Extraction**
```bash
echo "Phase 3/4: Extracting scopes..."
./.scripts/scope-extractor.sh --count <count> --branch <branch> --min-frequency 2
```
**Phase 4: Convention Recommendations**
```bash
echo "Phase 4/4: Generating recommendations..."
./.scripts/convention-recommender.py --count <count> --branch <branch> --priority all
```
### 3. Aggregate and Synthesize Results
Combine all analysis data into comprehensive project profile:
```python
project_profile = {
'metadata': {
'project_name': get_repo_name(),
'analysis_date': datetime.now(),
'commits_analyzed': count,
'branch': branch,
'first_commit_date': get_first_commit_date(),
'last_commit_date': get_last_commit_date()
},
'style': style_analysis_results,
'patterns': pattern_detection_results,
'scopes': scope_extraction_results,
'recommendations': convention_recommendations,
'confidence': calculate_confidence_score()
}
```
### 4. Generate Project Profile
**Output Structure:**
```
═══════════════════════════════════════════════════════════════
PROJECT COMMIT PROFILE
═══════════════════════════════════════════════════════════════
Repository: git-commit-assistant
Branch: main
Analysis Date: 2024-03-10 14:30:00
Commits Analyzed: 100 (from 2024-01-01 to 2024-03-10)
Overall Consistency Score: 85/100 (GOOD)
Confidence Level: HIGH (100 commits analyzed)
═══════════════════════════════════════════════════════════════
📊 EXECUTIVE SUMMARY
═══════════════════════════════════════════════════════════════
✓ Project uses conventional commits consistently (87%)
✓ Strong imperative mood usage (92%)
✓ Good issue reference practice (67%)
○ Moderate body usage (34%)
○ Occasional breaking change documentation (8%)
Recommended Actions:
1. Maintain conventional commits format
2. Increase body usage for complex changes
3. Standardize breaking change documentation
═══════════════════════════════════════════════════════════════
📝 COMMIT STYLE ANALYSIS
═══════════════════════════════════════════════════════════════
Format Distribution:
Conventional Commits: 87% ████████████████████████░░░
Simple Subject: 10% ███░░░░░░░░░░░░░░░░░░░░░░░
Other: 3% █░░░░░░░░░░░░░░░░░░░░░░░░░
Subject Lines:
Average Length: 47 characters (recommended: < 50)
Standard Dev: 8 characters
Exceeds 50 chars: 15% of commits
Length Distribution:
30-40 chars: ████████ (35%)
41-50 chars: ██████████ (42%)
51-60 chars: ████ (15%)
61+ chars: ██ (8%)
Body Usage:
Has Body: 34% of commits
Average Length: 120 characters
Bullet Points: 89% of bodies
Wrapping: 94% wrap at 72 chars
Footer Usage:
Issue References: 67% ████████████████████░░░░░
Breaking Changes: 8% ██░░░░░░░░░░░░░░░░░░░░░░
Co-Authors: 2% ░░░░░░░░░░░░░░░░░░░░░░░░
Signed-Off: 12% ███░░░░░░░░░░░░░░░░░░░░░
═══════════════════════════════════════════════════════════════
🎯 COMMIT TYPE ANALYSIS
═══════════════════════════════════════════════════════════════
Type Distribution (from 87 conventional commits):
1. feat 35% ████████████████████ (30 commits)
└─ New features and capabilities
2. fix 30% ████████████████░ (26 commits)
└─ Bug fixes and corrections
3. docs 16% █████████░ (14 commits)
└─ Documentation updates
4. refactor 8% ████░ (7 commits)
└─ Code restructuring
5. test 5% ███░ (4 commits)
└─ Test additions/updates
6. chore 4% ██░ (3 commits)
└─ Maintenance tasks
7. perf 2% █░ (2 commits)
└─ Performance improvements
Type Usage Timeline (last 20 commits):
feat: ████████ (8 commits)
fix: ██████ (6 commits)
docs: ███ (3 commits)
refactor: ██ (2 commits)
chore: █ (1 commit)
═══════════════════════════════════════════════════════════════
🎨 SCOPE ANALYSIS
═══════════════════════════════════════════════════════════════
Scopes Found: 15 unique
Scoped Commits: 88% (76/87 conventional commits)
Top Scopes by Frequency:
1. auth 23% ████████████████████ (20 commits)
Category: Authentication
Status: ACTIVE (used in last 10 commits)
Examples:
• feat(auth): implement OAuth2 authentication
• fix(auth): handle session timeout correctly
• refactor(auth): simplify middleware logic
2. api 19% ████████████████ (17 commits)
Category: Backend
Status: ACTIVE
Hierarchy: api/endpoints (7), api/middleware (5), api/validation (3)
3. ui 15% █████████████ (13 commits)
Category: Frontend
Status: ACTIVE
Hierarchy: ui/components (8), ui/styles (4)
4. db 12% ██████████ (10 commits)
Category: Database
Status: ACTIVE
5. docs 11% █████████ (9 commits)
Category: Documentation
Status: ACTIVE
6-15. (core, config, test, ci, deploy, utils, types, scripts, docker, nginx)
Combined: 20% (17 commits)
Scope Categories:
Features: 45% (auth, payment, search, notifications)
Backend: 32% (api, db, server, cache)
Frontend: 19% (ui, components, styles)
Infrastructure: 12% (ci, docker, deploy, nginx)
Documentation: 11% (docs, readme, changelog)
═══════════════════════════════════════════════════════════════
🔍 CONVENTION PATTERNS
═══════════════════════════════════════════════════════════════
Writing Style:
Imperative Mood: 92% ██████████████████████░░
Capitalized Subject: 94% ██████████████████████░░
No Period at End: 88% ████████████████████░░░
Lowercase Scopes: 100% ████████████████████████
Message Structure:
Blank Line Before Body: 100% (all 34 bodies)
Body Wrapped at 72: 94% (32/34 bodies)
Bullet Points in Body: 89% (30/34 bodies)
Footer Separated: 100% (all 67 footers)
Issue References:
Format: "Closes #123" 45% ████████████
Format: "Fixes #456" 38% ██████████
Format: "Refs #789" 17% █████
Breaking Changes:
Format: "BREAKING CHANGE:" 100% (all 7 instances)
Always in footer: 100%
Includes description: 100%
═══════════════════════════════════════════════════════════════
💡 RECOMMENDATIONS (PRIORITIZED)
═══════════════════════════════════════════════════════════════
🔴 HIGH PRIORITY (Critical for Consistency)
1. ✓ Continue Using Conventional Commits
Current: 87% adoption
Target: Maintain 85%+
Impact: HIGH - Enables automation
2. ✓ Maintain Imperative Mood
Current: 92% compliance
Target: Maintain 90%+
Impact: HIGH - Readability and clarity
🟡 MEDIUM PRIORITY (Improve Quality)
3. ○ Increase Body Usage for Complex Changes
Current: 34% of commits
Target: 50% for multi-file changes
Impact: MEDIUM - Better documentation
When to add body:
• Changes affect >3 files
• Complex logic modifications
• Breaking changes
• Security-related changes
4. ○ Document Breaking Changes Consistently
Current: 8% when applicable
Target: 100% of breaking changes documented
Impact: MEDIUM - User experience
🟢 LOW PRIORITY (Polish)
5. ○ Consider Co-Author Attribution
Current: 2% usage
Target: Use for pair programming
Impact: LOW - Team recognition
6. ○ Add Signed-off-by for Compliance
Current: 12% usage
Target: If required by project policy
Impact: LOW - Legal compliance
═══════════════════════════════════════════════════════════════
📚 PROJECT-SPECIFIC STYLE GUIDE
═══════════════════════════════════════════════════════════════
COMMIT MESSAGE FORMAT
---------------------
<type>(<scope>): <subject> ← 50 chars max, imperative mood
<body> ← Optional, explain "why"
- Use bullet points ← Wrap at 72 characters
- Multiple lines OK
- Blank line before footer
<footer> ← Optional
BREAKING CHANGE: description
Closes #123
APPROVED TYPES (use these)
--------------------------
feat - New feature (35% of project commits)
fix - Bug fix (30% of project commits)
docs - Documentation (16% of project commits)
refactor - Code restructuring (8%)
test - Testing (5%)
chore - Maintenance (4%)
perf - Performance (2%)
STANDARD SCOPES (project-specific)
----------------------------------
auth - Authentication/authorization
api - Backend API endpoints
ui - User interface
db - Database operations
docs - Documentation
core - Core functionality
config - Configuration
test - Testing infrastructure
ci - CI/CD pipelines
deploy - Deployment
STYLE RULES
-----------
✓ Use imperative mood ("add" not "added")
✓ Capitalize first letter of subject
✓ No period at end of subject line
✓ Use lowercase for scopes
✓ Wrap body at 72 characters
✓ Separate body and footer with blank line
✓ Use bullet points in body (with - or •)
✓ Reference issues: "Closes #123", "Fixes #456"
✓ Document breaking changes in footer
REAL EXAMPLES FROM THIS PROJECT
--------------------------------
Example 1: Feature with body
feat(auth): implement OAuth2 authentication
- Add OAuth2 flow implementation
- Support Google and GitHub providers
- Include middleware for route protection
- Add configuration management
Closes #123
Example 2: Bug fix
fix(api): handle null pointer in user endpoint
The endpoint was not checking for null user objects
before accessing properties, causing crashes when
invalid user IDs were provided.
Fixes #456
Example 3: Breaking change
feat(api): change authentication flow
Update authentication to use OAuth2 tokens instead
of API keys for improved security.
BREAKING CHANGE: API now requires OAuth tokens
instead of API keys. Update all client applications
to use the new authentication flow.
Closes #789
═══════════════════════════════════════════════════════════════
🔧 IMPLEMENTATION GUIDE
═══════════════════════════════════════════════════════════════
1. SHARE WITH TEAM
• Add style guide to CONTRIBUTING.md
• Present in team meeting
• Add to onboarding docs
2. CONFIGURE GIT
Create .gitmessage template:
# <type>(<scope>): <subject>
#
# <body>
#
# <footer>
#
# Types: feat, fix, docs, refactor, test, chore, perf
# Scopes: auth, api, ui, db, docs, core, config
Then: git config commit.template .gitmessage
3. ADD PRE-COMMIT HOOKS
Install commitlint:
npm install --save-dev @commitlint/cli @commitlint/config-conventional
Configure commitlint.config.js:
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'scope-enum': [2, 'always', [
'auth', 'api', 'ui', 'db', 'docs', 'core',
'config', 'test', 'ci', 'deploy'
]]
}
};
4. ENABLE AUTOMATION
• Automated changelog: standard-version
• Semantic versioning: semantic-release
• Commit linting: commitlint + husky
5. MONITOR COMPLIANCE
Run this analysis monthly:
/history-analysis learn-project count:100
═══════════════════════════════════════════════════════════════
📈 CONFIDENCE ASSESSMENT
═══════════════════════════════════════════════════════════════
Data Quality: ████████████████████████ HIGH
Sample Size: 100 commits ✓ Sufficient
Time Range: 70 days ✓ Representative
Consistency: 85/100 ✓ Good
Pattern Clarity: ████████████████████████ HIGH
Confidence Level: HIGH
This analysis is reliable for:
✓ Establishing project guidelines
✓ Onboarding new developers
✓ Configuring automation tools
✓ Team discussions and decisions
═══════════════════════════════════════════════════════════════
💾 SAVE OPTIONS
═══════════════════════════════════════════════════════════════
This analysis can be saved for reference:
/history-analysis learn-project save:docs/commit-conventions.md
Or export as JSON for tooling:
/history-analysis learn-project format:json save:commit-profile.json
═══════════════════════════════════════════════════════════════
Analysis Complete - Ready to Apply
═══════════════════════════════════════════════════════════════
```
### 5. Save to File (if requested)
If `save:path` parameter provided:
```bash
# Save text format
echo "$output" > "$save_path"
# Save JSON format
echo "$json_output" > "$save_path"
echo "✓ Analysis saved to: $save_path"
```
### 6. JSON Output Structure
```json
{
"metadata": {
"project_name": "git-commit-assistant",
"analysis_date": "2024-03-10T14:30:00Z",
"commits_analyzed": 100,
"branch": "main",
"date_range": {
"first_commit": "2024-01-01",
"last_commit": "2024-03-10"
}
},
"scores": {
"overall_consistency": 85,
"style_consistency": 87,
"pattern_consistency": 92,
"content_consistency": 67,
"confidence": "high"
},
"style_analysis": {
"conventional_commits_percentage": 87,
"average_subject_length": 47,
"body_usage_percentage": 34,
"footer_usage_percentage": 67
},
"types": [...],
"scopes": [...],
"patterns": {...},
"recommendations": {
"high_priority": [...],
"medium_priority": [...],
"low_priority": [...]
},
"style_guide": {
"format": "<type>(<scope>): <subject>",
"types": [...],
"scopes": [...],
"rules": [...]
},
"examples": [...],
"automation": {
"commitlint_config": {...},
"changelog_generator": "standard-version",
"semantic_release": true
}
}
```
## Error Handling
**No git repository:**
- Error: "Not in a git repository"
- Guidance: Run from within git project directory
**Insufficient commits:**
- Warning: "Only X commits available (recommended: 50+)"
- Adjust: Analyze all available commits
- Note: Lower confidence level
**Analysis failure:**
- Partial results: Return what was successfully analyzed
- Error details: Indicate which phase failed
- Retry: Suggest re-running with different parameters
## Integration Usage
**New project setup:**
```
Developer: "What are the commit conventions?"
→ Run: /history-analysis learn-project
→ Get: Complete style guide
→ Configure: Git template and hooks
```
**Team standardization:**
```
Lead: "Let's review our commit practices"
→ Run: /history-analysis learn-project save:docs/conventions.md
→ Review: Recommendations with team
→ Implement: Top priorities
→ Document: In CONTRIBUTING.md
```
**Automation setup:**
```
DevOps: "Configure commit validation"
→ Run: /history-analysis learn-project format:json
→ Extract: Approved types and scopes
→ Configure: commitlint with project rules
→ Deploy: Pre-commit hooks
```
## Success Criteria
Operation succeeds when:
- [x] All 4 analysis phases complete
- [x] Results aggregated correctly
- [x] Comprehensive profile generated
- [x] Recommendations prioritized
- [x] Style guide created
- [x] Examples included
- [x] Implementation guidance provided
- [x] Confidence level assessed
## Performance
- **Phase 1 (Style):** ~2 seconds
- **Phase 2 (Patterns):** ~3 seconds
- **Phase 3 (Scopes):** ~2 seconds
- **Phase 4 (Recommendations):** ~1 second
- **Total:** ~8-10 seconds for 100 commits

View File

@@ -0,0 +1,118 @@
---
description: Analyze git history to learn project's commit style and conventions
---
# History Analysis Skill
**Purpose:** Analyze git commit history to learn project-specific commit patterns, conventions, scope usage, and message styles. Provides intelligent recommendations that match the team's existing practices.
## Router
Parse `$ARGUMENTS` to determine which history analysis operation to execute:
**Available Operations:**
- `analyze-style` - Learn commit style from recent history
- `detect-patterns` - Identify project-specific conventions
- `extract-scopes` - Discover commonly used scopes
- `suggest-conventions` - Recommend conventions based on history
- `learn-project` - Comprehensive project pattern learning
- `learn` - Alias for learn-project (full analysis)
**Base Directory:** `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/history-analysis`
## Routing Logic
```
Request: $ARGUMENTS
Parse: operation = first word, parameters = remainder
If operation is:
- "analyze-style" → Read "./analyze-style.md" with parameters
- "detect-patterns" → Read "./detect-patterns.md" with parameters
- "extract-scopes" → Read "./extract-scopes.md" with parameters
- "suggest-conventions" → Read "./suggest-conventions.md" with parameters
- "learn-project" or "learn" → Read "./learn-project.md" with parameters
- empty or unknown → Display usage information
```
## Usage Examples
```bash
# Analyze commit style from recent history
/history-analysis analyze-style
# Analyze with custom commit count
/history-analysis analyze-style count:100
# Detect project conventions
/history-analysis detect-patterns
# Extract common scopes
/history-analysis extract-scopes
# Get convention recommendations
/history-analysis suggest-conventions
# Full project learning (comprehensive analysis)
/history-analysis learn-project
# Short alias for full learning
/history-analysis learn
```
## Parameters
All operations support these optional parameters:
- `count:N` - Number of commits to analyze (default: 50)
- `branch:name` - Branch to analyze (default: current branch)
- `format:json|text` - Output format (default: text)
## Integration Points
This skill is designed to be invoked by:
- **commit-assistant agent** - Learn project conventions before generating messages
- **message-generation skill** - Validate messages against project style
- **User commands** - Understand project commit patterns
## Error Handling
If not in a git repository:
- Return error: "Not in a git repository. Please run this command from within a git project."
If no commit history exists:
- Return error: "No commit history found. This appears to be a new repository."
If git command fails:
- Return error with git error message and troubleshooting guidance
## Output Structure
All operations return structured data including:
- **Analysis results** - Patterns, frequencies, statistics
- **Recommendations** - Project-specific guidance
- **Examples** - Sample commits from project
- **Confidence score** - How reliable the analysis is
---
**Execution:**
1. Parse `$ARGUMENTS` to extract operation and parameters
2. Validate git repository exists
3. Read and execute the appropriate operation file
4. Return structured results
If operation is unrecognized, display:
```
Unknown operation: {operation}
Available operations:
analyze-style - Analyze commit message style
detect-patterns - Detect project conventions
extract-scopes - Extract common scopes
suggest-conventions - Get convention recommendations
learn-project - Full project pattern learning
Usage: /history-analysis <operation> [parameters]
Example: /history-analysis analyze-style count:100
```

View File

@@ -0,0 +1,431 @@
# Operation: Suggest Conventions
**Purpose:** Generate project-specific commit message convention recommendations based on historical analysis and best practices.
## Parameters
From `$ARGUMENTS` (after operation name):
- `count:N` - Number of commits to analyze (default: 50)
- `branch:name` - Branch to analyze (default: current branch)
- `format:json|text` - Output format (default: text)
- `include_examples:true|false` - Include example commits (default: true)
- `priority:high|medium|low|all` - Filter by recommendation priority (default: all)
## Workflow
### 1. Validate Repository
```bash
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "Error: Not in a git repository"
exit 1
fi
```
### 2. Gather Historical Data
Execute analysis operations to collect data:
**A. Style Analysis:**
```bash
./.scripts/style-analyzer.sh <count> <branch>
```
**B. Pattern Detection:**
```bash
./.scripts/pattern-detector.py --count <count> --branch <branch>
```
**C. Scope Extraction:**
```bash
./.scripts/scope-extractor.sh --count <count> --branch <branch>
```
### 3. Execute Convention Recommender
Invoke the convention-recommender.py utility script:
```bash
./.scripts/convention-recommender.py --count <count> --branch <branch> --priority <priority>
```
The script will:
- Analyze aggregated historical data
- Identify current conventions and gaps
- Generate recommendations by priority
- Provide examples and implementation guidance
- Create project-specific guidelines
### 4. Recommendation Categories
**A. Format Recommendations**
- Commit message structure
- Subject line format
- Body organization
- Footer conventions
**B. Type Recommendations**
- Commonly used types
- Type selection guidance
- Project-specific type meanings
**C. Scope Recommendations**
- Standard scopes for the project
- Scope naming patterns
- When to use each scope
**D. Content Recommendations**
- Writing style (imperative mood, tense)
- Capitalization and punctuation
- Issue reference format
- Breaking change documentation
**E. Process Recommendations**
- Pre-commit validation
- Atomicity guidelines
- Review practices
### 5. Recommendation Priority Levels
**HIGH Priority (Critical for consistency):**
- Adopt conventional commits if not using (< 50% usage)
- Fix major inconsistencies (> 30% deviation)
- Establish missing critical patterns
**MEDIUM Priority (Improve quality):**
- Refine existing patterns (50-80% consistency)
- Add missing optional elements
- Enhance documentation practices
**LOW Priority (Nice-to-have):**
- Fine-tune minor details
- Advanced features (co-authors, trailers)
- Optimization opportunities
### 6. Output Structure
**Text Format (default):**
```
Project-Specific Commit Convention Recommendations
==================================================
Analysis: 50 commits on branch 'main'
Current Consistency: 85/100 (GOOD)
Generated: 2024-03-10
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔴 HIGH PRIORITY RECOMMENDATIONS
1. Continue Using Conventional Commits
Status: ✓ Already adopted (87% usage)
Action: Maintain current practice
Benefit: Enables automated changelog, semantic versioning
Format: <type>(<scope>): <subject>
Example from project:
✓ feat(auth): implement OAuth2 authentication
✓ fix(api): handle null pointer in user endpoint
2. Standardize Subject Line Length
Status: ○ Needs improvement (avg: 47 chars, σ=12)
Action: Keep subject lines under 50 characters
Current: 15% exceed limit
Target: < 5% exceed limit
✓ Good: "feat(auth): add OAuth provider support"
✗ Too long: "feat(auth): implement OAuth2 authentication with support for multiple providers including Google and GitHub"
✓ Better: "feat(auth): add OAuth multi-provider support"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🟡 MEDIUM PRIORITY RECOMMENDATIONS
3. Increase Body Usage for Complex Changes
Status: ○ Moderate usage (34% of commits)
Action: Add body for non-trivial changes
Benefit: Better context for code review and history
When to add body:
• Multiple files changed (>3)
• Complex logic changes
• Breaking changes
• Security-related changes
Example from project:
feat(auth): implement OAuth2 authentication
- Add OAuth2 flow implementation
- Support Google and GitHub providers
- Include middleware for route protection
- Add configuration management
4. Consistent Issue References
Status: ✓ Good usage (67% of commits)
Action: Reference issues consistently
Current format: "Closes #123", "Fixes #456"
Recommendation: Continue current practice
Patterns detected in project:
✓ Closes #123 (45% of references)
✓ Fixes #456 (38% of references)
✓ Refs #789 (17% of references)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🟢 LOW PRIORITY RECOMMENDATIONS
5. Consider Adding Co-Author Attribution
Status: ✗ Rare usage (2% of commits)
Action: Add co-authors for pair programming
Format: Co-authored-by: Name <email>
Example:
feat(api): add user management endpoint
Co-authored-by: Jane Doe <jane@example.com>
6. Document Breaking Changes Explicitly
Status: ○ Occasional (8% of commits)
Action: Use BREAKING CHANGE footer when applicable
Format:
feat(api): change authentication flow
BREAKING CHANGE: API now requires OAuth tokens
instead of API keys. Update client applications.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 PROJECT-SPECIFIC STYLE GUIDE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
COMMIT MESSAGE FORMAT
---------------------
<type>(<scope>): <subject>
<body>
<footer>
TYPES (in order of frequency)
------------------------------
feat - New features (35% of commits)
fix - Bug fixes (30% of commits)
docs - Documentation (16% of commits)
refactor - Code restructuring (8% of commits)
test - Testing (5% of commits)
chore - Maintenance (6% of commits)
COMMON SCOPES (use these)
-------------------------
auth - Authentication/authorization (18%)
api - Backend API (15%)
ui - User interface (12%)
db - Database (9%)
docs - Documentation (9%)
config - Configuration (6%)
STYLE RULES (current project standards)
----------------------------------------
✓ Use imperative mood ("add" not "added")
✓ Capitalize first letter of subject
✓ No period at end of subject
✓ Wrap body at 72 characters
✓ Blank line between subject and body
✓ Reference issues when applicable
✓ Use bullet points in body
EXAMPLES FROM THIS PROJECT
---------------------------
feat(auth): implement OAuth2 authentication
- Add OAuth2 flow implementation
- Support Google and GitHub providers
- Include middleware for route protection
Closes #123
---
fix(api): handle null pointer in user endpoint
The endpoint was not checking for null user objects
before accessing properties, causing crashes.
Fixes #456
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 CONSISTENCY METRICS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Format: 87% ████████████████████████░░░
Conventions: 92% ██████████████████████████░
Content: 67% ███████████████████░░░░░░░░
Overall: 85% █████████████████████░░░░░
NEXT STEPS
----------
1. Share this guide with team
2. Add to CONTRIBUTING.md
3. Configure git commit template
4. Set up pre-commit hooks
5. Review in team meeting
AUTOMATION OPPORTUNITIES
-------------------------
• Pre-commit hook to validate format
• Automated changelog generation
• Semantic version bumping
• Commit message linting (commitlint)
```
**JSON Format:**
```json
{
"analysis_type": "convention_recommendations",
"commits_analyzed": 50,
"branch": "main",
"consistency_score": 85,
"generated_date": "2024-03-10",
"recommendations": {
"high_priority": [
{
"id": 1,
"title": "Continue Using Conventional Commits",
"status": "good",
"current_usage": 87,
"target_usage": 90,
"action": "Maintain current practice",
"benefit": "Enables automated tooling",
"examples": [
"feat(auth): implement OAuth2 authentication",
"fix(api): handle null pointer"
]
}
],
"medium_priority": [...],
"low_priority": [...]
},
"style_guide": {
"format": "<type>(<scope>): <subject>",
"types": [
{"name": "feat", "percentage": 35, "description": "New features"},
{"name": "fix", "percentage": 30, "description": "Bug fixes"}
],
"scopes": [
{"name": "auth", "percentage": 18, "description": "Authentication"},
{"name": "api", "percentage": 15, "description": "Backend API"}
],
"rules": [
"Use imperative mood",
"Capitalize first letter",
"No period at end"
]
},
"automation": [
"Pre-commit hook validation",
"Automated changelog",
"Semantic versioning"
]
}
```
### 7. Recommendation Generation Algorithm
```python
def generate_recommendations(analysis_data):
"""
Generate prioritized recommendations based on analysis.
"""
recommendations = {
'high_priority': [],
'medium_priority': [],
'low_priority': []
}
# Check conventional commits usage
if analysis_data['conventional_commits_pct'] < 50:
recommendations['high_priority'].append({
'title': 'Adopt Conventional Commits',
'action': 'Migrate to conventional commits format',
'reason': 'Low usage ({}%)'.format(
analysis_data['conventional_commits_pct']
)
})
elif analysis_data['conventional_commits_pct'] < 80:
recommendations['medium_priority'].append({
'title': 'Increase Conventional Commits Usage',
'action': 'Encourage team to use format consistently'
})
# Check subject line length
avg_length = analysis_data['avg_subject_length']
if avg_length > 60:
recommendations['high_priority'].append({
'title': 'Reduce Subject Line Length',
'action': 'Keep subjects under 50 characters',
'current': avg_length
})
# Check imperative mood
if analysis_data['imperative_mood_pct'] < 80:
recommendations['high_priority'].append({
'title': 'Use Imperative Mood Consistently',
'action': 'Use "add" not "added", "fix" not "fixed"'
})
# More checks...
return recommendations
```
## Error Handling
**Insufficient data:**
- Warning: "Limited data (< 20 commits), recommendations may be less accurate"
- Provide recommendations with confidence scores
**No patterns detected:**
- Return: "No clear patterns detected. Consider establishing conventions."
- Suggest: Standard conventional commits format as starting point
**Mixed conventions:**
- Identify: "Multiple convention styles detected (X% conventional, Y% other)"
- Recommend: "Migrate to single consistent style"
## Integration Usage
**By commit-assistant agent:**
```
New developer onboarding:
→ Invoke: /history-analysis suggest-conventions
→ Present: Project-specific style guide
→ Configure: Git commit template
```
**By team lead:**
```
Improving consistency:
→ Run: /history-analysis suggest-conventions
→ Review: High-priority recommendations
→ Implement: Top 3 improvements
→ Document: In CONTRIBUTING.md
```
## Success Criteria
Operation succeeds when:
- [x] All analysis data gathered
- [x] Recommendations prioritized correctly
- [x] Style guide generated
- [x] Examples provided
- [x] Actionable guidance included
- [x] Automation opportunities identified
## Performance
- **Analysis Time:** ~3-5 seconds for complete analysis
- **Recommendation Generation:** ~1 second
- **Total Time:** ~4-6 seconds

View File

@@ -0,0 +1,304 @@
#!/usr/bin/env bash
# Script: body-composer.sh
# Purpose: Compose commit message body with proper formatting and wrapping
# Author: Git Commit Assistant Plugin
# Version: 1.0.0
#
# Usage:
# export CHANGES="change1,change2,change3"
# export WRAP_LENGTH=72
# export FORMAT=bullets
# ./body-composer.sh
#
# Environment Variables:
# CHANGES - Comma-separated list of changes or file paths
# WRAP_LENGTH - Line wrap length (default: 72)
# FORMAT - Output format: bullets or paragraphs (default: bullets)
# WHY_CONTEXT - Optional context about why changes were made
#
# Returns:
# Formatted body text to stdout
# JSON summary to stderr (optional)
#
# Exit Codes:
# 0 - Success
# 1 - Invalid input
# 2 - Processing error
set -euo pipefail
# Default values
WRAP_LENGTH="${WRAP_LENGTH:-72}"
FORMAT="${FORMAT:-bullets}"
WHY_CONTEXT="${WHY_CONTEXT:-}"
# Validate CHANGES is provided
if [ -z "${CHANGES:-}" ]; then
echo "ERROR: CHANGES environment variable is required" >&2
exit 1
fi
# Validate FORMAT
if [ "$FORMAT" != "bullets" ] && [ "$FORMAT" != "paragraphs" ]; then
echo "ERROR: FORMAT must be 'bullets' or 'paragraphs'" >&2
exit 1
fi
# Validate WRAP_LENGTH
if ! [[ "$WRAP_LENGTH" =~ ^[0-9]+$ ]]; then
echo "ERROR: WRAP_LENGTH must be a positive integer" >&2
exit 1
fi
# Function to convert to imperative mood
convert_to_imperative() {
local text="$1"
# Common past tense -> imperative
text=$(echo "$text" | sed -E 's/\b(added|adds)\b/add/gi')
text=$(echo "$text" | sed -E 's/\b(fixed|fixes)\b/fix/gi')
text=$(echo "$text" | sed -E 's/\b(updated|updates)\b/update/gi')
text=$(echo "$text" | sed -E 's/\b(removed|removes)\b/remove/gi')
text=$(echo "$text" | sed -E 's/\b(changed|changes)\b/change/gi')
text=$(echo "$text" | sed -E 's/\b(improved|improves)\b/improve/gi')
text=$(echo "$text" | sed -E 's/\b(refactored|refactors)\b/refactor/gi')
text=$(echo "$text" | sed -E 's/\b(implemented|implements)\b/implement/gi')
text=$(echo "$text" | sed -E 's/\b(created|creates)\b/create/gi')
text=$(echo "$text" | sed -E 's/\b(deleted|deletes)\b/delete/gi')
# Lowercase first letter
text="$(echo "${text:0:1}" | tr '[:upper:]' '[:lower:]')${text:1}"
echo "$text"
}
# Function to wrap text at specified length
wrap_text() {
local text="$1"
local width="$2"
echo "$text" | fold -s -w "$width"
}
# Function to format file path as readable change
format_file_path() {
local filepath="$1"
# Extract filename and directory
local filename=$(basename "$filepath")
local dirname=$(dirname "$filepath")
# Remove extension
local name_no_ext="${filename%.*}"
# Convert to readable format
# Example: src/auth/oauth.js -> "add OAuth authentication module"
# Example: tests/unit/user.test.js -> "add user unit tests"
if [[ "$filepath" == *"/test/"* ]] || [[ "$filepath" == *"/tests/"* ]] || [[ "$filename" == *".test."* ]] || [[ "$filename" == *".spec."* ]]; then
echo "add ${name_no_ext} tests"
elif [[ "$dirname" == "." ]]; then
echo "update ${name_no_ext}"
else
# Extract meaningful part from path
local component=$(echo "$dirname" | sed 's|.*/||')
echo "update ${component} ${name_no_ext}"
fi
}
# Function to generate bullet points
generate_bullets() {
local changes_list="$1"
# Split by comma
IFS=',' read -ra items <<< "$changes_list"
local body=""
local bullet_count=0
for item in "${items[@]}"; do
# Trim whitespace
item=$(echo "$item" | xargs)
if [ -z "$item" ]; then
continue
fi
# Check if it's a file path
if [[ "$item" == *"/"* ]] || [[ "$item" == *"."* ]]; then
# Format as file path
item=$(format_file_path "$item")
fi
# Convert to imperative mood
item=$(convert_to_imperative "$item")
# Ensure first letter is capitalized for bullet
item="$(echo "${item:0:1}" | tr '[:lower:]' '[:upper:]')${item:1}"
# Wrap if needed (account for "- " prefix)
local max_width=$((WRAP_LENGTH - 2))
local wrapped=$(wrap_text "$item" "$max_width")
# Add bullet point
echo "$wrapped" | while IFS= read -r line; do
if [ "$bullet_count" -eq 0 ] || [ -z "$line" ]; then
body="${body}- ${line}\n"
else
body="${body} ${line}\n" # Indent continuation lines
fi
done
bullet_count=$((bullet_count + 1))
done
# Output body (remove trailing newline)
echo -ne "$body"
}
# Function to generate paragraphs
generate_paragraphs() {
local changes_list="$1"
# Split by comma and join into sentences
IFS=',' read -ra items <<< "$changes_list"
local body=""
for item in "${items[@]}"; do
# Trim whitespace
item=$(echo "$item" | xargs)
if [ -z "$item" ]; then
continue
fi
# Check if it's a file path
if [[ "$item" == *"/"* ]] || [[ "$item" == *"."* ]]; then
item=$(format_file_path "$item")
fi
# Convert to imperative mood
item=$(convert_to_imperative "$item")
# Ensure first letter is capitalized
item="$(echo "${item:0:1}" | tr '[:lower:]' '[:upper:]')${item:1}"
# Add to body
if [ -z "$body" ]; then
body="$item"
else
body="${body}. ${item}"
fi
done
# Add period at end if not present
if [[ ! "$body" =~ \.$ ]]; then
body="${body}."
fi
# Wrap text
wrapped=$(wrap_text "$body" "$WRAP_LENGTH")
echo "$wrapped"
}
# Function to add context (why)
add_context() {
local body="$1"
local context="$2"
if [ -z "$context" ]; then
echo "$body"
return
fi
# Ensure first letter is capitalized
context="$(echo "${context:0:1}" | tr '[:lower:]' '[:upper:]')${context:1}"
# Add period if not present
if [[ ! "$context" =~ \.$ ]]; then
context="${context}."
fi
# Wrap context
wrapped_context=$(wrap_text "$context" "$WRAP_LENGTH")
# Combine with blank line
echo -e "${body}\n\n${wrapped_context}"
}
# Main composition logic
compose_body() {
local body=""
# Generate based on format
if [ "$FORMAT" = "bullets" ]; then
body=$(generate_bullets "$CHANGES")
else
body=$(generate_paragraphs "$CHANGES")
fi
# Add context if provided
if [ -n "$WHY_CONTEXT" ]; then
body=$(add_context "$body" "$WHY_CONTEXT")
fi
echo "$body"
}
# Validate body
validate_body() {
local body="$1"
local line_count=$(echo "$body" | wc -l)
local longest_line=$(echo "$body" | awk '{ print length }' | sort -rn | head -1)
local bullet_count=$(echo "$body" | grep -c '^- ' || true)
local warnings=()
# Check longest line
if [ "$longest_line" -gt "$WRAP_LENGTH" ]; then
warnings+=("Line exceeds $WRAP_LENGTH characters ($longest_line chars)")
fi
# Check for empty lines at start
if echo "$body" | head -1 | grep -q '^$'; then
warnings+=("Body starts with empty line")
fi
# Output validation summary to stderr
{
echo "{"
echo " \"line_count\": $line_count,"
echo " \"longest_line\": $longest_line,"
echo " \"wrap_length\": $WRAP_LENGTH,"
echo " \"bullet_count\": $bullet_count,"
echo " \"format\": \"$FORMAT\","
echo " \"has_context\": $([ -n "$WHY_CONTEXT" ] && echo "true" || echo "false"),"
echo " \"warnings\": ["
for i in "${!warnings[@]}"; do
echo " \"${warnings[$i]}\"$([ $i -lt $((${#warnings[@]} - 1)) ] && echo "," || echo "")"
done
echo " ],"
echo " \"valid\": $([ ${#warnings[@]} -eq 0 ] && echo "true" || echo "false")"
echo "}"
} >&2
}
# Main execution
main() {
# Compose body
body=$(compose_body)
# Validate
validate_body "$body"
# Output body to stdout
echo "$body"
exit 0
}
# Run main
main

View File

@@ -0,0 +1,292 @@
#!/usr/bin/env python3
# Script: footer-builder.py
# Purpose: Build commit message footer with breaking changes and issue references
# Author: Git Commit Assistant Plugin
# Version: 1.0.0
#
# Usage:
# echo '{"breaking":"API changed","closes":"123,456"}' | ./footer-builder.py
# cat input.json | ./footer-builder.py
#
# Returns:
# JSON: {"footer": "...", "components": {...}, "valid": true}
#
# Exit Codes:
# 0 - Success
# 1 - Invalid input
# 2 - Processing error
import sys
import json
import re
import textwrap
def wrap_text(text, width=72, subsequent_indent=''):
"""Wrap text at specified width."""
wrapper = textwrap.TextWrapper(
width=width,
subsequent_indent=subsequent_indent,
break_long_words=False,
break_on_hyphens=False
)
return wrapper.fill(text)
def format_breaking_change(description):
"""Format breaking change notice."""
if not description:
return None
# Ensure BREAKING CHANGE is uppercase
# Wrap at 72 characters with continuation indentation
wrapped = wrap_text(
description,
width=72,
subsequent_indent=''
)
return f"BREAKING CHANGE: {wrapped}"
def parse_issue_numbers(issue_string):
"""Parse comma-separated issue numbers into list."""
if not issue_string:
return []
# Remove any # symbols
issue_string = issue_string.replace('#', '')
# Split by comma and clean
issues = [num.strip() for num in issue_string.split(',') if num.strip()]
# Validate all are numbers
valid_issues = []
for issue in issues:
if issue.isdigit():
valid_issues.append(issue)
else:
# Try to extract number
match = re.search(r'\d+', issue)
if match:
valid_issues.append(match.group())
return valid_issues
def format_issue_references(closes=None, fixes=None, refs=None):
"""Format issue references."""
lines = []
# Closes (for features/pull requests)
if closes:
issues = parse_issue_numbers(closes)
if issues:
if len(issues) == 1:
lines.append(f"Closes #{issues[0]}")
else:
# Format as comma-separated list
issue_refs = ', '.join([f"#{num}" for num in issues])
lines.append(f"Closes {issue_refs}")
# Fixes (for bug fixes)
if fixes:
issues = parse_issue_numbers(fixes)
if issues:
if len(issues) == 1:
lines.append(f"Fixes #{issues[0]}")
else:
issue_refs = ', '.join([f"#{num}" for num in issues])
lines.append(f"Fixes {issue_refs}")
# Refs (for related issues)
if refs:
issues = parse_issue_numbers(refs)
if issues:
if len(issues) == 1:
lines.append(f"Refs #{issues[0]}")
else:
issue_refs = ', '.join([f"#{num}" for num in issues])
lines.append(f"Refs {issue_refs}")
return lines
def format_metadata(reviewed=None, signed=None):
"""Format metadata like Reviewed-by and Signed-off-by."""
lines = []
if reviewed:
lines.append(f"Reviewed-by: {reviewed}")
if signed:
# Validate email format
if '@' in signed and '<' in signed and '>' in signed:
lines.append(f"Signed-off-by: {signed}")
else:
# Try to format properly
lines.append(f"Signed-off-by: {signed}")
return lines
def build_footer(data):
"""
Build commit message footer from input data.
Args:
data: dict with keys: breaking, closes, fixes, refs, reviewed, signed
Returns:
dict with footer, components, valid status
"""
# Extract parameters
breaking = data.get('breaking', '').strip()
closes = data.get('closes', '').strip()
fixes = data.get('fixes', '').strip()
refs = data.get('refs', '').strip()
reviewed = data.get('reviewed', '').strip()
signed = data.get('signed', '').strip()
# Check if any parameter provided
has_content = any([breaking, closes, fixes, refs, reviewed, signed])
if not has_content:
return {
'error': 'At least one footer component is required',
'footer': None,
'valid': False
}
# Build footer components
footer_lines = []
components = {
'breaking_change': False,
'closes_issues': 0,
'fixes_issues': 0,
'refs_issues': 0,
'reviewed_by': False,
'signed_off': False
}
# Breaking change (always first)
if breaking:
breaking_line = format_breaking_change(breaking)
if breaking_line:
footer_lines.append(breaking_line)
components['breaking_change'] = True
# Issue references
issue_lines = format_issue_references(closes, fixes, refs)
footer_lines.extend(issue_lines)
# Count issues
if closes:
components['closes_issues'] = len(parse_issue_numbers(closes))
if fixes:
components['fixes_issues'] = len(parse_issue_numbers(fixes))
if refs:
components['refs_issues'] = len(parse_issue_numbers(refs))
# Metadata
metadata_lines = format_metadata(reviewed, signed)
footer_lines.extend(metadata_lines)
if reviewed:
components['reviewed_by'] = True
if signed:
components['signed_off'] = True
# Join all lines
footer = '\n'.join(footer_lines)
# Validate footer
warnings = []
# Check breaking change format
if breaking and not footer.startswith('BREAKING CHANGE:'):
warnings.append('BREAKING CHANGE must be uppercase')
# Check issue reference format
for line in footer_lines:
if 'closes' in line.lower() and not line.startswith('Closes'):
warnings.append('Use "Closes" (capitalized)')
if 'fixes' in line.lower() and not line.startswith('Fixes'):
warnings.append('Use "Fixes" (capitalized)')
if 'refs' in line.lower() and not line.startswith('Refs'):
warnings.append('Use "Refs" (capitalized)')
# Check for proper issue number format
if any([closes, fixes, refs]):
# Make sure all issue numbers are valid
all_issues = parse_issue_numbers(closes) + parse_issue_numbers(fixes) + parse_issue_numbers(refs)
if not all_issues:
warnings.append('No valid issue numbers found')
# Build response
response = {
'footer': footer,
'components': components,
'line_count': len(footer_lines),
'has_breaking': components['breaking_change'],
'total_issues': components['closes_issues'] + components['fixes_issues'] + components['refs_issues'],
'warnings': warnings,
'valid': len(warnings) == 0
}
# Add quality score
score = 100
if not components['breaking_change'] and breaking:
score -= 10
if warnings:
score -= len(warnings) * 5
response['quality_score'] = max(0, score)
return response
def main():
"""Main entry point."""
try:
# Read JSON input from stdin
input_data = sys.stdin.read()
if not input_data or not input_data.strip():
print(json.dumps({
'error': 'No input provided',
'footer': None,
'valid': False
}))
sys.exit(1)
# Parse JSON
try:
data = json.loads(input_data)
except json.JSONDecodeError as e:
print(json.dumps({
'error': f'Invalid JSON: {str(e)}',
'footer': None,
'valid': False
}))
sys.exit(1)
# Build footer
result = build_footer(data)
# Output result
print(json.dumps(result, indent=2))
# Exit code based on result
if 'error' in result:
sys.exit(2)
elif not result.get('valid', False):
sys.exit(1)
else:
sys.exit(0)
except Exception as e:
print(json.dumps({
'error': f'Unexpected error: {str(e)}',
'footer': None,
'valid': False
}))
sys.exit(2)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,335 @@
#!/usr/bin/env bash
# Script: message-validator.sh
# Purpose: Validate commit message against conventional commits standard
# Author: Git Commit Assistant Plugin
# Version: 1.0.0
#
# Usage:
# export MESSAGE="feat: add feature"
# export STRICT_MODE=false
# export MAX_SUBJECT=50
# export MAX_LINE=72
# ./message-validator.sh
#
# Environment Variables:
# MESSAGE - Commit message to validate (required)
# STRICT_MODE - Enable strict validation (default: false)
# MAX_SUBJECT - Maximum subject length (default: 50)
# MAX_LINE - Maximum body line length (default: 72)
#
# Returns:
# Validation report to stdout
# Exit code indicates validation status
#
# Exit Codes:
# 0 - Valid message
# 1 - Invalid message (warnings in normal mode, any issue in strict mode)
# 2 - Error (missing input, malformed message)
set -euo pipefail
# Default values
STRICT_MODE="${STRICT_MODE:-false}"
MAX_SUBJECT="${MAX_SUBJECT:-50}"
MAX_LINE="${MAX_LINE:-72}"
# Validation counters
declare -a ERRORS=()
declare -a WARNINGS=()
declare -a SUGGESTIONS=()
# Score tracking
SCORE=100
# Validate MESSAGE is provided
if [ -z "${MESSAGE:-}" ]; then
echo "ERROR: MESSAGE environment variable is required" >&2
exit 2
fi
# Valid commit types
VALID_TYPES="feat fix docs style refactor perf test build ci chore revert"
# Function to check commit type
validate_type() {
local subject="$1"
# Extract type (before colon or parenthesis)
local type=$(echo "$subject" | grep -oP '^[a-z]+' || echo "")
if [ -z "$type" ]; then
ERRORS+=("No commit type found")
SCORE=$((SCORE - 20))
return 1
fi
# Check if type is valid
if ! echo "$VALID_TYPES" | grep -qw "$type"; then
ERRORS+=("Invalid commit type: '$type'")
ERRORS+=("Valid types: $VALID_TYPES")
SCORE=$((SCORE - 20))
return 1
fi
return 0
}
# Function to check scope format
validate_scope() {
local subject="$1"
# Check if scope exists
if echo "$subject" | grep -qP '^\w+\([^)]+\):'; then
local scope=$(echo "$subject" | grep -oP '^\w+\(\K[^)]+')
# Scope should be lowercase alphanumeric with hyphens
if ! echo "$scope" | grep -qP '^[a-z0-9-]+$'; then
WARNINGS+=("Scope should be lowercase alphanumeric with hyphens: '$scope'")
SCORE=$((SCORE - 5))
fi
fi
}
# Function to validate subject line
validate_subject() {
local subject="$1"
# Check format: type(scope): description or type: description
if ! echo "$subject" | grep -qP '^[a-z]+(\([a-z0-9-]+\))?: .+'; then
ERRORS+=("Subject does not match conventional commits format")
ERRORS+=("Expected: <type>(<scope>): <description> or <type>: <description>")
SCORE=$((SCORE - 30))
return 1
fi
# Validate type
validate_type "$subject"
# Validate scope if present
validate_scope "$subject"
# Extract description (after ": ")
local description=$(echo "$subject" | sed 's/^[^:]*: //')
# Check length
local length=${#subject}
if [ "$length" -gt "$MAX_SUBJECT" ]; then
if [ "$length" -gt 72 ]; then
ERRORS+=("Subject exceeds hard limit of 72 characters ($length chars)")
SCORE=$((SCORE - 30))
else
WARNINGS+=("Subject exceeds recommended $MAX_SUBJECT characters ($length chars)")
SUGGESTIONS+=("Consider shortening subject or moving details to body")
SCORE=$((SCORE - 10))
fi
fi
# Check for capital letter after colon
if echo "$description" | grep -qP '^[A-Z]'; then
WARNINGS+=("Description should not start with capital letter")
SUGGESTIONS+=("Use lowercase after colon: '$(echo "${description:0:1}" | tr '[:upper:]' '[:lower:]')${description:1}'")
SCORE=$((SCORE - 5))
fi
# Check for period at end
if [[ "$description" =~ \.$ ]]; then
WARNINGS+=("Subject should not end with period")
SUGGESTIONS+=("Remove period at end")
SCORE=$((SCORE - 3))
fi
# Check for imperative mood (simple heuristics)
if echo "$description" | grep -qP '\b(added|fixed|updated|removed|changed|improved|created|deleted)\b'; then
WARNINGS+=("Use imperative mood (add, fix, update) not past tense")
SCORE=$((SCORE - 5))
fi
if echo "$description" | grep -qP '\b(adds|fixes|updates|removes|changes|improves|creates|deletes)\b'; then
WARNINGS+=("Use imperative mood (add, fix, update) not present tense")
SCORE=$((SCORE - 5))
fi
# Check for vague descriptions
if echo "$description" | grep -qiP '\b(update|change|fix|improve)\s+(code|file|stuff|thing)\b'; then
SUGGESTIONS+=("Be more specific in description")
SCORE=$((SCORE - 5))
fi
return 0
}
# Function to validate body
validate_body() {
local body="$1"
if [ -z "$body" ]; then
return 0 # Body is optional
fi
# Check line lengths
while IFS= read -r line; do
local length=${#line}
if [ "$length" -gt "$MAX_LINE" ]; then
WARNINGS+=("Body line exceeds $MAX_LINE characters ($length chars)")
SCORE=$((SCORE - 3))
fi
done <<< "$body"
# Check for imperative mood in body
if echo "$body" | grep -qP '\b(added|fixed|updated|removed|changed|improved|created|deleted)\b'; then
SUGGESTIONS+=("Consider using imperative mood in body")
fi
return 0
}
# Function to validate footer
validate_footer() {
local footer="$1"
if [ -z "$footer" ]; then
return 0 # Footer is optional
fi
# Check for BREAKING CHANGE format
if echo "$footer" | grep -qi "breaking change"; then
if ! echo "$footer" | grep -q "^BREAKING CHANGE:"; then
ERRORS+=("Use 'BREAKING CHANGE:' (uppercase, singular) not 'breaking change'")
SCORE=$((SCORE - 15))
fi
fi
# Check for issue references
if echo "$footer" | grep -qiP '\b(close|fix|resolve)[sd]?\b'; then
# Check format
if ! echo "$footer" | grep -qP '^(Closes|Fixes|Resolves|Refs) #[0-9]'; then
WARNINGS+=("Issue references should use proper format: 'Closes #123'")
SUGGESTIONS+=("Capitalize keyword and use # prefix for issue numbers")
SCORE=$((SCORE - 5))
fi
fi
return 0
}
# Function to check overall structure
validate_structure() {
local message="$1"
# Count lines
local line_count=$(echo "$message" | wc -l)
# Split message into parts
local subject=$(echo "$message" | head -1)
local rest=$(echo "$message" | tail -n +2)
# Validate subject
validate_subject "$subject"
# If multi-line, check for blank line after subject
if [ "$line_count" -gt 1 ]; then
local second_line=$(echo "$message" | sed -n '2p')
if [ -n "$second_line" ]; then
ERRORS+=("Blank line required between subject and body")
SCORE=$((SCORE - 10))
fi
# Extract body (after blank line, before footer)
local body=""
local footer=""
local in_footer=false
while IFS= read -r line; do
# Check if line is footer token
if echo "$line" | grep -qP '^(BREAKING CHANGE:|Closes|Fixes|Resolves|Refs|Reviewed-by|Signed-off-by)'; then
in_footer=true
fi
if [ "$in_footer" = true ]; then
footer="${footer}${line}\n"
else
body="${body}${line}\n"
fi
done <<< "$rest"
# Remove leading blank line from body
body=$(echo -e "$body" | sed '1{/^$/d;}')
# Validate body and footer
validate_body "$body"
validate_footer "$footer"
fi
}
# Main validation logic
main() {
echo "COMMIT MESSAGE VALIDATION"
echo "═══════════════════════════════════════════════"
echo ""
echo "MESSAGE:"
echo "───────────────────────────────────────────────"
echo "$MESSAGE"
echo ""
# Perform validation
validate_structure "$MESSAGE"
# Calculate final status
local status="VALID"
if [ "${#ERRORS[@]}" -gt 0 ]; then
status="INVALID"
elif [ "$STRICT_MODE" = "true" ] && [ "${#WARNINGS[@]}" -gt 0 ]; then
status="INVALID"
fi
# Display results
echo "VALIDATION RESULTS:"
echo "───────────────────────────────────────────────"
if [ "${#ERRORS[@]}" -gt 0 ]; then
echo "✗ ERRORS:"
for error in "${ERRORS[@]}"; do
echo " - $error"
done
echo ""
fi
if [ "${#WARNINGS[@]}" -gt 0 ]; then
echo "⚠ WARNINGS:"
for warning in "${WARNINGS[@]}"; do
echo " - $warning"
done
echo ""
fi
if [ "${#SUGGESTIONS[@]}" -gt 0 ]; then
echo "💡 SUGGESTIONS:"
for suggestion in "${SUGGESTIONS[@]}"; do
echo " - $suggestion"
done
echo ""
fi
if [ "${#ERRORS[@]}" -eq 0 ] && [ "${#WARNINGS[@]}" -eq 0 ]; then
echo "✓ All checks passed"
echo ""
fi
echo "STATUS: $status"
echo "SCORE: $SCORE/100"
echo "STRICT MODE: $STRICT_MODE"
echo ""
echo "═══════════════════════════════════════════════"
# Exit based on status
if [ "$status" = "INVALID" ]; then
exit 1
else
exit 0
fi
}
# Run main
main

View File

@@ -0,0 +1,354 @@
#!/usr/bin/env python3
# Script: subject-generator.py
# Purpose: Generate conventional commit subject line with validation
# Author: Git Commit Assistant Plugin
# Version: 1.0.0
#
# Usage:
# echo '{"type":"feat","scope":"auth","description":"add OAuth"}' | ./subject-generator.py
# cat input.json | ./subject-generator.py
#
# Returns:
# JSON: {"subject": "feat(auth): add OAuth", "length": 22, "warnings": [], "suggestions": []}
#
# Exit Codes:
# 0 - Success
# 1 - Invalid input
# 2 - Validation error
import sys
import json
import re
def enforce_imperative_mood(text):
"""Convert common non-imperative forms to imperative mood."""
# Common past tense to imperative conversions
conversions = {
r'\badded\b': 'add',
r'\bfixed\b': 'fix',
r'\bupdated\b': 'update',
r'\bremoved\b': 'remove',
r'\bchanged\b': 'change',
r'\bimproved\b': 'improve',
r'\brefactored\b': 'refactor',
r'\bimplemented\b': 'implement',
r'\bcreated\b': 'create',
r'\bdeleted\b': 'delete',
r'\bmodified\b': 'modify',
r'\boptimized\b': 'optimize',
r'\bmoved\b': 'move',
r'\brenamed\b': 'rename',
r'\bcleaned\b': 'clean',
r'\bintroduced\b': 'introduce',
}
# Present tense (3rd person) to imperative
present_conversions = {
r'\badds\b': 'add',
r'\bfixes\b': 'fix',
r'\bupdates\b': 'update',
r'\bremoves\b': 'remove',
r'\bchanges\b': 'change',
r'\bimproves\b': 'improve',
r'\brefactors\b': 'refactor',
r'\bimplements\b': 'implement',
r'\bcreates\b': 'create',
r'\bdeletes\b': 'delete',
r'\bmodifies\b': 'modify',
r'\boptimizes\b': 'optimize',
r'\bmoves\b': 'move',
r'\brenames\b': 'rename',
r'\bcleans\b': 'clean',
r'\bintroduces\b': 'introduce',
}
original = text
# Apply conversions
for pattern, replacement in conversions.items():
text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
for pattern, replacement in present_conversions.items():
text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
# Track if changes were made
changed = (original != text)
return text, changed
def check_capitalization(text):
"""Check if description starts with lowercase (should not be capitalized)."""
if not text:
return True, []
warnings = []
if text[0].isupper():
warnings.append({
'type': 'capitalization',
'message': 'Description should start with lowercase',
'current': text,
'suggested': text[0].lower() + text[1:]
})
return False, warnings
return True, warnings
def check_period_at_end(text):
"""Check if description ends with period (should not)."""
warnings = []
if text.endswith('.'):
warnings.append({
'type': 'punctuation',
'message': 'Subject should not end with period',
'current': text,
'suggested': text[:-1]
})
return False, warnings
return True, warnings
def shorten_description(description, max_length, type_scope_part):
"""Attempt to shorten description to fit within max_length."""
# Calculate available space for description
prefix_length = len(type_scope_part) + 2 # +2 for ": "
available_length = max_length - prefix_length
if len(description) <= available_length:
return description, []
suggestions = []
# Strategy 1: Remove filler words
filler_words = ['a', 'an', 'the', 'some', 'very', 'really', 'just', 'quite']
shortened = description
for word in filler_words:
shortened = re.sub(r'\b' + word + r'\b\s*', '', shortened, flags=re.IGNORECASE)
shortened = shortened.strip()
if len(shortened) <= available_length:
suggestions.append({
'strategy': 'remove_filler',
'description': shortened,
'saved': len(description) - len(shortened)
})
return shortened, suggestions
# Strategy 2: Truncate with ellipsis (not recommended but possible)
if available_length > 3:
truncated = description[:available_length - 3] + '...'
suggestions.append({
'strategy': 'truncate',
'description': truncated,
'warning': 'Truncation loses information - consider moving details to body'
})
# Strategy 3: Suggest moving to body
suggestions.append({
'strategy': 'move_to_body',
'description': description[:available_length],
'remaining': description[available_length:],
'warning': 'Move detailed information to commit body'
})
return description, suggestions
def generate_subject(data):
"""
Generate commit subject line from input data.
Args:
data: dict with keys: type, scope (optional), description, max_length (optional)
Returns:
dict with subject, length, warnings, suggestions
"""
# Extract parameters
commit_type = data.get('type', '').strip().lower()
scope = data.get('scope', '').strip()
description = data.get('description', '').strip()
max_length = int(data.get('max_length', 50))
# Validate required fields
if not commit_type:
return {
'error': 'type is required',
'subject': None
}
if not description:
return {
'error': 'description is required',
'subject': None
}
# Validate type
valid_types = ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert']
if commit_type not in valid_types:
return {
'error': f'Invalid type "{commit_type}". Valid types: {", ".join(valid_types)}',
'subject': None
}
# Enforce imperative mood
original_description = description
description, mood_changed = enforce_imperative_mood(description)
# Ensure lowercase after colon
if description and description[0].isupper():
description = description[0].lower() + description[1:]
# Remove period at end
if description.endswith('.'):
description = description[:-1]
# Build type(scope) part
if scope:
type_scope_part = f"{commit_type}({scope})"
else:
type_scope_part = commit_type
# Build initial subject
subject = f"{type_scope_part}: {description}"
subject_length = len(subject)
# Collect warnings and suggestions
warnings = []
suggestions = []
# Check mood change
if mood_changed:
warnings.append({
'type': 'mood',
'message': 'Changed to imperative mood',
'original': original_description,
'corrected': description
})
# Check length
if subject_length > max_length:
warnings.append({
'type': 'length',
'message': f'Subject exceeds {max_length} characters ({subject_length} chars)',
'length': subject_length,
'max': max_length,
'excess': subject_length - max_length
})
# Try to shorten
shortened_desc, shorten_suggestions = shorten_description(description, max_length, type_scope_part)
if shorten_suggestions:
suggestions.extend(shorten_suggestions)
# If we successfully shortened, update subject
if len(shortened_desc) < len(description):
alternative_subject = f"{type_scope_part}: {shortened_desc}"
if len(alternative_subject) <= max_length:
suggestions.append({
'type': 'shortened_subject',
'subject': alternative_subject,
'saved': subject_length - len(alternative_subject)
})
# Warning if close to limit
elif subject_length > 45 and max_length == 50:
suggestions.append({
'type': 'near_limit',
'message': f'Subject is close to {max_length} character limit ({subject_length} chars)'
})
# Check for common issues
if ' and ' in description or ' & ' in description:
suggestions.append({
'type': 'multiple_changes',
'message': 'Subject mentions multiple changes - consider splitting into multiple commits or using bullet points in body'
})
# Check for filler words
filler_pattern = r'\b(just|very|really|quite|some)\b'
if re.search(filler_pattern, description, re.IGNORECASE):
cleaned = re.sub(filler_pattern, '', description, flags=re.IGNORECASE)
cleaned = re.sub(r'\s+', ' ', cleaned).strip()
suggestions.append({
'type': 'filler_words',
'message': 'Remove filler words for clarity',
'current': description,
'suggested': cleaned
})
# Build response
response = {
'subject': subject,
'length': subject_length,
'max_length': max_length,
'type': commit_type,
'scope': scope if scope else None,
'description': description,
'valid': subject_length <= max_length and subject_length <= 72, # 72 is hard limit
'warnings': warnings,
'suggestions': suggestions
}
# Add quality score
score = 100
if subject_length > max_length:
score -= 20
if subject_length > 72:
score -= 30 # Major penalty for exceeding hard limit
if mood_changed:
score -= 5
if len(warnings) > 0:
score -= len(warnings) * 3
response['quality_score'] = max(0, score)
return response
def main():
"""Main entry point."""
try:
# Read JSON input from stdin
input_data = sys.stdin.read()
if not input_data or not input_data.strip():
print(json.dumps({
'error': 'No input provided',
'subject': None
}))
sys.exit(1)
# Parse JSON
try:
data = json.loads(input_data)
except json.JSONDecodeError as e:
print(json.dumps({
'error': f'Invalid JSON: {str(e)}',
'subject': None
}))
sys.exit(1)
# Generate subject
result = generate_subject(data)
# Output result
print(json.dumps(result, indent=2))
# Exit code based on result
if 'error' in result:
sys.exit(2)
elif not result.get('valid', False):
sys.exit(1)
else:
sys.exit(0)
except Exception as e:
print(json.dumps({
'error': f'Unexpected error: {str(e)}',
'subject': None
}))
sys.exit(2)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,371 @@
---
description: Add footer with breaking changes and issue references to commit message
---
# Operation: Add Footer
Add a properly formatted footer to commit message with breaking changes, issue references, and other metadata.
## Parameters from $ARGUMENTS
**Optional (at least one required):**
- `breaking:` - Breaking change description
- `closes:` - Comma-separated issue numbers to close
- `fixes:` - Comma-separated issue numbers to fix
- `refs:` - Comma-separated issue numbers to reference
- `reviewed:` - Reviewer name(s)
- `signed:` - Signed-off-by name and email
**Format:** `footer breaking:"API changed" closes:123,456`
## Workflow
### Step 1: Parse Parameters
Extract parameters from $ARGUMENTS:
```bash
# Parse footer components
breaking=$(echo "$ARGUMENTS" | grep -oP 'breaking:"\K[^"]+')
closes=$(echo "$ARGUMENTS" | grep -oP 'closes:\K[0-9,]+')
fixes=$(echo "$ARGUMENTS" | grep -oP 'fixes:\K[0-9,]+')
refs=$(echo "$ARGUMENTS" | grep -oP 'refs:\K[0-9,]+')
reviewed=$(echo "$ARGUMENTS" | grep -oP 'reviewed:"\K[^"]+')
signed=$(echo "$ARGUMENTS" | grep -oP 'signed:"\K[^"]+')
```
### Step 2: Validate Parameters
**Check at least one parameter provided:**
```bash
if [ -z "$breaking" ] && [ -z "$closes" ] && [ -z "$fixes" ] && [ -z "$refs" ] && [ -z "$reviewed" ] && [ -z "$signed" ]; then
echo "ERROR: At least one footer parameter is required"
echo "Usage: footer [breaking:\"<desc>\"] [closes:<nums>] [fixes:<nums>] [refs:<nums>]"
exit 1
fi
```
### Step 3: Invoke Footer Builder Script
Pass parameters to the utility script for proper formatting:
```bash
# Prepare JSON input
cat <<EOF | /home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation/.scripts/footer-builder.py
{
"breaking": "$breaking",
"closes": "$closes",
"fixes": "$fixes",
"refs": "$refs",
"reviewed": "$reviewed",
"signed": "$signed"
}
EOF
```
The script will:
- Format BREAKING CHANGE properly
- Convert issue numbers to proper references
- Order footer elements correctly
- Ensure proper spacing and formatting
### Step 4: Format Output
Present the generated footer:
```
FOOTER GENERATED
═══════════════════════════════════════════════
FOOTER:
───────────────────────────────────────────────
<blank line>
<formatted footer content>
VALIDATION:
───────────────────────────────────────────────
✓ Blank line before footer
✓ BREAKING CHANGE format correct
✓ Issue references valid
✓ Proper formatting
COMPONENTS:
───────────────────────────────────────────────
Breaking Changes: <yes/no>
Issues Closed: X
Issues Fixed: X
References: X
Signed-off: <yes/no>
═══════════════════════════════════════════════
```
## Output Format
Return structured output:
- Formatted footer text
- Validation results
- Component breakdown
- Suggestions (if any)
## Error Handling
**No parameters provided:**
```
ERROR: At least one footer parameter is required
Usage: footer [breaking:"<desc>"] [closes:<nums>] [fixes:<nums>]
Example: footer breaking:"authentication API changed" closes:123
```
**Invalid issue number format:**
```
ERROR: Invalid issue number format
Expected: closes:123 or closes:123,456
Received: closes:abc
```
**Missing breaking change description:**
```
ERROR: breaking parameter requires description
Usage: footer breaking:"<description of breaking change>"
Example: footer breaking:"API endpoint /auth removed"
```
## Footer Format Rules
**Order of Elements:**
```
<blank line>
BREAKING CHANGE: <description>
Closes #<issue>
Fixes #<issue>
Refs #<issue>
Reviewed-by: <name>
Signed-off-by: <name> <email>
```
**Breaking Changes:**
- Always use `BREAKING CHANGE:` (uppercase, singular)
- Provide clear description
- Can also use `!` in subject: `feat!: change API`
**Issue References:**
- Use `Closes #123` for issues closed by this commit
- Use `Fixes #123` for bugs fixed by this commit
- Use `Refs #123` for related issues
- Multiple issues: `Closes #123, #456`
**Metadata:**
- `Reviewed-by:` for code review
- `Signed-off-by:` for DCO compliance
- Custom trailers as needed
## Integration with Agent
The commit-assistant agent uses this operation to:
1. Add breaking change notices
2. Link commits to issues
3. Add review metadata
4. Ensure proper footer formatting
## Usage Examples
### Example 1: Breaking Change
```bash
# Input
/message-generation footer breaking:"authentication API endpoint changed from /login to /auth/login"
# Output
FOOTER:
BREAKING CHANGE: authentication API endpoint changed from /login to
/auth/login
```
### Example 2: Close Issues
```bash
# Input
/message-generation footer closes:123,456,789
# Output
FOOTER:
Closes #123, #456, #789
```
### Example 3: Fix and Close
```bash
# Input
/message-generation footer fixes:42 closes:100
# Output
FOOTER:
Fixes #42
Closes #100
```
### Example 4: Complete Footer
```bash
# Input
/message-generation footer breaking:"remove deprecated API" closes:200 signed:"John Doe <john@example.com>"
# Output
FOOTER:
BREAKING CHANGE: remove deprecated API
Closes #200
Signed-off-by: John Doe <john@example.com>
```
### Example 5: Multiple Issue Fixes
```bash
# Input
/message-generation footer fixes:10,20,30 refs:100
# Output
FOOTER:
Fixes #10, #20, #30
Refs #100
```
## Best Practices
**Breaking Changes:**
- ✅ "BREAKING CHANGE: API endpoint changed"
- ❌ "Breaking change: api endpoint changed"
- ✅ Clear description of what broke
- ❌ Vague "things changed"
**Issue References:**
- ✅ "Closes #123" (actually closes)
- ❌ "Closes #123" (just mentions)
- ✅ "Refs #100" (related)
- ❌ "See issue 100"
**Issue Linking Semantics:**
- `Closes` - Pull request or feature complete
- `Fixes` - Bug fix
- `Refs` - Related but not closed
- `Resolves` - Alternative to Closes
## When to Include a Footer
**Include footer when:**
- Breaking changes introduced
- Closes or fixes issues
- Multiple reviewers
- DCO/signing required
- Related work references
**Omit footer when:**
- No breaking changes
- No issue tracking
- No special metadata
- Simple standalone commit
## Breaking Change Detection
**Patterns that indicate breaking changes:**
- API endpoint changes
- Function signature changes
- Removed features
- Changed behavior
- Dependency major version bumps
- Configuration format changes
**How to communicate breaking changes:**
```
BREAKING CHANGE: <brief description>
<longer explanation of what changed>
<migration path if applicable>
```
**Alternative notation:**
```
feat!: change API endpoint
# The ! indicates breaking change
```
## Footer Templates
**Feature with Issue:**
```
Closes #<issue>
```
**Bug Fix:**
```
Fixes #<issue>
```
**Breaking Change with Migration:**
```
BREAKING CHANGE: <what changed>
Migration: <how to update>
Closes #<issue>
```
**Multiple Issues:**
```
Fixes #<bug1>, #<bug2>
Closes #<feature>
Refs #<related>
```
**Signed Commit:**
```
Reviewed-by: <reviewer>
Signed-off-by: <author> <email>
```
## GitHub/GitLab Integration
**GitHub Keywords (auto-close issues):**
- Closes, Closed, Close
- Fixes, Fixed, Fix
- Resolves, Resolved, Resolve
**GitLab Keywords:**
- Closes, Closed, Close (same as GitHub)
- Fixes, Fixed, Fix
- Resolves, Resolved, Resolve
- Implements, Implemented, Implement
**Format:**
```
Closes #123 # Same repository
Closes user/repo#123 # Different repository
Closes https://github.com/... # Full URL
```
## Footer Validation
**Valid footer format:**
```
✓ Blank line before footer
✓ BREAKING CHANGE in capitals
✓ Issue numbers have # prefix
✓ Proper token format (Closes, Fixes, etc.)
✓ Valid email in Signed-off-by
```
**Invalid footer format:**
```
✗ No blank line before footer
✗ "Breaking change:" (lowercase)
✗ "Closes 123" (missing #)
✗ "Resolves issue 123" (wrong format)
✗ Invalid email format
```

View File

@@ -0,0 +1,592 @@
---
description: Generate complete commit message orchestrating subject, body, and footer
---
# Operation: Complete Message
Generate a complete, well-formatted commit message by orchestrating subject generation, body composition, and footer creation.
## Parameters from $ARGUMENTS
**Required:**
- `type:` - Commit type (feat, fix, docs, etc.)
**Optional:**
- `scope:` - Affected module/component
- `description:` - Brief description (if not provided, derived from files)
- `files:` - Comma-separated file paths for context
- `changes:` - Comma-separated list of changes
- `why:` - Explanation of why changes were made
- `breaking:` - Breaking change description
- `closes:` - Issue numbers to close
- `fixes:` - Issue numbers to fix
- `include_body:` - Include body (true|false, default: true if multiple files)
- `include_footer:` - Include footer (true|false, default: true if breaking/issues)
**Format:** `complete type:feat scope:auth files:"file1.js,file2.js" breaking:"API changed" closes:123`
## Workflow
### Step 1: Parse All Parameters
Extract all parameters from $ARGUMENTS:
```bash
# Required
type=$(echo "$ARGUMENTS" | grep -oP 'type:\K[^ ]+')
# Optional
scope=$(echo "$ARGUMENTS" | grep -oP 'scope:\K[^ ]+')
description=$(echo "$ARGUMENTS" | grep -oP 'description:"\K[^"]+' || echo "$ARGUMENTS" | grep -oP 'description:\K[^ ]+')
files=$(echo "$ARGUMENTS" | grep -oP 'files:"\K[^"]+' || echo "$ARGUMENTS" | grep -oP 'files:\K[^ ]+')
changes=$(echo "$ARGUMENTS" | grep -oP 'changes:"\K[^"]+')
why=$(echo "$ARGUMENTS" | grep -oP 'why:"\K[^"]+')
breaking=$(echo "$ARGUMENTS" | grep -oP 'breaking:"\K[^"]+')
closes=$(echo "$ARGUMENTS" | grep -oP 'closes:\K[0-9,]+')
fixes=$(echo "$ARGUMENTS" | grep -oP 'fixes:\K[0-9,]+')
include_body=$(echo "$ARGUMENTS" | grep -oP 'include_body:\K(true|false)' || echo "auto")
include_footer=$(echo "$ARGUMENTS" | grep -oP 'include_footer:\K(true|false)' || echo "auto")
```
### Step 2: Validate Required Parameters
**Check type is provided:**
```bash
if [ -z "$type" ]; then
echo "ERROR: type parameter is required"
echo "Usage: complete type:<type> [scope:<scope>] [files:\"<files>\"]"
exit 1
fi
```
**Validate type:**
```bash
valid_types="feat fix docs style refactor perf test build ci chore revert"
if ! echo "$valid_types" | grep -qw "$type"; then
echo "ERROR: Invalid type '$type'"
echo "Valid types: $valid_types"
exit 1
fi
```
### Step 3: Derive Missing Parameters
**If description not provided, derive from files:**
```bash
if [ -z "$description" ] && [ -n "$files" ]; then
# Analyze files to create description
file_count=$(echo "$files" | tr ',' '\n' | wc -l)
if [ $file_count -eq 1 ]; then
description="update $(basename $files)"
else
# Extract common directory or feature
description="update ${file_count} files"
fi
fi
```
**Determine body inclusion:**
```bash
if [ "$include_body" = "auto" ]; then
file_count=$(echo "$files" | tr ',' '\n' | wc -l)
if [ $file_count -gt 1 ] || [ -n "$changes" ] || [ -n "$why" ]; then
include_body="true"
else
include_body="false"
fi
fi
```
**Determine footer inclusion:**
```bash
if [ "$include_footer" = "auto" ]; then
if [ -n "$breaking" ] || [ -n "$closes" ] || [ -n "$fixes" ]; then
include_footer="true"
else
include_footer="false"
fi
fi
```
### Step 4: Generate Subject Line
**Read and execute generate-subject.md:**
```bash
# Build subject arguments
subject_args="subject type:$type"
[ -n "$scope" ] && subject_args="$subject_args scope:$scope"
[ -n "$description" ] && subject_args="$subject_args description:\"$description\""
# Invoke subject generation
subject_result=$(bash -c "cd /home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation && cat generate-subject.md")
# Extract generated subject line
subject_line="<generated subject>"
```
**Store subject line:**
```bash
COMMIT_MESSAGE="$subject_line"
```
### Step 5: Generate Body (if needed)
**If include_body is true, read and execute write-body.md:**
```bash
if [ "$include_body" = "true" ]; then
# Build body arguments
body_args="body"
if [ -n "$changes" ]; then
body_args="$body_args changes:\"$changes\""
elif [ -n "$files" ]; then
# Derive changes from files
body_args="$body_args changes:\"$files\""
fi
[ -n "$why" ] && body_args="$body_args why:\"$why\""
# Invoke body generation
body_result=$(bash -c "cd /home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation && cat write-body.md")
# Extract generated body
body_content="<generated body>"
# Append to message (with blank line)
COMMIT_MESSAGE="$COMMIT_MESSAGE
$body_content"
fi
```
### Step 6: Generate Footer (if needed)
**If include_footer is true, read and execute add-footer.md:**
```bash
if [ "$include_footer" = "true" ]; then
# Build footer arguments
footer_args="footer"
[ -n "$breaking" ] && footer_args="$footer_args breaking:\"$breaking\""
[ -n "$closes" ] && footer_args="$footer_args closes:$closes"
[ -n "$fixes" ] && footer_args="$footer_args fixes:$fixes"
# Invoke footer generation
footer_result=$(bash -c "cd /home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation && cat add-footer.md")
# Extract generated footer
footer_content="<generated footer>"
# Append to message (with blank line)
COMMIT_MESSAGE="$COMMIT_MESSAGE
$footer_content"
fi
```
### Step 7: Validate Complete Message
**Read and execute validate-message.md:**
```bash
# Invoke validation
validation_result=$(bash -c "cd /home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation && cat validate-message.md")
# Parse validation result
validation_status="<VALID|INVALID>"
validation_score="<score>"
```
### Step 8: Format Final Output
Present the complete commit message:
```
COMPLETE COMMIT MESSAGE GENERATED
═══════════════════════════════════════════════
MESSAGE:
───────────────────────────────────────────────
<subject line>
<body content if present>
<footer content if present>
───────────────────────────────────────────────
COMPONENTS:
───────────────────────────────────────────────
Subject: ✓ Generated (XX chars)
Body: ✓ Generated (X lines) / ⊘ Omitted
Footer: ✓ Generated / ⊘ Omitted
VALIDATION:
───────────────────────────────────────────────
Format: ✓ Conventional Commits
Status: ✓ VALID
Score: XX/100
STATISTICS:
───────────────────────────────────────────────
Total Lines: X
Subject Length: XX/50 chars
Body Lines: X (if present)
Footer Elements: X (if present)
READY TO COMMIT:
───────────────────────────────────────────────
git commit -m "$(cat <<'EOF'
<complete message here>
EOF
)"
═══════════════════════════════════════════════
```
## Output Format
Return structured output with:
- Complete formatted message
- Component breakdown
- Validation results
- Statistics
- Ready-to-use git command
## Error Handling
**Missing required parameters:**
```
ERROR: type parameter is required
Usage: complete type:<type> [scope:<scope>] [files:"<files>"]
Example: complete type:feat scope:auth files:"auth.js,provider.js"
```
**Invalid type:**
```
ERROR: Invalid type 'feature'
Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
```
**Validation fails:**
```
WARNING: Generated message has issues
<validation errors>
Message generated but may need manual adjustment.
```
**Insufficient context:**
```
ERROR: Insufficient information to generate message
Provide either:
- description: explicit description
- files: files changed for context
- changes: list of changes made
Example: complete type:feat description:"add authentication"
```
## Integration with Agent
The commit-assistant agent uses this operation as the primary message generation workflow:
```
1. User: "commit my changes"
2. Agent: Analyze changes (/commit-analysis analyze)
→ type=feat, scope=auth, files=[...]
3. Agent: Generate complete message (THIS OPERATION)
→ /message-generation complete type:feat scope:auth files:"..."
4. Operation: Orchestrate generation
→ Generate subject
→ Generate body (if multiple files)
→ Generate footer (if issues/breaking)
→ Validate complete message
5. Return: Complete, validated message
6. Agent: Present to user for approval
```
## Usage Examples
### Example 1: Simple Commit (Subject Only)
```bash
# Input
/message-generation complete type:fix description:"resolve login crash"
# Output
MESSAGE:
fix: resolve login crash
COMPONENTS:
Subject: ✓ (26/50 chars)
Body: ⊘ Omitted (single change)
Footer: ⊘ Omitted (no issues)
VALIDATION: ✓ VALID (100/100)
```
### Example 2: Feature with Multiple Files
```bash
# Input
/message-generation complete type:feat scope:auth files:"oauth.js,providers/google.js,providers/github.js"
# Output
MESSAGE:
feat(auth): add OAuth authentication
- Add OAuth2 authentication module
- Implement Google provider
- Implement GitHub provider
COMPONENTS:
Subject: ✓ (36/50 chars)
Body: ✓ Generated (3 lines)
Footer: ⊘ Omitted
VALIDATION: ✓ VALID (95/100)
```
### Example 3: Breaking Change with Issues
```bash
# Input
/message-generation complete type:feat scope:api description:"redesign authentication API" breaking:"authentication endpoints changed" closes:100,101
# Output
MESSAGE:
feat(api): redesign authentication API
- Redesign authentication endpoints
- Improve security with OAuth2
- Simplify client integration
BREAKING CHANGE: authentication endpoints changed from /login and
/logout to /auth/login and /auth/logout
Closes #100, #101
COMPONENTS:
Subject: ✓ (42/50 chars)
Body: ✓ Generated (3 lines)
Footer: ✓ Generated (breaking + issues)
VALIDATION: ✓ VALID (100/100)
```
### Example 4: Bug Fix with Context
```bash
# Input
/message-generation complete type:fix scope:api files:"user.js" why:"prevent null pointer when user not found" fixes:42
# Output
MESSAGE:
fix(api): resolve null pointer in user endpoint
- Add null check for user lookup
- Return 404 when user not found
- Add error handling
Prevent null pointer exception when user is not found in database.
Fixes #42
COMPONENTS:
Subject: ✓ (46/50 chars)
Body: ✓ Generated (5 lines with context)
Footer: ✓ Generated (issue fix)
VALIDATION: ✓ VALID (100/100)
```
### Example 5: Documentation Update
```bash
# Input
/message-generation complete type:docs files:"README.md" description:"add installation instructions"
# Output
MESSAGE:
docs: add installation instructions
COMPONENTS:
Subject: ✓ (36/50 chars)
Body: ⊘ Omitted (docs only)
Footer: ⊘ Omitted
VALIDATION: ✓ VALID (100/100)
```
### Example 6: Explicit Body and Footer Control
```bash
# Input
/message-generation complete type:refactor scope:database description:"optimize queries" include_body:true include_footer:false changes:"Add indexes,Optimize joins,Cache results"
# Output
MESSAGE:
refactor(database): optimize queries
- Add database indexes
- Optimize query joins
- Implement result caching
COMPONENTS:
Subject: ✓ (35/50 chars)
Body: ✓ Forced inclusion
Footer: ⊘ Forced omission
VALIDATION: ✓ VALID (95/100)
```
## Best Practices
**Provide sufficient context:**
- ✅ type + scope + files (agent can derive details)
- ✅ type + description + changes (explicit)
- ❌ type only (insufficient)
**Let automation work:**
- ✅ Trust body/footer auto-inclusion logic
- ❌ Manually force inclusion when not needed
**Use breaking and issues:**
- ✅ Always include breaking changes
- ✅ Always link to issues
- ❌ Forget to document breaking changes
**Validate assumptions:**
- ✅ Review generated message
- ✅ Check validation results
- ❌ Blindly commit generated message
## Decision Logic
**Body inclusion decision tree:**
```
if include_body explicitly set:
use explicit value
else if multiple files changed (>1):
include body
else if changes list provided:
include body
else if why context provided:
include body
else:
omit body
```
**Footer inclusion decision tree:**
```
if include_footer explicitly set:
use explicit value
else if breaking change provided:
include footer
else if issue numbers provided (closes/fixes):
include footer
else:
omit footer
```
## Orchestration Flow
**Step-by-step orchestration:**
```
1. Parse parameters
2. Validate required (type)
3. Derive missing (description from files)
4. Determine body/footer inclusion
5. Generate subject (always)
→ Invoke: generate-subject.md
6. Generate body (if needed)
→ Invoke: write-body.md
7. Generate footer (if needed)
→ Invoke: add-footer.md
8. Combine all parts
9. Validate complete message
→ Invoke: validate-message.md
10. Return complete message with validation
```
## Error Recovery
**If subject generation fails:**
- Return error immediately
- Do not proceed to body/footer
**If body generation fails:**
- Return subject only
- Mark body as "generation failed"
- Continue to footer if needed
**If footer generation fails:**
- Return subject + body
- Mark footer as "generation failed"
**If validation fails:**
- Return message anyway
- Include validation errors
- Let user decide to fix or use
## Performance Considerations
**Typical generation time:**
- Subject only: <100ms
- Subject + body: <200ms
- Subject + body + footer: <300ms
- With validation: +50ms
**Optimization:**
- Cache repeated operations
- Minimize script invocations
- Stream output for large bodies
## Advanced Usage
**Template-based generation:**
```bash
# Feature template
complete type:feat scope:$SCOPE template:feature
# Bugfix template
complete type:fix scope:$SCOPE template:bugfix fixes:$ISSUE
```
**From analysis results:**
```bash
# Agent workflow
analysis=$(/commit-analysis analyze)
type=$(echo "$analysis" | jq -r '.type')
scope=$(echo "$analysis" | jq -r '.scope')
files=$(echo "$analysis" | jq -r '.files | join(",")')
/message-generation complete type:$type scope:$scope files:"$files"
```
**Interactive refinement:**
```bash
# Generate initial
msg=$(/message-generation complete type:feat scope:auth)
# User: "too long"
# Regenerate with constraint
msg=$(/message-generation complete type:feat scope:auth description:"add OAuth" include_body:false)
```

View File

@@ -0,0 +1,246 @@
---
description: Generate conventional commit subject line with type, scope, and description
---
# Operation: Generate Subject Line
Create a properly formatted subject line following the conventional commits standard: `<type>(<scope>): <description>`
## Parameters from $ARGUMENTS
**Required:**
- `type:` - Commit type (feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert)
- `description:` - Brief description of changes
**Optional:**
- `scope:` - Affected module/component
- `max_length:` - Maximum length (default: 50, hard limit: 72)
**Format:** `subject type:feat scope:auth description:"add OAuth authentication"`
## Workflow
### Step 1: Parse Parameters
Extract parameters from $ARGUMENTS:
```bash
# Parse key:value pairs
type=$(echo "$ARGUMENTS" | grep -oP 'type:\K[^ ]+')
scope=$(echo "$ARGUMENTS" | grep -oP 'scope:\K[^ ]+')
description=$(echo "$ARGUMENTS" | grep -oP 'description:"\K[^"]+' || echo "$ARGUMENTS" | grep -oP 'description:\K[^ ]+')
max_length=$(echo "$ARGUMENTS" | grep -oP 'max_length:\K[0-9]+' || echo "50")
```
### Step 2: Validate Parameters
**Check required parameters:**
```bash
if [ -z "$type" ]; then
echo "ERROR: type parameter is required"
echo "Usage: subject type:<type> description:\"<desc>\" [scope:<scope>]"
exit 1
fi
if [ -z "$description" ]; then
echo "ERROR: description parameter is required"
echo "Usage: subject type:<type> description:\"<desc>\" [scope:<scope>]"
exit 1
fi
```
**Validate type:**
```bash
valid_types="feat fix docs style refactor perf test build ci chore revert"
if ! echo "$valid_types" | grep -qw "$type"; then
echo "ERROR: Invalid type '$type'"
echo "Valid types: $valid_types"
exit 1
fi
```
### Step 3: Invoke Subject Generator Script
Pass parameters to the utility script for intelligent formatting:
```bash
# Prepare JSON input
cat <<EOF | /home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation/.scripts/subject-generator.py
{
"type": "$type",
"scope": "$scope",
"description": "$description",
"max_length": $max_length
}
EOF
```
The script will:
- Format the subject line
- Enforce imperative mood
- Ensure proper capitalization
- Check character limits
- Suggest improvements if needed
### Step 4: Format Output
Present the generated subject line:
```
SUBJECT LINE GENERATED
═══════════════════════════════════════════════
Subject: <type>(<scope>): <description>
Length: XX/50 characters
VALIDATION:
───────────────────────────────────────────────
✓ Type is valid
✓ Imperative mood used
✓ No capitalization after colon
✓ No period at end
✓ Within character limit
SUGGESTIONS:
───────────────────────────────────────────────
- <improvement 1 if applicable>
- <improvement 2 if applicable>
═══════════════════════════════════════════════
```
## Output Format
Return structured output:
- Generated subject line
- Character count
- Validation results
- Suggestions for improvement (if any)
## Error Handling
**Missing required parameters:**
```
ERROR: Missing required parameter 'type'
Usage: subject type:<type> description:"<desc>" [scope:<scope>]
Example: subject type:feat description:"add user authentication"
```
**Invalid type:**
```
ERROR: Invalid type 'feature'
Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
```
**Description too long:**
```
WARNING: Subject line exceeds 50 characters (XX chars)
Current: <type>(<scope>): <very long description>
Suggestion: Shorten description or move details to body
Recommended: <type>(<scope>): <shortened description>
```
**Non-imperative mood:**
```
WARNING: Use imperative mood
Current: "added authentication"
Correct: "add authentication"
```
## Subject Line Rules
**Imperative Mood:**
- ✅ "add feature" (correct)
- ❌ "added feature" (past tense)
- ❌ "adds feature" (present tense)
**Capitalization:**
- ✅ "feat: add login" (lowercase after colon)
- ❌ "feat: Add login" (uppercase after colon)
**Punctuation:**
- ✅ "fix: resolve crash" (no period)
- ❌ "fix: resolve crash." (period at end)
**Length:**
- Target: 50 characters maximum
- Hard limit: 72 characters
- Include type, scope, colon, and description
## Integration with Agent
The commit-assistant agent uses this operation to:
1. Generate subject lines during commit message creation
2. Validate subject line format
3. Suggest improvements for clarity
4. Ensure conventional commits compliance
## Usage Examples
### Example 1: Basic Subject
```bash
# Input
/message-generation subject type:feat description:"add user authentication"
# Output
Subject: feat: add user authentication
Length: 30/50 characters
Status: ✓ Valid
```
### Example 2: Subject with Scope
```bash
# Input
/message-generation subject type:fix scope:api description:"resolve null pointer"
# Output
Subject: fix(api): resolve null pointer
Length: 30/50 characters
Status: ✓ Valid
```
### Example 3: Long Description Warning
```bash
# Input
/message-generation subject type:feat description:"add comprehensive OAuth2 authentication with multiple providers"
# Output
WARNING: Subject exceeds 50 characters (69 chars)
Suggested: feat: add OAuth2 authentication
Move details to body: "with multiple providers"
```
### Example 4: Mood Correction
```bash
# Input
/message-generation subject type:fix description:"fixed login bug"
# Output
Subject: fix: fix login bug
WARNING: Use imperative mood
Suggested: fix: resolve login bug
```
## Best Practices
**Be Specific:**
- ✅ "add OAuth authentication"
- ❌ "update code"
**Focus on What:**
- ✅ "fix crash on login"
- ❌ "fix issue with the login button that crashes when clicked"
**Omit Implementation:**
- ✅ "improve query performance"
- ❌ "add database index to users table"
**Use Conventional Types:**
- ✅ "feat: add feature"
- ❌ "feature: add feature"

View File

@@ -0,0 +1,93 @@
---
description: Generate conventional commit messages following best practices
---
# Message Generation Skill - Semantic Commit Message Generation
Generate well-formatted commit messages that follow the Conventional Commits standard with proper type, scope, subject, body, and footer.
## Operations
- **subject** - Generate subject line: `<type>(<scope>): <description>`
- **body** - Compose commit body with bullet points
- **footer** - Add footer with breaking changes and issue references
- **validate** - Check conventional commits compliance
- **complete** - Generate full commit message (subject + body + footer)
## Router Logic
Parse $ARGUMENTS to determine which operation to perform:
1. Extract operation from first word of $ARGUMENTS
2. Extract remaining arguments as operation parameters
3. Route to appropriate instruction file:
- "subject" → Read `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation/generate-subject.md`
- "body" → Read `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation/write-body.md`
- "footer" → Read `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation/add-footer.md`
- "validate" → Read `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation/validate-message.md`
- "complete" → Read `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation/complete-message.md`
4. Execute instructions with parameters
5. Return formatted commit message or validation results
## Error Handling
- If operation is unknown, list available operations
- If required parameters are missing, show required format
- If message validation fails, provide specific corrections
- If character limits exceeded, suggest rewording
## Usage Examples
```bash
# Generate subject line only
/message-generation subject type:feat scope:auth description:"add OAuth authentication"
# Write commit body
/message-generation body changes:"Implement OAuth2 flow,Add provider support,Include middleware"
# Add footer with issue references
/message-generation footer breaking:"authentication API changed" closes:123
# Validate existing message
/message-generation validate message:"feat(auth): add OAuth"
# Generate complete commit message
/message-generation complete type:feat scope:auth files:"src/auth/oauth.js,src/auth/providers.js"
```
## Conventional Commits Format
**Message Structure:**
```
<type>(<scope>): <subject> ← Max 50 chars, imperative mood
<body> ← Optional, wrap at 72 chars
- Bullet point describing change 1
- Bullet point describing change 2
<footer> ← Optional
BREAKING CHANGE: description
Closes #123, #456
```
**Valid Types (priority order):**
1. feat - New feature
2. fix - Bug fix
3. docs - Documentation only
4. style - Formatting (no code change)
5. refactor - Code restructuring
6. perf - Performance improvement
7. test - Test additions/updates
8. build - Build system or dependencies
9. ci - CI/CD configuration
10. chore - Other maintenance
11. revert - Revert previous commit
---
**Base directory:** `/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation`
**Current request:** $ARGUMENTS
Parse operation and route to appropriate instruction file.

View File

@@ -0,0 +1,494 @@
---
description: Validate commit message against conventional commits standard
---
# Operation: Validate Message
Validate a commit message against the conventional commits standard, checking format, style, and best practices.
## Parameters from $ARGUMENTS
**Required:**
- `message:` - Full commit message to validate (use quotes for multi-line)
**Optional:**
- `strict:` - Enable strict mode (true|false, default: false)
- `max_subject:` - Maximum subject length (default: 50)
- `max_line:` - Maximum body line length (default: 72)
**Format:** `validate message:"feat: add feature"` or `validate message:"$(cat commit.txt)"`
## Workflow
### Step 1: Parse Parameters
Extract message from $ARGUMENTS:
```bash
# Parse message (supports quoted multi-line)
message=$(echo "$ARGUMENTS" | sed -n 's/.*message:"\(.*\)".*/\1/p')
if [ -z "$message" ]; then
message=$(echo "$ARGUMENTS" | sed 's/^validate //')
fi
strict=$(echo "$ARGUMENTS" | grep -oP 'strict:\K(true|false)' || echo "false")
max_subject=$(echo "$ARGUMENTS" | grep -oP 'max_subject:\K[0-9]+' || echo "50")
max_line=$(echo "$ARGUMENTS" | grep -oP 'max_line:\K[0-9]+' || echo "72")
```
### Step 2: Validate Parameters
**Check message provided:**
```bash
if [ -z "$message" ]; then
echo "ERROR: message parameter is required"
echo "Usage: validate message:\"<commit message>\""
exit 1
fi
```
### Step 3: Invoke Message Validator Script
Pass message to validation script:
```bash
# Export variables
export MESSAGE="$message"
export STRICT_MODE="$strict"
export MAX_SUBJECT="$max_subject"
export MAX_LINE="$max_line"
# Run validator
/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation/.scripts/message-validator.sh
```
The script performs comprehensive validation:
- Format compliance
- Type validation
- Subject line rules
- Body formatting
- Footer format
- Character limits
- Mood and style
- Best practices
### Step 4: Format Validation Report
Present detailed validation results:
```
COMMIT MESSAGE VALIDATION
═══════════════════════════════════════════════
MESSAGE:
───────────────────────────────────────────────
<commit message displayed>
FORMAT VALIDATION:
───────────────────────────────────────────────
✓ Conventional commits format
✓ Valid commit type
✓ Scope format correct (if present)
✓ Blank line before body (if present)
✓ Blank line before footer (if present)
SUBJECT LINE:
───────────────────────────────────────────────
✓ Length: XX/50 characters
✓ Imperative mood
✓ No capitalization after colon
✓ No period at end
✓ Clear and descriptive
BODY (if present):
───────────────────────────────────────────────
✓ Blank line after subject
✓ Lines wrapped at 72 characters
✓ Bullet points formatted correctly
✓ Explains what and why
FOOTER (if present):
───────────────────────────────────────────────
✓ Blank line before footer
✓ BREAKING CHANGE format correct
✓ Issue references valid
✓ Metadata formatted properly
OVERALL:
───────────────────────────────────────────────
Status: ✓ VALID / ✗ INVALID
Score: XX/100
WARNINGS (if any):
───────────────────────────────────────────────
- Subject line is long (consider shortening)
- Body line exceeds 72 characters
ERRORS (if any):
───────────────────────────────────────────────
- Invalid commit type
- Missing imperative mood
SUGGESTIONS:
───────────────────────────────────────────────
- <improvement 1>
- <improvement 2>
═══════════════════════════════════════════════
```
## Output Format
Return structured validation report with:
- Format compliance check
- Subject line validation
- Body validation (if present)
- Footer validation (if present)
- Overall score
- Warnings (non-critical issues)
- Errors (critical issues)
- Suggestions for improvement
## Error Handling
**No message provided:**
```
ERROR: message parameter is required
Usage: validate message:"<commit message>"
Example: validate message:"feat: add authentication"
```
**Empty message:**
```
ERROR: Commit message is empty
Provide a commit message to validate
```
**Completely invalid format:**
```
VALIDATION FAILED
Format does not match conventional commits standard
Expected: <type>(<scope>): <subject>
Received: <message>
See: https://www.conventionalcommits.org/
```
## Validation Rules
### Subject Line Validation
**Format:** `<type>(<scope>): <subject>`
**Type validation:**
- ✓ Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
- ✗ Invalid types: feature, bugfix, update, change
**Scope validation:**
- ✓ Lowercase alphanumeric
- ✓ Hyphen allowed
- ✗ Spaces not allowed
- ✗ Special characters not allowed
**Subject validation:**
- ✓ Imperative mood (add, fix, update)
- ✗ Past tense (added, fixed)
- ✗ Present tense (adds, fixes)
- ✓ Lowercase after colon
- ✗ Uppercase after colon
- ✓ No period at end
- ✗ Period at end
- ✓ Length ≤ 50 chars (warning if > 50, error if > 72)
### Body Validation
**Structure:**
- ✓ Blank line after subject
- ✓ Lines wrapped at 72 characters
- ✓ Bullet points start with `-` or `*`
- ✓ Proper paragraph spacing
**Content:**
- ✓ Explains what and why
- ✓ Imperative mood
- ✗ Implementation details
- ✗ Overly verbose
### Footer Validation
**Format:**
- ✓ Blank line before footer
-`BREAKING CHANGE:` (uppercase, singular)
- ✓ Issue references: `Closes #123`
- ✓ Metadata format: `Key: value`
**Issue references:**
-`Closes #123`
-`Fixes #42`
-`Refs #100`
-`Closes 123` (missing #)
-`closes #123` (lowercase)
## Integration with Agent
The commit-assistant agent uses this operation to:
1. Validate messages before commit
2. Check user-provided messages
3. Verify generated messages
4. Provide improvement suggestions
## Usage Examples
### Example 1: Valid Message
```bash
# Input
/message-generation validate message:"feat(auth): add OAuth authentication"
# Output
✓ VALID
Subject: feat(auth): add OAuth authentication (42/50 chars)
All checks passed
```
### Example 2: Message with Warnings
```bash
# Input
/message-generation validate message:"feat: add a comprehensive OAuth2 authentication system"
# Output
⚠ VALID WITH WARNINGS
Subject exceeds 50 characters (57 chars)
Suggestion: Shorten subject or move details to body
Example: "feat: add OAuth2 authentication"
```
### Example 3: Invalid Type
```bash
# Input
/message-generation validate message:"feature: add login"
# Output
✗ INVALID
Invalid commit type: 'feature'
Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
Suggested: feat: add login
```
### Example 4: Wrong Mood
```bash
# Input
/message-generation validate message:"fix: fixed login bug"
# Output
⚠ VALID WITH WARNINGS
Use imperative mood in subject
Current: "fixed login bug"
Correct: "fix login bug"
```
### Example 5: Multi-line Message
```bash
# Input
/message-generation validate message:"feat(auth): add OAuth
Implement OAuth2 authentication flow with Google and GitHub providers.
BREAKING CHANGE: authentication endpoint changed
Closes #123"
# Output
✓ VALID
Subject: ✓ (28/50 chars)
Body: ✓ (proper formatting)
Footer: ✓ (BREAKING CHANGE and issue reference)
Score: 100/100
```
### Example 6: Invalid Footer
```bash
# Input
/message-generation validate message:"feat: add feature
Breaking change: API changed"
# Output
✗ INVALID - Footer format incorrect
Current: "Breaking change: API changed"
Correct: "BREAKING CHANGE: API changed"
Footer tokens must be uppercase.
```
## Validation Scoring
**Score breakdown (100 points total):**
**Subject (40 points):**
- Valid type (10 pts)
- Proper format (10 pts)
- Imperative mood (10 pts)
- Length ≤ 50 chars (10 pts)
**Body (30 points, if present):**
- Blank line after subject (10 pts)
- Proper wrapping (10 pts)
- Clear explanation (10 pts)
**Footer (15 points, if present):**
- Blank line before footer (5 pts)
- Proper format (10 pts)
**Style (15 points):**
- Consistent style (5 pts)
- No typos (5 pts)
- Professional tone (5 pts)
**Scoring thresholds:**
- 90-100: Excellent
- 75-89: Good
- 60-74: Acceptable
- Below 60: Needs improvement
## Strict Mode
**Normal mode (default):**
- Warnings for non-critical issues
- Accepts messages with minor issues
- Suggests improvements
**Strict mode (strict:true):**
- Errors for any deviation
- Rejects messages with warnings
- Enforces all best practices
- Useful for pre-commit hooks
**Example difference:**
Normal mode:
```
⚠ Subject is 55 characters (warning)
Status: VALID
```
Strict mode:
```
✗ Subject exceeds 50 characters (error)
Status: INVALID
```
## Best Practices Validation
**Checks performed:**
**Subject best practices:**
- Be specific (not "update code")
- Focus on what (not how)
- Avoid filler words
- Use consistent terminology
**Body best practices:**
- Explain motivation
- Describe high-level approach
- Mention side effects
- Link to related work
**Footer best practices:**
- Clear breaking change description
- Accurate issue references
- Proper DCO/sign-off
- Relevant metadata only
## Common Validation Failures
**Invalid type:**
```
✗ Type "feature" is not valid
→ Use "feat" instead
```
**Past tense:**
```
✗ Use imperative: "add" not "added"
→ Subject should use present tense
```
**Capitalization:**
```
✗ Don't capitalize after colon
→ "feat: Add feature" should be "feat: add feature"
```
**Period at end:**
```
✗ No period at end of subject
→ "feat: add feature." should be "feat: add feature"
```
**No blank line:**
```
✗ Blank line required between subject and body
→ Add empty line after subject
```
**Line too long:**
```
✗ Body line exceeds 72 characters
→ Wrap text at 72 characters
```
**Invalid footer:**
```
✗ Footer format incorrect
→ Use "BREAKING CHANGE:" not "Breaking change:"
```
## Pre-commit Integration
This validation can be used in pre-commit hooks:
```bash
#!/bin/bash
# .git/hooks/commit-msg
MESSAGE=$(cat "$1")
# Validate message
result=$(/message-generation validate message:"$MESSAGE" strict:true)
if echo "$result" | grep -q "✗ INVALID"; then
echo "$result"
exit 1
fi
```
## Validation vs Generation
**Validation:**
- Checks existing messages
- Identifies problems
- Suggests corrections
- Pass/fail result
**Generation:**
- Creates new messages
- Follows rules automatically
- Optimized output
- Always valid (when properly configured)
**Workflow:**
```
User writes message → Validate → If invalid → Suggest corrections
Agent generates message → Validate → Should always pass
```

View File

@@ -0,0 +1,309 @@
---
description: Compose commit message body with bullet points and proper formatting
---
# Operation: Write Commit Body
Compose a well-formatted commit message body with bullet points, proper wrapping, and clear explanation of changes.
## Parameters from $ARGUMENTS
**Required:**
- `changes:` - Comma-separated list of changes or file paths
**Optional:**
- `wrap:` - Line wrap length (default: 72)
- `format:` - Output format (bullets|paragraphs, default: bullets)
- `why:` - Additional context about why changes were made
**Format:** `body changes:"Change 1,Change 2,Change 3" [why:"explanation"]`
## Workflow
### Step 1: Parse Parameters
Extract parameters from $ARGUMENTS:
```bash
# Parse changes (supports both quoted and comma-separated)
changes=$(echo "$ARGUMENTS" | grep -oP 'changes:"\K[^"]+' || echo "$ARGUMENTS" | grep -oP 'changes:\K[^,]+')
wrap=$(echo "$ARGUMENTS" | grep -oP 'wrap:\K[0-9]+' || echo "72")
format=$(echo "$ARGUMENTS" | grep -oP 'format:\K[^ ]+' || echo "bullets")
why=$(echo "$ARGUMENTS" | grep -oP 'why:"\K[^"]+')
```
### Step 2: Validate Parameters
**Check required parameters:**
```bash
if [ -z "$changes" ]; then
echo "ERROR: changes parameter is required"
echo "Usage: body changes:\"<change1>,<change2>\" [why:\"<explanation>\"]"
exit 1
fi
```
**Validate format:**
```bash
if [ "$format" != "bullets" ] && [ "$format" != "paragraphs" ]; then
echo "ERROR: Invalid format '$format'"
echo "Valid formats: bullets, paragraphs"
exit 1
fi
```
### Step 3: Invoke Body Composer Script
Pass parameters to the utility script for intelligent formatting:
```bash
# Export variables for script
export CHANGES="$changes"
export WRAP_LENGTH="$wrap"
export FORMAT="$format"
export WHY_CONTEXT="$why"
# Run composer
/home/danie/projects/plugins/architect/open-plugins/plugins/git-commit-assistant/commands/message-generation/.scripts/body-composer.sh
```
The script will:
- Split changes into individual items
- Format as bullet points or paragraphs
- Wrap lines at specified length
- Add context if provided
- Ensure imperative mood
### Step 4: Format Output
Present the generated body:
```
COMMIT BODY GENERATED
═══════════════════════════════════════════════
BODY:
───────────────────────────────────────────────
<blank line>
<formatted body content with bullet points>
<wrapped at 72 characters>
VALIDATION:
───────────────────────────────────────────────
✓ Blank line before body
✓ Lines wrapped at 72 characters
✓ Bullet points used
✓ Imperative mood
✓ Proper formatting
STATISTICS:
───────────────────────────────────────────────
Lines: X
Longest line: XX chars
Bullet points: X
═══════════════════════════════════════════════
```
## Output Format
Return structured output:
- Formatted body text
- Validation results
- Statistics (line count, wrapping)
- Suggestions for improvement (if any)
## Error Handling
**Missing required parameters:**
```
ERROR: Missing required parameter 'changes'
Usage: body changes:"<change1>,<change2>" [why:"<explanation>"]
Example: body changes:"Implement OAuth2 flow,Add provider support"
```
**Line too long:**
```
WARNING: Line exceeds 72 characters (XX chars)
Line: "- Very long description that goes on and on..."
Suggestion: Split into multiple bullet points or wrap text
```
**Non-imperative mood:**
```
WARNING: Use imperative mood in body
Current: "- Added authentication"
Correct: "- Add authentication"
```
## Body Formatting Rules
**Structure:**
```
<blank line required between subject and body>
<body content>
- Bullet point 1
- Bullet point 2
- Bullet point 3
```
**Line Wrapping:**
- Target: 72 characters per line
- Hard limit: No line should exceed 80 characters
- Use hard wraps, not soft wraps
**Bullet Points:**
- Use `-` for bullet points
- Consistent indentation
- One thought per bullet
- Imperative mood
**Content Focus:**
- Explain WHAT and WHY, not HOW
- Focus on user-facing changes
- Provide context when needed
- Avoid implementation details
## Integration with Agent
The commit-assistant agent uses this operation to:
1. Generate body content from analyzed changes
2. Format file lists into readable bullet points
3. Add context about why changes were made
4. Ensure proper formatting and wrapping
## Usage Examples
### Example 1: Basic Body with Changes
```bash
# Input
/message-generation body changes:"Implement OAuth2 flow,Add Google provider,Add GitHub provider,Include middleware"
# Output
BODY:
- Implement OAuth2 flow
- Add Google provider
- Add GitHub provider
- Include middleware
```
### Example 2: Body with Context
```bash
# Input
/message-generation body changes:"Refactor database queries,Add connection pooling" why:"Improve performance under load"
# Output
BODY:
- Refactor database queries
- Add connection pooling
Improve performance under high load conditions.
```
### Example 3: Body from File List
```bash
# Input
/message-generation body changes:"src/auth/oauth.js,src/auth/providers/google.js,src/auth/providers/github.js"
# Output
BODY:
- Add OAuth authentication module
- Implement Google provider
- Implement GitHub provider
- Add provider configuration
```
### Example 4: Paragraph Format
```bash
# Input
/message-generation body changes:"Update authentication flow" why:"Previous implementation had security vulnerabilities" format:paragraphs
# Output
BODY:
Update authentication flow to address security vulnerabilities
discovered in the previous implementation. The new approach uses
industry-standard OAuth2 protocol with secure token handling.
```
## Best Practices
**Be Clear:**
- ✅ "Add user authentication with OAuth2"
- ❌ "Add stuff"
**Use Bullet Points:**
- ✅ Multiple related changes as bullets
- ❌ Long paragraphs of text
**Focus on What/Why:**
- ✅ "Add caching to improve performance"
- ❌ "Add Redis instance with 5-minute TTL"
**Keep It Concise:**
- ✅ Brief, clear explanations
- ❌ Essay-length descriptions
**Wrap Properly:**
- ✅ "This is a properly wrapped line that doesn't exceed\nthe 72 character limit"
- ❌ "This is a very long line that goes on and on and on and exceeds the character limit"
## When to Include a Body
**Include body when:**
- Multiple files changed
- Changes need explanation
- Context is important
- Implications not obvious
**Omit body when:**
- Change is self-explanatory
- Subject line is sufficient
- Trivial change
- Documentation only
## Body Templates
**Feature Addition:**
```
- Add <feature name>
- Implement <capability 1>
- Implement <capability 2>
- Include <supporting feature>
```
**Bug Fix:**
```
- Resolve <issue>
- Add validation for <edge case>
- Update error handling
Fixes issue where <description of bug>
```
**Refactoring:**
```
- Extract <component>
- Simplify <logic>
- Improve <aspect>
No functional changes, improves code maintainability.
```
**Performance:**
```
- Optimize <operation>
- Add caching for <data>
- Reduce <metric>
Improves performance by <measurement>.
```