Commit Message

Validate or generate conventional commit messages.

1# ---------------------------------------------------------------------------
2# Create Agent
3# ---------------------------------------------------------------------------
4#!/usr/bin/env python3
5"""
6Commit Message
7=============================
8
9Validate or generate conventional commit messages.
10"""
11
12import json
13import sys
14
15COMMIT_TYPES = {
16 "feat": "A new feature",
17 "fix": "A bug fix",
18 "docs": "Documentation changes",
19 "style": "Formatting, no code change",
20 "refactor": "Code restructuring",
21 "perf": "Performance improvement",
22 "test": "Adding/updating tests",
23 "chore": "Maintenance tasks",
24 "build": "Build system changes",
25 "ci": "CI/CD changes",
26}
27
28
29def validate(message: str) -> dict:
30 """Validate a commit message."""
31 errors = []
32 warnings = []
33
34 lines = message.strip().split("\n")
35 if not lines or not lines[0]:
36 return {"valid": False, "errors": ["Empty commit message"]}
37
38 subject = lines[0]
39
40 # Check format: type: description
41 if ":" not in subject:
42 errors.append("Missing ':' separator (expected 'type: description')")
43 else:
44 type_part, desc = subject.split(":", 1)
45 type_part = type_part.strip().rstrip("!").split("(")[0]
46 desc = desc.strip()
47
48 if type_part not in COMMIT_TYPES:
49 errors.append(
50 f"Unknown type '{type_part}'. Valid: {', '.join(COMMIT_TYPES.keys())}"
51 )
52
53 if not desc:
54 errors.append("Description required after ':'")
55
56 if len(subject) > 72:
57 warnings.append(f"Subject is {len(subject)} chars (recommended: ≤72)")
58
59 return {"valid": len(errors) == 0, "errors": errors, "warnings": warnings}
60
61
62def generate(commit_type: str, description: str, scope: str = None) -> dict:
63 """Generate a commit message."""
64 if commit_type not in COMMIT_TYPES:
65 return {
66 "error": f"Unknown type '{commit_type}'. Valid: {', '.join(COMMIT_TYPES.keys())}"
67 }
68
69 if scope:
70 message = f"{commit_type}({scope}): {description}"
71 else:
72 message = f"{commit_type}: {description}"
73
74 return {"message": message, "type": commit_type, "description": description}
75
76
77def list_types() -> dict:
78 """List all valid commit types."""
79 return {"types": COMMIT_TYPES}
80
81
82# ---------------------------------------------------------------------------
83# Run Agent
84# ---------------------------------------------------------------------------
85if __name__ == "__main__":
86 try:
87 if len(sys.argv) < 2:
88 print(
89 json.dumps(
90 {
91 "error": "Usage: commit_message.py <validate|generate|types> [args]"
92 }
93 )
94 )
95 sys.exit(1)
96
97 command = sys.argv[1]
98
99 if command == "validate":
100 msg = sys.argv[2] if len(sys.argv) > 2 else sys.stdin.read()
101 result = validate(msg)
102 elif command == "generate":
103 if len(sys.argv) < 4:
104 result = {
105 "error": "Usage: commit_message.py generate <type> <description> [scope]"
106 }
107 else:
108 commit_type = sys.argv[2]
109 description = sys.argv[3]
110 scope = sys.argv[4] if len(sys.argv) > 4 else None
111 result = generate(commit_type, description, scope)
112 elif command == "types":
113 result = list_types()
114 else:
115 result = {
116 "error": f"Unknown command '{command}'. Use: validate, generate, types"
117 }
118
119 print(json.dumps(result, indent=2))
120 except Exception as e:
121 print(json.dumps({"error": str(e)}))

Run the Example

1# Clone and setup repo
2git clone https://github.com/kern-ai/kern.git
3cd kern/cookbook/02_agents/16_skills/sample_skills/git-workflow/scripts
4
5# Create and activate virtual environment
6./scripts/demo_setup.sh
7source .venvs/demo/bin/activate
8
9python commit_message.py