main

As a user, I want consistent blank line spacing in terminal output, so that the interface feels polished and predictable.

SYNOPSIS

Audit and standardize blank lines between all terminal output sections.

DESCRIPTION

Currently, the number of blank lines between sections varies depending on the order of events during a session. Sometimes there are 2 blank lines, sometimes 1, leading to an inconsistent visual experience.

This story involves:

  1. Auditing all places that write to the terminal
  2. Establishing spacing rules (e.g., 1 blank line between sections)
  3. Ensuring consistent application of those rules

DESIGN

Root cause: Spacing is caller-side (each method decides its own prefix/suffix spacing) instead of boundary-aware (spacing happens at transitions).

Scenarios causing inconsistency:

  • Dots running → stop_dots adds newline + markdown adds 2 newlines = 3 lines
  • No dots → markdown adds 2 newlines = 2 lines
  • header returns \n... + say adds newline = double spacing

Solution: One flag (@at_line_start), one method (gap).

gap is idempotent: “ensure we’re at a blank line”. Call it anywhere between sections. If already at line start, it’s a no-op. If mid-content, it adds one newline.

Changes:

  • Terminal tracks cursor state via @at_line_start
  • gap method: newline unless @at_line_start
  • markdown uses gap instead of newline(n: 2)
  • header drops its \n prefix (caller uses gap)

Trade-offs:

  • Simplicity ✓ - 1 flag, 1 method, 3 file changes
  • No plugin changes needed - existing say/print calls just work
  • Idempotent - safe to call gap multiple times

SEE ALSO

  • lib/elelem/terminal.rb - Primary output methods (say, print, markdown, newline)
  • lib/elelem/agent.rb - REPL loop and turn processing with terminal calls
  • lib/elelem/toolbox.rb - header() method prepends \n to output
  • lib/elelem/plugins/read.rb - after hook uses terminal.say and display_file
  • lib/elelem/plugins/write.rb - after hook uses terminal.say and display_file
  • lib/elelem/plugins/execute.rb - streaming print and after hook
  • lib/elelem/plugins/tools.rb - markdown output for tool listings
  • lib/elelem/plugins/context.rb - multi-line output for context display
  • lib/elelem/plugins/builtins.rb - /clear and /help command output
  • lib/elelem/plugins/provider.rb - provider switching messages

Tasks

Terminal (lib/elelem/terminal.rb)

  • Add @at_line_start = true in initialize
  • Add gap method: newline unless @at_line_start (idempotent blank line)
  • Update say to set @at_line_start = true after output
  • Update print to set @at_line_start = false (mid-line content)
  • Update newline to set @at_line_start = true
  • Update stop_dots - already calls newline, will inherit correct state
  • Update markdown - replace newline(n: 2) with gap

Toolbox (lib/elelem/toolbox.rb)

  • Update header - remove leading \n from return string

Agent (lib/elelem/agent.rb)

  • Add terminal.gap before terminal.say toolbox.header(...) in process method

Testing

  • Add spec for gap idempotence: calling twice produces one blank line
  • Visual audit: conversation with tool calls
  • Visual audit: multi-tool execution
  • Visual audit: streaming execute output

Acceptance Criteria

  • Single blank line between distinct output sections
  • No double blank lines appear in any scenario
  • No missing blank lines between sections
  • Spacing is consistent regardless of event order
  • Visual audit of common workflows passes

Demo Notes

Verified: 2026-02-04 Status: ACCEPTED

Verification Run: 2026-02-04 (Final)

All 83 tests pass (0 failures).

Code Review Fixes Applied:

  • newline now respects quiet? mode
  • print and say only set @at_line_start when actually outputting
  • Test spec uses expect instead of allow for dots thread kill
  • Replaced @quiet with quiet? predicate throughout
  • Tests refactored to test behavior, not implementation (no instance_variable_get)

Manual Verification via ./bin/run:

  • Library loads without errors ✓
  • Spacing between sections is exactly 1 line ✓
  • No double blank lines in output ✓
  • gap is idempotent (multiple calls = single newline) ✓

Previous: Verification Run 2026-02-04

All 75 tests pass (0 failures).

Automated Tests Verified:

  • gap idempotence: calling multiple times produces single blank line ✓
  • @at_line_start state tracking across say, print, newline
  • gap stops dots before adding newline ✓

Scenarios Verified:

Scenario Expected Result
Dots → tool header 1 blank line
Tool → tool 1 blank line
Tool → markdown 1 blank line

Remaining: Visual audit of streaming execute output (requires manual LLM testing)


Implementation Summary

Added @at_line_start flag and gap method to Terminal class:

  • gap is idempotent: stops dots, then adds newline only if not at line start
  • All output methods (say, print, newline) update @at_line_start
  • markdown uses gap instead of hardcoded newline(n: 2)
  • header in Toolbox no longer prepends \n
  • Agent calls terminal.gap before tool headers

Tested Scenarios

  1. Dots → tool header: . prints, gap stops dots + newline, header prints

    • Result: Single blank line after dots ✓
  2. Tool header → tool header: gap adds single newline between headers

    • Result: Consistent single blank line ✓
  3. Tool header → markdown: gap inside markdown is no-op (already at line start)

    • Result: No extra blank lines ✓
  4. Idempotence: Calling gap multiple times produces only one newline

    • Result: Safe to call gap anywhere ✓

Files Changed

  • lib/elelem/terminal.rb - Added @at_line_start, gap, updated output methods
  • lib/elelem/toolbox.rb - Removed \n prefix from header
  • lib/elelem/agent.rb - Added terminal.gap before tool headers
  • spec/elelem/terminal_spec.rb - Added tests for gap behavior

Edge Case Fixed During Demo

Initial implementation missed that gap should stop dots if running. When dots thread prints directly to stdout, @at_line_start stays true but cursor is mid-line. Fixed by having gap call stop_dots before checking state.