Skip to content

Refactor: Split monolithic replay.cs into 12 focused files#15

Merged
lewing merged 19 commits intomainfrom
refactor/csproj-split
Feb 25, 2026
Merged

Refactor: Split monolithic replay.cs into 12 focused files#15
lewing merged 19 commits intomainfrom
refactor/csproj-split

Conversation

@lewing
Copy link
Owner

@lewing lewing commented Feb 24, 2026

Summary

Splits the monolithic
eplay.cs\ (4,421 lines) into 12 focused files while preserving all functionality. The tool continues to work identically — same CLI interface, same NuGet package, same behavior.

Architecture

File Lines Purpose
\
eplay.cs\ 652 CLI parsing & dispatch (was 4,421)
\SessionBrowser.cs\ 766 Interactive session browser TUI
\InteractivePager.cs\ 629 Interactive pager with search/scroll
\ContentRenderer.cs\ 558 Conversation content rendering
\StatsAnalyzer.cs\ 362 Stats extraction & reports
\TextUtils.cs\ 356 Static utility methods (VisibleWidth, etc.)
\DataParsers.cs\ 353 JSONL/Claude/Waza parsers
\MarkdownRenderer.cs\ 286 Markdown → terminal rendering
\OutputFormatters.cs\ 260 JSON/summary output formatting
\EvalProcessor.cs\ 259 Eval format detection/parsing
\Models.cs\ 111 Record types & enums
\ColorHelper.cs\ 36 Shared color/output utilities

Key decisions

  • Converted from file-based app to .csproj-based tool (enables multi-file)
  • Used primary constructors with dependency injection throughout
  • \DataParsers\ uses constructor injection for \ ail\ parameter
  • \StatsAnalyzer\ uses delegate injection for parse functions (they capture top-level state)
  • Display width calculations fixed for CJK/emoji (VisibleWidth instead of .Length)

Testing

  • All 67 tests pass
  • 0 build errors, 0 warnings
  • 85% reduction in replay.cs (4,421 → 652 lines)

Bug fixes included

  • Session browser: fixed dedup for Claude sessions via \knownSessionIds\
  • Display width: CJK characters and emoji now measure correctly in tables and truncation

lewing and others added 14 commits February 24, 2026 11:27
- Created dotnet-replay.csproj with PackAsTool metadata
- Extracted 13 record/class/enum types to Models.cs
- Removed #: directives from replay.cs (now in .csproj)
- Updated all test files to use --project instead of file-based run
- All 67 tests passing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move pure static utility functions (text manipulation, width calculation,
markup processing, formatting helpers, glob expansion) to TextUtils.cs.
replay.cs uses 'using static TextUtils;' so all call sites remain unchanged.

Reduces replay.cs from 4421 to 4073 lines (~348 lines extracted).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move Markdig markdown rendering into a dedicated MarkdownRenderer class.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move pure eval data processing functions (IsClaudeFormat, IsEvalFormat,
ParseEvalData, ProcessEvalEvent) to static EvalProcessor class.
Uses 'using static EvalProcessor;' for seamless call-site compatibility.
StreamEvalEvents/OutputEvalSummary remain in replay.cs (color deps).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move all content rendering functions (RenderJsonlHeaderLines,
RenderJsonlContentLines, RenderWazaHeaderLines, RenderWazaContentLines,
RenderEvalHeaderLines, RenderEvalContentLines, BuildJsonlInfoBar,
BuildWazaInfoBar, BuildEvalInfoBar, FormatJsonProperties, Truncate)
to ContentRenderer class with primary constructor (noColor, full, mdRenderer).
~610 lines extracted, replay.cs reduced to ~2930 lines.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create ColorHelper.cs: shared color methods, WriteMarkupLine,
  Truncate, Separator, serializer options (NoColor/Full properties)
- Refactor ContentRenderer to use ColorHelper instead of duplicated
  private color methods
- Internalize MarkdownPipeline into MarkdownRenderer (was passed from
  top-level, only used internally)
- Update replay.cs call sites: constructor wiring, serializer option
  references (SummarySerializer/JsonlSerializer)
- Fix MarkdownRenderer table column widths: StripMarkup().Length to
  VisibleWidth(StripMarkup()) for correct CJK/emoji alignment
- Fix session browser truncation: display.Length to VisibleWidth()
  for emoji-safe summary truncation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move ExtractStats and OutputStatsReport into StatsAnalyzer class
(362 lines). Uses delegate injection for ParseJsonlData, ParseClaudeData,
and ParseWazaData which remain in replay.cs as they capture top-level
state. replay.cs reduced from 2983 to 2628 lines.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…add dedup for filesystem IDs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- DataParsers.cs (345 lines): ParseJsonlData, ParseClaudeData, ParseWazaData
  Constructor injection for tail parameter
- OutputFormatters.cs (256 lines): OutputJsonl, OutputWazaJsonl, OutputSummary, OutputWazaSummary
  Constructor injection for colors and full parameters
- replay.cs reduced from 2,629 to 2,027 lines
- All 67 tests pass, 0 build errors

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- InteractivePager.cs (~617 lines): RunInteractivePager<T> with all nested helpers
  Primary constructor with ColorHelper, ContentRenderer, and display options
- replay.cs reduced from 2,027 to 1,410 lines (total: 68% reduction from original 4,421)
- All 67 tests pass, 0 build errors, 0 warnings

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- SessionBrowser.cs (~650 lines): BrowseSessions, LoadSessionsFromDb, LaunchResume
  Primary constructor with ColorHelper, ContentRenderer, DataParsers, and session config
- replay.cs reduced from 1,410 to 652 lines (85% reduction from original 4,421)
- All 67 tests pass, 0 build errors

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove unused ColorHelper and tail parameters from primary constructors
in InteractivePager, OutputFormatters, SessionBrowser, and StatsAnalyzer.
Remove unreachable return statement in InteractivePager.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update ci.yml to use `dotnet build dotnet-replay.csproj` instead of
`dotnet build replay.cs` after the file-based app to csproj conversion.
Add test step to CI.

Remove .squad/ state files that should not be merged to main.
The squad-main-guard workflow enforces this boundary.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the monolithic replay.cs file (4,421 lines) into 12 focused, maintainable components while preserving all functionality. The refactoring converts the single-file app to a .csproj-based project, enabling better code organization and maintainability without changing the CLI interface or behavior.

Changes:

  • Split 4,421-line replay.cs into 12 focused files with clear responsibilities
  • Converted from file-based app to .csproj-based tool while maintaining NuGet package compatibility
  • Fixed display width calculations for CJK/emoji characters and Claude session deduplication bug

Reviewed changes

Copilot reviewed 54 out of 55 changed files in this pull request and generated no comments.

Show a summary per file
File Description
dotnet-replay.csproj New project file defining tool packaging and dependencies
TextUtils.cs Static utility functions for text processing, width calculations, and formatting
StatsAnalyzer.cs Stats extraction and batch reporting logic with delegate injection
SessionBrowser.cs Interactive TUI session browser with DB polling and live updates
OutputFormatters.cs JSON and summary output formatting for all data types
Models.cs Record types and enums for data structures
MarkdownRenderer.cs Markdown-to-terminal rendering with Spectre.Console
InteractivePager.cs Interactive pager with search, scroll, and follow mode
EvalProcessor.cs Eval format detection and parsing logic
DataParsers.cs JSONL/Claude/Waza parsers with constructor injection
ContentRenderer.cs Conversation content rendering with dependency injection
ColorHelper.cs Shared color and output utilities
tests/*.cs Updated test files to reference new .csproj instead of replay.cs
.github/workflows/ci.yml Updated build command to use .csproj
.squad/**, .github/workflows/squad-*.yml Squad team infrastructure files removed (team state belongs on dev branches)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

These belong in a separate squad-setup PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing and others added 3 commits February 24, 2026 17:45
These were accidentally removed. PR #15 should only contain refactoring changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ot null, raw strings

- Replace new List<T>() / new Dictionary / new[] with collection expressions []
- Convert if/else if type dispatch chains to switch expressions in InteractivePager
- Use 'is not null' pattern matching instead of '!= null'
- Use multiline interpolated raw strings for multi-line output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lewing lewing force-pushed the refactor/csproj-split branch from b824596 to 0cd6e99 Compare February 25, 2026 00:09
DataParsers: Replace hand-built JSON string concatenation with typed
records (SyntheticContentTurn, SyntheticToolStartTurn, etc.) serialized
via JsonSerializer, eliminating fragile manual escaping.

StatsAnalyzer: Replace anonymous types with BatchSummary, FileStatsSummary,
ModelGroupSummary, and TaskGroupSummary records defined in Models.cs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lewing lewing merged commit a610f9b into main Feb 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants