legionio 1.4.117 → 1.4.118

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: d03cb4a8c6be7dd7d726f3999ebf88826914163aa9c59ac3a7206f7975616892
4
- data.tar.gz: 1002f6425389c201d397f07163ccc40101880d0f9dadce0fb4c4159078b531cb
3
+ metadata.gz: a10f06b3cc591d8fa5aae3b31216e037826f961962df05276ba5fbd78805235c
4
+ data.tar.gz: 5a9e88f6bbb42b27586adba45becd7ecaedc69f190bd3f0fbddb3de27fcffd79
5
5
  SHA512:
6
- metadata.gz: 3f1679456170344d1ea966e7b08f5ba518f2f1957030b1cfa18c614516502bab2848861fd2c698f86ef72889cf4f79882ee1cafbf65a4bf8f3b00b23e8fb517c
7
- data.tar.gz: 72c3d056636a57c41244a9483824d6eaa448feb3b970bff31dc79741ba884e89045d4cec22b32e716d350bdbbd72b67465d09ca91aade426c8b9596f7fb12e92
6
+ metadata.gz: 3cab7e4e400553baf1b57918d610dbb7a0dd1143dd4df52f104bd03e55d23e53bfbffd815c32b9836ec421ba58c613b32ec97d6af0b68f26cf0fdb55570e7790
7
+ data.tar.gz: 13c8e47feeccf21c4ea33b2ddd31f37f8afd6f73765e6df48ead40e6e2a96125019c9b5aa1107383db46958bcaf51a7cd593344f70b1d295e807dedf33b1d41f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Legion Changelog
2
2
 
3
+ ## [1.4.118] - 2026-03-22
4
+
5
+ ### Added
6
+ - `legion detect --install` interactive extension picker: multi-select via tty-prompt (when available) or numbered list fallback
7
+ - `legion detect --install-all` for non-interactive bulk install of all missing extensions
8
+ - Signal context shown in picker (e.g., which app/formula triggered the recommendation)
9
+
3
10
  ## [1.4.117] - 2026-03-22
4
11
 
5
12
  ### Added
@@ -19,7 +19,8 @@ module Legion
19
19
  default_task :scan
20
20
 
21
21
  desc 'scan', 'Scan environment and recommend extensions (default)'
22
- option :install, type: :boolean, default: false, desc: 'Install missing extensions after scan'
22
+ option :install, type: :boolean, default: false, desc: 'Interactive install of missing extensions after scan'
23
+ option :install_all, type: :boolean, default: false, desc: 'Install all missing extensions without prompting'
23
24
  option :dry_run, type: :boolean, default: false, desc: 'Show what would be installed without installing'
24
25
  option :format, type: :string, enum: %w[sarif markdown json], desc: 'Output format (sarif, markdown, json)'
25
26
  def scan
@@ -35,7 +36,11 @@ module Legion
35
36
  out.json(detections: results)
36
37
  else
37
38
  display_detections(out, results)
38
- install_missing(out) if options[:install]
39
+ if options[:install]
40
+ interactive_install(out, results)
41
+ elsif options[:install_all]
42
+ install_missing(out)
43
+ end
39
44
  end
40
45
  end
41
46
 
@@ -130,6 +135,98 @@ module Legion
130
135
  puts " #{installed_count} of #{total_count} extension(s) installed"
131
136
  end
132
137
 
138
+ def interactive_install(out, results)
139
+ missing_gems = Legion::Extensions::Detect.missing
140
+ return out.success('All detected extensions are installed') if missing_gems.empty?
141
+
142
+ signal_map = build_signal_map(results)
143
+ selected = pick_extensions(out, missing_gems, signal_map)
144
+ if selected.empty?
145
+ puts ' No extensions selected'
146
+ return
147
+ end
148
+
149
+ if options[:dry_run]
150
+ out.header('Would install')
151
+ selected.each { |name| puts " #{name}" }
152
+ return
153
+ end
154
+
155
+ install_selected(out, selected)
156
+ end
157
+
158
+ def pick_extensions(out, missing_gems, signal_map)
159
+ if tty_prompt_available?
160
+ pick_with_tty_prompt(missing_gems, signal_map)
161
+ else
162
+ pick_with_numbers(out, missing_gems, signal_map)
163
+ end
164
+ end
165
+
166
+ def pick_with_tty_prompt(missing_gems, signal_map)
167
+ require 'tty-prompt'
168
+ prompt = ::TTY::Prompt.new
169
+
170
+ choices = missing_gems.map do |name|
171
+ label = signal_map[name] ? "#{name} (#{signal_map[name]})" : name
172
+ { name: label, value: name }
173
+ end
174
+
175
+ prompt.multi_select('Select extensions to install:', choices, per_page: 20, echo: false)
176
+ end
177
+
178
+ def pick_with_numbers(out, missing_gems, signal_map)
179
+ out.spacer
180
+ out.header('Missing Extensions')
181
+ missing_gems.each_with_index do |name, idx|
182
+ reason = signal_map[name] ? " (#{signal_map[name]})" : ''
183
+ puts " #{out.colorize((idx + 1).to_s.rjust(3), :label)} #{name}#{reason}"
184
+ end
185
+ out.spacer
186
+ puts ' Enter numbers to install (comma-separated), "all", or "none":'
187
+ print ' > '
188
+ input = $stdin.gets&.strip || 'none'
189
+
190
+ return missing_gems.dup if input.downcase == 'all'
191
+ return [] if input.empty? || input.downcase == 'none'
192
+
193
+ indices = input.split(/[,\s]+/).filter_map { |s| s.to_i - 1 if s.match?(/\A\d+\z/) }
194
+ indices.filter_map { |i| missing_gems[i] if i >= 0 && i < missing_gems.size }.uniq
195
+ end
196
+
197
+ def build_signal_map(results)
198
+ map = {}
199
+ results.each do |detection|
200
+ signals = detection[:matched_signals].join(', ')
201
+ detection[:installed].each do |gem_name, installed|
202
+ map[gem_name] = signals unless installed
203
+ end
204
+ end
205
+ map
206
+ end
207
+
208
+ def install_selected(out, selected)
209
+ out.header("Installing #{selected.size} extension(s)")
210
+ result = Legion::Extensions::Detect::Installer.install(selected)
211
+
212
+ result[:installed].each { |name| out.success(" Installed #{name}") }
213
+ result[:failed].each { |f| out.error(" Failed: #{f[:name]} — #{f[:error]}") }
214
+
215
+ out.spacer
216
+ if result[:failed].empty?
217
+ out.success("#{result[:installed].size} extension(s) installed")
218
+ else
219
+ out.warn("#{result[:installed].size} installed, #{result[:failed].size} failed")
220
+ end
221
+ end
222
+
223
+ def tty_prompt_available?
224
+ require 'tty-prompt'
225
+ true
226
+ rescue LoadError
227
+ false
228
+ end
229
+
133
230
  def install_missing(out)
134
231
  missing_gems = Legion::Extensions::Detect.missing
135
232
  return if missing_gems.empty?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.4.117'
4
+ VERSION = '1.4.118'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legionio
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.117
4
+ version: 1.4.118
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity