Initial commit
This commit is contained in:
19
.claude-plugin/plugin.json
Normal file
19
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "git-commit-assistant",
|
||||
"description": "Intelligent git commit helper with semantic commit message generation, change analysis, and atomic commit guidance using conventional commits format",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "Daniel Hofheinz",
|
||||
"email": "daniel@danielhofheinz.com",
|
||||
"url": "https://github.com/dhofheinz/open-plugins"
|
||||
},
|
||||
"agents": [
|
||||
"./agents"
|
||||
],
|
||||
"commands": [
|
||||
"./commands"
|
||||
],
|
||||
"hooks": [
|
||||
"./hooks"
|
||||
]
|
||||
}
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# git-commit-assistant
|
||||
|
||||
Intelligent git commit helper with semantic commit message generation, change analysis, and atomic commit guidance using conventional commits format
|
||||
1038
agents/commit-assistant.md
Normal file
1038
agents/commit-assistant.md
Normal file
File diff suppressed because it is too large
Load Diff
342
commands/atomic-commit/.scripts/commit-planner.py
Executable file
342
commands/atomic-commit/.scripts/commit-planner.py
Executable 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()
|
||||
328
commands/atomic-commit/.scripts/dependency-checker.sh
Executable file
328
commands/atomic-commit/.scripts/dependency-checker.sh
Executable 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
|
||||
395
commands/atomic-commit/.scripts/file-grouper.sh
Executable file
395
commands/atomic-commit/.scripts/file-grouper.sh
Executable 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
|
||||
283
commands/atomic-commit/.scripts/split-analyzer.py
Executable file
283
commands/atomic-commit/.scripts/split-analyzer.py
Executable 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()
|
||||
138
commands/atomic-commit/analyze-splitting.md
Normal file
138
commands/atomic-commit/analyze-splitting.md
Normal 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
|
||||
```
|
||||
352
commands/atomic-commit/create-sequence.md
Normal file
352
commands/atomic-commit/create-sequence.md
Normal 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.
|
||||
209
commands/atomic-commit/group-files.md
Normal file
209
commands/atomic-commit/group-files.md
Normal 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.
|
||||
455
commands/atomic-commit/interactive-split.md
Normal file
455
commands/atomic-commit/interactive-split.md
Normal 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.
|
||||
99
commands/atomic-commit/skill.md
Normal file
99
commands/atomic-commit/skill.md
Normal 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
|
||||
```
|
||||
272
commands/atomic-commit/suggest-commits.md
Normal file
272
commands/atomic-commit/suggest-commits.md
Normal 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.
|
||||
186
commands/commit-analysis/.scripts/atomicity-checker.py
Executable file
186
commands/commit-analysis/.scripts/atomicity-checker.py
Executable 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()
|
||||
103
commands/commit-analysis/.scripts/git-diff-analyzer.sh
Executable file
103
commands/commit-analysis/.scripts/git-diff-analyzer.sh
Executable 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
|
||||
131
commands/commit-analysis/.scripts/scope-identifier.sh
Executable file
131
commands/commit-analysis/.scripts/scope-identifier.sh
Executable 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
|
||||
246
commands/commit-analysis/.scripts/type-detector.py
Executable file
246
commands/commit-analysis/.scripts/type-detector.py
Executable 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()
|
||||
196
commands/commit-analysis/analyze-changes.md
Normal file
196
commands/commit-analysis/analyze-changes.md
Normal 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
|
||||
```
|
||||
363
commands/commit-analysis/assess-atomicity.md
Normal file
363
commands/commit-analysis/assess-atomicity.md
Normal 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"
|
||||
```
|
||||
341
commands/commit-analysis/detect-type.md
Normal file
341
commands/commit-analysis/detect-type.md
Normal 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
|
||||
```
|
||||
327
commands/commit-analysis/file-stats.md
Normal file
327
commands/commit-analysis/file-stats.md
Normal 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"
|
||||
```
|
||||
231
commands/commit-analysis/identify-scope.md
Normal file
231
commands/commit-analysis/identify-scope.md
Normal 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): ..."
|
||||
```
|
||||
65
commands/commit-analysis/skill.md
Normal file
65
commands/commit-analysis/skill.md
Normal 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.
|
||||
259
commands/commit-best-practices/.scripts/amend-safety.sh
Executable file
259
commands/commit-best-practices/.scripts/amend-safety.sh
Executable 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 "$@"
|
||||
345
commands/commit-best-practices/.scripts/commit-reviewer.py
Executable file
345
commands/commit-best-practices/.scripts/commit-reviewer.py
Executable 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()
|
||||
322
commands/commit-best-practices/.scripts/pre-commit-check.sh
Executable file
322
commands/commit-best-practices/.scripts/pre-commit-check.sh
Executable 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 "$@"
|
||||
259
commands/commit-best-practices/.scripts/revert-helper.sh
Executable file
259
commands/commit-best-practices/.scripts/revert-helper.sh
Executable 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 "$@"
|
||||
255
commands/commit-best-practices/USAGE_EXAMPLES.md
Normal file
255
commands/commit-best-practices/USAGE_EXAMPLES.md
Normal 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.
|
||||
373
commands/commit-best-practices/amend-guidance.md
Normal file
373
commands/commit-best-practices/amend-guidance.md
Normal 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.
|
||||
214
commands/commit-best-practices/check-pre-commit.md
Normal file
214
commands/commit-best-practices/check-pre-commit.md
Normal 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.
|
||||
419
commands/commit-best-practices/revert-guidance.md
Normal file
419
commands/commit-best-practices/revert-guidance.md
Normal 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.
|
||||
347
commands/commit-best-practices/review-commit.md
Normal file
347
commands/commit-best-practices/review-commit.md
Normal 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.
|
||||
78
commands/commit-best-practices/skill.md
Normal file
78
commands/commit-best-practices/skill.md
Normal 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.
|
||||
607
commands/commit-best-practices/workflow-tips.md
Normal file
607
commands/commit-best-practices/workflow-tips.md
Normal 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.
|
||||
70
commands/commit-error-handling/.scripts/changes-detector.sh
Executable file
70
commands/commit-error-handling/.scripts/changes-detector.sh
Executable 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 "$@"
|
||||
173
commands/commit-error-handling/.scripts/conflict-detector.py
Executable file
173
commands/commit-error-handling/.scripts/conflict-detector.py
Executable 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)
|
||||
54
commands/commit-error-handling/.scripts/repo-checker.sh
Executable file
54
commands/commit-error-handling/.scripts/repo-checker.sh
Executable 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 "$@"
|
||||
183
commands/commit-error-handling/.scripts/state-analyzer.sh
Executable file
183
commands/commit-error-handling/.scripts/state-analyzer.sh
Executable 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 "$@"
|
||||
466
commands/commit-error-handling/diagnose-issues.md
Normal file
466
commands/commit-error-handling/diagnose-issues.md
Normal 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
|
||||
310
commands/commit-error-handling/handle-conflicts.md
Normal file
310
commands/commit-error-handling/handle-conflicts.md
Normal 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
|
||||
366
commands/commit-error-handling/handle-detached-head.md
Normal file
366
commands/commit-error-handling/handle-detached-head.md
Normal 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
|
||||
220
commands/commit-error-handling/handle-no-changes.md
Normal file
220
commands/commit-error-handling/handle-no-changes.md
Normal 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**
|
||||
161
commands/commit-error-handling/handle-no-repo.md
Normal file
161
commands/commit-error-handling/handle-no-repo.md
Normal 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
|
||||
96
commands/commit-error-handling/skill.md
Normal file
96
commands/commit-error-handling/skill.md
Normal 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
686
commands/commit-review.md
Normal 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
835
commands/commit-split.md
Normal 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
655
commands/commit.md
Normal 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
|
||||
408
commands/history-analysis/.scripts/convention-recommender.py
Executable file
408
commands/history-analysis/.scripts/convention-recommender.py
Executable 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()
|
||||
421
commands/history-analysis/.scripts/pattern-detector.py
Executable file
421
commands/history-analysis/.scripts/pattern-detector.py
Executable 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()
|
||||
230
commands/history-analysis/.scripts/scope-extractor.sh
Executable file
230
commands/history-analysis/.scripts/scope-extractor.sh
Executable 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
|
||||
232
commands/history-analysis/.scripts/style-analyzer.sh
Executable file
232
commands/history-analysis/.scripts/style-analyzer.sh
Executable 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
|
||||
245
commands/history-analysis/analyze-style.md
Normal file
245
commands/history-analysis/analyze-style.md
Normal 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
|
||||
302
commands/history-analysis/detect-patterns.md
Normal file
302
commands/history-analysis/detect-patterns.md
Normal 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)
|
||||
378
commands/history-analysis/extract-scopes.md
Normal file
378
commands/history-analysis/extract-scopes.md
Normal 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)
|
||||
586
commands/history-analysis/learn-project.md
Normal file
586
commands/history-analysis/learn-project.md
Normal 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
|
||||
118
commands/history-analysis/skill.md
Normal file
118
commands/history-analysis/skill.md
Normal 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
|
||||
```
|
||||
431
commands/history-analysis/suggest-conventions.md
Normal file
431
commands/history-analysis/suggest-conventions.md
Normal 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
|
||||
304
commands/message-generation/.scripts/body-composer.sh
Executable file
304
commands/message-generation/.scripts/body-composer.sh
Executable 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
|
||||
292
commands/message-generation/.scripts/footer-builder.py
Executable file
292
commands/message-generation/.scripts/footer-builder.py
Executable 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()
|
||||
335
commands/message-generation/.scripts/message-validator.sh
Executable file
335
commands/message-generation/.scripts/message-validator.sh
Executable 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
|
||||
354
commands/message-generation/.scripts/subject-generator.py
Executable file
354
commands/message-generation/.scripts/subject-generator.py
Executable 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()
|
||||
371
commands/message-generation/add-footer.md
Normal file
371
commands/message-generation/add-footer.md
Normal 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
|
||||
```
|
||||
592
commands/message-generation/complete-message.md
Normal file
592
commands/message-generation/complete-message.md
Normal 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)
|
||||
```
|
||||
246
commands/message-generation/generate-subject.md
Normal file
246
commands/message-generation/generate-subject.md
Normal 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"
|
||||
93
commands/message-generation/skill.md
Normal file
93
commands/message-generation/skill.md
Normal 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.
|
||||
494
commands/message-generation/validate-message.md
Normal file
494
commands/message-generation/validate-message.md
Normal 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
|
||||
```
|
||||
309
commands/message-generation/write-body.md
Normal file
309
commands/message-generation/write-body.md
Normal 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>.
|
||||
```
|
||||
13
hooks/hooks.json
Normal file
13
hooks/hooks.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/suggest-commit.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
305
plugin.lock.json
Normal file
305
plugin.lock.json
Normal file
@@ -0,0 +1,305 @@
|
||||
{
|
||||
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||
"pluginId": "gh:dhofheinz/open-plugins:plugins/git-commit-assistant",
|
||||
"normalized": {
|
||||
"repo": null,
|
||||
"ref": "refs/tags/v20251128.0",
|
||||
"commit": "4147d5d163fbdcb156b87d0863795afc206c2ba9",
|
||||
"treeHash": "67c1ef02e222080168240747830e5d0aab183f691dc0240f5f5593d7737f6053",
|
||||
"generatedAt": "2025-11-28T10:16:24.154127Z",
|
||||
"toolVersion": "publish_plugins.py@0.2.0"
|
||||
},
|
||||
"origin": {
|
||||
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||
"branch": "master",
|
||||
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||
},
|
||||
"manifest": {
|
||||
"name": "git-commit-assistant",
|
||||
"description": "Intelligent git commit helper with semantic commit message generation, change analysis, and atomic commit guidance using conventional commits format",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"content": {
|
||||
"files": [
|
||||
{
|
||||
"path": "README.md",
|
||||
"sha256": "58662f89355d711a4db4a778212132bff12663c39e4435d1275a36cfef7d862a"
|
||||
},
|
||||
{
|
||||
"path": "agents/commit-assistant.md",
|
||||
"sha256": "c5c390028e43e2db657d7f6445831ba3b1c2e73bfb69d16fe4432c23bc237bd5"
|
||||
},
|
||||
{
|
||||
"path": "hooks/hooks.json",
|
||||
"sha256": "c6a967f992da6c19edb1d5db95c024f3198f49787111f2f2f2fa986511ad7b44"
|
||||
},
|
||||
{
|
||||
"path": ".claude-plugin/plugin.json",
|
||||
"sha256": "e2be07c3e1878454f5e689a5d85b2dc47e443359804a37b4e50c7f5bbb145450"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-split.md",
|
||||
"sha256": "7b41ee7dd78227c78e6ccc23e4d81208d331e14baf65caac49b6e1556001e2e7"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-review.md",
|
||||
"sha256": "49aa52cff86ea8126951524e94f3eec1656be518455c05015bc5b8bd515b9e58"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit.md",
|
||||
"sha256": "6e4a3eea6ed27b568fab4026a6b11eb30d7005b556ab9ccf9b815f39700f3138"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/revert-guidance.md",
|
||||
"sha256": "cdbd8dffc3a09ec38be6483cbd2a0e0c0b7ee17c00c554c3aa4908e45097c0e0"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/USAGE_EXAMPLES.md",
|
||||
"sha256": "746e799eb367220d10088d6dc650547e7421ee20c7fd7d026f4ecb5851ada7b7"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/review-commit.md",
|
||||
"sha256": "1c1863826e6bea37d00e934b184827c44769eb0ec0a09c6771c4a41f4e0467db"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/skill.md",
|
||||
"sha256": "7f6a7a1af1c1947d513ff2e77a4774eee77dd8a412b823cd5efee9995b1286b7"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/workflow-tips.md",
|
||||
"sha256": "9fd9aea964921b8d27ccd4a36c2f94f00b5ff39c6c69043d3a7c02662c9b134b"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/amend-guidance.md",
|
||||
"sha256": "291a5e27c32eb54cb94bf5ad429a1747a7464ff694d456d2b07b65449540616e"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/check-pre-commit.md",
|
||||
"sha256": "c28510a6c49565e936ad40c282107ed387c41043b2c7923a364aee25d2c5d7a7"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/.scripts/pre-commit-check.sh",
|
||||
"sha256": "a8d786e88f587c3c5edfc9785a18b3c93fa10d43655d62184165479fdc1775f0"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/.scripts/revert-helper.sh",
|
||||
"sha256": "3f01e26479b71aad1a30f60bab6d3c587852b7d833509096b9021fcd4f996aa5"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/.scripts/commit-reviewer.py",
|
||||
"sha256": "8baca31af97a5a2c61581317a1d638746c2d700f7d914da4be673344d2725dd8"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-best-practices/.scripts/amend-safety.sh",
|
||||
"sha256": "fa8f9b1b2ff0310a5688dbd389a266852931d58357675639030346366d8cc7b6"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-analysis/assess-atomicity.md",
|
||||
"sha256": "11ea4da3284f055bbe894978a6cd1fa819360580056ca52d9850480920585055"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-analysis/analyze-changes.md",
|
||||
"sha256": "596e5ac67004af135fbecb35fc87a29edb341b4e797d705c5b8c63fa5836b595"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-analysis/detect-type.md",
|
||||
"sha256": "16312c08f586363e2bb5b683c5aab7c7280c552e958fb678dd13d798e55c9563"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-analysis/skill.md",
|
||||
"sha256": "3e4361cc3d54b622c0220c845b85268f60c068cbf43ba786e99500cc13ce0ffb"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-analysis/file-stats.md",
|
||||
"sha256": "83f3f1809d51869d1351a0da672dc661bc6d42762dad8673bb9aad716a69f9cc"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-analysis/identify-scope.md",
|
||||
"sha256": "879f1aafc9411f13e5b80ed38a3eb07d7d9b2e0a6874c259f9f5674f15d6b747"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-analysis/.scripts/atomicity-checker.py",
|
||||
"sha256": "2268709a0501e319250731020df4fcc480695d6a0db6c0f63313269797700af6"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-analysis/.scripts/scope-identifier.sh",
|
||||
"sha256": "91ab0d9f315e49d3c0883a33422e63ab58750f54957c575eff5344182f87cf5c"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-analysis/.scripts/type-detector.py",
|
||||
"sha256": "507c89e50a86ee27367e0d38c8eaab38d4a4b0778500dc74d1dfc39cf96fac67"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-analysis/.scripts/git-diff-analyzer.sh",
|
||||
"sha256": "6e816c56c6ad74f6ed1f749e8cfebe52b1a43c8a6a5d50f314b9f8c9c5f02335"
|
||||
},
|
||||
{
|
||||
"path": "commands/history-analysis/suggest-conventions.md",
|
||||
"sha256": "369e8761d0c589cf4de1b2df5448671bc05c6a2bc9fee5f2ac81c2c071afa3cc"
|
||||
},
|
||||
{
|
||||
"path": "commands/history-analysis/analyze-style.md",
|
||||
"sha256": "c033ca1b0ff8e208508e731ff9e5a6cce523eaa88dedc47418366a626f2b7bdf"
|
||||
},
|
||||
{
|
||||
"path": "commands/history-analysis/extract-scopes.md",
|
||||
"sha256": "7cbfdc8321df8cbb9ab8b4e5880f01499524277709a2a83e0aaa52800c000adb"
|
||||
},
|
||||
{
|
||||
"path": "commands/history-analysis/learn-project.md",
|
||||
"sha256": "dd7b47f4a627b99deee41fa01319ce2b8999bf1537231f73eeb51b8a785c9304"
|
||||
},
|
||||
{
|
||||
"path": "commands/history-analysis/skill.md",
|
||||
"sha256": "98ab129c45380bc5de2c52032ded95a569893d3ed2c873af56d59fc354638172"
|
||||
},
|
||||
{
|
||||
"path": "commands/history-analysis/detect-patterns.md",
|
||||
"sha256": "1a87882506988c1207b08b563c2b22612e6a80690db3164ba7ff1638a6f2496e"
|
||||
},
|
||||
{
|
||||
"path": "commands/history-analysis/.scripts/pattern-detector.py",
|
||||
"sha256": "4c46177f0d29fc74da7c4daad0d8e82cf922cb7be44afe274db8a2f5a9456ec2"
|
||||
},
|
||||
{
|
||||
"path": "commands/history-analysis/.scripts/convention-recommender.py",
|
||||
"sha256": "b3bc11a2e618700e71637e80856cd3ac4059e9f5ea92f0daf5de8f0242300393"
|
||||
},
|
||||
{
|
||||
"path": "commands/history-analysis/.scripts/scope-extractor.sh",
|
||||
"sha256": "6804a26cdb466a524b7ddef5db0b8b8f2b12b15b0b7a541d41903c73aa0918c2"
|
||||
},
|
||||
{
|
||||
"path": "commands/history-analysis/.scripts/style-analyzer.sh",
|
||||
"sha256": "0af31c782f2c2ee1a27396f6dcde5851703ed25746f874db86d91fa6bd41ad90"
|
||||
},
|
||||
{
|
||||
"path": "commands/atomic-commit/interactive-split.md",
|
||||
"sha256": "e0493411ec7b7bacbff9d594f995c336bccf5959e883a1c646ffc410e61f65d0"
|
||||
},
|
||||
{
|
||||
"path": "commands/atomic-commit/suggest-commits.md",
|
||||
"sha256": "c6f94b9722c52fcd55739d7335eee3a746d6d52012489aed787c987da351e11c"
|
||||
},
|
||||
{
|
||||
"path": "commands/atomic-commit/create-sequence.md",
|
||||
"sha256": "daa30d7a10690c202fca5b4c412344eef2bdd0a217fcee85baccb5ca6502829f"
|
||||
},
|
||||
{
|
||||
"path": "commands/atomic-commit/analyze-splitting.md",
|
||||
"sha256": "e86b60fc527abdad47ec759819c204c7c10fb6a60b831aff2c7d017951070c58"
|
||||
},
|
||||
{
|
||||
"path": "commands/atomic-commit/group-files.md",
|
||||
"sha256": "bb4476d635e02a98e9175e24312ac5b8ecdc7751adb732418558a81a63e186cc"
|
||||
},
|
||||
{
|
||||
"path": "commands/atomic-commit/skill.md",
|
||||
"sha256": "693e9c077308b2be7446006a593316aed02c31fd475ffdb1486c5df47c7bed6b"
|
||||
},
|
||||
{
|
||||
"path": "commands/atomic-commit/.scripts/file-grouper.sh",
|
||||
"sha256": "269f2e48e173f5e768c1633db98ad10ea1b4e67b9fdfd610f3e671f2cdc39835"
|
||||
},
|
||||
{
|
||||
"path": "commands/atomic-commit/.scripts/split-analyzer.py",
|
||||
"sha256": "a707d5cd5a7f635fe4caecdc9f7f7a46ecd2980b6b29d474a713d2eb1f90dfc2"
|
||||
},
|
||||
{
|
||||
"path": "commands/atomic-commit/.scripts/commit-planner.py",
|
||||
"sha256": "54a913c09faebc4927c24a1fb804673a0215dc576d7642ba5ab1224cfc3c8543"
|
||||
},
|
||||
{
|
||||
"path": "commands/atomic-commit/.scripts/dependency-checker.sh",
|
||||
"sha256": "3ac4d42fc83730e94ad92d2b053ababdaea0d2434e80dabee496ee49949c5f7e"
|
||||
},
|
||||
{
|
||||
"path": "commands/message-generation/complete-message.md",
|
||||
"sha256": "ee317b2242aeb05579ead6965ceac44fff31b8a5d6541402dc2f35bb2665c14e"
|
||||
},
|
||||
{
|
||||
"path": "commands/message-generation/generate-subject.md",
|
||||
"sha256": "afb0a361396bbfc3a0822b5edb023af62a9b12ed8b9284215d659464580d5e53"
|
||||
},
|
||||
{
|
||||
"path": "commands/message-generation/validate-message.md",
|
||||
"sha256": "4f3b22c77eaf3f42dcfece1ab1354fb491ac8c3046540beca2eb7ce4917c5935"
|
||||
},
|
||||
{
|
||||
"path": "commands/message-generation/write-body.md",
|
||||
"sha256": "da78ee8340d29dc69ed53ce46631e855f894e660b8ef217f8ae266228cce0628"
|
||||
},
|
||||
{
|
||||
"path": "commands/message-generation/skill.md",
|
||||
"sha256": "667e87d1e3d0d8a5e20579bbd2bec5730619c6268d338ebf857d670e05a08b79"
|
||||
},
|
||||
{
|
||||
"path": "commands/message-generation/add-footer.md",
|
||||
"sha256": "20e2f8bdb6d96c7ff8cfd52304f996661c488f0ecedc5bcca37a613f5a9a3911"
|
||||
},
|
||||
{
|
||||
"path": "commands/message-generation/.scripts/message-validator.sh",
|
||||
"sha256": "f07dfd9e1cae3f657094192023302db0e8f6c41d2c266cd4fb6efae2207a4fb2"
|
||||
},
|
||||
{
|
||||
"path": "commands/message-generation/.scripts/body-composer.sh",
|
||||
"sha256": "0f87689993c879b531279caa52eae672a8201c5fe373b3fa5f4bbf327ed57d49"
|
||||
},
|
||||
{
|
||||
"path": "commands/message-generation/.scripts/subject-generator.py",
|
||||
"sha256": "1b1802b3caef13dffd0826784d2e38450190ca417ed767c0008a6d8d85d3c869"
|
||||
},
|
||||
{
|
||||
"path": "commands/message-generation/.scripts/footer-builder.py",
|
||||
"sha256": "6f93db25e3392f0088059988ba23483b9417106e8628a673f83a9ffc1c1df0c5"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-error-handling/diagnose-issues.md",
|
||||
"sha256": "5858b2f0e93a0855846c001d1978de28b439e2b0f959607b2d5b6f72ccf61e43"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-error-handling/handle-conflicts.md",
|
||||
"sha256": "04c89d7d1428b9946eeac4f17c98fccf0d97a5aacb57b4dc48faa019d56657ed"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-error-handling/handle-no-changes.md",
|
||||
"sha256": "4b9e3d8ae77e437cd6cc169c868e07929f6a8e006f2f2ffc6b7ba091059e98dc"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-error-handling/handle-no-repo.md",
|
||||
"sha256": "e771b4a2244057a735a5bd09f66dd08225bf6cff10d5930a447f15be2a9b3c17"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-error-handling/skill.md",
|
||||
"sha256": "0ddc5158801479da0efaf386173411ae7025e74e3768212e406efdc8e1c7a0b0"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-error-handling/handle-detached-head.md",
|
||||
"sha256": "bb1d931f7f4ec05f550a5798560930110f24a4792c7211a3b712b76d814a5b3c"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-error-handling/.scripts/conflict-detector.py",
|
||||
"sha256": "d2ebfe35e3af0392269a0559b6e7d1dfdda3db2886fa5e79cd6eaa21eedfe587"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-error-handling/.scripts/state-analyzer.sh",
|
||||
"sha256": "7bae0b2300fc1352346207a3074a0b46206038b953c5df2c2bd3ee9a83dc38e3"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-error-handling/.scripts/repo-checker.sh",
|
||||
"sha256": "72ee07cd0022355127c243216f898a21a69a2acf04b4b1d46d507a5c35313503"
|
||||
},
|
||||
{
|
||||
"path": "commands/commit-error-handling/.scripts/changes-detector.sh",
|
||||
"sha256": "36ab4d315c5018b3038740a3377122cc508dd77082d688dc5b6fafd809e58984"
|
||||
}
|
||||
],
|
||||
"dirSha256": "67c1ef02e222080168240747830e5d0aab183f691dc0240f5f5593d7737f6053"
|
||||
},
|
||||
"security": {
|
||||
"scannedAt": null,
|
||||
"scannerVersion": null,
|
||||
"flags": []
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user