Skip to content

Commit d6848f2

Browse files
Ankit Guptaclaude
andcommitted
address review bot feedback (reviewloop iteration 1)
- Fix quote stripping order: use body_text when available, then strip quoted content on plain text (Greptile P1) - Align test helper with production preprocessing pipeline (Greptile P2) - Populate to_address in fetchAndCacheSentEmails rows (Devin) - Use dedicated inferError state for style tab error display (Devin) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 474a1f5 commit d6848f2

File tree

3 files changed

+36
-7
lines changed

3 files changed

+36
-7
lines changed

src/main/services/style-profiler.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ async function fetchAndCacheSentEmails(
219219
body: email.body,
220220
date: email.date,
221221
is_reply: email.subject.toLowerCase().startsWith("re:") ? 1 : 0,
222+
to_address: email.to,
222223
});
223224
}
224225

@@ -424,9 +425,10 @@ export async function inferStyleFromSentEmails(
424425
// Build the email samples for the prompt
425426
const emailSamples = emails
426427
.map((e) => {
427-
// Strip quoted/forwarded content first so we only analyze the user's own writing
428-
const stripped = stripQuotedContent(e.body);
429-
const text = stripHtmlForSearch(stripped);
428+
// Use pre-extracted plain text when available, otherwise strip HTML first
429+
const plainText = e.body_text ?? stripHtmlForSearch(e.body);
430+
// Strip quoted/forwarded content so we only analyze the user's own writing
431+
const text = stripQuotedContent(plainText);
430432
const truncated = truncateBody(text, 300);
431433
const type = e.is_reply ? "reply" : "new";
432434
return `---\nTo: ${e.to_address ?? "unknown"}\nSubject: ${e.subject}\nDate: ${e.date}\nType: ${type}\n\n${truncated}\n---`;

src/renderer/components/SettingsPanel.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export function SettingsPanel({ onClose, initialTab }: SettingsPanelProps) {
6363
const [saveResult, setSaveResult] = useState<string | null>(null);
6464
const [stylePrompt, setStylePrompt] = useState("");
6565
const [isInferring, setIsInferring] = useState(false);
66+
const [inferError, setInferError] = useState<string | null>(null);
6667
const [agentDrafterPrompt, setAgentDrafterPrompt] = useState("");
6768
const [isRerunningAll, setIsRerunningAll] = useState(false);
6869
const [rerunResult, setRerunResult] = useState<string | null>(null);
@@ -480,7 +481,7 @@ export function SettingsPanel({ onClose, initialTab }: SettingsPanelProps) {
480481

481482
const handleInferStyle = async () => {
482483
setIsInferring(true);
483-
setSaveResult(null);
484+
setInferError(null);
484485
try {
485486
const result = (await window.api.style.infer()) as {
486487
success: boolean;
@@ -490,10 +491,10 @@ export function SettingsPanel({ onClose, initialTab }: SettingsPanelProps) {
490491
if (result.success && result.data) {
491492
setStylePrompt(result.data);
492493
} else {
493-
setSaveResult(result.error || "Failed to infer writing style");
494+
setInferError(result.error || "Failed to infer writing style");
494495
}
495496
} catch {
496-
setSaveResult("Failed to infer writing style");
497+
setInferError("Failed to infer writing style");
497498
} finally {
498499
setIsInferring(false);
499500
}
@@ -2111,6 +2112,9 @@ export function SettingsPanel({ onClose, initialTab }: SettingsPanelProps) {
21112112
This prompt is prepended to your draft generation when style examples are
21122113
available. It tells the AI how to interpret the examples of your past emails.
21132114
</p>
2115+
{inferError && (
2116+
<p className="text-xs text-red-600 dark:text-red-400 mt-1">{inferError}</p>
2117+
)}
21142118
</div>
21152119

21162120
<button

tests/unit/style-inference.spec.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,33 @@ Do NOT include example phrases or quoted text from the emails.`;
5151
* Build the email samples portion of the prompt, mirroring the logic
5252
* in inferStyleFromSentEmails.
5353
*/
54+
// Simplified version of stripQuotedContent for plain text
55+
// (matches the production code path: body_text → stripQuotedContent → truncateBody)
56+
function stripQuotedContent(text: string): string {
57+
if (!text) return text;
58+
const lines = text.split("\n");
59+
for (let i = 0; i < lines.length; i++) {
60+
const line = lines[i].trim();
61+
// "On ... wrote:" header
62+
if (/^on\s.+wrote:\s*$/i.test(line)) {
63+
return lines.slice(0, i).join("\n").trimEnd();
64+
}
65+
// Block of ">" quoted lines
66+
if (line.startsWith(">")) {
67+
const above = lines.slice(0, i).join("\n").trimEnd();
68+
if (above && !above.split("\n").every((l) => l.trim().startsWith(">"))) {
69+
return above;
70+
}
71+
}
72+
}
73+
return text;
74+
}
75+
5476
function buildEmailSamples(emails: SentEmailRow[]): string {
5577
return emails
5678
.map((e) => {
57-
const text = e.body_text ?? stripHtmlForSearch(e.body);
79+
const plainText = e.body_text ?? stripHtmlForSearch(e.body);
80+
const text = stripQuotedContent(plainText);
5881
const truncated = truncateBody(text, 300);
5982
const type = e.is_reply ? "reply" : "new";
6083
return `---\nTo: ${e.to_address ?? "unknown"}\nSubject: ${e.subject}\nDate: ${e.date}\nType: ${type}\n\n${truncated}\n---`;

0 commit comments

Comments
 (0)