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 +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/legion/cli/detect_command.rb +99 -2
- data/lib/legion/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: a10f06b3cc591d8fa5aae3b31216e037826f961962df05276ba5fbd78805235c
|
|
4
|
+
data.tar.gz: 5a9e88f6bbb42b27586adba45becd7ecaedc69f190bd3f0fbddb3de27fcffd79
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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: '
|
|
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
|
-
|
|
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?
|
data/lib/legion/version.rb
CHANGED