diff --git a/internal/logger/markdown_logger.go b/internal/logger/markdown_logger.go index d39d850f..6a432c72 100644 --- a/internal/logger/markdown_logger.go +++ b/internal/logger/markdown_logger.go @@ -134,8 +134,22 @@ func (ml *MarkdownLogger) Log(level LogLevel, category, format string, args ...i isPreformatted := strings.HasPrefix(message, "**") && (strings.Contains(message, "→") || strings.Contains(message, "←")) if isPreformatted { - // Pre-formatted content (like RPC messages) - just add bullet and emoji - logLine = fmt.Sprintf("- %s %s %s\n", emoji, category, message) + // Pre-formatted content (like RPC messages) - add bullet and emoji + // If the message contains newlines (e.g., JSON code blocks), indent them properly + if strings.Contains(message, "\n") { + // Split the message into lines and indent continuation lines with 2 spaces + lines := strings.Split(message, "\n") + firstLine := lines[0] + // Start with the first line + logLine = fmt.Sprintf("- %s %s %s\n", emoji, category, firstLine) + // Add remaining lines with proper indentation (2 spaces to nest under bullet) + for i := 1; i < len(lines); i++ { + logLine += " " + lines[i] + "\n" + } + } else { + // Single-line pre-formatted message + logLine = fmt.Sprintf("- %s %s %s\n", emoji, category, message) + } } else if strings.Contains(message, "\n") || strings.Contains(message, "command=") || strings.Contains(message, "args=") { // Multi-line or technical content - use code block logLine = fmt.Sprintf("- %s **%s**\n ```\n %s\n ```\n", emoji, category, message) diff --git a/internal/logger/markdown_logger_test.go b/internal/logger/markdown_logger_test.go index d769e501..8296d091 100644 --- a/internal/logger/markdown_logger_test.go +++ b/internal/logger/markdown_logger_test.go @@ -304,9 +304,16 @@ func TestMarkdownLoggerRPCFormatting(t *testing.T) { assert.True(t, strings.Contains(logContent, "- 🔍 rpc **github**→`tools/list`"), "RPC message should be on single line without extra code block wrapping") // Check that RPC messages with JSON blocks are properly formatted - // The title should be on one line, followed by the JSON block + // The title should be on one line, followed by the JSON block INDENTED with 2 spaces assert.True(t, strings.Contains(logContent, "- 🔍 rpc **safeoutputs**→`tools/call`"), "RPC message with JSON should have title on single line") + // Verify that JSON code block lines are properly indented under the bullet point + // The empty line after the first line should be indented + assert.True(t, strings.Contains(logContent, "- 🔍 rpc **safeoutputs**→`tools/call`\n \n ```json"), "JSON code block should be indented with 2 spaces") + + // Verify the closing code fence is also indented + assert.True(t, strings.Contains(logContent, " ```"), "Closing code fence should be indented") + // Regular multi-line messages should still use code blocks assert.True(t, strings.Contains(logContent, "- ✓ **backend**\n ```\n command="), "Regular multi-line messages should still use code blocks")