Class: Legion::CLI::Setup

Inherits:
Thor
  • Object
show all
Defined in:
lib/legion/cli/setup_command.rb

Constant Summary collapse

LEGION_MCP_ENTRY =
{
  'command' => 'legionio',
  'args'    => %w[mcp stdio]
}.freeze
PACKS =
{
  agentic:  {
    description: 'Full cognitive stack: core libs, agentic domains, AI providers, and operational extensions',
    gems:        %w[
      legion-apollo legion-gaia legion-llm legion-mcp legion-rbac
      lex-acp lex-adapter lex-agentic-affect lex-agentic-attention
      lex-agentic-defense lex-agentic-executive lex-agentic-homeostasis
      lex-agentic-imagination lex-agentic-inference lex-agentic-integration
      lex-agentic-language lex-agentic-learning lex-agentic-memory
      lex-agentic-self lex-agentic-social lex-apollo lex-audit lex-autofix
      lex-azure-ai lex-bedrock lex-claude lex-codegen lex-coldstart
      lex-conditioner lex-cost-scanner lex-dataset lex-detect
      lex-eval lex-exec lex-extinction lex-factory lex-finops lex-foundry
      lex-gemini lex-governance lex-kerberos lex-knowledge lex-llm-gateway
      lex-metering lex-mesh lex-microsoft_teams lex-mind-growth lex-node
      lex-onboard lex-openai lex-pilot-infra-monitor
      lex-pilot-knowledge-assist lex-privatecore lex-prompt lex-react
      lex-swarm lex-swarm-github lex-synapse lex-telemetry lex-tick
      lex-transformer lex-xai
    ]
  },
  llm:      {
    description: 'LLM routing and provider integration (no cognitive stack)',
    gems:        %w[legion-llm]
  },
  channels: {
    description: 'Channel adapters for chat platforms',
    gems:        %w[lex-slack lex-microsoft_teams]
  }
}.freeze
PYTHON_PACKAGES =
Legion::Python::PACKAGES
PYTHON_VENV_DIR =
Legion::Python::VENV_DIR
PYTHON_MARKER =
Legion::Python::MARKER
SKILL_CONTENT =
<<~MARKDOWN
  ---
  name: legion
  description: Orchestrate LegionIO extensions and agents
  ---

  You have access to LegionIO MCP tools. When the user asks you to work with Legion:

  1. Use `legion.discover_tools` to find relevant capabilities
  2. Use `legion.do_action` for natural language task routing
  3. Use `legion.run_task` to execute specific extension functions
  4. Use `legion.list_peers` and `legion.ask_peer` for agent coordination
  5. Present results as a consolidated summary
MARKDOWN

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.exit_on_failure?Boolean

Returns:

  • (Boolean)


17
18
19
# File 'lib/legion/cli/setup_command.rb', line 17

def self.exit_on_failure?
  true
end

Instance Method Details

#agenticObject



137
138
139
# File 'lib/legion/cli/setup_command.rb', line 137

def agentic
  install_pack(:agentic)
end

#channelsObject



151
152
153
# File 'lib/legion/cli/setup_command.rb', line 151

def channels
  install_pack(:channels)
end

#claude_codeObject



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/legion/cli/setup_command.rb', line 81

def claude_code
  out = formatter
  installed = []

  install_claude_mcp(installed)
  install_claude_skill(installed)
  install_claude_hooks(installed)

  if options[:json]
    out.json(platform: 'claude-code', installed: installed)
  else
    out.spacer
    out.success("Legion configured for Claude Code (#{installed.size} item(s))")
    out.spacer
    puts "  Run '/legion' in Claude Code to use your LegionIO tools."
  end
end

#cursorObject



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/legion/cli/setup_command.rb', line 100

def cursor
  out = formatter
  path = File.join(Dir.pwd, '.cursor', 'mcp.json')
  installed = []

  write_mcp_servers_json(nil, path, installed)

  if options[:json]
    out.json(platform: 'cursor', installed: installed)
  else
    out.spacer
    out.success("Legion configured for Cursor (#{installed.size} item(s))")
    out.spacer
    puts "  MCP config written to: #{path}"
  end
end

#llmObject



145
146
147
# File 'lib/legion/cli/setup_command.rb', line 145

def llm
  install_pack(:llm)
end

#packsObject



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/legion/cli/setup_command.rb', line 224

def packs
  out = formatter
  pack_statuses = PACKS.map do |name, pack|
    installed, missing = partition_gems(pack[:gems])
    { name: name, description: pack[:description],
      installed: installed.map { |g| { name: g, version: gem_version(g) } },
      missing: missing }
  end

  if options[:json]
    out.json(packs: pack_statuses)
  else
    out.header('Feature Packs')
    out.spacer
    pack_statuses.each do |ps|
      all_installed = ps[:missing].empty?
      icon = all_installed ? out.colorize('installed', :success) : out.colorize('not installed', :muted)
      puts "  #{out.colorize(ps[:name].to_s.ljust(12), :label)} #{icon}  #{ps[:description]}"
      ps[:installed].each do |g|
        puts "    #{out.colorize(g[:name], :success)} #{g[:version]}"
      end
      ps[:missing].each do |g|
        puts "    #{out.colorize(g, :muted)} (missing)"
      end
    end
    out.spacer
  end
end

#pythonObject

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/legion/cli/setup_command.rb', line 158

def python # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
  out = formatter
  results = []

  python3 = find_python3
  unless python3
    out.error('python3 not found. Install it with: brew install python')
    exit 1
  end

  if options[:rebuild] && Dir.exist?(PYTHON_VENV_DIR)
    out.header("Rebuilding Python venv at #{PYTHON_VENV_DIR}") unless options[:json]
    FileUtils.rm_rf(PYTHON_VENV_DIR)
  end

  unless File.exist?("#{PYTHON_VENV_DIR}/pyvenv.cfg")
    out.header("Creating Python venv at #{PYTHON_VENV_DIR}") unless options[:json]
    FileUtils.mkdir_p(File.dirname(PYTHON_VENV_DIR))
    unless system(python3, '-m', 'venv', PYTHON_VENV_DIR)
      out.error('Failed to create Python venv')
      exit 1
    end
    results << { action: 'created_venv', path: PYTHON_VENV_DIR }
  end

  pip = "#{PYTHON_VENV_DIR}/bin/pip"
  unless File.executable?(pip)
    out.error("pip not found at #{pip} — try: legionio setup python --rebuild")
    exit 1
  end

  packages = PYTHON_PACKAGES + Array(options[:packages])
  packages.uniq!

  failed = false
  packages.each do |pkg|
    puts "  Installing #{pkg}..." unless options[:json]
    output, status = Open3.capture2e(pip, 'install', '--quiet', '--upgrade', pkg)
    if status.success?
      out.success("  #{pkg}") unless options[:json]
      results << { package: pkg, status: 'installed' }
    else
      failed = true
      out.error("  #{pkg} failed") unless options[:json]
      results << { package: pkg, status: 'failed', error: output.strip.lines.last&.strip }
    end
  end

  write_python_marker(python3, packages)

  if options[:json]
    out.json(venv: PYTHON_VENV_DIR, python: python_version(python3), results: results)
  else
    out.spacer
    out.success("Python environment ready: #{PYTHON_VENV_DIR}/bin/python3")
    out.spacer
    puts "  Interpreter:    #{PYTHON_VENV_DIR}/bin/python3"
    puts '  Env var:        $LEGION_PYTHON'
    puts '  Add packages:   legionio setup python --packages <name> [<name>...]'
    puts '  Rebuild venv:   legionio setup python --rebuild'
  end

  exit 1 if failed
end

#statusObject



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/legion/cli/setup_command.rb', line 254

def status
  out = formatter
  platforms = check_all_platforms

  if options[:json]
    out.json(platforms: platforms)
  else
    out.header('Legion MCP Setup Status')
    out.spacer
    platforms.each do |p|
      icon = p[:configured] ? out.colorize('configured', :success) : out.colorize('not configured', :muted)
      puts "  #{out.colorize(p[:name].ljust(16), :label)} #{icon}"
      puts "    #{out.colorize(p[:path], :muted)}" if p[:path]
    end
    out.spacer
    configured_count = platforms.count { |p| p[:configured] }
    puts "  #{configured_count} of #{platforms.size} platform(s) configured"
  end
end

#vscodeObject



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/legion/cli/setup_command.rb', line 118

def vscode
  out = formatter
  path = File.join(Dir.pwd, '.vscode', 'mcp.json')
  installed = []

  write_vscode_mcp_json(nil, path, installed)

  if options[:json]
    out.json(platform: 'vscode', installed: installed)
  else
    out.spacer
    out.success("Legion configured for VS Code (#{installed.size} item(s))")
    out.spacer
    puts "  MCP config written to: #{path}"
  end
end