From a4213b8f66a55c0eadf869be4895a810b36cfef2 Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Thu, 22 Jan 2026 13:25:40 -0600 Subject: [PATCH] =?UTF-8?q?##=20CHANGES=20-=20Synopsis=20parsing=20now=20s?= =?UTF-8?q?trips=20conversational=20filler=20to=20surface=20real=20summari?= =?UTF-8?q?es=20=F0=9F=94=8D=20-=20Added=20robust=20filler=20phrase=20dete?= =?UTF-8?q?ction=20across=20many=20variants=20and=20punctuation=20?= =?UTF-8?q?=F0=9F=A7=B9=20-=20Synopsis=20fallback=20now=20skips=20filler-o?= =?UTF-8?q?nly=20outputs,=20returning=20=E2=80=9CTask=20completed=E2=80=9D?= =?UTF-8?q?=20=F0=9F=9B=9F=20-=20Renderer=20short-summary=20generator=20ig?= =?UTF-8?q?nores=20filler=20sentences=20before=20summarizing=20?= =?UTF-8?q?=F0=9F=A7=A0=20-=20Autorun=20synopsis=20prompt=20updated=20to?= =?UTF-8?q?=20enforce=20scientific,=20verb-first=20logging=20=F0=9F=93=93?= =?UTF-8?q?=20-=20Expanded=20test=20suite=20to=20verify=20filler=20filteri?= =?UTF-8?q?ng=20and=20fallback=20behavior=20=F0=9F=A7=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/shared/synopsis.test.ts | 62 +++++++++++++++++++++++++++ src/prompts/autorun-synopsis.md | 4 +- src/renderer/App.tsx | 10 ++++- src/shared/synopsis.ts | 24 +++++++++-- 4 files changed, 95 insertions(+), 5 deletions(-) diff --git a/src/__tests__/shared/synopsis.test.ts b/src/__tests__/shared/synopsis.test.ts index 14ce7afe..a2839eb6 100644 --- a/src/__tests__/shared/synopsis.test.ts +++ b/src/__tests__/shared/synopsis.test.ts @@ -128,6 +128,68 @@ describe('synopsis', () => { }); }); + describe('conversational filler filtering', () => { + it('should skip "Excellent!" and use next meaningful line', () => { + const response = 'Excellent!\n\nThe markdown generation is working perfectly.'; + const result = parseSynopsis(response); + + expect(result.shortSummary).toBe('The markdown generation is working perfectly.'); + }); + + it('should skip "Perfect!" and use next meaningful line', () => { + const response = 'Perfect!\n\nAll tests are passing now.'; + const result = parseSynopsis(response); + + expect(result.shortSummary).toBe('All tests are passing now.'); + }); + + it('should skip multiple filler words at start', () => { + const response = 'Great!\n\nExcellent!\n\nFixed the authentication bug in login handler.'; + const result = parseSynopsis(response); + + expect(result.shortSummary).toBe('Fixed the authentication bug in login handler.'); + }); + + it('should skip filler with exclamation marks and variations', () => { + const fillers = [ + 'Excellent!', + 'Perfect!', + 'Great!', + 'Awesome!', + 'Done!', + 'Wonderful!', + 'Fantastic!', + ]; + + for (const filler of fillers) { + const response = `${filler}\n\nActual content here.`; + const result = parseSynopsis(response); + expect(result.shortSummary).toBe('Actual content here.'); + } + }); + + it('should skip phrase fillers like "Looks good!"', () => { + const response = 'Looks good!\n\nUpdated the config file with new settings.'; + const result = parseSynopsis(response); + + expect(result.shortSummary).toBe('Updated the config file with new settings.'); + }); + + it('should skip "All done!" style fillers', () => { + const response = 'All done!\n\nRefactored the component to use hooks.'; + const result = parseSynopsis(response); + + expect(result.shortSummary).toBe('Refactored the component to use hooks.'); + }); + + it('should fall back to "Task completed" if only filler exists', () => { + const response = 'Excellent!'; + const result = parseSynopsis(response); + + expect(result.shortSummary).toBe('Task completed'); + }); + }); + describe('fallback behavior', () => { it('should use first line as summary when no format detected', () => { const response = 'Just a plain text response\nWith multiple lines.\nAnd more content.'; diff --git a/src/prompts/autorun-synopsis.md b/src/prompts/autorun-synopsis.md index 84cf0340..f21de457 100644 --- a/src/prompts/autorun-synopsis.md +++ b/src/prompts/autorun-synopsis.md @@ -5,9 +5,11 @@ Provide a brief synopsis of what you just accomplished in this task using this e **Details:** [A paragraph with more specifics about what was done, files changed, etc.] Rules: +- Write in a scientific log style: factual, concise, and informative. Example: "Added user authentication endpoint with JWT validation" not "I helped you add authentication". - Be specific about what was actually accomplished, not what was attempted. - Focus only on meaningful work that was done. Omit filler phrases like "the task is complete", "no further action needed", "everything is working", etc. +- NEVER start with conversational words like "Excellent!", "Perfect!", "Great!", "Awesome!", "Done!", or any similar expressions. These add no information value. - NEVER include preamble about session context, interaction history, or caveats like "This is our first interaction", "there's no prior work to summarize", "you asked me to", etc. Jump straight to the accomplishment. -- Start directly with the action taken (e.g., "Fixed button visibility..." not "You asked me to fix..."). +- Start directly with the action taken using a verb (e.g., "Fixed button visibility..." not "You asked me to fix..." and not "Excellent! Fixed..."). - If nothing meaningful was accomplished (no code changes, no files modified, no research completed, just greetings or introductions), respond with ONLY the text: NOTHING_TO_REPORT - Use NOTHING_TO_REPORT when the conversation was just a greeting, introduction, or there genuinely was no work to summarize. diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index b230f646..5e507f42 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1983,11 +1983,19 @@ function MaestroConsoleInner() { } // Create a short summary from the last AI response + // Skip conversational fillers like "Excellent!", "Perfect!", etc. let summary = ''; if (lastAiLog?.text) { const text = lastAiLog.text.trim(); if (text.length > 10) { - const firstSentence = text.match(/^[^.!?\n]*[.!?]/)?.[0] || text.substring(0, 120); + // Match sentences (text ending with . ! or ?) + const sentences = text.match(/[^.!?\n]+[.!?]+/g) || []; + // Pattern to detect conversational filler sentences + const fillerPattern = + /^(excellent|perfect|great|awesome|wonderful|fantastic|good|nice|cool|done|ok|okay|alright|sure|yes|yeah|absolutely|certainly|definitely|looks?\s+good|all\s+(set|done|ready)|got\s+it|understood|will\s+do|on\s+it|no\s+problem|no\s+worries|happy\s+to\s+help)[!.\s]*$/i; + // Find the first non-filler sentence + const meaningfulSentence = sentences.find((s) => !fillerPattern.test(s.trim())); + const firstSentence = meaningfulSentence?.trim() || text.substring(0, 120); summary = firstSentence.length < text.length ? firstSentence diff --git a/src/shared/synopsis.ts b/src/shared/synopsis.ts index 9324e33b..917701a1 100644 --- a/src/shared/synopsis.ts +++ b/src/shared/synopsis.ts @@ -38,6 +38,23 @@ function isTemplatePlaceholder(text: string): boolean { return placeholderPatterns.some((pattern) => pattern.test(text.trim())); } +/** + * Check if text is a conversational filler that should be stripped. + * These are words/phrases that add no information value to a scientific log. + */ +function isConversationalFiller(text: string): boolean { + const fillerPatterns = [ + /^(excellent|perfect|great|awesome|wonderful|fantastic|good|nice|cool|done|ok|okay|alright|sure|yes|yeah|yep|absolutely|certainly|definitely|indeed|affirmative)[\s!.]*$/i, + /^(that's|that is|this is|it's|it is)\s+(great|good|perfect|excellent|done|complete|finished)[\s!.]*$/i, + /^(all\s+)?(set|done|ready|complete|finished|good\s+to\s+go)[\s!.]*$/i, + /^(looks?\s+)?(good|great|perfect)[\s!.]*$/i, + /^(here\s+you\s+go|there\s+you\s+go|there\s+we\s+go|here\s+it\s+is)[\s!.]*$/i, + /^(got\s+it|understood|will\s+do|on\s+it|right\s+away)[\s!.]*$/i, + /^(no\s+problem|no\s+worries|happy\s+to\s+help)[\s!.]*$/i, + ]; + return fillerPatterns.some((pattern) => pattern.test(text.trim())); +} + /** * Check if a response indicates nothing meaningful to report. * Looks for the NOTHING_TO_REPORT sentinel token anywhere in the response. @@ -94,15 +111,16 @@ export function parseSynopsis(response: string): ParsedSynopsis { let shortSummary = summaryMatch?.[1]?.trim() || ''; let details = detailsMatch?.[1]?.trim() || ''; - // Check if summary is a template placeholder (model output format instructions literally) - if (!shortSummary || isTemplatePlaceholder(shortSummary)) { - // Try to find actual content by looking for non-placeholder lines + // Check if summary is a template placeholder or conversational filler + if (!shortSummary || isTemplatePlaceholder(shortSummary) || isConversationalFiller(shortSummary)) { + // Try to find actual content by looking for non-placeholder, non-filler lines const lines = clean.split('\n').filter((line) => { const trimmed = line.trim(); return ( trimmed && !trimmed.startsWith('**') && !isTemplatePlaceholder(trimmed) && + !isConversationalFiller(trimmed) && !trimmed.match(/^Rules:/i) && !trimmed.match(/^-\s+Be specific/i) && !trimmed.match(/^-\s+Focus only/i) &&