Class: Legion::TTY::Screens::Onboarding

Inherits:
Base
  • Object
show all
Includes:
Logging::Helper
Defined in:
lib/legion/tty/screens/onboarding.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

TYPED_DELAY =
0.05
GAIA_GEMS =
%w[
  lex-agentic-self lex-agentic-affect lex-agentic-attention
  lex-agentic-defense lex-agentic-executive lex-agentic-homeostasis
  lex-agentic-imagination lex-agentic-inference lex-agentic-integration
  lex-agentic-language lex-agentic-learning lex-agentic-memory
  lex-agentic-social
  lex-tick lex-extinction lex-mind-growth lex-mesh
  lex-synapse lex-react
  legion-gaia legion-apollo
].freeze

Instance Attribute Summary

Attributes inherited from Base

#app

Instance Method Summary collapse

Methods inherited from Base

#deactivate, #handle_input, #needs_input_bar?, #render, #teardown

Constructor Details

#initialize(app, wizard: nil, output: $stdout, skip_rain: false) ⇒ Onboarding

Returns a new instance of Onboarding.



34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/legion/tty/screens/onboarding.rb', line 34

def initialize(app, wizard: nil, output: $stdout, skip_rain: false)
  super(app)
  @wizard = wizard || Components::WizardPrompt.new
  @output = output
  @skip_rain = skip_rain
  initialize_queues
  @kerberos_identity = nil
  @github_quick = nil
  @vault_results = nil
  @bootstrap_data = nil
  @boot_log = BootLogger.new
end

Instance Method Details

#activateObject

rubocop:disable Metrics/AbcSize



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/legion/tty/screens/onboarding.rb', line 48

def activate
  @boot_log.log('onboarding', 'activate started')
  start_background_threads
  run_rain unless @skip_rain
  run_intro
  config = run_wizard
  @boot_log.log('wizard', "name=#{config[:name]} provider=#{config[:provider]}")
  collect_bootstrap_result
  run_vault_auth
  scan_data, github_data = collect_background_results
  run_cache_awakening(scan_data)
  run_gaia_awakening
  run_extension_detection
  run_service_auth
  run_reveal(name: config[:name], scan_data: scan_data, github_data: github_data)
  @boot_log.log('onboarding', 'activate complete')
  build_onboarding_result(config, scan_data, github_data)
end

#build_summary(name:, scan_data:, github_data:) ⇒ Object

rubocop:disable Metrics/AbcSize



286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/legion/tty/screens/onboarding.rb', line 286

def build_summary(name:, scan_data:, github_data:)
  lines = ["Hello, #{name}!", '', "Here's what I found:"]
  lines.concat(bootstrap_summary_lines)
  lines.concat(identity_summary_lines)
  lines.concat(scan_summary_lines(scan_data))
  lines.concat(dotfiles_summary_lines(scan_data))
  lines.concat(github_summary_lines(github_data))
  lines.concat(vault_summary_lines)
  lines.concat(cache_summary_lines(scan_data))
  lines.concat(gaia_summary_lines)
  lines.join("\n")
end

#collect_background_resultsObject



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/legion/tty/screens/onboarding.rb', line 251

def collect_background_results
  @boot_log.log('collect', 'waiting for scanner results (10s timeout)')
  scan_result = drain_with_timeout(@scan_queue, timeout: 10)
  scan_data = scan_result&.dig(:data) || { services: {}, repos: [], tools: {} }
  log_scan_data(scan_data)

  # Now launch GitHub probe with discovered remotes
  remotes = scan_data[:repos]&.filter_map { |r| r[:remote] } || []
  @boot_log.log('collect', "launching github probe with #{remotes.size} remotes")
  @github_probe.run_async(@github_queue, remotes: remotes, quick_profile: @github_quick)
  github_result = drain_with_timeout(@github_queue, timeout: 8)
  github_data = github_result&.dig(:data)
  log_github_data(github_data)
  [scan_data, github_data]
end

#detect_providersObject



123
124
125
126
127
128
129
130
131
132
# File 'lib/legion/tty/screens/onboarding.rb', line 123

def detect_providers
  typed_output('Detecting AI providers...')
  @output.puts
  @output.puts
  llm_data = drain_with_timeout(@llm_queue, timeout: 15)
  providers = llm_data&.dig(:data, :providers) || []
  @wizard.display_provider_results(providers)
  @output.puts
  providers
end

#run_cache_awakening(scan_data) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/legion/tty/screens/onboarding.rb', line 177

def run_cache_awakening(scan_data)
  services = scan_data.is_a?(Hash) ? scan_data[:services] : nil
  return unless services.is_a?(Hash)

  if services.dig(:memcached, :running) || services.dig(:redis, :running)
    typed_output('... extending neural pathways...')
    sleep 0.8
    typed_output('Additional memory online.')
  else
    run_cache_offer
  end
  @output.puts
end

#run_cache_offerObject



191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/legion/tty/screens/onboarding.rb', line 191

def run_cache_offer
  typed_output('No extended memory detected.')
  sleep 0.8
  @output.puts
  return unless @wizard.confirm('Shall I activate a memory cache?')

  binary = detect_cache_binary
  if binary
    run_cache_start(binary)
  else
    typed_output('No cache service found. Install with: brew install memcached')
  end
end

#run_cache_start(binary) ⇒ Object



205
206
207
208
209
210
211
212
# File 'lib/legion/tty/screens/onboarding.rb', line 205

def run_cache_start(binary)
  started = start_cache_service(binary.to_s)
  if started
    typed_output('Memory cache activated. Neural capacity expanded.')
  else
    typed_output("No cache service found. Install with: brew install #{binary}")
  end
end

#run_gaia_awakeningObject



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/legion/tty/screens/onboarding.rb', line 214

def run_gaia_awakening
  typed_output('Scanning for active cognition threads...')
  sleep 1.2
  @output.puts

  gaia_active = if legionio_running?
                  typed_output('GAIA is awake.')
                  sleep 0.5
                  typed_output('Heuristic mesh: nominal.')
                  sleep 0.8
                  typed_output('Cognitive threads synchronized.')
                  true
                else
                  wake_gaia_daemon
                end

  offer_gaia_gems if gaia_active
  @output.puts
end

#run_introObject

rubocop:enable Metrics/AbcSize



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/legion/tty/screens/onboarding.rb', line 88

def run_intro
  # Collect background results that ran during the rain
  collect_kerberos_identity
  collect_github_quick

  sleep 2
  typed_output('...')
  sleep 1.2
  @output.puts
  @output.puts
  typed_output("Hello. I'm Legion.")
  @output.puts
  sleep 1.5
  if @kerberos_identity
    run_intro_with_identity
  else
    typed_output("Let's get you set up.")
    @output.puts
    @output.puts
  end
  run_intro_with_github if @github_quick
end

#run_rainObject

rubocop:disable Metrics/AbcSize



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/legion/tty/screens/onboarding.rb', line 69

def run_rain
  require 'tty-cursor'
  require 'tty-font'
  width = terminal_width
  height = terminal_height
  rain = Components::DigitalRain.new(width: width, height: height)
  rain.run(duration_seconds: 20, fps: 18, output: @output)
  @output.print ::TTY::Cursor.clear_screen
  font = ::TTY::Font.new(:standard)
  title = font.write('LEGION')
  title.each_line do |line|
    @output.puts line.center(width)
  end
  @output.puts Theme.c(:muted, 'async cognition engine').center(width + 20)
  sleep 5
  @output.print ::TTY::Cursor.clear_screen
end

#run_reveal(name:, scan_data:, github_data:) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/legion/tty/screens/onboarding.rb', line 267

def run_reveal(name:, scan_data:, github_data:)
  require 'tty-box'
  @output.puts
  typed_output('One moment...')
  @output.puts
  sleep 1.5
  summary = build_summary(name: name, scan_data: scan_data, github_data: github_data)
  box = ::TTY::Box.frame(summary, padding: 1, border: :thick)
  @output.puts box
  @output.puts
  @wizard.confirm('Does this look right?')
  @output.puts
  sleep 0.8
  typed_output("Let's chat.")
  @output.puts
  sleep 1
end

#run_wizardObject



111
112
113
114
115
116
117
118
119
120
121
# File 'lib/legion/tty/screens/onboarding.rb', line 111

def run_wizard
  name = ask_for_name
  sleep 0.8
  typed_output("  Nice to meet you, #{name}.")
  @output.puts
  sleep 1
  providers = detect_providers
  default = select_provider_default(providers)
  @output.puts
  { name: name, provider: default, providers: providers }
end

#select_provider_default(providers) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/legion/tty/screens/onboarding.rb', line 134

def select_provider_default(providers)
  working = providers.select { |p| p[:status] == :ok }
  working = providers.select { |p| p[:status] == :configured } if working.empty?
  if working.any?
    default = @wizard.select_default_provider(working)
    sleep 0.5
    typed_output("Connected. Let's chat.")
    default
  else
    typed_output('No AI providers detected. Configure one in ~/.legionio/settings/llm.json')
    nil
  end
end

#start_background_threadsObject



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/legion/tty/screens/onboarding.rb', line 148

def start_background_threads
  @boot_log.log('threads', 'launching scanner, kerberos probe, github quick probe')
  @scanner = Background::Scanner.new(logger: @boot_log)
  @github_probe = Background::GitHubProbe.new(logger: @boot_log)
  @kerberos_probe = Background::KerberosProbe.new(logger: @boot_log)
  @scanner.run_async(@scan_queue)
  @kerberos_probe.run_async(@kerberos_queue)
  @github_probe.run_quick_async(@github_quick_queue)
  require_relative '../background/llm_probe'
  @llm_probe = Background::LlmProbe.new(logger: @boot_log, wait_queue: @bootstrap_queue)
  @llm_probe.run_async(@llm_queue)
  @bootstrap_probe = Background::BootstrapConfig.new(logger: @boot_log)
  @bootstrap_probe.run_async(@bootstrap_queue)
  start_detect_probe
end

#start_detect_probeObject



164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/legion/tty/screens/onboarding.rb', line 164

def start_detect_probe
  return unless detect_gem_available?

  @boot_log.log('detect', 'launching environment scan')
  Thread.new do
    results = Legion::Extensions::Detect.scan
    @detect_queue.push({ type: :detect_complete, data: results })
  rescue StandardError => e
    @boot_log.log('detect', "ERROR: #{e.message}")
    @detect_queue.push({ type: :detect_error, error: e.message })
  end
end

#wake_gaia_daemonObject



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/legion/tty/screens/onboarding.rb', line 234

def wake_gaia_daemon
  typed_output('GAIA is dormant.')
  sleep 1
  @output.puts
  return unless @wizard.confirm('Shall I wake her?')

  started = start_legionio_daemon
  if started
    typed_output('... initializing cognitive substrate...')
    sleep 1
    typed_output('GAIA online. All systems nominal.')
  else
    typed_output("Could not start daemon. Run 'legionio start' manually.")
  end
  started
end