legion-tty 0.4.26 → 0.4.27
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 +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +21 -9
- data/lib/legion/tty/screens/chat/custom_commands.rb +36 -0
- data/lib/legion/tty/screens/chat/message_commands.rb +104 -0
- data/lib/legion/tty/screens/chat/ui_commands.rb +72 -0
- data/lib/legion/tty/screens/chat.rb +24 -2
- data/lib/legion/tty/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1f14c29bb844005733b887586662c8c08947f757567ec9438029b423b0c991e
|
|
4
|
+
data.tar.gz: 3e0244816b27e6174541fceba9aea3df6d87a7698f40766cf7341cf12b8ab0b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3af49d57dacb01af6ffa504d2e547d0ca920e0b675c119cdfdf95bf6f5926e5ba3f212144e3f139c005929abd52c147fa57dc06848aaae0a098a2444b84db20a
|
|
7
|
+
data.tar.gz: cb4b0902b3c3669231ff0ffc9bbe7b198f334fa8f3f68454a79345f808313c4f13ad63ea3c01efb36225beac388b84b28dc8c2a3d2558aead4f8c0a61acb69e6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.27] - 2026-03-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `/transform <op>` command: apply string transformations (upcase/downcase/reverse/strip/squeeze) to last assistant message
|
|
7
|
+
- `/concat` command: concatenate all assistant messages into a single system message
|
|
8
|
+
- `/split <N> [pattern]` command: split a message at index N by pattern (default: paragraph break)
|
|
9
|
+
- `/swap <A> <B>` command: swap two messages by index
|
|
10
|
+
- `/prefix [text|clear]` command: set/show/clear auto-prefix for outgoing messages
|
|
11
|
+
- `/suffix [text|clear]` command: set/show/clear auto-suffix for outgoing messages
|
|
12
|
+
- `/timer <seconds> [message] | cancel` command: countdown timer with background thread and status bar notification
|
|
13
|
+
- `/notify <message>` command: send a manual toast notification to status bar
|
|
14
|
+
- `apply_message_decorators` method: prepends prefix and appends suffix to user messages before LLM send
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Total slash commands: 115
|
|
18
|
+
- Total specs: 1817 examples, 150 files
|
|
19
|
+
|
|
3
20
|
## [0.4.26] - 2026-03-19
|
|
4
21
|
|
|
5
22
|
### Added
|
data/README.md
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Rich terminal UI for the LegionIO async cognition engine.
|
|
4
4
|
|
|
5
|
-
**Version**: 0.4.
|
|
5
|
+
**Version**: 0.4.27
|
|
6
6
|
|
|
7
|
-
Think Claude Code meets Codex CLI, but for LegionIO: onboarding wizard with identity detection, streaming AI chat shell with
|
|
7
|
+
Think Claude Code meets Codex CLI, but for LegionIO: onboarding wizard with identity detection, streaming AI chat shell with 115 slash commands, operational dashboard, extensions browser, config editor, and session persistence - all rendered with the [tty-ruby](https://ttytoolkit.org/) gem ecosystem.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
- **Onboarding wizard** - First-run setup with Kerberos identity detection, GitHub profile probing, environment scanning, and LLM provider selection
|
|
12
12
|
- **Digital rain intro** - Matrix-style rain using discovered LEX extension names
|
|
13
|
-
- **AI chat shell** - Streaming LLM chat with
|
|
13
|
+
- **AI chat shell** - Streaming LLM chat with 115 slash commands, tab completion, markdown rendering, and tool panels
|
|
14
14
|
- **Operational dashboard** - Service/LLM status, extension inventory, system info, panel navigation (Ctrl+D or `/dashboard`)
|
|
15
15
|
- **Extensions browser** - Browse installed LEX gems by category with detail view and homepage opener ('o' key)
|
|
16
16
|
- **Config viewer/editor** - View and edit `~/.legionio/settings/*.json` with vault:// masking and JSON validation
|
|
@@ -198,6 +198,18 @@ legion chat prompt "explain async cognition"
|
|
|
198
198
|
| `/wc` | Show word count statistics per role |
|
|
199
199
|
| `/welcome` | Redisplay the welcome message |
|
|
200
200
|
| `/wrap [N\|off]` | Set custom word wrap width |
|
|
201
|
+
| `/ago <N>` | Show what was said N messages ago |
|
|
202
|
+
| `/concat` | Concatenate all assistant messages into one |
|
|
203
|
+
| `/goto <N>` | Jump to specific message by index |
|
|
204
|
+
| `/inject <role> <text>` | Inject a message with specific role |
|
|
205
|
+
| `/notify <message>` | Send a toast notification to status bar |
|
|
206
|
+
| `/prefix [text\|clear]` | Set/show/clear auto-prefix for outgoing messages |
|
|
207
|
+
| `/split <N> [pattern]` | Split a message by pattern into multiple messages |
|
|
208
|
+
| `/stopwatch [start\|stop\|lap\|reset]` | Built-in stopwatch with MM:SS.ms format |
|
|
209
|
+
| `/suffix [text\|clear]` | Set/show/clear auto-suffix for outgoing messages |
|
|
210
|
+
| `/swap <A> <B>` | Swap two messages by index |
|
|
211
|
+
| `/timer <seconds> [message]` | Countdown timer with notification on expiry |
|
|
212
|
+
| `/transform <op>` | Apply string transformation to last assistant message |
|
|
201
213
|
|
|
202
214
|
## Hotkeys
|
|
203
215
|
|
|
@@ -222,13 +234,13 @@ legion-tty
|
|
|
222
234
|
|
|
223
235
|
Screens/
|
|
224
236
|
Onboarding # First-run wizard (rain -> intro -> wizard -> reveal)
|
|
225
|
-
Chat # AI chat REPL with streaming +
|
|
237
|
+
Chat # AI chat REPL with streaming + 115 slash commands
|
|
226
238
|
SessionCommands # save/load/sessions/delete/rename/import/merge/autosave
|
|
227
239
|
ExportCommands # export/bookmark/html/json/markdown/yaml
|
|
228
|
-
MessageCommands # compact/copy/diff/search/grep/undo/pin/pins/react/tag/fav/sort/count
|
|
229
|
-
UiCommands # help/clear/dashboard/hotkeys/palette/context/stats/debug/history/uptime/time/tips/welcome/focus/wc/log/version/mute + calc/rand/mark/freq/color/timestamps/top/bottom/head/tail/echo/env/speak/silent/wrap/number/truncate/about/commands/ask/define/status/prefs
|
|
240
|
+
MessageCommands # compact/copy/diff/search/grep/undo/pin/pins/react/tag/fav/sort/count/transform/concat/split/swap
|
|
241
|
+
UiCommands # help/clear/dashboard/hotkeys/palette/context/stats/debug/history/uptime/time/tips/welcome/focus/wc/log/version/mute + calc/rand/mark/freq/color/timestamps/top/bottom/head/tail/echo/env/speak/silent/wrap/number/truncate/about/commands/ask/define/status/prefs/timer/notify
|
|
230
242
|
ModelCommands # model/system/personality switching/retry/chain/info/scroll/summary/prompt/reset/replace/highlight/multiline/filter/annotate/annotations
|
|
231
|
-
CustomCommands # alias/snippet/template/macro/draft/revise/tee/pipe/archive/archives/ls/pwd/
|
|
243
|
+
CustomCommands # alias/snippet/template/macro/draft/revise/tee/pipe/archive/archives/ls/pwd/prefix/suffix
|
|
232
244
|
Dashboard # Service/LLM status, panel navigation (j/k/1-5)
|
|
233
245
|
Extensions # LEX gem browser by category with homepage opener
|
|
234
246
|
Config # Settings file viewer/editor with JSON validation
|
|
@@ -277,8 +289,8 @@ Boot logs go to `~/.legionio/logs/tty-boot.log`.
|
|
|
277
289
|
|
|
278
290
|
```bash
|
|
279
291
|
bundle install
|
|
280
|
-
bundle exec rspec #
|
|
281
|
-
bundle exec rubocop #
|
|
292
|
+
bundle exec rspec # 1817 examples, 0 failures
|
|
293
|
+
bundle exec rubocop # 150 files, 0 offenses
|
|
282
294
|
```
|
|
283
295
|
|
|
284
296
|
## License
|
|
@@ -408,6 +408,42 @@ module Legion
|
|
|
408
408
|
:handled
|
|
409
409
|
end
|
|
410
410
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
411
|
+
|
|
412
|
+
def handle_prefix(input)
|
|
413
|
+
arg = input.split(nil, 2)[1]
|
|
414
|
+
if arg.nil?
|
|
415
|
+
if @message_prefix
|
|
416
|
+
@message_stream.add_message(role: :system, content: "Current prefix: \"#{@message_prefix}\"")
|
|
417
|
+
else
|
|
418
|
+
@message_stream.add_message(role: :system, content: 'No prefix set. Usage: /prefix <text>')
|
|
419
|
+
end
|
|
420
|
+
elsif arg == 'clear'
|
|
421
|
+
@message_prefix = nil
|
|
422
|
+
@message_stream.add_message(role: :system, content: 'Prefix cleared.')
|
|
423
|
+
else
|
|
424
|
+
@message_prefix = arg
|
|
425
|
+
@message_stream.add_message(role: :system, content: "Prefix set: \"#{@message_prefix}\"")
|
|
426
|
+
end
|
|
427
|
+
:handled
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def handle_suffix(input)
|
|
431
|
+
arg = input.split(nil, 2)[1]
|
|
432
|
+
if arg.nil?
|
|
433
|
+
if @message_suffix
|
|
434
|
+
@message_stream.add_message(role: :system, content: "Current suffix: \"#{@message_suffix}\"")
|
|
435
|
+
else
|
|
436
|
+
@message_stream.add_message(role: :system, content: 'No suffix set. Usage: /suffix <text>')
|
|
437
|
+
end
|
|
438
|
+
elsif arg == 'clear'
|
|
439
|
+
@message_suffix = nil
|
|
440
|
+
@message_stream.add_message(role: :system, content: 'Suffix cleared.')
|
|
441
|
+
else
|
|
442
|
+
@message_suffix = arg
|
|
443
|
+
@message_stream.add_message(role: :system, content: "Suffix set: \"#{@message_suffix}\"")
|
|
444
|
+
end
|
|
445
|
+
:handled
|
|
446
|
+
end
|
|
411
447
|
end
|
|
412
448
|
end
|
|
413
449
|
end
|
|
@@ -6,6 +6,7 @@ module Legion
|
|
|
6
6
|
class Chat < Base
|
|
7
7
|
module MessageCommands
|
|
8
8
|
INJECT_VALID_ROLES = %w[user assistant system].freeze
|
|
9
|
+
TRANSFORM_OPS = %w[upcase downcase reverse strip squeeze].freeze
|
|
9
10
|
|
|
10
11
|
private
|
|
11
12
|
|
|
@@ -677,6 +678,43 @@ module Legion
|
|
|
677
678
|
:handled
|
|
678
679
|
end
|
|
679
680
|
|
|
681
|
+
def handle_transform(input)
|
|
682
|
+
op = input.split(nil, 2)[1]
|
|
683
|
+
unless op && TRANSFORM_OPS.include?(op)
|
|
684
|
+
@message_stream.add_message(
|
|
685
|
+
role: :system,
|
|
686
|
+
content: "Usage: /transform <#{TRANSFORM_OPS.join('|')}>"
|
|
687
|
+
)
|
|
688
|
+
return :handled
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
msg = @message_stream.messages.reverse.find { |m| m[:role] == :assistant }
|
|
692
|
+
unless msg
|
|
693
|
+
@message_stream.add_message(role: :system, content: 'No assistant message to transform.')
|
|
694
|
+
return :handled
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
msg[:content] = msg[:content].to_s.send(op)
|
|
698
|
+
@message_stream.add_message(role: :system, content: "Transformed last assistant message: #{op}.")
|
|
699
|
+
:handled
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
def handle_concat
|
|
703
|
+
assistant_msgs = @message_stream.messages.select { |m| m[:role] == :assistant }
|
|
704
|
+
if assistant_msgs.empty?
|
|
705
|
+
@message_stream.add_message(role: :system, content: 'No assistant messages to concatenate.')
|
|
706
|
+
return :handled
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
combined = assistant_msgs.map { |m| m[:content].to_s }.join("\n\n")
|
|
710
|
+
@message_stream.add_message(role: :system, content: combined)
|
|
711
|
+
@message_stream.add_message(
|
|
712
|
+
role: :system,
|
|
713
|
+
content: "Concatenated #{assistant_msgs.size} assistant message(s)."
|
|
714
|
+
)
|
|
715
|
+
:handled
|
|
716
|
+
end
|
|
717
|
+
|
|
680
718
|
def handle_ago(input)
|
|
681
719
|
n = (input.split(nil, 2)[1] || '1').to_i
|
|
682
720
|
msgs = @message_stream.messages
|
|
@@ -697,6 +735,72 @@ module Legion
|
|
|
697
735
|
)
|
|
698
736
|
:handled
|
|
699
737
|
end
|
|
738
|
+
|
|
739
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
740
|
+
def handle_split(input)
|
|
741
|
+
parts = input.split(nil, 3)
|
|
742
|
+
n_str = parts[1]
|
|
743
|
+
pattern_str = parts[2]
|
|
744
|
+
|
|
745
|
+
unless n_str&.match?(/\A\d+\z/)
|
|
746
|
+
@message_stream.add_message(role: :system, content: 'Usage: /split <N> [pattern]')
|
|
747
|
+
return :handled
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
idx = n_str.to_i
|
|
751
|
+
msgs = @message_stream.messages
|
|
752
|
+
unless idx < msgs.size
|
|
753
|
+
@message_stream.add_message(role: :system,
|
|
754
|
+
content: "No message at index #{idx} (#{msgs.size} message(s)).")
|
|
755
|
+
return :handled
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
pattern = pattern_str || "\n\n"
|
|
759
|
+
original = msgs[idx]
|
|
760
|
+
segments = original[:content].to_s.split(pattern).reject(&:empty?)
|
|
761
|
+
|
|
762
|
+
if segments.size <= 1
|
|
763
|
+
@message_stream.add_message(role: :system, content: 'Message could not be split (no pattern found).')
|
|
764
|
+
return :handled
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
new_msgs = segments.map { |seg| { role: original[:role], content: seg } }
|
|
768
|
+
msgs.delete_at(idx)
|
|
769
|
+
msgs.insert(idx, *new_msgs)
|
|
770
|
+
@status_bar.update(message_count: msgs.size)
|
|
771
|
+
@message_stream.add_message(role: :system, content: "Split into #{new_msgs.size} messages.")
|
|
772
|
+
:handled
|
|
773
|
+
end
|
|
774
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
775
|
+
|
|
776
|
+
# rubocop:disable Metrics/AbcSize
|
|
777
|
+
def handle_swap(input)
|
|
778
|
+
parts = input.split(nil, 3)
|
|
779
|
+
a_str = parts[1]
|
|
780
|
+
b_str = parts[2]
|
|
781
|
+
|
|
782
|
+
unless a_str&.match?(/\A\d+\z/) && b_str&.match?(/\A\d+\z/)
|
|
783
|
+
@message_stream.add_message(role: :system, content: 'Usage: /swap <A> <B>')
|
|
784
|
+
return :handled
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
a = a_str.to_i
|
|
788
|
+
b = b_str.to_i
|
|
789
|
+
msgs = @message_stream.messages
|
|
790
|
+
|
|
791
|
+
if a >= msgs.size || b >= msgs.size
|
|
792
|
+
@message_stream.add_message(
|
|
793
|
+
role: :system,
|
|
794
|
+
content: "Index out of range (conversation has #{msgs.size} message(s))."
|
|
795
|
+
)
|
|
796
|
+
return :handled
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
msgs[a], msgs[b] = msgs[b], msgs[a]
|
|
800
|
+
@message_stream.add_message(role: :system, content: "Swapped messages #{a} and #{b}.")
|
|
801
|
+
:handled
|
|
802
|
+
end
|
|
803
|
+
# rubocop:enable Metrics/AbcSize
|
|
700
804
|
end
|
|
701
805
|
end
|
|
702
806
|
end
|
|
@@ -944,6 +944,78 @@ module Legion
|
|
|
944
944
|
mins = total_ms / 60_000
|
|
945
945
|
format('%<mins>02d:%<secs>02d.%<ms>03d', mins: mins, secs: secs, ms: ms)
|
|
946
946
|
end
|
|
947
|
+
|
|
948
|
+
def handle_timer(input)
|
|
949
|
+
arg = input.split(nil, 2)[1]&.strip
|
|
950
|
+
|
|
951
|
+
return timer_status if arg.nil? || arg.empty?
|
|
952
|
+
|
|
953
|
+
return timer_cancel if arg == 'cancel'
|
|
954
|
+
|
|
955
|
+
seconds_str, *msg_parts = arg.split
|
|
956
|
+
unless seconds_str.match?(/\A\d+\z/)
|
|
957
|
+
@message_stream.add_message(role: :system, content: 'Usage: /timer <seconds> [message] | cancel')
|
|
958
|
+
return :handled
|
|
959
|
+
end
|
|
960
|
+
|
|
961
|
+
seconds = seconds_str.to_i
|
|
962
|
+
message = msg_parts.empty? ? 'Timer expired!' : msg_parts.join(' ')
|
|
963
|
+
start_timer(seconds, message)
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
def handle_notify(input)
|
|
967
|
+
text = input.split(nil, 2)[1]&.strip
|
|
968
|
+
unless text && !text.empty?
|
|
969
|
+
@message_stream.add_message(role: :system, content: 'Usage: /notify <message>')
|
|
970
|
+
return :handled
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
@status_bar.notify(message: text, level: :info, ttl: 5)
|
|
974
|
+
:handled
|
|
975
|
+
end
|
|
976
|
+
|
|
977
|
+
def timer_status
|
|
978
|
+
if @timer_thread&.alive?
|
|
979
|
+
remaining = @timer_end - Time.now
|
|
980
|
+
remaining = [remaining, 0].max.ceil
|
|
981
|
+
@message_stream.add_message(role: :system, content: "Timer running: #{remaining}s remaining.")
|
|
982
|
+
else
|
|
983
|
+
@message_stream.add_message(role: :system, content: 'No active timer.')
|
|
984
|
+
end
|
|
985
|
+
:handled
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
def timer_cancel
|
|
989
|
+
if @timer_thread&.alive?
|
|
990
|
+
@timer_thread.kill
|
|
991
|
+
@timer_thread = nil
|
|
992
|
+
@timer_end = nil
|
|
993
|
+
@message_stream.add_message(role: :system, content: 'Timer cancelled.')
|
|
994
|
+
else
|
|
995
|
+
@message_stream.add_message(role: :system, content: 'No active timer to cancel.')
|
|
996
|
+
end
|
|
997
|
+
:handled
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
def start_timer(seconds, message)
|
|
1001
|
+
if @timer_thread&.alive?
|
|
1002
|
+
@message_stream.add_message(role: :system,
|
|
1003
|
+
content: 'A timer is already running. Use /timer cancel first.')
|
|
1004
|
+
return :handled
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
@timer_end = Time.now + seconds
|
|
1008
|
+
@message_stream.add_message(role: :system, content: "Timer set for #{seconds}s: #{message}")
|
|
1009
|
+
@status_bar.notify(message: "Timer: #{seconds}s", level: :info, ttl: 3)
|
|
1010
|
+
@timer_thread = Thread.new do
|
|
1011
|
+
sleep(seconds)
|
|
1012
|
+
@message_stream.add_message(role: :system, content: "Timer: #{message}")
|
|
1013
|
+
@status_bar.notify(message: message, level: :info, ttl: 10)
|
|
1014
|
+
@timer_thread = nil
|
|
1015
|
+
@timer_end = nil
|
|
1016
|
+
end
|
|
1017
|
+
:handled
|
|
1018
|
+
end
|
|
947
1019
|
end
|
|
948
1020
|
end
|
|
949
1021
|
end
|
|
@@ -50,7 +50,11 @@ module Legion
|
|
|
50
50
|
/ask /define
|
|
51
51
|
/status /prefs
|
|
52
52
|
/stopwatch /ago
|
|
53
|
-
/goto /inject
|
|
53
|
+
/goto /inject
|
|
54
|
+
/transform /concat
|
|
55
|
+
/prefix /suffix
|
|
56
|
+
/split /swap
|
|
57
|
+
/timer /notify].freeze
|
|
54
58
|
|
|
55
59
|
PERSONALITIES = {
|
|
56
60
|
'default' => 'You are Legion, an async cognition engine and AI assistant. Be helpful and concise.',
|
|
@@ -97,6 +101,9 @@ module Legion
|
|
|
97
101
|
@draft = nil
|
|
98
102
|
@stopwatch_start = nil
|
|
99
103
|
@stopwatch_elapsed = 0
|
|
104
|
+
@timer_thread = nil
|
|
105
|
+
@message_prefix = nil
|
|
106
|
+
@message_suffix = nil
|
|
100
107
|
end
|
|
101
108
|
|
|
102
109
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
@@ -167,7 +174,7 @@ module Legion
|
|
|
167
174
|
@message_stream.add_message(role: :system, content: '(bookmarked)')
|
|
168
175
|
else
|
|
169
176
|
@message_stream.add_message(role: :assistant, content: '')
|
|
170
|
-
send_to_llm(input)
|
|
177
|
+
send_to_llm(apply_message_decorators(input))
|
|
171
178
|
end
|
|
172
179
|
@status_bar.update(message_count: @message_stream.messages.size)
|
|
173
180
|
check_autosave
|
|
@@ -509,6 +516,14 @@ module Legion
|
|
|
509
516
|
when '/ago' then handle_ago(input)
|
|
510
517
|
when '/goto' then handle_goto(input)
|
|
511
518
|
when '/inject' then handle_inject(input)
|
|
519
|
+
when '/transform' then handle_transform(input)
|
|
520
|
+
when '/concat' then handle_concat
|
|
521
|
+
when '/prefix' then handle_prefix(input)
|
|
522
|
+
when '/suffix' then handle_suffix(input)
|
|
523
|
+
when '/split' then handle_split(input)
|
|
524
|
+
when '/swap' then handle_swap(input)
|
|
525
|
+
when '/timer' then handle_timer(input)
|
|
526
|
+
when '/notify' then handle_notify(input)
|
|
512
527
|
else :handled
|
|
513
528
|
end
|
|
514
529
|
end
|
|
@@ -644,6 +659,13 @@ module Legion
|
|
|
644
659
|
cost: @token_tracker.total_cost
|
|
645
660
|
)
|
|
646
661
|
end
|
|
662
|
+
|
|
663
|
+
def apply_message_decorators(message)
|
|
664
|
+
result = message
|
|
665
|
+
result = "#{@message_prefix}#{result}" if @message_prefix
|
|
666
|
+
result = "#{result}#{@message_suffix}" if @message_suffix
|
|
667
|
+
result
|
|
668
|
+
end
|
|
647
669
|
end
|
|
648
670
|
# rubocop:enable Metrics/ClassLength
|
|
649
671
|
end
|
data/lib/legion/tty/version.rb
CHANGED