legion-tty 0.4.17 → 0.4.18
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 +8 -0
- data/lib/legion/tty/screens/chat/message_commands.rb +48 -0
- data/lib/legion/tty/screens/chat/model_commands.rb +18 -0
- data/lib/legion/tty/screens/chat/session_commands.rb +23 -0
- data/lib/legion/tty/screens/chat/ui_commands.rb +10 -0
- data/lib/legion/tty/screens/chat.rb +31 -11
- 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: 6faa2820fd40c949a40eb6e336cf75067e3ab400d9631980fc49fda2f0b5dc29
|
|
4
|
+
data.tar.gz: 35c674132cb7f41f0c2c8d4150afe10c99f4ac30fed392e78477fb074c383d67
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3766b9312e3fa871e39cfc1b42eac013c82ab680509f10f6f034474fda23381d1bfa4bc67e9e7a95d1557ca546ae3cd6d209818d4fe814111b33cc35b167f46f
|
|
7
|
+
data.tar.gz: c0644da99c0c8cd8b0002366e2e9f46edf23d99a157a8e5652165e3112442516da2178e16ca91596afdbef04a57fe379a347bd6f5c6883903bae24f599a294dd
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.18] - 2026-03-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `/focus` command: toggle minimal UI mode (hides status bar for distraction-free writing)
|
|
7
|
+
- `/retry` command: resend last user message to LLM, replacing previous assistant response
|
|
8
|
+
- `/merge <session>` command: import messages from another saved session into current conversation
|
|
9
|
+
- `/sort [length|role]` command: display messages sorted by character length or grouped by role
|
|
10
|
+
|
|
3
11
|
## [0.4.17] - 2026-03-19
|
|
4
12
|
|
|
5
13
|
### Added
|
|
@@ -299,6 +299,54 @@ module Legion
|
|
|
299
299
|
end
|
|
300
300
|
end
|
|
301
301
|
|
|
302
|
+
def handle_sort(input)
|
|
303
|
+
arg = input.split(nil, 2)[1]
|
|
304
|
+
if arg == 'role'
|
|
305
|
+
sort_by_role
|
|
306
|
+
else
|
|
307
|
+
sort_by_length
|
|
308
|
+
end
|
|
309
|
+
:handled
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def sort_by_length
|
|
313
|
+
msgs = @message_stream.messages
|
|
314
|
+
if msgs.empty?
|
|
315
|
+
@message_stream.add_message(role: :system, content: 'No messages to sort.')
|
|
316
|
+
return
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
sorted = msgs.sort_by { |m| -m[:content].to_s.length }.first(10)
|
|
320
|
+
lines = sorted.map { |m| format_length_line(m) }
|
|
321
|
+
@message_stream.add_message(
|
|
322
|
+
role: :system,
|
|
323
|
+
content: "Messages by length (top #{sorted.size}):\n#{lines.join("\n")}"
|
|
324
|
+
)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def format_length_line(msg)
|
|
328
|
+
len = msg[:content].to_s.length
|
|
329
|
+
preview = truncate_text(msg[:content].to_s, 60)
|
|
330
|
+
" [#{msg[:role]}] (#{len} chars) #{preview}"
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def sort_by_role
|
|
334
|
+
msgs = @message_stream.messages
|
|
335
|
+
if msgs.empty?
|
|
336
|
+
@message_stream.add_message(role: :system, content: 'No messages to sort.')
|
|
337
|
+
return
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
counts = msgs.group_by { |m| m[:role] }
|
|
341
|
+
.transform_values(&:size)
|
|
342
|
+
.sort_by { |_, count| -count }
|
|
343
|
+
lines = counts.map { |role, count| " #{role}: #{count}" }
|
|
344
|
+
@message_stream.add_message(
|
|
345
|
+
role: :system,
|
|
346
|
+
content: "Messages by role:\n#{lines.join("\n")}"
|
|
347
|
+
)
|
|
348
|
+
end
|
|
349
|
+
|
|
302
350
|
def favorites_file
|
|
303
351
|
File.expand_path('~/.legionio/favorites.json')
|
|
304
352
|
end
|
|
@@ -4,6 +4,7 @@ module Legion
|
|
|
4
4
|
module TTY
|
|
5
5
|
module Screens
|
|
6
6
|
class Chat < Base
|
|
7
|
+
# rubocop:disable Metrics/ModuleLength
|
|
7
8
|
module ModelCommands
|
|
8
9
|
private
|
|
9
10
|
|
|
@@ -116,7 +117,24 @@ module Legion
|
|
|
116
117
|
@message_stream.add_message(role: :system, content: "Personality set to: #{name} (no active LLM)")
|
|
117
118
|
end
|
|
118
119
|
end
|
|
120
|
+
|
|
121
|
+
def handle_retry
|
|
122
|
+
unless @last_user_input
|
|
123
|
+
@message_stream.add_message(role: :system, content: 'Nothing to retry.')
|
|
124
|
+
return :handled
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
msgs = @message_stream.messages
|
|
128
|
+
last_assistant_idx = msgs.rindex { |m| m[:role] == :assistant }
|
|
129
|
+
msgs.delete_at(last_assistant_idx) if last_assistant_idx
|
|
130
|
+
|
|
131
|
+
@status_bar.notify(message: 'Retrying...', level: :info, ttl: 2)
|
|
132
|
+
@message_stream.add_message(role: :assistant, content: '')
|
|
133
|
+
send_to_llm(@last_user_input)
|
|
134
|
+
:handled
|
|
135
|
+
end
|
|
119
136
|
end
|
|
137
|
+
# rubocop:enable Metrics/ModuleLength
|
|
120
138
|
end
|
|
121
139
|
end
|
|
122
140
|
end
|
|
@@ -156,6 +156,29 @@ module Legion
|
|
|
156
156
|
rescue StandardError
|
|
157
157
|
nil
|
|
158
158
|
end
|
|
159
|
+
|
|
160
|
+
def handle_merge(input)
|
|
161
|
+
name = input.split(nil, 2)[1]
|
|
162
|
+
unless name
|
|
163
|
+
@message_stream.add_message(role: :system, content: 'Usage: /merge <session-name>')
|
|
164
|
+
return :handled
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
data = @session_store.load(name)
|
|
168
|
+
unless data
|
|
169
|
+
@message_stream.add_message(role: :system, content: 'Session not found.')
|
|
170
|
+
return :handled
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
imported = data[:messages]
|
|
174
|
+
@message_stream.messages.concat(imported)
|
|
175
|
+
@status_bar.update(message_count: @message_stream.messages.size)
|
|
176
|
+
@message_stream.add_message(
|
|
177
|
+
role: :system,
|
|
178
|
+
content: "Merged #{imported.size} messages from '#{name}'."
|
|
179
|
+
)
|
|
180
|
+
:handled
|
|
181
|
+
end
|
|
159
182
|
end
|
|
160
183
|
# rubocop:enable Metrics/ModuleLength
|
|
161
184
|
end
|
|
@@ -303,6 +303,16 @@ module Legion
|
|
|
303
303
|
)
|
|
304
304
|
:handled
|
|
305
305
|
end
|
|
306
|
+
|
|
307
|
+
def handle_focus
|
|
308
|
+
@focus_mode = !@focus_mode
|
|
309
|
+
if @focus_mode
|
|
310
|
+
@status_bar.notify(message: 'Focus mode ON', level: :info, ttl: 2)
|
|
311
|
+
else
|
|
312
|
+
@status_bar.notify(message: 'Focus mode OFF', level: :info, ttl: 2)
|
|
313
|
+
end
|
|
314
|
+
:handled
|
|
315
|
+
end
|
|
306
316
|
end
|
|
307
317
|
# rubocop:enable Metrics/ModuleLength
|
|
308
318
|
end
|
|
@@ -30,7 +30,8 @@ module Legion
|
|
|
30
30
|
/theme /search /grep /stats /personality /undo /history /pin /pins /rename
|
|
31
31
|
/context /alias /snippet /debug /uptime /time /bookmark /welcome /tips
|
|
32
32
|
/wc /import /mute /autosave /react /macro /tag /tags /repeat /count
|
|
33
|
-
/template /fav /favs /log /version
|
|
33
|
+
/template /fav /favs /log /version
|
|
34
|
+
/focus /retry /merge /sort].freeze
|
|
34
35
|
|
|
35
36
|
PERSONALITIES = {
|
|
36
37
|
'default' => 'You are Legion, an async cognition engine and AI assistant. Be helpful and concise.',
|
|
@@ -68,6 +69,8 @@ module Legion
|
|
|
68
69
|
@recording_macro = nil
|
|
69
70
|
@macro_buffer = []
|
|
70
71
|
@last_command = nil
|
|
72
|
+
@focus_mode = false
|
|
73
|
+
@last_user_input = nil
|
|
71
74
|
end
|
|
72
75
|
|
|
73
76
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
@@ -131,6 +134,7 @@ module Legion
|
|
|
131
134
|
end
|
|
132
135
|
|
|
133
136
|
def handle_user_message(input)
|
|
137
|
+
@last_user_input = input
|
|
134
138
|
@message_stream.add_message(role: :user, content: input)
|
|
135
139
|
if @plan_mode
|
|
136
140
|
@message_stream.add_message(role: :system, content: '(bookmarked)')
|
|
@@ -160,16 +164,9 @@ module Legion
|
|
|
160
164
|
end
|
|
161
165
|
|
|
162
166
|
def render(width, height)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
extra_rows = dbg ? 1 : 0
|
|
167
|
-
stream_height = [height - 2 - extra_rows, 1].max
|
|
168
|
-
stream_lines = @message_stream.render(width: width, height: stream_height)
|
|
169
|
-
@status_bar.update(scroll: @message_stream.scroll_position)
|
|
170
|
-
lines = stream_lines + [divider, bar_line]
|
|
171
|
-
lines << dbg if dbg
|
|
172
|
-
lines
|
|
167
|
+
return render_focus(width, height) if @focus_mode
|
|
168
|
+
|
|
169
|
+
render_normal(width, height)
|
|
173
170
|
end
|
|
174
171
|
|
|
175
172
|
def handle_input(key)
|
|
@@ -187,6 +184,25 @@ module Legion
|
|
|
187
184
|
|
|
188
185
|
private
|
|
189
186
|
|
|
187
|
+
def render_focus(width, height)
|
|
188
|
+
stream_lines = @message_stream.render(width: width, height: [height, 1].max)
|
|
189
|
+
@status_bar.update(scroll: @message_stream.scroll_position)
|
|
190
|
+
stream_lines
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def render_normal(width, height)
|
|
194
|
+
bar_line = @status_bar.render(width: width)
|
|
195
|
+
divider = Theme.c(:muted, '-' * width)
|
|
196
|
+
dbg = debug_segment
|
|
197
|
+
extra_rows = dbg ? 1 : 0
|
|
198
|
+
stream_height = [height - 2 - extra_rows, 1].max
|
|
199
|
+
stream_lines = @message_stream.render(width: width, height: stream_height)
|
|
200
|
+
@status_bar.update(scroll: @message_stream.scroll_position)
|
|
201
|
+
lines = stream_lines + [divider, bar_line]
|
|
202
|
+
lines << dbg if dbg
|
|
203
|
+
lines
|
|
204
|
+
end
|
|
205
|
+
|
|
190
206
|
def record_macro_step(input, cmd, result)
|
|
191
207
|
return unless @recording_macro
|
|
192
208
|
return if cmd == '/macro'
|
|
@@ -385,6 +401,10 @@ module Legion
|
|
|
385
401
|
when '/favs' then handle_favs
|
|
386
402
|
when '/log' then handle_log(input)
|
|
387
403
|
when '/version' then handle_version
|
|
404
|
+
when '/focus' then handle_focus
|
|
405
|
+
when '/retry' then handle_retry
|
|
406
|
+
when '/merge' then handle_merge(input)
|
|
407
|
+
when '/sort' then handle_sort(input)
|
|
388
408
|
else :handled
|
|
389
409
|
end
|
|
390
410
|
end
|
data/lib/legion/tty/version.rb
CHANGED