commiti 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +70 -8
- data/lib/commiti.rb +3 -0
- data/lib/flows/base_flow.rb +7 -2
- data/lib/flows/commit_flow.rb +6 -4
- data/lib/flows/pr_flow.rb +39 -11
- data/lib/services/diff_summarization/fallback_builder.rb +69 -30
- data/lib/services/flow_context_builder.rb +3 -2
- data/lib/services/git/commit/commit_execution.rb +47 -28
- data/lib/services/git/commit/commit_staging.rb +3 -3
- data/lib/services/git/git_reader.rb +29 -19
- data/lib/services/git/pr/browser_opener.rb +44 -0
- data/lib/services/git/pr/pr_creator.rb +167 -0
- data/lib/services/git/pr/pr_opener.rb +96 -113
- data/lib/services/git/pr/remote_parser.rb +73 -0
- data/lib/services/google_client.rb +6 -13
- data/lib/services/helpers/clipboard.rb +4 -8
- data/lib/services/helpers/config_loader.rb +42 -5
- data/lib/services/helpers/interactive_prompt.rb +5 -5
- data/lib/services/helpers/prompt_builder.rb +44 -3
- data/lib/services/helpers/spinner.rb +19 -11
- data/lib/services/helpers/terminal_ui.rb +58 -0
- data/lib/services/message_generator.rb +43 -132
- data/lib/services/message_generator_support.rb +111 -0
- data/lib/services/message_presenter.rb +10 -8
- data/lib/services/text_generation_style.rb +172 -0
- metadata +10 -4
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Commiti
|
|
4
|
+
module TextGenerationStyle
|
|
5
|
+
DEFAULT_PR_SECTIONS = [
|
|
6
|
+
{ name: 'Summary', guidance: 'Summarize the change in one concise paragraph.' },
|
|
7
|
+
{ name: 'Motivation', guidance: 'Explain why this change is needed.' },
|
|
8
|
+
{ name: 'Changes Made', guidance: 'List the concrete changes made in the diff.' },
|
|
9
|
+
{ name: 'Testing Notes', guidance: 'Describe the tests, checks, or verification performed.' }
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
12
|
+
DEFAULT_CONFIG = {
|
|
13
|
+
commit: {
|
|
14
|
+
subject_case: 'preserve'
|
|
15
|
+
},
|
|
16
|
+
pr: {
|
|
17
|
+
sections: DEFAULT_PR_SECTIONS
|
|
18
|
+
}
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
ALLOWED_COMMIT_SUBJECT_CASES = %w[preserve uppercase lowercase].freeze
|
|
22
|
+
|
|
23
|
+
def self.normalize(raw_config)
|
|
24
|
+
raw_hash = raw_config.is_a?(Hash) ? raw_config : {}
|
|
25
|
+
style_hash = lookup(raw_hash, :text_generation) || lookup(raw_hash, :generation) || raw_hash
|
|
26
|
+
|
|
27
|
+
{
|
|
28
|
+
commit: {
|
|
29
|
+
subject_case: normalize_subject_case(lookup(lookup(style_hash, :commit), :subject_case))
|
|
30
|
+
},
|
|
31
|
+
pr: {
|
|
32
|
+
sections: normalize_pr_sections(lookup(lookup(style_hash, :pr), :sections))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.commit_subject_case(style_config)
|
|
38
|
+
lookup(lookup(style_config, :commit), :subject_case) || DEFAULT_CONFIG[:commit][:subject_case]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.commit_subject_case_instruction(style_config)
|
|
42
|
+
case commit_subject_case(style_config)
|
|
43
|
+
when 'uppercase'
|
|
44
|
+
'Capitalize the first alphabetic character in the subject line.'
|
|
45
|
+
when 'lowercase'
|
|
46
|
+
'Keep the first alphabetic character in the subject line lowercase.'
|
|
47
|
+
else
|
|
48
|
+
'Preserve the natural casing chosen by the content of the change.'
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.apply_commit_subject_case(subject, style_config)
|
|
53
|
+
normalized_subject = subject.to_s
|
|
54
|
+
|
|
55
|
+
case commit_subject_case(style_config)
|
|
56
|
+
when 'uppercase'
|
|
57
|
+
normalized_subject.sub(/\A([[:alpha:]])/) { Regexp.last_match(1).upcase }
|
|
58
|
+
when 'lowercase'
|
|
59
|
+
normalized_subject.sub(/\A([[:alpha:]])/) { Regexp.last_match(1).downcase }
|
|
60
|
+
else
|
|
61
|
+
normalized_subject
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.pr_sections(style_config)
|
|
66
|
+
sections = lookup(lookup(style_config, :pr), :sections)
|
|
67
|
+
sections = DEFAULT_PR_SECTIONS if sections.nil? || sections.empty?
|
|
68
|
+
sections
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.pr_section_headers(style_config)
|
|
72
|
+
pr_sections(style_config).map { |section| "## #{section[:name]}" }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.pr_section_prompt_lines(style_config)
|
|
76
|
+
pr_sections(style_config).each_with_index.map do |section, index|
|
|
77
|
+
guidance = section[:guidance] || default_pr_section_guidance(index, section[:name])
|
|
78
|
+
" - ## #{section[:name]}\n #{guidance}"
|
|
79
|
+
end.join("\n")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.first_pr_section_header(style_config)
|
|
83
|
+
pr_section_headers(style_config).first
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.normalize_subject_case(value)
|
|
87
|
+
normalized = string_value(value)
|
|
88
|
+
ALLOWED_COMMIT_SUBJECT_CASES.include?(normalized) ? normalized : DEFAULT_CONFIG[:commit][:subject_case]
|
|
89
|
+
end
|
|
90
|
+
private_class_method :normalize_subject_case
|
|
91
|
+
|
|
92
|
+
def self.normalize_pr_sections(value)
|
|
93
|
+
sections = Array(value).filter_map { |section| normalize_section(section) }
|
|
94
|
+
sections.empty? ? DEFAULT_PR_SECTIONS : sections
|
|
95
|
+
end
|
|
96
|
+
private_class_method :normalize_pr_sections
|
|
97
|
+
|
|
98
|
+
def self.normalize_section(section)
|
|
99
|
+
case section
|
|
100
|
+
when String
|
|
101
|
+
name = string_value(section)
|
|
102
|
+
return nil if name.empty?
|
|
103
|
+
|
|
104
|
+
{ name: name, guidance: nil }
|
|
105
|
+
when Hash
|
|
106
|
+
# Strict schema: only accept `name` and `guidance` keys
|
|
107
|
+
allowed_keys = %w[name guidance]
|
|
108
|
+
provided_keys = section.keys.map(&:to_s)
|
|
109
|
+
return nil unless (provided_keys - allowed_keys).empty?
|
|
110
|
+
|
|
111
|
+
name = string_value(lookup(section, :name))
|
|
112
|
+
return nil if name.empty?
|
|
113
|
+
|
|
114
|
+
guidance_raw = lookup(section, :guidance)
|
|
115
|
+
guidance = sanitize_guidance(guidance_raw)
|
|
116
|
+
{ name: name, guidance: guidance.empty? ? nil : guidance }
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
private_class_method :normalize_section
|
|
120
|
+
|
|
121
|
+
def self.sanitize_guidance(raw)
|
|
122
|
+
return '' if raw.nil?
|
|
123
|
+
|
|
124
|
+
text = raw.to_s.dup
|
|
125
|
+
# Remove code fences and backtick blocks
|
|
126
|
+
text.gsub!(/```.*?```/m, ' ')
|
|
127
|
+
text.gsub!(/`.*?`/, ' ')
|
|
128
|
+
# Remove HTML tags
|
|
129
|
+
text.gsub!(/<[^>]+>/, ' ')
|
|
130
|
+
# Neutralize suspicious phrases that look like instructions
|
|
131
|
+
suspicious = Regexp.union(
|
|
132
|
+
/ignore previous/i,
|
|
133
|
+
/disregard previous/i,
|
|
134
|
+
/do not follow/i,
|
|
135
|
+
/only follow/i,
|
|
136
|
+
/override previous/i,
|
|
137
|
+
/ignore all previous instructions/i,
|
|
138
|
+
/follow only/i
|
|
139
|
+
)
|
|
140
|
+
text.gsub!(suspicious, '[redacted]')
|
|
141
|
+
# Collapse whitespace and truncate
|
|
142
|
+
text = text.strip.gsub(/ +/, ' ').gsub(/\s+/, ' ')
|
|
143
|
+
max = 300
|
|
144
|
+
text[0, max]
|
|
145
|
+
end
|
|
146
|
+
private_class_method :sanitize_guidance
|
|
147
|
+
|
|
148
|
+
def self.default_pr_section_guidance(index, name)
|
|
149
|
+
defaults = [
|
|
150
|
+
'Summarize the overall change and the problem it solves.',
|
|
151
|
+
'Explain the motivation or context behind the change.',
|
|
152
|
+
'List the concrete code, docs, or workflow updates.',
|
|
153
|
+
'Describe the verification performed or why it was not needed.'
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
defaults[index] || "Describe the #{name} aspect of the change using concrete details from the diff."
|
|
157
|
+
end
|
|
158
|
+
private_class_method :default_pr_section_guidance
|
|
159
|
+
|
|
160
|
+
def self.lookup(hash, key)
|
|
161
|
+
return nil unless hash.is_a?(Hash)
|
|
162
|
+
|
|
163
|
+
hash[key] || hash[key.to_s] || hash[key.to_sym]
|
|
164
|
+
end
|
|
165
|
+
private_class_method :lookup
|
|
166
|
+
|
|
167
|
+
def self.string_value(value)
|
|
168
|
+
value.to_s.strip
|
|
169
|
+
end
|
|
170
|
+
private_class_method :string_value
|
|
171
|
+
end
|
|
172
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: commiti
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Setoju
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: dotenv
|
|
@@ -38,8 +38,8 @@ dependencies:
|
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0.21'
|
|
41
|
-
description: Generates git commit messages and PR descriptions using Google AI text
|
|
42
|
-
generation models.
|
|
41
|
+
description: 'Generates git commit messages and PR descriptions using Google AI text
|
|
42
|
+
generation models. '
|
|
43
43
|
email:
|
|
44
44
|
- setoju48@gmail.com
|
|
45
45
|
executables:
|
|
@@ -64,15 +64,21 @@ files:
|
|
|
64
64
|
- lib/services/git/diff_parser.rb
|
|
65
65
|
- lib/services/git/git_reader.rb
|
|
66
66
|
- lib/services/git/git_writer.rb
|
|
67
|
+
- lib/services/git/pr/browser_opener.rb
|
|
68
|
+
- lib/services/git/pr/pr_creator.rb
|
|
67
69
|
- lib/services/git/pr/pr_opener.rb
|
|
70
|
+
- lib/services/git/pr/remote_parser.rb
|
|
68
71
|
- lib/services/google_client.rb
|
|
69
72
|
- lib/services/helpers/clipboard.rb
|
|
70
73
|
- lib/services/helpers/config_loader.rb
|
|
71
74
|
- lib/services/helpers/interactive_prompt.rb
|
|
72
75
|
- lib/services/helpers/prompt_builder.rb
|
|
73
76
|
- lib/services/helpers/spinner.rb
|
|
77
|
+
- lib/services/helpers/terminal_ui.rb
|
|
74
78
|
- lib/services/message_generator.rb
|
|
79
|
+
- lib/services/message_generator_support.rb
|
|
75
80
|
- lib/services/message_presenter.rb
|
|
81
|
+
- lib/services/text_generation_style.rb
|
|
76
82
|
homepage: https://github.com/setoju/commiti
|
|
77
83
|
licenses:
|
|
78
84
|
- MIT
|