use crate::copilot::ChatMessage;
const DIFF_CHAR_LIMIT: usize = 32_000;
const SYSTEM_PROMPT: &str = "\
You are a senior engineer writing a git commit message for a staged diff.
Respond with ONLY the commit message — no prose before or after, no code
fences, no quoting. It will be piped verbatim into `git commit`.
Follow Conventional Commits strictly:
<type>[optional scope]: <subject>
[optional body]
Rules:
- type ∈ {feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert}
- subject: imperative mood, lowercase, no trailing period, ≤ 72 chars
- leave a blank line between subject and body
- body: wrap at ~72 cols; explain WHY, not a line-by-line summary of the diff
- omit body entirely if the subject is self-explanatory
- do not invent requirements, tickets, or co-authors that aren't in the diff
";
pub fn build(diff: &str) -> Vec<ChatMessage> {
vec![
ChatMessage::system(SYSTEM_PROMPT),
ChatMessage::user(format!("Staged diff:\n\n```diff\n{}\n```", truncate(diff))),
]
}
fn truncate(diff: &str) -> String {
if diff.len() <= DIFF_CHAR_LIMIT {
return diff.to_string();
}
let mut end = DIFF_CHAR_LIMIT;
while !diff.is_char_boundary(end) {
end -= 1;
}
let mut out = String::with_capacity(end + 64);
out.push_str(&diff[..end]);
out.push_str("\n\n# ... diff truncated to ");
out.push_str(&end.to_string());
out.push_str(" bytes ...\n");
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn short_diff_passes_through_unchanged() {
let diff = "diff --git a/x b/x\n+new line\n";
let msgs = build(diff);
assert_eq!(msgs.len(), 2);
assert_eq!(msgs[0].role, "system");
assert!(msgs[1].content.contains("+new line"));
}
#[test]
fn oversize_diff_gets_truncated_with_marker() {
let diff: String = "+".repeat(DIFF_CHAR_LIMIT + 100);
let msgs = build(&diff);
assert!(msgs[1].content.contains("diff truncated"));
assert!(msgs[1].content.len() < DIFF_CHAR_LIMIT + 500);
}
#[test]
fn truncate_at_multibyte_char_boundary_does_not_panic() {
let mut s = "a".repeat(DIFF_CHAR_LIMIT - 1);
s.push('é');
s.push_str(&"x".repeat(100));
assert!(s.len() > DIFF_CHAR_LIMIT);
let msgs = build(&s);
assert!(msgs[1].content.contains("diff truncated"));
}
}