legion-tty 0.4.12 → 0.4.14

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: 9506a28fb68d7623dd1e70e2b69b80e80b2a96486c068dbc2d13a7e2bd9a80ab
4
- data.tar.gz: 7ce9e32334ba94891569b073e04246240d8b1637edb930f0c8c86ed80841a155
3
+ metadata.gz: bce8d6f696692fc8e9173eee8cb0afd87f37dc48b12325a149a7de1748f13447
4
+ data.tar.gz: d0e115df860ddd59c02e92b27d65a74a3a1413bafd25c7484f8dcc8c631832fb
5
5
  SHA512:
6
- metadata.gz: b7cf759adc8fcaad726906fd6e95d12a00d5e09bbf3724685a1fae91bdd31d9df31036bc4d826cc7b756f2cb97ae2d560290dbb6c1f0473b8334da48508d5a39
7
- data.tar.gz: 7fcce87bc7f909f1e5b5c6d468871860a0a2be6f621a8b446d7948cf2761bf354035911d946c134b74d04b226e19f2b174c010d1fb9e7a39533162f826883865
6
+ metadata.gz: c6bc9b9eb07cf1838830fddd0bf626c4d9a0404db3330f7f6a658c5dd6425f9293d316a3a6f013858262f47ddb2875421c768a849a14213432028d66f56d94c7
7
+ data.tar.gz: 4eddcc425c258ec26d3c74e9ec3a9ce1ac470f8b25e985bed26db41f504e3825a61f21113587ccb70c5e7ce35789e06325affba7b704e0580687165687d4dbf5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.14] - 2026-03-19
4
+
5
+ ### Added
6
+ - LLM response timing: tracks elapsed time per response, shows notification, includes avg in `/stats`
7
+ - `/wc` command: word count statistics per role (user/assistant/system) with averages
8
+ - `/import <path>` command: import session from any JSON file path with validation
9
+ - `/mute` command: toggle system message display in chat (messages still tracked, just hidden)
10
+
11
+ ## [0.4.13] - 2026-03-19
12
+
13
+ ### Added
14
+ - Help overlay: `/help` now renders as a categorized overlay (SESSION/CHAT/LLM/NAV/DISPLAY/TOOLS) via screen manager
15
+ - Session message count in status bar ("N msgs" segment)
16
+ - `/welcome` command: redisplay the welcome message
17
+ - `/tips` command: show random usage tips (15 tips covering commands, hotkeys, features)
18
+
3
19
  ## [0.4.12] - 2026-03-19
4
20
 
5
21
  ### Added
data/README.md CHANGED
@@ -2,30 +2,36 @@
2
2
 
3
3
  Rich terminal UI for the LegionIO async cognition engine.
4
4
 
5
- **Version**: 0.4.8
5
+ **Version**: 0.4.12
6
6
 
7
- Think Claude Code meets Codex CLI, but for LegionIO: onboarding wizard with identity detection, streaming AI chat shell, operational dashboard, extensions browser, config editor, and session persistence - all rendered with the [tty-ruby](https://ttytoolkit.org/) gem ecosystem.
7
+ Think Claude Code meets Codex CLI, but for LegionIO: onboarding wizard with identity detection, streaming AI chat shell with 40 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 27 slash commands, tab completion, markdown rendering, and tool panels
14
- - **Operational dashboard** - Service status, extension inventory, system info, recent activity (Ctrl+D or `/dashboard`)
15
- - **Extensions browser** - Browse installed LEX gems by category (core, agentic, service, AI, other) with detail view
16
- - **Config viewer/editor** - View and edit `~/.legionio/settings/*.json` with vault:// masking
13
+ - **AI chat shell** - Streaming LLM chat with 40 slash commands, tab completion, markdown rendering, and tool panels
14
+ - **Operational dashboard** - Service/LLM status, extension inventory, system info, panel navigation (Ctrl+D or `/dashboard`)
15
+ - **Extensions browser** - Browse installed LEX gems by category with detail view and homepage opener ('o' key)
16
+ - **Config viewer/editor** - View and edit `~/.legionio/settings/*.json` with vault:// masking and JSON validation
17
17
  - **Command palette** - Fuzzy-search overlay for all commands, screens, and sessions (Ctrl+K or `/palette`)
18
18
  - **Model picker** - Switch LLM providers interactively
19
- - **Session management** - Auto-save on quit, `/save`, `/load`, `/sessions`, session picker (Ctrl+S)
19
+ - **Session management** - Auto-save on quit, `/save`, `/load`, `/sessions`, `/rename`, session picker (Ctrl+S)
20
20
  - **Token tracking** - Per-model pricing for 9 models across 8 providers via `/cost`
21
21
  - **Plan mode** - Bookmark messages without sending to LLM (`/plan`)
22
22
  - **Personality styles** - Switch between default, concise, detailed, friendly, technical (`/personality`)
23
23
  - **Theme selection** - Four built-in themes: purple (default), green, blue, amber (`/theme`)
24
- - **Conversation tools** - `/compact`, `/copy`, `/diff`, `/search`, `/stats`
24
+ - **Conversation tools** - `/compact`, `/copy`, `/diff`, `/search`, `/grep`, `/stats`, `/undo`
25
+ - **Message pinning** - Pin important messages (`/pin`), view pins (`/pins`), export bookmarks (`/bookmark`)
26
+ - **Command aliases** - Create custom shortcuts for frequently used commands (`/alias`)
27
+ - **Code snippets** - Save and load reusable text snippets (`/snippet`)
28
+ - **Debug mode** - Toggle internal state display (`/debug`)
29
+ - **Session context** - View active settings summary (`/context`)
30
+ - **Toast notifications** - Transient status bar messages for save/load/export/theme actions
25
31
  - **Hotkey navigation** - Ctrl+D (dashboard), Ctrl+K (palette), Ctrl+S (sessions), Escape (back)
26
32
  - **Tab completion** - Type `/` and Tab to auto-complete slash commands
27
- - **Input history** - Up/down arrow to navigate previous inputs
28
- - **Progress panel** - Visual progress bars for long operations (extension scanning, gem ops)
33
+ - **Input history** - Up/down arrow to navigate previous inputs, `/history` to view
34
+ - **Progress panel** - Visual progress bars for long operations
29
35
  - **Animated spinner** - Status bar spinner during LLM thinking
30
36
  - **Daemon routing** - Routes through LegionIO daemon when available, falls back to direct
31
37
  - **Second-run flow** - Skips onboarding, re-scans environment, drops into chat
@@ -74,9 +80,9 @@ legion chat prompt "explain async cognition"
74
80
  | `/quit` | Exit (auto-saves session) |
75
81
  | `/clear` | Clear message history |
76
82
  | `/model <name>` | Switch LLM model at runtime |
77
- | `/session <name>` | Rename current session |
83
+ | `/session <name>` | Set session name |
78
84
  | `/cost` | Show token usage and estimated cost |
79
- | `/export [md\|json]` | Export chat history to file |
85
+ | `/export [md\|json\|html]` | Export chat history to file |
80
86
  | `/tools` | List discovered LEX extensions |
81
87
  | `/dashboard` | Toggle operational dashboard |
82
88
  | `/hotkeys` | Show registered hotkey bindings |
@@ -91,11 +97,24 @@ legion chat prompt "explain async cognition"
91
97
  | `/config` | View and edit settings files |
92
98
  | `/theme [name]` | Switch color theme (purple/green/blue/amber) |
93
99
  | `/search <text>` | Search message history |
100
+ | `/grep <pattern>` | Regex search across messages |
94
101
  | `/compact [N]` | Keep last N message pairs, remove older |
95
102
  | `/copy` | Copy last assistant response to clipboard |
96
103
  | `/diff` | Show new messages since last session load |
97
104
  | `/stats` | Show conversation statistics |
98
105
  | `/personality [style]` | Switch personality (default/concise/detailed/friendly/technical) |
106
+ | `/undo` | Remove last user+assistant message pair |
107
+ | `/history` | Show input history |
108
+ | `/pin [N]` | Pin a message (last assistant or by index) |
109
+ | `/pins` | Show all pinned messages |
110
+ | `/rename <name>` | Rename current session (moves saved file) |
111
+ | `/context` | Show active session state summary |
112
+ | `/alias [name] [cmd]` | Create or list command aliases |
113
+ | `/snippet <action>` | Save/load/list/delete code snippets |
114
+ | `/debug` | Toggle debug mode |
115
+ | `/uptime` | Show session elapsed time |
116
+ | `/bookmark` | Export pinned messages to file |
117
+ | `/time` | Show current date and time |
99
118
 
100
119
  ## Hotkeys
101
120
 
@@ -120,16 +139,22 @@ legion-tty
120
139
 
121
140
  Screens/
122
141
  Onboarding # First-run wizard (rain -> intro -> wizard -> reveal)
123
- Chat # AI chat REPL with streaming + slash commands
124
- Dashboard # Operational status panels
125
- Extensions # LEX gem browser by category
126
- Config # Settings file viewer/editor
142
+ Chat # AI chat REPL with streaming + 40 slash commands
143
+ SessionCommands # save/load/sessions/delete/rename
144
+ ExportCommands # export/bookmark/html/json/markdown
145
+ MessageCommands # compact/copy/diff/search/grep/undo/pin/pins
146
+ UiCommands # help/clear/dashboard/hotkeys/palette/context/stats/debug/history/uptime/time
147
+ ModelCommands # model/system/personality switching
148
+ CustomCommands # alias/snippet management
149
+ Dashboard # Service/LLM status, panel navigation (j/k/1-5)
150
+ Extensions # LEX gem browser by category with homepage opener
151
+ Config # Settings file viewer/editor with JSON validation
127
152
 
128
153
  Components/
129
154
  DigitalRain # Matrix-style falling characters
130
- InputBar # Prompt line with tab completion + thinking indicator
131
- MessageStream # Scrollable message history with markdown rendering
132
- StatusBar # Model, plan mode, thinking, tokens, cost, session
155
+ InputBar # Prompt line with tab completion + input history
156
+ MessageStream # Scrollable message history with markdown + timestamps
157
+ StatusBar # Model, plan, debug, notifications, thinking, tokens, cost, session, scroll
133
158
  ToolPanel # Expandable tool use panels
134
159
  MarkdownView # TTY::Markdown rendering
135
160
  WizardPrompt # TTY::Prompt wrappers
@@ -161,6 +186,7 @@ Identity and credentials are stored in `~/.legionio/settings/`:
161
186
  - `credentials.json` - LLM provider and API key (chmod 600)
162
187
 
163
188
  Sessions are stored in `~/.legionio/sessions/`.
189
+ Snippets are stored in `~/.legionio/snippets/`.
164
190
  Exports go to `~/.legionio/exports/`.
165
191
  Boot logs go to `~/.legionio/logs/tty-boot.log`.
166
192
 
@@ -168,8 +194,8 @@ Boot logs go to `~/.legionio/logs/tty-boot.log`.
168
194
 
169
195
  ```bash
170
196
  bundle install
171
- bundle exec rspec # 653 examples, 0 failures
172
- bundle exec rubocop # 77 files, 0 offenses
197
+ bundle exec rspec # 836 examples, 0 failures
198
+ bundle exec rubocop # 92 files, 0 offenses
173
199
  ```
174
200
 
175
201
  ## License
@@ -5,12 +5,15 @@ require_relative '../theme'
5
5
  module Legion
6
6
  module TTY
7
7
  module Components
8
+ # rubocop:disable Metrics/ClassLength
8
9
  class MessageStream
9
10
  attr_reader :messages, :scroll_offset
11
+ attr_accessor :mute_system
10
12
 
11
13
  def initialize
12
14
  @messages = []
13
15
  @scroll_offset = 0
16
+ @mute_system = false
14
17
  end
15
18
 
16
19
  def add_message(role:, content:)
@@ -69,7 +72,11 @@ module Legion
69
72
  private
70
73
 
71
74
  def build_all_lines(width)
72
- @messages.flat_map { |msg| render_message(msg, width) }
75
+ @messages.flat_map do |msg|
76
+ next [] if @mute_system && msg[:role] == :system
77
+
78
+ render_message(msg, width)
79
+ end
73
80
  end
74
81
 
75
82
  def render_message(msg, width)
@@ -131,6 +138,7 @@ module Legion
131
138
  panel.instance_variable_set(:@error, error) if error
132
139
  end
133
140
  end
141
+ # rubocop:enable Metrics/ClassLength
134
142
  end
135
143
  end
136
144
  end
@@ -6,10 +6,11 @@ require_relative 'notification'
6
6
  module Legion
7
7
  module TTY
8
8
  module Components
9
+ # rubocop:disable Metrics/ClassLength
9
10
  class StatusBar
10
11
  def initialize
11
12
  @state = { model: nil, tokens: 0, cost: 0.0, session: 'default', thinking: false, plan_mode: false,
12
- debug_mode: false }
13
+ debug_mode: false, message_count: 0 }
13
14
  @notifications = []
14
15
  end
15
16
 
@@ -46,6 +47,7 @@ module Legion
46
47
  notification_segment,
47
48
  tokens_segment,
48
49
  cost_segment,
50
+ message_count_segment,
49
51
  session_segment,
50
52
  scroll_segment
51
53
  ].compact
@@ -90,6 +92,11 @@ module Legion
90
92
  Theme.c(:success, format('$%.3f', @state[:cost])) if @state[:cost].to_f.positive?
91
93
  end
92
94
 
95
+ def message_count_segment
96
+ count = @state[:message_count].to_i
97
+ Theme.c(:muted, "#{count} msgs") if count.positive?
98
+ end
99
+
93
100
  def session_segment
94
101
  Theme.c(:muted, @state[:session]) if @state[:session]
95
102
  end
@@ -132,6 +139,7 @@ module Legion
132
139
  result
133
140
  end
134
141
  end
142
+ # rubocop:enable Metrics/ClassLength
135
143
  end
136
144
  end
137
145
  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 SessionCommands
8
9
  private
9
10
 
@@ -76,6 +77,42 @@ module Legion
76
77
  :handled
77
78
  end
78
79
 
80
+ def handle_import(input)
81
+ path = input.split(nil, 2)[1]
82
+ unless path
83
+ @message_stream.add_message(role: :system, content: 'Usage: /import <path>')
84
+ return :handled
85
+ end
86
+
87
+ load_import_file(File.expand_path(path))
88
+ rescue ::JSON::ParserError => e
89
+ @message_stream.add_message(role: :system, content: "Invalid JSON: #{e.message}")
90
+ :handled
91
+ end
92
+
93
+ def load_import_file(path)
94
+ unless File.exist?(path)
95
+ @message_stream.add_message(role: :system, content: "File not found: #{path}")
96
+ return :handled
97
+ end
98
+
99
+ data = ::JSON.parse(File.read(path), symbolize_names: true)
100
+ unless data.is_a?(Hash) && data[:messages].is_a?(Array)
101
+ @message_stream.add_message(role: :system, content: 'Invalid session file: missing messages array.')
102
+ return :handled
103
+ end
104
+
105
+ apply_imported_messages(data[:messages], path)
106
+ end
107
+
108
+ def apply_imported_messages(messages, path)
109
+ imported = messages.map { |m| { role: m[:role].to_sym, content: m[:content].to_s } }
110
+ @message_stream.messages.replace(imported)
111
+ @status_bar.notify(message: "Imported #{imported.size} messages", level: :success, ttl: 3)
112
+ @message_stream.add_message(role: :system, content: "Imported #{imported.size} messages from #{path}.")
113
+ :handled
114
+ end
115
+
79
116
  def auto_save_session
80
117
  return if @message_stream.messages.empty?
81
118
 
@@ -87,6 +124,7 @@ module Legion
87
124
  nil
88
125
  end
89
126
  end
127
+ # rubocop:enable Metrics/ModuleLength
90
128
  end
91
129
  end
92
130
  end
@@ -6,40 +6,61 @@ module Legion
6
6
  class Chat < Base
7
7
  # rubocop:disable Metrics/ModuleLength
8
8
  module UiCommands
9
+ TIPS = [
10
+ 'Press Tab after / to auto-complete commands',
11
+ 'Use /alias to create shortcuts (e.g., /alias s /save)',
12
+ 'Press Ctrl+K to open the command palette',
13
+ 'Use /grep for regex search (e.g., /grep error|warning)',
14
+ 'Pin important messages with /pin, export with /bookmark',
15
+ 'Use /compact 3 to keep only the last 3 message pairs',
16
+ "Press 'o' in Extensions browser to open gem homepage",
17
+ '/export html creates a styled dark-theme HTML export',
18
+ 'Use /snippet save name to save assistant responses for reuse',
19
+ 'The dashboard updates every 5 seconds; press r to refresh',
20
+ '/context shows your full session state at a glance',
21
+ 'Use /personality technical for code-focused responses',
22
+ '/debug shows internal state counters in the status bar',
23
+ 'Navigate dashboard panels with j/k or number keys 1-5',
24
+ 'Use /diff to see new messages since a session was loaded'
25
+ ].freeze
26
+
27
+ HELP_TEXT = [
28
+ 'SESSION : /save /load /sessions /delete /rename',
29
+ 'CHAT : /clear /undo /compact /copy /search /grep /diff /stats',
30
+ 'LLM : /model /system /personality /cost',
31
+ 'NAV : /dashboard /extensions /config /palette /hotkeys',
32
+ 'DISPLAY : /theme /plan /debug /context /time /uptime',
33
+ 'TOOLS : /tools /export /bookmark /pin /pins /alias /snippet /history',
34
+ '',
35
+ 'Hotkeys: Ctrl+D=dashboard Ctrl+K=palette Ctrl+S=sessions Esc=back'
36
+ ].freeze
37
+
9
38
  private
10
39
 
11
- # rubocop:disable Metrics/MethodLength
12
40
  def handle_help
41
+ text = HELP_TEXT.join("\n")
42
+ if @app.respond_to?(:screen_manager) && @app.screen_manager
43
+ @app.screen_manager.show_overlay(text)
44
+ else
45
+ @message_stream.add_message(role: :system, content: text)
46
+ end
47
+ :handled
48
+ end
49
+
50
+ def handle_welcome
51
+ cfg = safe_config
13
52
  @message_stream.add_message(
14
53
  role: :system,
15
- content: "Commands:\n /help /quit /clear /model <name> /session <name> /cost\n " \
16
- "/export [md|json] /tools /dashboard /hotkeys /save /load /sessions\n " \
17
- "/system <prompt> /delete <session> /plan /palette /extensions /config\n " \
18
- "/theme [name] -- switch color theme (purple, green, blue, amber)\n " \
19
- "/search <text> -- search message history\n " \
20
- "/grep <regex> -- regex search message history\n " \
21
- "/compact [n] -- keep last n message pairs (default 5)\n " \
22
- "/copy -- copy last assistant message to clipboard\n " \
23
- "/diff -- show new messages since session was loaded\n " \
24
- "/stats -- show conversation statistics\n " \
25
- "/personality [name] -- switch assistant personality\n " \
26
- "/undo -- remove last user+assistant message pair\n " \
27
- "/history -- show recent input history\n " \
28
- "/pin [N] -- pin last assistant message (or message at index N)\n " \
29
- "/pins -- show all pinned messages\n " \
30
- "/rename <name> -- rename current session\n " \
31
- "/context -- show active session context summary\n " \
32
- "/alias [shortname /command] -- create or list command aliases\n " \
33
- "/snippet save|load|list|delete <name> -- manage reusable text snippets\n " \
34
- "/debug -- toggle debug mode (shows internal state)\n " \
35
- "/uptime -- show how long this session has been active\n " \
36
- "/time -- show current date, time, and timezone\n " \
37
- "/bookmark -- export pinned messages to a markdown file\n\n" \
38
- 'Hotkeys: Ctrl+D=dashboard Ctrl+K=palette Ctrl+S=sessions Esc=back'
54
+ content: "Welcome#{", #{cfg[:name]}" if cfg[:name]}. Type /help for commands."
39
55
  )
40
56
  :handled
41
57
  end
42
- # rubocop:enable Metrics/MethodLength
58
+
59
+ def handle_tips
60
+ tip = TIPS.sample
61
+ @message_stream.add_message(role: :system, content: "Tip: #{tip}")
62
+ :handled
63
+ end
43
64
 
44
65
  def handle_clear
45
66
  @message_stream.messages.clear
@@ -183,15 +204,62 @@ module Legion
183
204
  end
184
205
  end
185
206
 
207
+ def handle_wc
208
+ msgs = @message_stream.messages
209
+ by_role = word_counts_by_role(msgs)
210
+ total = by_role.values.sum
211
+ avg = (total.to_f / [msgs.size, 1].max).round
212
+ @message_stream.add_message(role: :system, content: build_wc_lines(by_role, total, avg).join("\n"))
213
+ :handled
214
+ end
215
+
216
+ def word_counts_by_role(msgs)
217
+ %i[user assistant system].to_h do |role|
218
+ words = msgs.select { |m| m[:role] == role }.sum { |m| m[:content].to_s.split.size }
219
+ [role, words]
220
+ end
221
+ end
222
+
223
+ def build_wc_lines(by_role, total, avg)
224
+ [
225
+ 'Word count:',
226
+ " Total: #{format_stat_number(total)}",
227
+ " User: #{format_stat_number(by_role[:user])}",
228
+ " Assistant: #{format_stat_number(by_role[:assistant])}",
229
+ " System: #{format_stat_number(by_role[:system])}",
230
+ " Avg words/message: #{avg}"
231
+ ]
232
+ end
233
+
234
+ def handle_mute
235
+ @muted_system = !@muted_system
236
+ @message_stream.mute_system = @muted_system
237
+ if @muted_system
238
+ @status_bar.notify(message: 'System messages hidden', level: :info, ttl: 3)
239
+ else
240
+ @status_bar.notify(message: 'System messages visible', level: :info, ttl: 3)
241
+ end
242
+ :handled
243
+ end
244
+
186
245
  def build_stats_lines
187
246
  msgs = @message_stream.messages
188
247
  counts = count_by_role(msgs)
189
248
  total_chars = msgs.sum { |m| m[:content].to_s.length }
190
249
  lines = stats_header_lines(msgs, counts, total_chars)
191
250
  lines << " Tool calls: #{counts[:tool]}" if counts[:tool].positive?
251
+ append_response_time_stat(lines, msgs)
192
252
  lines
193
253
  end
194
254
 
255
+ def append_response_time_stat(lines, msgs)
256
+ timed = msgs.select { |m| m[:response_time] }
257
+ return unless timed.any?
258
+
259
+ avg_rt = timed.sum { |m| m[:response_time] }.to_f / timed.size
260
+ lines << " Avg response time: #{avg_rt.round(2)}s (#{timed.size} responses)"
261
+ end
262
+
195
263
  def count_by_role(msgs)
196
264
  %i[user assistant system tool].to_h { |role| [role, msgs.count { |m| m[:role] == role }] }
197
265
  end
@@ -28,7 +28,8 @@ module Legion
28
28
  SLASH_COMMANDS = %w[/help /quit /clear /compact /copy /diff /model /session /cost /export /tools /dashboard
29
29
  /hotkeys /save /load /sessions /system /delete /plan /palette /extensions /config
30
30
  /theme /search /grep /stats /personality /undo /history /pin /pins /rename
31
- /context /alias /snippet /debug /uptime /time /bookmark].freeze
31
+ /context /alias /snippet /debug /uptime /time /bookmark /welcome /tips
32
+ /wc /import /mute].freeze
32
33
 
33
34
  PERSONALITIES = {
34
35
  'default' => 'You are Legion, an async cognition engine and AI assistant. Be helpful and concise.',
@@ -58,6 +59,7 @@ module Legion
58
59
  @snippets = {}
59
60
  @debug_mode = false
60
61
  @session_start = Time.now
62
+ @muted_system = false
61
63
  end
62
64
 
63
65
  # rubocop:enable Metrics/AbcSize
@@ -71,6 +73,7 @@ module Legion
71
73
  role: :system,
72
74
  content: "Welcome#{", #{cfg[:name]}" if cfg[:name]}. Type /help for commands."
73
75
  )
76
+ @status_bar.update(message_count: @message_stream.messages.size)
74
77
  end
75
78
 
76
79
  def running?
@@ -124,6 +127,7 @@ module Legion
124
127
  @message_stream.add_message(role: :assistant, content: '')
125
128
  send_to_llm(input)
126
129
  end
130
+ @status_bar.update(message_count: @message_stream.messages.size)
127
131
  render_screen
128
132
  end
129
133
 
@@ -200,15 +204,23 @@ module Legion
200
204
 
201
205
  @status_bar.update(thinking: true)
202
206
  render_screen
207
+ start_time = Time.now
203
208
  response = @llm_chat.ask(message) do |chunk|
204
209
  @status_bar.update(thinking: false)
205
210
  @message_stream.append_streaming(chunk.content) if chunk.content
206
211
  render_screen
207
212
  end
213
+ record_response_time(Time.now - start_time)
208
214
  @status_bar.update(thinking: false)
209
215
  track_response_tokens(response)
210
216
  end
211
217
 
218
+ def record_response_time(elapsed)
219
+ @last_response_time = elapsed
220
+ @message_stream.messages.last[:response_time] = elapsed if @message_stream.messages.last
221
+ @status_bar.notify(message: "Response: #{elapsed.round(1)}s", level: :info, ttl: 4)
222
+ end
223
+
212
224
  def daemon_available?
213
225
  !!(defined?(Legion::LLM::DaemonClient) && Legion::LLM::DaemonClient.available?)
214
226
  end
@@ -336,6 +348,11 @@ module Legion
336
348
  when '/uptime' then handle_uptime
337
349
  when '/time' then handle_time
338
350
  when '/bookmark' then handle_bookmark
351
+ when '/welcome' then handle_welcome
352
+ when '/tips' then handle_tips
353
+ when '/wc' then handle_wc
354
+ when '/import' then handle_import(input)
355
+ when '/mute' then handle_mute
339
356
  else :handled
340
357
  end
341
358
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module TTY
5
- VERSION = '0.4.12'
5
+ VERSION = '0.4.14'
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.12
4
+ version: 0.4.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity