1use super::{
2 change_analyzer::AnalyzedChange,
3 models::{ChangeMetrics, ChangelogResponse, ReleaseNotesResponse},
4};
5use crate::common::{get_combined_instructions, DetailLevel};
6use crate::config::Config;
7use crate::gitmoji::get_gitmoji_list;
8
9#[allow(clippy::unwrap_used)] pub fn create_changelog_system_prompt(config: &Config) -> String {
11 let changelog_schema = schemars::schema_for!(ChangelogResponse);
12 let changelog_schema_str = serde_json::to_string_pretty(&changelog_schema).unwrap();
13
14 let mut prompt = String::from(
15 "You are an AI assistant specialized in generating clear, concise, and informative changelogs for software projects. \
16 Your task is to create a well-structured changelog based on the provided commit information and analysis. \
17 The changelog should adhere to the Keep a Changelog 1.1.0 format (https://keepachangelog.com/en/1.1.0/).
18
19 Work step-by-step and follow these guidelines exactly:
20
21 1. Categorize changes into the following types: Added, Changed, Deprecated, Removed, Fixed, Security.
22 2. Use present tense and imperative mood in change descriptions.
23 3. Start each change entry with a capital letter and do not end with a period.
24 4. Be concise but descriptive in change entries and ensure good grammar, capitalization, and punctuation.
25 5. Include *short* commit hashes at the end of each entry.
26 6. Focus on the impact and significance of the changes and omit trivial changes below the relevance threshold.
27 7. Find commonalities and group related changes together under the appropriate category.
28 8. List the most impactful changes first within each category.
29 9. Mention associated issue numbers and pull request numbers when available.
30 10. Clearly identify and explain any breaking changes.
31 11. Avoid common cliché words (like 'enhance', 'streamline', 'leverage', etc) and phrases.
32 12. Do not speculate about the purpose of a change or add any information not directly supported by the context.
33 13. Mention any changes to dependencies or build configurations under the appropriate category.
34 14. Highlight changes that affect multiple parts of the codebase or have cross-cutting concerns.
35 15. NO YAPPING!
36
37 Your response must be a valid JSON object with the following structure:
38
39 {
40 \"version\": \"string or null\",
41 \"release_date\": \"string or null\",
42 \"sections\": {
43 \"Added\": [{ \"description\": \"string\", \"commit_hashes\": [\"string\"], \"associated_issues\": [\"string\"], \"pull_request\": \"string or null\" }],
44 \"Changed\": [...],
45 \"Deprecated\": [...],
46 \"Removed\": [...],
47 \"Fixed\": [...],
48 \"Security\": [...]
49 },
50 \"breaking_changes\": [{ \"description\": \"string\", \"commit_hash\": \"string\" }],
51 \"metrics\": {
52 \"total_commits\": number,
53 \"files_changed\": number,
54 \"insertions\": number,
55 \"deletions\": number
56 }
57 }
58
59 Follow these steps to generate the changelog:
60
61 1. Analyze the provided commit information and group changes by type (Added, Changed, etc.).
62 2. For each change type, create an array of change entries with description, commit hashes, associated issues, and pull request (if available).
63 3. Identify any breaking changes and add them to the breaking_changes array.
64 4. Calculate the metrics based on the overall changes.
65 5. If provided, include the version and release date.
66 6. Construct the final JSON object ensuring all required fields are present.
67
68 Here's a minimal example of the expected output format:
69
70 {
71 \"version\": \"1.0.0\",
72 \"release_date\": \"2023-08-15\",
73 \"sections\": {
74 \"Added\": [
75 {
76 \"description\": \"add new feature X\",
77 \"commit_hashes\": [\"abc123\"],
78 }
79 ],
80 \"Changed\": [],
81 \"Deprecated\": [],
82 \"Removed\": [],
83 \"Fixed\": [],
84 \"Security\": []
85 },
86 \"breaking_changes\": [],
87 \"metrics\": {
88 \"total_commits\": 1,
89 \"files_changed\": 3,
90 \"insertions\": 100,
91 \"deletions\": 50
92 }
93 }
94
95 Ensure that your response is a valid JSON object matching this structure. Include all required fields, even if they are empty arrays or null values.
96 "
97 );
98
99 prompt.push_str(&changelog_schema_str);
100
101 prompt.push_str(get_combined_instructions(config).as_str());
102
103 if config.use_gitmoji {
104 prompt.push_str(
105 "\n\nWhen generating the changelog, include tasteful, appropriate, and intelligent use of emojis to add visual interest.\n \
106 Here are some examples of emojis you can use:\n");
107 prompt.push_str(&get_gitmoji_list());
108 }
109
110 prompt.push_str(
111 "\n\nYou will be provided with detailed information about each change, including file-level analysis, impact scores, and classifications. \
112 Use this information to create a comprehensive and insightful changelog. \
113 Adjust the level of detail based on the specified detail level (Minimal, Standard, or Detailed)."
114 );
115
116 prompt
117}
118
119#[allow(clippy::unwrap_used)] pub fn create_release_notes_system_prompt(config: &Config) -> String {
121 let release_notes_schema = schemars::schema_for!(ReleaseNotesResponse);
122 let release_notes_schema_str = serde_json::to_string_pretty(&release_notes_schema).unwrap();
123
124 let mut prompt = String::from(
125 "You are an AI assistant specialized in generating comprehensive and user-friendly release notes for software projects. \
126 Your task is to create detailed release notes based on the provided commit information and analysis. \
127 Aim for a tone that is professional, approachable, and authoritative, keeping in mind any additional user instructions.
128
129 Work step-by-step and follow these guidelines exactly:
130
131 1. Provide a high-level summary of the release, highlighting key features, improvements, and fixes.
132 2. Find commonalities and group changes into meaningful sections (e.g., 'New Features', 'Improvements', 'Bug Fixes', 'Breaking Changes').
133 3. Focus on the impact and benefits of the changes to users and developers.
134 4. Highlight any significant new features or major improvements.
135 5. Explain the rationale behind important changes when possible.
136 6. Note any breaking changes and provide clear upgrade instructions.
137 7. Mention any changes to dependencies or system requirements.
138 8. Include any relevant documentation updates or new resources for users.
139 9. Use clear, non-technical language where possible to make the notes accessible to a wide audience.
140 10. Provide context for technical changes when necessary.
141 11. Highlight any security updates or important bug fixes.
142 12. Include overall metrics to give context about the scope of the release.
143 13. Mention associated issue numbers and pull request numbers when relevant.
144 14. NO YAPPING!
145
146 Your response must be a valid JSON object with the following structure:
147
148 {
149 \"version\": \"string or null\",
150 \"release_date\": \"string or null\",
151 \"sections\": {
152 \"Added\": [{ \"description\": \"string\", \"commit_hashes\": [\"string\"], \"associated_issues\": [\"string\"], \"pull_request\": \"string or null\" }],
153 \"Changed\": [...],
154 \"Deprecated\": [...],
155 \"Removed\": [...],
156 \"Fixed\": [...],
157 \"Security\": [...]
158 },
159 \"breaking_changes\": [{ \"description\": \"string\", \"commit_hash\": \"string\" }],
160 \"metrics\": {
161 \"total_commits\": number,
162 \"files_changed\": number,
163 \"insertions\": number,
164 \"deletions\": number
165 \"total_lines_changed\": number
166 }
167 }
168
169 Follow these steps to generate the changelog:
170
171 1. Analyze the provided commit information and group changes by type (Added, Changed, etc.).
172 2. For each change type, create an array of change entries with description, commit hashes, associated issues, and pull request (if available).
173 3. Identify any breaking changes and add them to the breaking_changes array.
174 4. Calculate the metrics based on the overall changes.
175 5. If provided, include the version and release date.
176 6. Construct the final JSON object ensuring all required fields are present.
177
178 Here's a minimal example of the expected output format:
179
180 {
181 \"version\": \"1.0.0\",
182 \"release_date\": \"2023-08-15\",
183 \"sections\": {
184 \"Added\": [
185 {
186 \"description\": \"add new feature X\",
187 \"commit_hashes\": [\"abc123\"],
188 \"associated_issues\": [\"#42\"],
189 \"pull_request\": \"PR #100\"
190 }
191 ],
192 \"Changed\": [],
193 \"Deprecated\": [],
194 \"Removed\": [],
195 \"Fixed\": [],
196 \"Security\": []
197 },
198 \"breaking_changes\": [],
199 \"metrics\": {
200 \"total_commits\": 1,
201 \"files_changed\": 3,
202 \"insertions\": 100,
203 \"deletions\": 50
204 \"total_lines_changed\": 150
205 }
206 }
207
208 Ensure that your response is a valid JSON object matching this structure. Include all required fields, even if they are empty arrays or null values.
209 "
210 );
211
212 prompt.push_str(&release_notes_schema_str);
213
214 prompt.push_str(get_combined_instructions(config).as_str());
215
216 if config.use_gitmoji {
217 prompt.push_str(
218 "\n\nWhen generating the release notes, include tasteful, appropriate, and intelligent use of emojis to add visual interest.\n \
219 Here are some examples of emojis you can use:\n");
220 prompt.push_str(&get_gitmoji_list());
221 }
222
223 prompt
224}
225
226pub fn create_changelog_user_prompt(
227 changes: &[AnalyzedChange],
228 total_metrics: &ChangeMetrics,
229 detail_level: DetailLevel,
230 from: &str,
231 to: &str,
232 readme_summary: Option<&str>,
233) -> String {
234 let mut prompt =
235 format!("Based on the following changes from {from} to {to}, generate a changelog:\n\n");
236
237 prompt.push_str("Overall Changes:\n");
238 prompt.push_str(&format!("Total commits: {}\n", total_metrics.total_commits));
239 prompt.push_str(&format!("Files changed: {}\n", total_metrics.files_changed));
240 prompt.push_str(&format!(
241 "Total lines changed: {}\n",
242 total_metrics.total_lines_changed
243 ));
244 prompt.push_str(&format!("Insertions: {}\n", total_metrics.insertions));
245 prompt.push_str(&format!("Deletions: {}\n\n", total_metrics.deletions));
246
247 for change in changes {
248 prompt.push_str(&format!("Commit: {}\n", change.commit_hash));
249 prompt.push_str(&format!("Author: {}\n", change.author));
250 prompt.push_str(&format!("Message: {}\n", change.commit_message));
251 prompt.push_str(&format!("Type: {:?}\n", change.change_type));
252 prompt.push_str(&format!("Breaking Change: {}\n", change.is_breaking_change));
253 prompt.push_str(&format!(
254 "Associated Issues: {}\n",
255 change.associated_issues.join(", ")
256 ));
257 if let Some(pr) = &change.pull_request {
258 prompt.push_str(&format!("Pull Request: {pr}\n"));
259 }
260 prompt.push_str(&format!(
261 "Files changed: {}\n",
262 change.metrics.files_changed
263 ));
264 prompt.push_str(&format!(
265 "Lines changed: {}\n",
266 change.metrics.total_lines_changed
267 ));
268 prompt.push_str(&format!("Insertions: {}\n", change.metrics.insertions));
269 prompt.push_str(&format!("Deletions: {}\n", change.metrics.deletions));
270 prompt.push_str(&format!("Impact score: {:.2}\n", change.impact_score));
271
272 match detail_level {
273 DetailLevel::Minimal => {
274 }
276 DetailLevel::Standard | DetailLevel::Detailed => {
277 prompt.push_str("File changes:\n");
278 for file_change in &change.file_changes {
279 prompt.push_str(&format!(
280 " - {} ({:?})\n",
281 file_change.new_path, file_change.change_type
282 ));
283 if detail_level == DetailLevel::Detailed {
284 for analysis in &file_change.analysis {
285 prompt.push_str(&format!(" * {analysis}\n"));
286 }
287 }
288 }
289 }
290 }
291
292 prompt.push('\n');
293 }
294
295 if let Some(summary) = readme_summary {
296 prompt.push_str("\nProject README Summary:\n");
297 prompt.push_str(summary);
298 prompt.push_str("\n\n");
299 }
300
301 prompt.push_str(&format!("Please generate a {} changelog for the changes from {} to {}, adhering to the Keep a Changelog format. ",
302 match detail_level {
303 DetailLevel::Minimal => "concise",
304 DetailLevel::Standard => "comprehensive",
305 DetailLevel::Detailed => "highly detailed",
306 },
307 from,
308 to
309 ));
310
311 prompt.push_str("Categorize the changes appropriately and focus on the most significant updates and their impact on the project. ");
312 prompt.push_str("For each change, provide a clear description of what was changed, adhering to the guidelines in the system prompt. ");
313 prompt.push_str("Include the commit hashes, associated issues, and pull request numbers for each entry when available. ");
314 prompt.push_str("Clearly identify and explain any breaking changes. ");
315
316 if readme_summary.is_some() {
317 prompt.push_str("Use the README summary to provide context about the project and ensure the changelog reflects the project's goals and main features. ");
318 }
319
320 prompt
321}
322
323pub fn create_release_notes_user_prompt(
324 changes: &[AnalyzedChange],
325 total_metrics: &ChangeMetrics,
326 detail_level: DetailLevel,
327 from: &str,
328 to: &str,
329 readme_summary: Option<&str>,
330) -> String {
331 let mut prompt =
332 format!("Based on the following changes from {from} to {to}, generate release notes:\n\n");
333
334 prompt.push_str("Overall Changes:\n");
335 prompt.push_str(&format!("Total commits: {}\n", changes.len()));
336 prompt.push_str(&format!("Files changed: {}\n", total_metrics.files_changed));
337 prompt.push_str(&format!(
338 "Total lines changed: {}\n",
339 total_metrics.total_lines_changed
340 ));
341 prompt.push_str(&format!("Insertions: {}\n", total_metrics.insertions));
342 prompt.push_str(&format!("Deletions: {}\n\n", total_metrics.deletions));
343
344 for change in changes {
345 prompt.push_str(&format!("Commit: {}\n", change.commit_hash));
346 prompt.push_str(&format!("Author: {}\n", change.author));
347 prompt.push_str(&format!("Message: {}\n", change.commit_message));
348 prompt.push_str(&format!("Type: {:?}\n", change.change_type));
349 prompt.push_str(&format!("Breaking Change: {}\n", change.is_breaking_change));
350 prompt.push_str(&format!(
351 "Associated Issues: {}\n",
352 change.associated_issues.join(", ")
353 ));
354 if let Some(pr) = &change.pull_request {
355 prompt.push_str(&format!("Pull Request: {pr}\n"));
356 }
357 prompt.push_str(&format!("Impact score: {:.2}\n", change.impact_score));
358
359 match detail_level {
360 DetailLevel::Minimal => {
361 }
363 DetailLevel::Standard | DetailLevel::Detailed => {
364 prompt.push_str("File changes:\n");
365 for file_change in &change.file_changes {
366 prompt.push_str(&format!(
367 " - {} ({:?})\n",
368 file_change.new_path, file_change.change_type
369 ));
370 if detail_level == DetailLevel::Detailed {
371 for analysis in &file_change.analysis {
372 prompt.push_str(&format!(" * {analysis}\n"));
373 }
374 }
375 }
376 }
377 }
378
379 prompt.push('\n');
380 }
381
382 if let Some(summary) = readme_summary {
383 prompt.push_str("\nProject README Summary:\n");
384 prompt.push_str(summary);
385 prompt.push_str("\n\n");
386 }
387
388 prompt.push_str(&format!(
389 "Please generate {} release notes for the changes from {} to {}. ",
390 match detail_level {
391 DetailLevel::Minimal => "concise",
392 DetailLevel::Standard => "comprehensive",
393 DetailLevel::Detailed => "highly detailed",
394 },
395 from,
396 to
397 ));
398
399 prompt.push_str("Focus on the impact and benefits of the changes to users and developers. ");
400 prompt.push_str("Highlight key features, improvements, and fixes. ");
401 prompt.push_str("Include a high-level summary of the release, major changes, and any breaking changes or important upgrade notes. ");
402 prompt.push_str("Group changes into meaningful sections and explain the rationale behind important changes when possible. ");
403 prompt.push_str("Include associated issue numbers and pull request numbers when relevant. ");
404
405 match detail_level {
406 DetailLevel::Minimal => {
407 prompt.push_str(
408 "Keep the release notes brief and focused on the most significant changes. ",
409 );
410 }
411 DetailLevel::Standard => {
412 prompt.push_str("Provide a balanced overview of all important changes, with some details on major features or fixes. ");
413 }
414 DetailLevel::Detailed => {
415 prompt.push_str("Include detailed explanations of changes, their rationale, and potential impact on the project or workflow. ");
416 prompt.push_str("Provide context for technical changes and include file-level details where relevant. ");
417 }
418 }
419
420 if readme_summary.is_some() {
421 prompt.push_str("Ensure the release notes align with the project's overall goals and main features as described in the README summary. ");
422 }
423
424 prompt.push_str(
425 "Incorporate the overall metrics to give context about the scope of this release. ",
426 );
427 prompt.push_str("Pay special attention to changes with high impact scores, as they are likely to be the most significant. ");
428
429 prompt
430}