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

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

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

TYPED_DELAY =
0.05

Instance Attribute Summary

Attributes inherited from Base

#app

Instance Method Summary collapse

Methods inherited from Base

#deactivate, #handle_input, #render, #teardown

Constructor Details

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

Returns a new instance of Onboarding.



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/legion/tty/screens/onboarding.rb', line 20

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
  @log = BootLogger.new
end

Instance Method Details

#activateObject

rubocop:disable Metrics/AbcSize



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

def activate
  @log.log('onboarding', 'activate started')
  start_background_threads
  run_rain unless @skip_rain
  run_intro
  config = run_wizard
  @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)
  @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



266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/legion/tty/screens/onboarding.rb', line 266

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

rubocop:enable Metrics/AbcSize



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/legion/tty/screens/onboarding.rb', line 231

def collect_background_results
  @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] } || []
  @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



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

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



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

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



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

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



190
191
192
193
194
195
196
197
# File 'lib/legion/tty/screens/onboarding.rb', line 190

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

rubocop:disable Metrics/AbcSize



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/legion/tty/screens/onboarding.rb', line 200

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

  if legionio_running?
    typed_output('GAIA is awake.')
    sleep 0.5
    typed_output('Heuristic mesh: nominal.')
    sleep 0.8
    typed_output('Cognitive threads synchronized.')
  else
    typed_output('GAIA is dormant.')
    sleep 1
    @output.puts
    if @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
    end
  end

  @output.puts
end

#run_introObject

rubocop:enable Metrics/AbcSize



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/legion/tty/screens/onboarding.rb', line 74

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



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/legion/tty/screens/onboarding.rb', line 55

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



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

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



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/legion/tty/screens/onboarding.rb', line 97

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



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

def select_provider_default(providers)
  working = providers.select { |p| p[:status] == :ok }
  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



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

def start_background_threads
  @log.log('threads', 'launching scanner, kerberos probe, github quick probe')
  @scanner = Background::Scanner.new(logger: @log)
  @github_probe = Background::GitHubProbe.new(logger: @log)
  @kerberos_probe = Background::KerberosProbe.new(logger: @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: @log)
  @llm_probe.run_async(@llm_queue)
  @bootstrap_probe = Background::BootstrapConfig.new(logger: @log)
  @bootstrap_probe.run_async(@bootstrap_queue)
  start_detect_probe
end

#start_detect_probeObject



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

def start_detect_probe
  return unless detect_gem_available?

  @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
    @log.log('detect', "ERROR: #{e.message}")
    @detect_queue.push({ type: :detect_error, error: e.message })
  end
end