diff --git a/apps/app/src/components/ui/log-viewer.tsx b/apps/app/src/components/ui/log-viewer.tsx
index 059ebc78..d962a4fc 100644
--- a/apps/app/src/components/ui/log-viewer.tsx
+++ b/apps/app/src/components/ui/log-viewer.tsx
@@ -156,6 +156,13 @@ function LogEntryItem({ entry, isExpanded, onToggle }: LogEntryItemProps) {
content = content.trim();
}
+ // For summary entries, remove the and tags
+ if (entry.title === "Summary") {
+ content = content.replace(/^\s*/i, "");
+ content = content.replace(/\s*<\/summary>\s*$/i, "");
+ content = content.trim();
+ }
+
// Try to find and format JSON blocks
const jsonRegex = /(\{[\s\S]*?\}|\[[\s\S]*?\])/g;
let lastIndex = 0;
@@ -192,7 +199,7 @@ function LogEntryItem({ entry, isExpanded, onToggle }: LogEntryItemProps) {
}
return parts.length > 0 ? parts : [{ type: "text" as const, content }];
- }, [entry.content, isToolCall]);
+ }, [entry.content, entry.title, isToolCall]);
// Get colors - use tool category colors for tool_call entries
const colorParts = toolCategoryColors.split(" ");
diff --git a/apps/app/src/lib/log-parser.ts b/apps/app/src/lib/log-parser.ts
index 725b9b24..85fa96c6 100644
--- a/apps/app/src/lib/log-parser.ts
+++ b/apps/app/src/lib/log-parser.ts
@@ -126,7 +126,9 @@ function detectEntryType(content: string): LogEntryType {
trimmed.startsWith("✅") ||
trimmed.toLowerCase().includes("success") ||
trimmed.toLowerCase().includes("completed") ||
- // Markdown summary headers
+ // Summary tags (preferred format from agent)
+ trimmed.startsWith("") ||
+ // Markdown summary headers (fallback)
trimmed.match(/^##\s+(Summary|Feature|Changes|Implementation)/i) ||
trimmed.match(/^(I've|I have) (successfully |now )?(completed|finished|implemented)/i)
) {
@@ -138,10 +140,11 @@ function detectEntryType(content: string): LogEntryType {
return "warning";
}
- // Thinking/Preparation info
+ // Thinking/Preparation info (be specific to avoid matching summary content)
if (
trimmed.toLowerCase().includes("ultrathink") ||
- trimmed.toLowerCase().includes("thinking level") ||
+ trimmed.match(/thinking level[:\s]*(low|medium|high|none|\d)/i) ||
+ trimmed.match(/^thinking level\s*$/i) ||
trimmed.toLowerCase().includes("estimated cost") ||
trimmed.toLowerCase().includes("estimated time") ||
trimmed.toLowerCase().includes("budget tokens") ||
@@ -346,6 +349,9 @@ function generateTitle(type: LogEntryType, content: string): string {
return "Error";
case "success": {
// Check if it's a summary section
+ if (content.startsWith("") || content.includes("")) {
+ return "Summary";
+ }
if (content.match(/^##\s+(Summary|Feature|Changes|Implementation)/i)) {
return "Summary";
}
@@ -420,6 +426,9 @@ export function parseLogOutput(rawOutput: string): LogEntry[] {
let jsonBraceDepth = 0;
let jsonBracketDepth = 0;
+ // Summary tag accumulation state
+ let inSummaryAccumulation = false;
+
const finalizeEntry = () => {
if (currentEntry && currentContent.length > 0) {
currentEntry.content = currentContent.join("\n").trim();
@@ -451,6 +460,7 @@ export function parseLogOutput(rawOutput: string): LogEntry[] {
inJsonAccumulation = false;
jsonBraceDepth = 0;
jsonBracketDepth = 0;
+ inSummaryAccumulation = false;
};
let lineIndex = 0;
@@ -480,6 +490,18 @@ export function parseLogOutput(rawOutput: string): LogEntry[] {
continue;
}
+ // If we're in summary accumulation mode, keep accumulating until
+ if (inSummaryAccumulation) {
+ currentContent.push(line);
+ // Summary is complete when we see closing tag
+ if (trimmedLine.includes("")) {
+ inSummaryAccumulation = false;
+ // Don't finalize here - let normal flow handle it
+ }
+ lineIndex++;
+ continue;
+ }
+
// Detect if this line starts a new entry
const lineType = detectEntryType(trimmedLine);
const isNewEntry =
@@ -498,8 +520,10 @@ export function parseLogOutput(rawOutput: string): LogEntry[] {
trimmedLine.match(/\[ERROR\]/i) ||
trimmedLine.match(/\[Status\]/i) ||
trimmedLine.toLowerCase().includes("ultrathink preparation") ||
- trimmedLine.toLowerCase().includes("thinking level") ||
- // Agent summary sections (markdown headers after tool calls)
+ trimmedLine.match(/thinking level[:\s]*(low|medium|high|none|\d)/i) ||
+ // Summary tags (preferred format from agent)
+ trimmedLine.startsWith("") ||
+ // Agent summary sections (markdown headers - fallback)
trimmedLine.match(/^##\s+(Summary|Feature|Changes|Implementation)/i) ||
// Summary introduction lines
trimmedLine.match(/^All tasks completed/i) ||
@@ -526,6 +550,11 @@ export function parseLogOutput(rawOutput: string): LogEntry[] {
},
};
currentContent.push(trimmedLine);
+
+ // If this is a tag, start summary accumulation mode
+ if (trimmedLine.startsWith("") && !trimmedLine.includes("")) {
+ inSummaryAccumulation = true;
+ }
} else if (isInputLine && currentEntry) {
// Start JSON accumulation mode
currentContent.push(trimmedLine);
diff --git a/apps/server/src/services/auto-mode-service.ts b/apps/server/src/services/auto-mode-service.ts
index 1f81ff0c..40c434c0 100644
--- a/apps/server/src/services/auto-mode-service.ts
+++ b/apps/server/src/services/auto-mode-service.ts
@@ -1143,7 +1143,22 @@ Implement this feature by:
4. Add or update tests as needed
5. Ensure the code follows existing patterns and conventions
-When done, summarize what you implemented and any notes for the developer.`;
+When done, wrap your final summary in tags like this:
+
+
+## Summary: [Feature Title]
+
+### Changes Implemented
+- [List of changes made]
+
+### Files Modified
+- [List of files]
+
+### Notes for Developer
+- [Any important notes]
+
+
+This helps parse your summary correctly in the output logs.`;
return prompt;
}