legionio 1.4.198 → 1.4.199
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 +13 -0
- data/exe/legion +7 -7
- data/lib/legion/cli/detect_command.rb +28 -0
- data/lib/legion/cli/setup_command.rb +170 -0
- data/lib/legion/cli.rb +1 -1
- 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: 49f46da2cebfc684c3bfa9285698ed55e8d266bb4bdce293ab18fbf979496652
|
|
4
|
+
data.tar.gz: e290dd04b7341ebba8a1e567d17348930049ff16aa68409cfb8162db064225ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 62876302ad244a1ac5e4679fe16d682bc21c7e4264bcc5400907c0fa171d463f83193888d247f6437f7be9c3c4544f1b3b79b9d46fbe32fa3afb6ba1255c1403
|
|
7
|
+
data.tar.gz: 64738b0d3b30dd28524c50b7090631f97fcdc6f29941cd566e2804a004834e924babe008406077c0214b46dfe67495c804641ce4343ee268fc836f66e83da1de
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Legion Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.199] - 2026-03-24
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `legion setup agentic` — install full cognitive stack (legion-gaia + legion-llm + all transitive deps) in one command
|
|
7
|
+
- `legion setup llm` — install LLM routing only
|
|
8
|
+
- `legion setup channels` — install channel adapters (lex-slack, lex-microsoft_teams)
|
|
9
|
+
- `legion setup packs` — show installed/missing feature packs
|
|
10
|
+
- `--dry-run` flag on all pack install commands
|
|
11
|
+
- `legion detect` now recommends `legion setup agentic` when legion-gaia or legion-llm are missing
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Comment out Bootsnap.setup in exe/legion (matching exe/legionio)
|
|
15
|
+
|
|
3
16
|
## [1.4.198] - 2026-03-24
|
|
4
17
|
|
|
5
18
|
### Changed
|
data/exe/legion
CHANGED
|
@@ -10,13 +10,13 @@ ENV['RUBY_GC_MALLOC_LIMIT'] ||= '64000000'
|
|
|
10
10
|
ENV['RUBY_GC_MALLOC_LIMIT_MAX'] ||= '128000000'
|
|
11
11
|
|
|
12
12
|
require 'bootsnap'
|
|
13
|
-
Bootsnap.setup(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
)
|
|
13
|
+
# Bootsnap.setup(
|
|
14
|
+
# cache_dir: File.expand_path('~/.legionio/cache/bootsnap'),
|
|
15
|
+
# development_mode: false,
|
|
16
|
+
# load_path_cache: true,
|
|
17
|
+
# compile_cache_iseq: true,
|
|
18
|
+
# compile_cache_yaml: true
|
|
19
|
+
# )
|
|
20
20
|
|
|
21
21
|
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
|
22
22
|
|
|
@@ -16,6 +16,11 @@ module Legion
|
|
|
16
16
|
class_option :json, type: :boolean, default: false, desc: 'Output as JSON'
|
|
17
17
|
class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
|
|
18
18
|
|
|
19
|
+
CORE_RECOMMENDATIONS = {
|
|
20
|
+
'legion-gaia' => 'Cognitive coordination (GAIA + agentic extensions)',
|
|
21
|
+
'legion-llm' => 'LLM routing and provider integration'
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
19
24
|
default_task :scan
|
|
20
25
|
|
|
21
26
|
desc 'scan', 'Scan environment and recommend extensions (default)'
|
|
@@ -109,6 +114,8 @@ module Legion
|
|
|
109
114
|
end
|
|
110
115
|
|
|
111
116
|
def display_detections(out, results)
|
|
117
|
+
display_pack_recommendations(out)
|
|
118
|
+
|
|
112
119
|
if results.empty?
|
|
113
120
|
out.detail('No software detected that maps to Legion extensions.')
|
|
114
121
|
return
|
|
@@ -135,6 +142,27 @@ module Legion
|
|
|
135
142
|
puts " #{installed_count} of #{total_count} extension(s) installed"
|
|
136
143
|
end
|
|
137
144
|
|
|
145
|
+
def display_pack_recommendations(out)
|
|
146
|
+
missing = CORE_RECOMMENDATIONS.reject { |gem_name, _| gem_installed?(gem_name) }
|
|
147
|
+
return if missing.empty?
|
|
148
|
+
|
|
149
|
+
out.header('Recommended Feature Packs')
|
|
150
|
+
out.spacer
|
|
151
|
+
missing.each do |gem_name, desc|
|
|
152
|
+
puts " #{out.colorize(gem_name.ljust(20), :label)} #{desc}"
|
|
153
|
+
end
|
|
154
|
+
out.spacer
|
|
155
|
+
puts " Install with: #{out.colorize('legion setup agentic', :accent)}"
|
|
156
|
+
out.spacer
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def gem_installed?(name)
|
|
160
|
+
Gem::Specification.find_by_name(name)
|
|
161
|
+
true
|
|
162
|
+
rescue Gem::MissingSpecError
|
|
163
|
+
false
|
|
164
|
+
end
|
|
165
|
+
|
|
138
166
|
def interactive_install(out, results)
|
|
139
167
|
missing_gems = Legion::Extensions::Detect.missing
|
|
140
168
|
return out.success('All detected extensions are installed') if missing_gems.empty?
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'English'
|
|
3
4
|
require 'json'
|
|
4
5
|
require 'fileutils'
|
|
5
6
|
require 'thor'
|
|
7
|
+
require 'rbconfig'
|
|
6
8
|
require 'legion/cli/output'
|
|
7
9
|
|
|
8
10
|
module Legion
|
|
@@ -23,6 +25,21 @@ module Legion
|
|
|
23
25
|
'args' => %w[mcp stdio]
|
|
24
26
|
}.freeze
|
|
25
27
|
|
|
28
|
+
PACKS = {
|
|
29
|
+
agentic: {
|
|
30
|
+
description: 'Full cognitive stack: GAIA + LLM + MCP + Apollo',
|
|
31
|
+
gems: %w[legion-gaia legion-llm]
|
|
32
|
+
},
|
|
33
|
+
llm: {
|
|
34
|
+
description: 'LLM routing and provider integration (no cognitive stack)',
|
|
35
|
+
gems: %w[legion-llm]
|
|
36
|
+
},
|
|
37
|
+
channels: {
|
|
38
|
+
description: 'Channel adapters for chat platforms',
|
|
39
|
+
gems: %w[lex-slack lex-microsoft_teams]
|
|
40
|
+
}
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
26
43
|
SKILL_CONTENT = <<~MARKDOWN
|
|
27
44
|
---
|
|
28
45
|
name: legion
|
|
@@ -92,6 +109,54 @@ module Legion
|
|
|
92
109
|
end
|
|
93
110
|
end
|
|
94
111
|
|
|
112
|
+
desc 'agentic', 'Install full cognitive stack (GAIA + LLM + Apollo + all agentic extensions)'
|
|
113
|
+
option :dry_run, type: :boolean, default: false, desc: 'Show what would be installed without installing'
|
|
114
|
+
def agentic
|
|
115
|
+
install_pack(:agentic)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
desc 'llm', 'Install LLM routing and provider integration'
|
|
119
|
+
option :dry_run, type: :boolean, default: false, desc: 'Show what would be installed without installing'
|
|
120
|
+
def llm
|
|
121
|
+
install_pack(:llm)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
desc 'channels', 'Install channel adapters (Slack, Teams)'
|
|
125
|
+
option :dry_run, type: :boolean, default: false, desc: 'Show what would be installed without installing'
|
|
126
|
+
def channels
|
|
127
|
+
install_pack(:channels)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
desc 'packs', 'Show installed feature packs and available gems'
|
|
131
|
+
def packs
|
|
132
|
+
out = formatter
|
|
133
|
+
pack_statuses = PACKS.map do |name, pack|
|
|
134
|
+
installed, missing = partition_gems(pack[:gems])
|
|
135
|
+
{ name: name, description: pack[:description],
|
|
136
|
+
installed: installed.map { |g| { name: g, version: gem_version(g) } },
|
|
137
|
+
missing: missing }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if options[:json]
|
|
141
|
+
out.json(packs: pack_statuses)
|
|
142
|
+
else
|
|
143
|
+
out.header('Feature Packs')
|
|
144
|
+
out.spacer
|
|
145
|
+
pack_statuses.each do |ps|
|
|
146
|
+
all_installed = ps[:missing].empty?
|
|
147
|
+
icon = all_installed ? out.colorize('installed', :success) : out.colorize('not installed', :muted)
|
|
148
|
+
puts " #{out.colorize(ps[:name].to_s.ljust(12), :label)} #{icon} #{ps[:description]}"
|
|
149
|
+
ps[:installed].each do |g|
|
|
150
|
+
puts " #{out.colorize(g[:name], :success)} #{g[:version]}"
|
|
151
|
+
end
|
|
152
|
+
ps[:missing].each do |g|
|
|
153
|
+
puts " #{out.colorize(g, :muted)} (missing)"
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
out.spacer
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
95
160
|
desc 'status', 'Show which platforms have Legion MCP configured'
|
|
96
161
|
def status
|
|
97
162
|
out = formatter
|
|
@@ -123,6 +188,111 @@ module Legion
|
|
|
123
188
|
|
|
124
189
|
private
|
|
125
190
|
|
|
191
|
+
def install_pack(pack_name)
|
|
192
|
+
pack = PACKS[pack_name]
|
|
193
|
+
installed, missing = partition_gems(pack[:gems])
|
|
194
|
+
|
|
195
|
+
return report_already_installed(pack_name, installed) if missing.empty?
|
|
196
|
+
return report_dry_run(pack_name, installed, missing) if options[:dry_run]
|
|
197
|
+
|
|
198
|
+
execute_pack_install(pack_name, installed, missing)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def report_already_installed(pack_name, installed)
|
|
202
|
+
out = formatter
|
|
203
|
+
if options[:json]
|
|
204
|
+
out.json(pack: pack_name, status: 'already_installed',
|
|
205
|
+
gems: installed.map { |g| { name: g, version: gem_version(g) } })
|
|
206
|
+
else
|
|
207
|
+
out.success("#{pack_name} pack already installed")
|
|
208
|
+
installed.each { |g| puts " #{g} #{gem_version(g)}" }
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def report_dry_run(pack_name, installed, missing)
|
|
213
|
+
out = formatter
|
|
214
|
+
if options[:json]
|
|
215
|
+
out.json(pack: pack_name, status: 'dry_run', to_install: missing,
|
|
216
|
+
already_installed: installed.map { |g| { name: g, version: gem_version(g) } })
|
|
217
|
+
else
|
|
218
|
+
out.header("#{pack_name} pack (dry run)")
|
|
219
|
+
missing.each { |g| puts " #{out.colorize('install', :accent)} #{g}" }
|
|
220
|
+
installed.each { |g| puts " #{out.colorize('skip', :muted)} #{g} #{gem_version(g)} (already installed)" }
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def execute_pack_install(pack_name, installed, missing)
|
|
225
|
+
out = formatter
|
|
226
|
+
out.header("Installing #{pack_name} pack") unless options[:json]
|
|
227
|
+
gem_bin = File.join(RbConfig::CONFIG['bindir'], 'gem')
|
|
228
|
+
results = missing.map { |g| install_gem(g, gem_bin, out) }
|
|
229
|
+
|
|
230
|
+
Gem::Specification.reset
|
|
231
|
+
successes, failures = results.partition { |r| r[:status] == 'installed' }
|
|
232
|
+
|
|
233
|
+
if options[:json]
|
|
234
|
+
out.json(pack: pack_name, installed: successes, failed: failures,
|
|
235
|
+
already_present: installed.map { |g| { name: g, version: gem_version(g) } })
|
|
236
|
+
else
|
|
237
|
+
out.spacer
|
|
238
|
+
if failures.empty?
|
|
239
|
+
out.success("#{pack_name} pack installed (#{successes.size} gem(s))")
|
|
240
|
+
suggest_next_steps(out, pack_name)
|
|
241
|
+
else
|
|
242
|
+
out.error("#{failures.size} gem(s) failed to install")
|
|
243
|
+
failures.each { |f| puts " #{f[:name]}: #{f[:error]}" }
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def partition_gems(gem_names)
|
|
249
|
+
installed = []
|
|
250
|
+
missing = []
|
|
251
|
+
gem_names.each do |name|
|
|
252
|
+
Gem::Specification.find_by_name(name)
|
|
253
|
+
installed << name
|
|
254
|
+
rescue Gem::MissingSpecError
|
|
255
|
+
missing << name
|
|
256
|
+
end
|
|
257
|
+
[installed, missing]
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def gem_version(name)
|
|
261
|
+
Gem::Specification.find_by_name(name).version.to_s
|
|
262
|
+
rescue Gem::MissingSpecError
|
|
263
|
+
nil
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def install_gem(name, gem_bin, out)
|
|
267
|
+
puts " Installing #{name}..." unless options[:json]
|
|
268
|
+
output = `#{gem_bin} install #{name} --no-document 2>&1`
|
|
269
|
+
if $CHILD_STATUS.success?
|
|
270
|
+
out.success(" #{name} installed") unless options[:json]
|
|
271
|
+
{ name: name, status: 'installed' }
|
|
272
|
+
else
|
|
273
|
+
out.error(" #{name} failed") unless options[:json]
|
|
274
|
+
{ name: name, status: 'failed', error: output.strip.lines.last&.strip }
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def suggest_next_steps(out, pack_name)
|
|
279
|
+
out.spacer
|
|
280
|
+
case pack_name
|
|
281
|
+
when :agentic
|
|
282
|
+
puts ' Next steps:'
|
|
283
|
+
puts ' legion start # full daemon with cognitive stack'
|
|
284
|
+
puts ' legion start --lite # single-process, no external services'
|
|
285
|
+
puts ' legion chat # interactive AI conversation'
|
|
286
|
+
when :llm
|
|
287
|
+
puts ' Next steps:'
|
|
288
|
+
puts ' legion chat # interactive AI conversation'
|
|
289
|
+
puts ' legion llm status # check provider connectivity'
|
|
290
|
+
when :channels
|
|
291
|
+
puts ' Next steps:'
|
|
292
|
+
puts ' Configure channels in settings: {"gaia": {"channels": {"slack": {"enabled": true}}}}'
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
126
296
|
def install_claude_mcp(installed)
|
|
127
297
|
settings_path = File.expand_path('~/.claude/settings.json')
|
|
128
298
|
existing = load_json_file(settings_path)
|
data/lib/legion/cli.rb
CHANGED
|
@@ -269,7 +269,7 @@ module Legion
|
|
|
269
269
|
desc 'init', 'Initialize a new Legion workspace'
|
|
270
270
|
subcommand 'init', Legion::CLI::Init
|
|
271
271
|
|
|
272
|
-
desc 'setup SUBCOMMAND', '
|
|
272
|
+
desc 'setup SUBCOMMAND', 'Install feature packs and configure IDE integrations'
|
|
273
273
|
subcommand 'setup', Legion::CLI::Setup
|
|
274
274
|
|
|
275
275
|
desc 'skill', 'Manage skills (.legion/skills/ markdown files)'
|
data/lib/legion/version.rb
CHANGED