fix: make piped output human-readable instead of raw CommonMark#462
Merged
fix: make piped output human-readable instead of raw CommonMark#462
Conversation
Contributor
Semver Impact of This PR🟢 Patch (bug fixes) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Bug Fixes 🐛
Internal Changes 🔧
🤖 This preview updates automatically when you update the PR. |
Contributor
Codecov Results 📊✅ 116 passed | Total: 116 | Pass Rate: 100% | Execution Time: 0ms 📊 Comparison with Base Branch
✨ No test changes detected All tests are passing successfully. ✅ Patch coverage is 100.00%. Project has 1130 uncovered lines. Files with missing lines (4)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
+ Coverage 95.20% 95.30% +0.1%
==========================================
Files 176 177 +1
Lines 24063 24067 +4
Branches 0 0 —
==========================================
+ Hits 22907 22937 +30
- Misses 1156 1130 -26
- Partials 0 0 —Generated by Codecov Action |
Two problems are fixed:
1. FORCE_COLOR leaking ANSI to pipes: When FORCE_COLOR=1 is set (common
in VS Code terminals), ANSI escape codes leaked through to less/cat
because isPlainOutput() treated FORCE_COLOR as higher priority than
TTY detection. Now FORCE_COLOR only applies when stdout IS a TTY.
Users who truly want color in pipes can use SENTRY_PLAIN_OUTPUT=0.
2. Plain mode outputs raw CommonMark: When piped, the CLI produced raw
markdown syntax (| --- |, **bold**, [link](url), `code`) which is
unreadable in a pager. Now plain mode:
- renderMarkdown() parses and renders to structured plain text
(headings, aligned tables, blockquote indentation) then strips ANSI
- formatTable() uses renderTextTable() with box-drawing borders
instead of buildMarkdownTable() with raw CommonMark syntax
- renderInlineMarkdown() strips markdown syntax via stripMarkdownInline()
- terminalLink() returns plain text (no OSC 8 sequences) when piped
- divider() and footer/hint text use plainSafeMuted() to avoid ANSI
Architecture: isPlainOutput() extracted to plain-detect.ts to avoid
circular dependency between markdown.ts and colors.ts. Re-exported from
markdown.ts for backward compatibility.
Address BugBot review: formatLogTable and formatTraceTable were still using mdRow + mdTableHeader (pipe-delimited markdown) in plain mode instead of renderTextTable with box-drawing borders. Now consistent with formatTable.
…uted - stripMarkdownInline() now unescapes markdown backslash escapes (\|, \<, \>, \, \_, \*, \[, \]) so plain output shows literal characters instead of escaped ones. Addresses Seer review. - mdRow() replaces literal | with box-drawing │ after stripping to prevent breaking pipe-delimited table format. - Export plainSafeMuted() from human.ts and reuse in output.ts instead of duplicating. Addresses BugBot review.
…ore stripping
- formatTraceTable: pass alignments to renderTextTable in plain mode
so Duration column is right-aligned consistently. Addresses Seer review.
- stripMarkdownInline: remove _{1,2} regex that could corrupt identifiers
containing underscores (e.g. payment_service_handler). Backslash-escaped
underscores (\_) are already handled by the unescape step.
Code spans have higher precedence than emphasis in CommonMark. Process backtick code spans first so that bold markers inside code spans (e.g. `**text**`) are preserved as literal asterisks. Addresses BugBot review.
…rip-ANSI Eliminate the entire stripMarkdownInline() function and its accumulated edge-case bugs (code span precedence, underscore corruption, backslash escapes). Instead, reuse the existing marked.lexer() → renderInline() pipeline for both TTY and plain modes, stripping ANSI from the result in plain mode — the same approach renderMarkdown() already uses. Key changes: - renderInlineMarkdown(): always renders through marked, strips ANSI in plain mode instead of calling stripMarkdownInline() - renderCodespan(): extracted helper; skips padding spaces in plain mode so table column widths aren't inflated - formatTable/formatTraceTable/formatLogTable: merged TTY and plain branches into a single renderInlineMarkdown() call - mdRow(): same — single code path for both modes - Deleted stripMarkdownInline() entirely
bc87d07 to
f4a7bfd
Compare
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Problem
When piping
sentryoutput tolessor other programs, the output was either:FORCE_COLOR=1is set (common in VS Code terminals)| --- |,**bold**,[link](url)) — unreadable in a pagerSolution
FORCE_COLOR fix
isPlainOutput()now ignoresFORCE_COLORwhen stdout is piped (not a TTY). Users who want color in pipes can useSENTRY_PLAIN_OUTPUT=0.Plain output is now human-readable
Instead of raw CommonMark, plain mode now renders structured, readable text:
renderMarkdown()— parses markdown → renders with formatting (headings, aligned tables, lists) → strips ANSIformatTable()— usesrenderTextTable()with box-drawing borders instead of CommonMark pipe syntaxrenderInlineMarkdown()— strips markdown syntax (**bold**→bold,[text](url)→text,\code`→code`)terminalLink()— returns plain text (no OSC 8 sequences) when pipedplainSafeMuted()to skip ANSI when pipedArchitecture
isPlainOutput()extracted tosrc/lib/formatters/plain-detect.tsto avoid circular dependency betweenmarkdown.tsandcolors.ts. Re-exported frommarkdown.tsfor backward compatibility.Before / After
Before (piped to less):
After: