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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b3541eb1b73c73f13747ab36c76e033c98fe187dffd9ae39c872a69b63164d5
4
- data.tar.gz: 54c03dfd42a4dab7d2eb0e68e336dced9a5e756903e0cf8b52d4fb4570a92894
3
+ metadata.gz: 49f46da2cebfc684c3bfa9285698ed55e8d266bb4bdce293ab18fbf979496652
4
+ data.tar.gz: e290dd04b7341ebba8a1e567d17348930049ff16aa68409cfb8162db064225ff
5
5
  SHA512:
6
- metadata.gz: f0a8efa9f38c32aa0e7b1d81229dbafab7ed7a5a24e9b58b9c59eb69f2e41e36965bdee6cca9fafa699c76cd3e72f5ebd32b1559753317844876bc8d5e20f781
7
- data.tar.gz: c41f3347ab6d6c5639c617176090650234ca4b0fc4bbbda7709ae9d2f7fb91eca20543d26fb4921eb1417e3054f680c7b18f61e5c071c346203cf7cce5a382fb
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
- 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
- )
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', 'Set up Legion MCP integration for IDEs'
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)'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.4.198'
4
+ VERSION = '1.4.199'
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.198
4
+ version: 1.4.199
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity