legion-tty 0.4.22 → 0.4.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45ef88ce519140b5056ce0b934f3c3512781a4f26e30dc74203c431e6f20cc3c
4
- data.tar.gz: ba632f83ea2022683c097b5451849bf129287c46959a050a8db90b356abe1f29
3
+ metadata.gz: ab1a18bdbec9078051e19b84ede94cdaacb600ed12006674dc5696f711230349
4
+ data.tar.gz: 6735876baede0f4fd45637f6a5cb0cef4284f7117298fa96cb97c8ca667a23d4
5
5
  SHA512:
6
- metadata.gz: affafac87b03a47e29ae08bfe5815ba9eb8f72de5fc3feda9350b2871021537f6319261177de1a20f349b8d38cda3295d6b5cca6ff941dbe9414744fa27562e5
7
- data.tar.gz: bf14853b34688cd959cf6301a56542ec9448fc9f4db2fb2489e7c6aeec3d56e08fc6e44c1627dc31c33631a8b9291717786032dee7ab72b566c545693ca2b57e
6
+ metadata.gz: '01294f74d7805fb5aad66aa1538ffe38cb1f8b407aaaa662d90cacaf7f8c0b4e787257636007f8a74e88e1e13b76a5432da4a2ff47aad0dd5b6f81852116ddfb'
7
+ data.tar.gz: 789298565c5285d7ef1787fd7dfbf8034b181f57f3cab243cd4b424712b0ec7251836e8c8f5ff3b79faf4b0d98c5416e919e8cf80c42df09adbe443d5b77905f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.24] - 2026-03-19
4
+
5
+ ### Added
6
+ - `/mark <label>` command: insert named markers/bookmarks in conversation, list all markers
7
+ - `/freq` command: word frequency analysis with top 20 words (excludes stop words)
8
+ - `/draft <text>` command: save text to draft buffer, show/clear/send draft
9
+ - `/revise <text>` command: replace content of last user message
10
+ - `/color [on|off]` command: toggle colorized output (strip ANSI codes when off)
11
+ - `/timestamps [on|off]` command: toggle timestamp display on messages
12
+ - `/top` command: scroll to top of message history
13
+ - `/bottom` command: scroll to bottom of message history
14
+ - `/head [N]` command: peek at first N messages (default 5)
15
+ - `/tail [N]` command: peek at last N messages (default 5)
16
+
17
+ ## [0.4.23] - 2026-03-19
18
+
19
+ ### Added
20
+ - `/wrap [N|off]` command: set custom word wrap width for message display
21
+ - `/number [on|off]` command: toggle message numbering with `[N]` prefix
22
+ - `/echo <text>` command: add user-defined system messages (notes/markers)
23
+ - `/env` command: show environment info (Ruby version, platform, terminal, PID, Legion gems)
24
+ - `/speak [on|off]` command: toggle text-to-speech for assistant messages (macOS only, via `say`)
25
+ - `/silent` command: toggle silent mode (responses tracked but not displayed), `[SILENT]` indicator
26
+ - `/ls [path]` command: list directory contents with directory markers
27
+ - `/pwd` command: show current working directory
28
+
3
29
  ## [0.4.22] - 2026-03-19
4
30
 
5
31
  ### Added
@@ -9,7 +9,8 @@ module Legion
9
9
  # rubocop:disable Metrics/ClassLength
10
10
  class MessageStream
11
11
  attr_reader :messages, :scroll_offset
12
- attr_accessor :mute_system, :highlights, :filter, :truncate_limit
12
+ attr_accessor :mute_system, :silent_mode, :highlights, :filter, :truncate_limit, :wrap_width, :show_numbers,
13
+ :colorize, :show_timestamps
13
14
 
14
15
  HIGHLIGHT_COLOR = "\e[1;33m"
15
16
  HIGHLIGHT_RESET = "\e[0m"
@@ -18,7 +19,12 @@ module Legion
18
19
  @messages = []
19
20
  @scroll_offset = 0
20
21
  @mute_system = false
22
+ @silent_mode = false
21
23
  @highlights = []
24
+ @wrap_width = nil
25
+ @show_numbers = false
26
+ @colorize = true
27
+ @show_timestamps = true
22
28
  end
23
29
 
24
30
  def add_message(role:, content:)
@@ -61,11 +67,13 @@ module Legion
61
67
  end
62
68
 
63
69
  def render(width:, height:)
64
- all_lines = build_all_lines(width)
70
+ effective_width = @wrap_width || width
71
+ all_lines = build_all_lines(effective_width)
65
72
  total = all_lines.size
66
73
  start_idx = [total - height - @scroll_offset, 0].max
67
74
  start_idx = [start_idx, total].min
68
75
  result = all_lines[start_idx, height] || []
76
+ result = result.map { |l| strip_ansi(l) } unless @colorize
69
77
  @last_visible_count = result.size
70
78
  result
71
79
  end
@@ -77,10 +85,11 @@ module Legion
77
85
  private
78
86
 
79
87
  def build_all_lines(width)
80
- filtered_messages.flat_map do |msg|
88
+ filtered_messages.each_with_index.flat_map do |msg, idx|
81
89
  next [] if @mute_system && msg[:role] == :system
90
+ next [] if @silent_mode && msg[:role] == :assistant
82
91
 
83
- render_message(msg, width)
92
+ render_message(msg, width, @show_numbers ? idx + 1 : nil)
84
93
  end
85
94
  end
86
95
 
@@ -99,8 +108,17 @@ module Legion
99
108
  end
100
109
  end
101
110
 
102
- def render_message(msg, width)
103
- role_lines(msg, width) + panel_lines(msg, width)
111
+ def render_message(msg, width, number = nil)
112
+ lines = role_lines(msg, width) + panel_lines(msg, width)
113
+ prepend_number(lines, number)
114
+ end
115
+
116
+ def prepend_number(lines, number)
117
+ return lines unless number
118
+
119
+ lines.each_with_index.map do |line, i|
120
+ i == 1 ? "[#{number}] #{line}" : line
121
+ end
104
122
  end
105
123
 
106
124
  def role_lines(msg, width)
@@ -114,15 +132,18 @@ module Legion
114
132
  end
115
133
 
116
134
  def user_lines(msg, _width)
117
- ts = format_timestamp(msg[:timestamp])
118
- header = "#{Theme.c(:accent, 'You')} #{Theme.c(:muted, ts)}"
119
135
  content = apply_highlights(msg[:content].to_s)
120
- lines = ['', "#{header}: #{content}"]
136
+ lines = ['', "#{user_header(msg[:timestamp])}: #{content}"]
121
137
  lines << reaction_line(msg) if msg[:reactions]&.any?
122
138
  lines.concat(annotation_lines(msg)) if msg[:annotations]&.any?
123
139
  lines
124
140
  end
125
141
 
142
+ def user_header(timestamp)
143
+ ts = @show_timestamps ? format_timestamp(timestamp) : ''
144
+ ts.empty? ? Theme.c(:accent, 'You') : "#{Theme.c(:accent, 'You')} #{Theme.c(:muted, ts)}"
145
+ end
146
+
126
147
  def format_timestamp(time)
127
148
  return '' unless time
128
149
 
@@ -195,6 +216,10 @@ module Legion
195
216
  panel.instance_variable_set(:@result, result) if result
196
217
  panel.instance_variable_set(:@error, error) if error
197
218
  end
219
+
220
+ def strip_ansi(text)
221
+ text.gsub(/\e\[[0-9;]*m/, '')
222
+ end
198
223
  end
199
224
  # rubocop:enable Metrics/ClassLength
200
225
  end
@@ -10,7 +10,7 @@ module Legion
10
10
  class StatusBar
11
11
  def initialize
12
12
  @state = { model: nil, tokens: 0, cost: 0.0, session: 'default', thinking: false, plan_mode: false,
13
- debug_mode: false, message_count: 0, multiline: false }
13
+ debug_mode: false, message_count: 0, multiline: false, silent: false }
14
14
  @notifications = []
15
15
  end
16
16
 
@@ -42,6 +42,7 @@ module Legion
42
42
  [
43
43
  model_segment,
44
44
  plan_segment,
45
+ silent_segment,
45
46
  multiline_segment,
46
47
  debug_segment,
47
48
  thinking_segment,
@@ -70,6 +71,12 @@ module Legion
70
71
  Theme.c(:accent, '[ML]')
71
72
  end
72
73
 
74
+ def silent_segment
75
+ return nil unless @state[:silent]
76
+
77
+ Theme.c(:warning, '[SILENT]')
78
+ end
79
+
73
80
  def debug_segment
74
81
  return nil unless @state[:debug_mode]
75
82
 
@@ -560,6 +560,101 @@ module Legion
560
560
  @message_stream.add_message(role: :system, content: "Truncated to last #{n} messages.")
561
561
  :handled
562
562
  end
563
+
564
+ def handle_mark(input)
565
+ label = input.split(nil, 2)[1]
566
+ return list_markers if label.nil?
567
+
568
+ msg = { role: :system, content: "--- #{label} ---", marker: label }
569
+ @message_stream.messages << msg
570
+ @status_bar.update(message_count: @message_stream.messages.size)
571
+ :handled
572
+ end
573
+
574
+ def list_markers
575
+ markers = @message_stream.messages.each_with_index.select { |m, _| m[:marker] }
576
+ if markers.empty?
577
+ @message_stream.add_message(role: :system, content: 'No markers set.')
578
+ else
579
+ lines = markers.map { |m, i| " [#{i}] #{m[:content]}" }
580
+ @message_stream.add_message(role: :system, content: "Markers:\n#{lines.join("\n")}")
581
+ end
582
+ :handled
583
+ end
584
+
585
+ def handle_head(input)
586
+ n = (input.split(nil, 2)[1] || '5').to_i.clamp(1, 500)
587
+ msgs = @message_stream.messages.first(n)
588
+ if msgs.empty?
589
+ @message_stream.add_message(role: :system, content: 'No messages.')
590
+ return :handled
591
+ end
592
+
593
+ lines = msgs.map { |m| " [#{m[:role]}] #{truncate_text(m[:content].to_s, 80)}" }
594
+ @message_stream.add_message(role: :system, content: "First #{msgs.size} message(s):\n#{lines.join("\n")}")
595
+ :handled
596
+ end
597
+
598
+ def handle_tail(input)
599
+ n = (input.split(nil, 2)[1] || '5').to_i.clamp(1, 500)
600
+ msgs = @message_stream.messages.last(n)
601
+ if msgs.empty?
602
+ @message_stream.add_message(role: :system, content: 'No messages.')
603
+ return :handled
604
+ end
605
+
606
+ lines = msgs.map { |m| " [#{m[:role]}] #{truncate_text(m[:content].to_s, 80)}" }
607
+ @message_stream.add_message(role: :system, content: "Last #{msgs.size} message(s):\n#{lines.join("\n")}")
608
+ :handled
609
+ end
610
+
611
+ def handle_draft(input)
612
+ arg = input.split(nil, 2)[1]
613
+ case arg
614
+ when nil
615
+ content = @draft ? "Draft: #{@draft}" : 'No draft saved.'
616
+ @message_stream.add_message(role: :system, content: content)
617
+ when 'clear'
618
+ @draft = nil
619
+ @message_stream.add_message(role: :system, content: 'Draft cleared.')
620
+ when 'send'
621
+ return draft_send
622
+ else
623
+ @draft = arg
624
+ @message_stream.add_message(role: :system, content: "Draft saved: #{@draft}")
625
+ end
626
+ :handled
627
+ end
628
+
629
+ def draft_send
630
+ unless @draft
631
+ @message_stream.add_message(role: :system, content: 'No draft to send.')
632
+ return :handled
633
+ end
634
+
635
+ text = @draft
636
+ @draft = nil
637
+ handle_user_message(text)
638
+ :handled
639
+ end
640
+
641
+ def handle_revise(input)
642
+ new_content = input.split(nil, 2)[1]
643
+ unless new_content
644
+ @message_stream.add_message(role: :system, content: 'Usage: /revise <new content>')
645
+ return :handled
646
+ end
647
+
648
+ msg = @message_stream.messages.reverse.find { |m| m[:role] == :user }
649
+ unless msg
650
+ @message_stream.add_message(role: :system, content: 'No user message to revise.')
651
+ return :handled
652
+ end
653
+
654
+ msg[:content] = new_content
655
+ @message_stream.add_message(role: :system, content: "Revised: #{new_content}")
656
+ :handled
657
+ end
563
658
  end
564
659
  end
565
660
  end
@@ -132,6 +132,28 @@ module Legion
132
132
  send_to_llm(@last_user_input)
133
133
  :handled
134
134
  end
135
+
136
+ def handle_speak(input)
137
+ unless RUBY_PLATFORM =~ /darwin/
138
+ @message_stream.add_message(role: :system, content: 'Text-to-speech is only available on macOS.')
139
+ return :handled
140
+ end
141
+
142
+ arg = input.split(nil, 2)[1]&.strip&.downcase
143
+ case arg
144
+ when 'on'
145
+ @speak_mode = true
146
+ @message_stream.add_message(role: :system, content: 'Text-to-speech ON.')
147
+ when 'off'
148
+ @speak_mode = false
149
+ @message_stream.add_message(role: :system, content: 'Text-to-speech OFF.')
150
+ else
151
+ @speak_mode = !@speak_mode
152
+ state = @speak_mode ? 'ON' : 'OFF'
153
+ @message_stream.add_message(role: :system, content: "Text-to-speech #{state}.")
154
+ end
155
+ :handled
156
+ end
135
157
  end
136
158
  end
137
159
  end
@@ -37,6 +37,12 @@ module Legion
37
37
 
38
38
  CALC_SAFE_PATTERN = %r{\A[\d\s+\-*/.()%]*\z}
39
39
  CALC_MATH_PATTERN = %r{\A[\d\s+\-*/.()%]*(Math\.\w+\([\d\s+\-*/.()%,]*\)[\d\s+\-*/.()%]*)*\z}
40
+ FREQ_STOP_WORDS = %w[
41
+ the a an is are was were be been have has had do does did will would could should
42
+ may might can shall to of in for on with at by from it this that i you we they
43
+ he she my your our their and or but not no if then so as
44
+ ].freeze
45
+ FREQ_ROW_FMT = ' %<rank>2d. %-<word>20s %<count>5d %<pct>5.1f%%'
40
46
 
41
47
  private
42
48
 
@@ -471,10 +477,75 @@ module Legion
471
477
  end
472
478
  end
473
479
 
480
+ def handle_wrap(input)
481
+ arg = input.split(nil, 2)[1]&.strip
482
+ if arg.nil?
483
+ status = @message_stream.wrap_width ? "#{@message_stream.wrap_width} columns" : 'off'
484
+ @message_stream.add_message(role: :system, content: "Wrap: #{status}")
485
+ elsif arg == 'off'
486
+ @message_stream.wrap_width = nil
487
+ @message_stream.add_message(role: :system, content: 'Word wrap disabled.')
488
+ else
489
+ n = arg.to_i
490
+ if n >= 20
491
+ @message_stream.wrap_width = n
492
+ @message_stream.add_message(role: :system, content: "Word wrap set to #{n} columns.")
493
+ else
494
+ @message_stream.add_message(role: :system, content: 'Usage: /wrap [N|off]')
495
+ end
496
+ end
497
+ :handled
498
+ end
499
+
500
+ def handle_number(input)
501
+ arg = input.split(nil, 2)[1]&.strip
502
+ case arg
503
+ when 'on'
504
+ @message_stream.show_numbers = true
505
+ @message_stream.add_message(role: :system, content: 'Message numbering ON.')
506
+ when 'off'
507
+ @message_stream.show_numbers = false
508
+ @message_stream.add_message(role: :system, content: 'Message numbering OFF.')
509
+ else
510
+ @message_stream.show_numbers = !@message_stream.show_numbers
511
+ state = @message_stream.show_numbers ? 'ON' : 'OFF'
512
+ @message_stream.add_message(role: :system, content: "Message numbering #{state}.")
513
+ end
514
+ :handled
515
+ end
516
+
474
517
  def safe_calc_expr?(expr)
475
518
  CALC_SAFE_PATTERN.match?(expr) || CALC_MATH_PATTERN.match?(expr)
476
519
  end
477
520
 
521
+ def handle_echo(input)
522
+ text = input.split(nil, 2)[1]&.strip
523
+ unless text && !text.empty?
524
+ @message_stream.add_message(role: :system, content: 'Usage: /echo <text>')
525
+ return :handled
526
+ end
527
+
528
+ @message_stream.add_message(role: :system, content: text)
529
+ :handled
530
+ end
531
+
532
+ def handle_env
533
+ width = terminal_width
534
+ height = terminal_height
535
+ legion_gems = Gem::Specification.select { |s| s.name.start_with?('legion-', 'lex-') }
536
+ .map { |s| "#{s.name} #{s.version}" }
537
+ .sort
538
+ lines = [
539
+ "Ruby: #{RUBY_VERSION} (#{RUBY_PLATFORM})",
540
+ "Terminal: #{width}x#{height}",
541
+ "PID: #{::Process.pid}",
542
+ "TTY: legion-tty v#{Legion::TTY::VERSION}",
543
+ "Gems (#{legion_gems.size}): #{legion_gems.join(', ')}"
544
+ ]
545
+ @message_stream.add_message(role: :system, content: lines.join("\n"))
546
+ :handled
547
+ end
548
+
478
549
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
479
550
  def handle_summary
480
551
  msgs = @message_stream.messages
@@ -541,6 +612,101 @@ module Legion
541
612
  rescue StandardError => e
542
613
  raise "command failed: #{e.message}"
543
614
  end
615
+
616
+ # rubocop:disable Metrics/AbcSize
617
+ def handle_ls(input)
618
+ path = File.expand_path(input.split(nil, 2)[1]&.strip || '.')
619
+ entries = Dir.entries(path).sort.reject { |e| ['.', '..'].include?(e) }
620
+ entries = entries.map { |e| File.directory?(File.join(path, e)) ? "#{e}/" : e }
621
+ @message_stream.add_message(role: :system, content: "#{path}:\n#{entries.join("\n")}")
622
+ :handled
623
+ rescue Errno::ENOENT, Errno::EACCES => e
624
+ @message_stream.add_message(role: :system, content: "ls: #{e.message}")
625
+ :handled
626
+ end
627
+ # rubocop:enable Metrics/AbcSize
628
+
629
+ def handle_pwd
630
+ @message_stream.add_message(role: :system, content: Dir.pwd)
631
+ :handled
632
+ end
633
+
634
+ def handle_silent
635
+ @silent_mode = !@silent_mode
636
+ @message_stream.silent_mode = @silent_mode
637
+ if @silent_mode
638
+ @status_bar.update(silent: true)
639
+ @message_stream.add_message(role: :system, content: 'Silent mode ON -- assistant responses hidden.')
640
+ else
641
+ @status_bar.update(silent: false)
642
+ @message_stream.add_message(role: :system, content: 'Silent mode OFF -- assistant responses visible.')
643
+ end
644
+ :handled
645
+ end
646
+
647
+ def handle_color(input)
648
+ arg = input.split(nil, 2)[1]&.strip
649
+ new_state = case arg
650
+ when 'on' then true
651
+ when 'off' then false
652
+ else !@message_stream.colorize
653
+ end
654
+ @message_stream.colorize = new_state
655
+ state_label = new_state ? 'ON' : 'OFF'
656
+ @message_stream.add_message(role: :system, content: "Color output #{state_label}.")
657
+ :handled
658
+ end
659
+
660
+ def handle_timestamps(input)
661
+ arg = input.split(nil, 2)[1]&.strip
662
+ new_state = case arg
663
+ when 'on' then true
664
+ when 'off' then false
665
+ else !@message_stream.show_timestamps
666
+ end
667
+ @message_stream.show_timestamps = new_state
668
+ state_label = new_state ? 'ON' : 'OFF'
669
+ @message_stream.add_message(role: :system, content: "Timestamps #{state_label}.")
670
+ :handled
671
+ end
672
+
673
+ def handle_top
674
+ @message_stream.scroll_up(@message_stream.messages.size * 5)
675
+ :handled
676
+ end
677
+
678
+ def handle_bottom
679
+ @message_stream.scroll_down(@message_stream.scroll_offset)
680
+ :handled
681
+ end
682
+
683
+ def handle_freq
684
+ words = collect_freq_words
685
+ if words.empty?
686
+ @message_stream.add_message(role: :system, content: 'No words to analyse.')
687
+ return :handled
688
+ end
689
+
690
+ top = words.tally.sort_by { |_, c| -c }.first(20)
691
+ header = ' # word count %'
692
+ lines = format_freq_lines(top, words.size)
693
+ @message_stream.add_message(role: :system,
694
+ content: "Word frequency (top #{top.size}):\n#{header}\n#{lines.join("\n")}")
695
+ :handled
696
+ end
697
+
698
+ def collect_freq_words
699
+ @message_stream.messages
700
+ .flat_map { |m| m[:content].to_s.downcase.scan(/[a-z']+/) }
701
+ .reject { |w| FREQ_STOP_WORDS.include?(w) || w.length < 2 }
702
+ end
703
+
704
+ def format_freq_lines(top, total)
705
+ top.map.with_index(1) do |(word, count), rank|
706
+ pct = (count.to_f / total * 100).round(1)
707
+ format(FREQ_ROW_FMT, rank: rank, word: word, count: count, pct: pct)
708
+ end
709
+ end
544
710
  end
545
711
  end
546
712
  end
@@ -37,7 +37,15 @@ module Legion
37
37
  /annotate /annotations /filter /truncate
38
38
  /tee /pipe
39
39
  /archive /archives
40
- /calc /rand].freeze
40
+ /calc /rand
41
+ /echo /env
42
+ /ls /pwd
43
+ /wrap /number
44
+ /speak /silent
45
+ /color /timestamps
46
+ /top /bottom /head /tail
47
+ /draft /revise
48
+ /mark /freq].freeze
41
49
 
42
50
  PERSONALITIES = {
43
51
  'default' => 'You are Legion, an async cognition engine and AI assistant. Be helpful and concise.',
@@ -79,6 +87,9 @@ module Legion
79
87
  @last_user_input = nil
80
88
  @highlights = []
81
89
  @multiline_mode = false
90
+ @speak_mode = false
91
+ @silent_mode = false
92
+ @draft = nil
82
93
  end
83
94
 
84
95
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
@@ -244,20 +255,35 @@ module Legion
244
255
  send_via_direct(message)
245
256
  end
246
257
 
258
+ # rubocop:disable Metrics/AbcSize
247
259
  def send_via_direct(message)
248
260
  return unless @llm_chat
249
261
 
250
262
  @status_bar.update(thinking: true)
251
263
  render_screen
252
264
  start_time = Time.now
265
+ response_text = +''
253
266
  response = @llm_chat.ask(message) do |chunk|
254
267
  @status_bar.update(thinking: false)
255
- @message_stream.append_streaming(chunk.content) if chunk.content
268
+ if chunk.content
269
+ response_text << chunk.content
270
+ @message_stream.append_streaming(chunk.content)
271
+ end
256
272
  render_screen
257
273
  end
258
274
  record_response_time(Time.now - start_time)
259
275
  @status_bar.update(thinking: false)
260
276
  track_response_tokens(response)
277
+ speak_response(response_text) if @speak_mode
278
+ end
279
+ # rubocop:enable Metrics/AbcSize
280
+
281
+ def speak_response(text)
282
+ return unless RUBY_PLATFORM =~ /darwin/
283
+
284
+ ::Process.spawn('say', text[0..500], err: '/dev/null', out: '/dev/null')
285
+ rescue StandardError
286
+ nil
261
287
  end
262
288
 
263
289
  def record_response_time(elapsed)
@@ -448,6 +474,24 @@ module Legion
448
474
  when '/archives' then handle_archives
449
475
  when '/calc' then handle_calc(input)
450
476
  when '/rand' then handle_rand(input)
477
+ when '/echo' then handle_echo(input)
478
+ when '/env' then handle_env
479
+ when '/ls' then handle_ls(input)
480
+ when '/pwd' then handle_pwd
481
+ when '/wrap' then handle_wrap(input)
482
+ when '/number' then handle_number(input)
483
+ when '/speak' then handle_speak(input)
484
+ when '/silent' then handle_silent
485
+ when '/color' then handle_color(input)
486
+ when '/timestamps' then handle_timestamps(input)
487
+ when '/top' then handle_top
488
+ when '/bottom' then handle_bottom
489
+ when '/head' then handle_head(input)
490
+ when '/tail' then handle_tail(input)
491
+ when '/draft' then handle_draft(input)
492
+ when '/revise' then handle_revise(input)
493
+ when '/mark' then handle_mark(input)
494
+ when '/freq' then handle_freq
451
495
  else :handled
452
496
  end
453
497
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module TTY
5
- VERSION = '0.4.22'
5
+ VERSION = '0.4.24'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-tty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.22
4
+ version: 0.4.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity