17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
# File 'lib/ai_git/ollama.rb', line 17
def generate_commit_message(diff)
raise 'No staged changes to generate commit message for' if diff.to_s.strip.empty?
prompt = <<~PROMPT
You are an expert Git commit message writer. Your only job is to output a commit message — nothing else.
Here are the changes:
#{diff}
Rules you MUST follow exactly:
- Output ONLY the commit message. No explanations, no quotes, no markdown, no "Here is the commit message", no backticks.
- Use this exact structure:
1. First line: Conventional commit type + short imperative title (max 72 chars, ideally < 50)
2. Second line: blank
3. Body (optional but recommended): Clear, concise explanation of WHAT changed and WHY.
Examples of good output:
feat: add user authentication flow
Implemented JWT-based login with refresh tokens. Added protected routes middleware.
fix: prevent null pointer on missing metadata
Added nil check in ReportGenerator#process before accessing user preferences.
Now generate the commit message:
PROMPT
json_body = {
model: 'ministral-3:8b',
prompt: prompt,
stream: false,
temperature: 0.3,
top_p: 0.9,
stop: ["\n\n\n", "```", "Here is", "The commit message"],
num_predict: 400 }.to_json
uri = URI('http://localhost:11434/api/generate')
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request.body = json_body
response = Net::HTTP.start(uri.host, uri.port, read_timeout: 120) do |http|
http.request(request)
end
raise 'Failed to connect to Ollama. Is it running?' unless response.is_a?(Net::HTTPSuccess)
data = JSON.parse(response.body)
message = data['response'].to_s.strip
message = message.gsub(/^(Here is|The commit message is|```|json|markdown)/i, '')
.gsub(/^>\s*/, '')
.gsub(/\\n/, "\n")
.strip
lines = message.lines.map(&:strip)
lines.reject! { |line| line.match?(/^(Here|Output|Generated|Based on|The changes)/i) }
message = lines.join("\n").strip
if message.lines.count < 1 || message.strip.empty?
message = "chore: update code" end
message
end
|