legionio 1.6.7 → 1.6.8
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 -16
- data/lib/legion/cli/bootstrap_command.rb +399 -0
- data/lib/legion/cli/chat/context.rb +11 -0
- data/lib/legion/cli.rb +4 -0
- data/lib/legion/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd3587ed7997208005a0a8746aca477701811f8e506c1d8d9345a40d180317fc
|
|
4
|
+
data.tar.gz: e805096f6577c2b1317807d50beab27bbb91527e2a02807231596fbd6a1d91ca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5d6c20753f9803fc34cd4bb577304bc31d708df54f9e49f266dbb32073c3d06835e2d625efbed610244b5888085d823222627fb07ad889c1ff85d37d028f603a
|
|
7
|
+
data.tar.gz: 0a1dbbff5e24580f3b8b97fb434c626f029f449501b138d0a29f9fcb0eb15edd05808c5b34353260a6bdc4808d4f747362d8f4e6e9f3afdd47362b56035c1e0f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
# Legion Changelog
|
|
2
2
|
|
|
3
|
-
## [1.6.
|
|
4
|
-
|
|
5
|
-
### Fixed
|
|
6
|
-
- `setup_generated_functions` now runs only when `extensions: true` (inside the extensions gate) preventing unexpected boot side-effects in CLI flows that disable extensions
|
|
7
|
-
- Consumer tag entropy upgraded from `SecureRandom.hex(4)` (32-bit) to `SecureRandom.uuid` (122-bit) in both `prepare` and `subscribe` paths of subscription actor, eliminating the theoretical RabbitMQ `NOT_ALLOWED` tag collision
|
|
8
|
-
|
|
9
|
-
## [1.6.6] - 2026-03-26
|
|
3
|
+
## [1.6.8] - 2026-03-26
|
|
10
4
|
|
|
11
5
|
### Added
|
|
12
6
|
- `legionio bootstrap SOURCE` command: combines `config import`, `config scaffold`, and `setup agentic` into one command
|
|
13
7
|
- Pre-flight checks for klist (Kerberos ticket), brew availability, and legionio binary
|
|
14
|
-
- `--skip-packs
|
|
15
|
-
-
|
|
16
|
-
- `--force` flag to overwrite existing config files during bootstrap
|
|
17
|
-
- `--json` flag for machine-readable bootstrap output
|
|
18
|
-
- `shell_capture` helper extracted to make shell invocations stubbable in specs
|
|
19
|
-
- 62 specs covering preflight checks, pack extraction, config fetch/write delegation, pack install, summary output, all flags, and error handling
|
|
8
|
+
- `--skip-packs`, `--start`, `--force`, `--json` flags for bootstrap command
|
|
9
|
+
- Self-awareness system prompt enrichment: `Context.to_system_prompt` appends live metacognition self-narrative from `lex-agentic-self` when loaded; guarded with `defined?()` and `rescue StandardError`
|
|
20
10
|
|
|
21
|
-
## [1.6.
|
|
11
|
+
## [1.6.7] - 2026-03-26
|
|
22
12
|
|
|
23
|
-
###
|
|
24
|
-
- `
|
|
13
|
+
### Fixed
|
|
14
|
+
- `setup_generated_functions` now runs only when `extensions: true` (inside the extensions gate) preventing unexpected boot side-effects in CLI flows that disable extensions
|
|
15
|
+
- Consumer tag entropy upgraded from `SecureRandom.hex(4)` (32-bit) to `SecureRandom.uuid` (122-bit) in both `prepare` and `subscribe` paths of subscription actor, eliminating the theoretical RabbitMQ `NOT_ALLOWED` tag collision
|
|
25
16
|
|
|
26
17
|
## [1.6.4] - 2026-03-26
|
|
27
18
|
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'English'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
require 'rbconfig'
|
|
7
|
+
require 'thor'
|
|
8
|
+
require 'legion/cli/output'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module CLI
|
|
12
|
+
class Bootstrap < Thor
|
|
13
|
+
namespace 'bootstrap'
|
|
14
|
+
|
|
15
|
+
def self.exit_on_failure?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class_option :json, type: :boolean, default: false, desc: 'Machine-readable output'
|
|
20
|
+
class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
|
|
21
|
+
class_option :skip_packs, type: :boolean, default: false, desc: 'Skip gem pack installation (config only)'
|
|
22
|
+
class_option :start, type: :boolean, default: false, desc: 'Start redis + legionio via brew services after bootstrap'
|
|
23
|
+
class_option :force, type: :boolean, default: false, desc: 'Overwrite existing config files'
|
|
24
|
+
|
|
25
|
+
desc 'SOURCE', 'Bootstrap Legion from a URL or local config file (fetch config, scaffold, install packs)'
|
|
26
|
+
long_desc <<~DESC
|
|
27
|
+
Combines three manual steps into one:
|
|
28
|
+
|
|
29
|
+
legionio config import SOURCE (fetch + write config)
|
|
30
|
+
legionio config scaffold (fill gaps with env-detected defaults)
|
|
31
|
+
legionio setup agentic (install cognitive gem packs)
|
|
32
|
+
|
|
33
|
+
SOURCE may be an HTTPS URL or a local file path to a bootstrap JSON file.
|
|
34
|
+
The JSON may include a "packs" array (e.g. ["agentic"]) which controls which
|
|
35
|
+
gem packs are installed. That key is removed before the config is written.
|
|
36
|
+
|
|
37
|
+
Options:
|
|
38
|
+
--skip-packs Skip gem pack installation entirely
|
|
39
|
+
--start After bootstrap, run: brew services start redis && brew services start legionio
|
|
40
|
+
--force Overwrite existing config files
|
|
41
|
+
--json Machine-readable JSON output
|
|
42
|
+
DESC
|
|
43
|
+
def execute(source)
|
|
44
|
+
require_relative 'config_import'
|
|
45
|
+
require_relative 'config_scaffold'
|
|
46
|
+
require_relative 'setup_command'
|
|
47
|
+
|
|
48
|
+
out = formatter
|
|
49
|
+
results = {}
|
|
50
|
+
warns = []
|
|
51
|
+
|
|
52
|
+
# 1. Pre-flight checks
|
|
53
|
+
print_step(out, 'Pre-flight checks')
|
|
54
|
+
results[:preflight] = run_preflight_checks(out, warns)
|
|
55
|
+
|
|
56
|
+
# 2. Fetch + parse config
|
|
57
|
+
print_step(out, "Fetching config from #{source}")
|
|
58
|
+
body = ConfigImport.fetch_source(source)
|
|
59
|
+
config = ConfigImport.parse_payload(body)
|
|
60
|
+
|
|
61
|
+
# 3. Extract packs before writing (bootstrap-only directive)
|
|
62
|
+
pack_names = Array(config.delete(:packs)).map(&:to_s).reject(&:empty?)
|
|
63
|
+
results[:packs_requested] = pack_names
|
|
64
|
+
|
|
65
|
+
# 4. Write config
|
|
66
|
+
path = ConfigImport.write_config(config, force: options[:force])
|
|
67
|
+
results[:config_written] = path
|
|
68
|
+
out.success("Config written to #{path}") unless options[:json]
|
|
69
|
+
|
|
70
|
+
# 5. Scaffold missing subsystem files
|
|
71
|
+
results[:scaffold] = run_scaffold(out)
|
|
72
|
+
|
|
73
|
+
# 6. Install packs (unless --skip-packs)
|
|
74
|
+
results[:packs_installed] = install_packs_step(pack_names, out)
|
|
75
|
+
|
|
76
|
+
# 7. Post-bootstrap summary
|
|
77
|
+
summary = build_summary(config, results, warns)
|
|
78
|
+
results[:summary] = summary
|
|
79
|
+
print_summary(out, summary)
|
|
80
|
+
|
|
81
|
+
# 8. Optional --start
|
|
82
|
+
if options[:start]
|
|
83
|
+
print_step(out, 'Starting services')
|
|
84
|
+
results[:services_started] = start_services(out)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
out.json(results) if options[:json]
|
|
88
|
+
rescue CLI::Error => e
|
|
89
|
+
formatter.error(e.message)
|
|
90
|
+
raise SystemExit, 1
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
default_task :execute
|
|
94
|
+
|
|
95
|
+
no_commands do # rubocop:disable Metrics/BlockLength
|
|
96
|
+
def formatter
|
|
97
|
+
@formatter ||= Output::Formatter.new(
|
|
98
|
+
json: options[:json],
|
|
99
|
+
color: !options[:no_color]
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def print_step(out, message)
|
|
106
|
+
return if options[:json]
|
|
107
|
+
|
|
108
|
+
out.spacer
|
|
109
|
+
out.header(message)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Wraps backtick execution, returning [output, success_bool].
|
|
113
|
+
# Extracted as a method so specs can stub it cleanly.
|
|
114
|
+
def shell_capture(cmd)
|
|
115
|
+
output = `#{cmd} 2>&1`
|
|
116
|
+
[output, $CHILD_STATUS.success?]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# -----------------------------------------------------------------------
|
|
120
|
+
# Pre-flight checks
|
|
121
|
+
# -----------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
def run_preflight_checks(out, warns)
|
|
124
|
+
{
|
|
125
|
+
klist: check_klist(out, warns),
|
|
126
|
+
brew: check_brew(out, warns),
|
|
127
|
+
legionio: check_legionio_binary(out, warns)
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def check_klist(out, warns)
|
|
132
|
+
output, success = shell_capture('klist')
|
|
133
|
+
if success && output.match?(/principal|Credentials/i)
|
|
134
|
+
out.success('Kerberos ticket valid') unless options[:json]
|
|
135
|
+
{ status: :ok }
|
|
136
|
+
else
|
|
137
|
+
msg = 'No valid Kerberos ticket found. Run `kinit` before bootstrapping.'
|
|
138
|
+
warns << msg
|
|
139
|
+
out.warn(msg) unless options[:json]
|
|
140
|
+
{ status: :warn, message: msg }
|
|
141
|
+
end
|
|
142
|
+
rescue StandardError => e
|
|
143
|
+
msg = "klist check failed: #{e.message}"
|
|
144
|
+
warns << msg
|
|
145
|
+
out.warn(msg) unless options[:json]
|
|
146
|
+
{ status: :warn, message: msg }
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def check_brew(out, warns)
|
|
150
|
+
_, success = shell_capture('brew --version')
|
|
151
|
+
if success
|
|
152
|
+
out.success('Homebrew available') unless options[:json]
|
|
153
|
+
{ status: :ok }
|
|
154
|
+
else
|
|
155
|
+
msg = 'Homebrew not found. Install from https://brew.sh'
|
|
156
|
+
warns << msg
|
|
157
|
+
out.warn(msg) unless options[:json]
|
|
158
|
+
{ status: :warn, message: msg }
|
|
159
|
+
end
|
|
160
|
+
rescue StandardError => e
|
|
161
|
+
msg = "brew check failed: #{e.message}"
|
|
162
|
+
warns << msg
|
|
163
|
+
out.warn(msg) unless options[:json]
|
|
164
|
+
{ status: :warn, message: msg }
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def check_legionio_binary(out, warns)
|
|
168
|
+
_, success = shell_capture('legionio version')
|
|
169
|
+
if success
|
|
170
|
+
out.success('legionio binary works') unless options[:json]
|
|
171
|
+
{ status: :ok }
|
|
172
|
+
else
|
|
173
|
+
msg = 'legionio binary not responding. Try reinstalling: brew reinstall legionio'
|
|
174
|
+
warns << msg
|
|
175
|
+
out.warn(msg) unless options[:json]
|
|
176
|
+
{ status: :warn, message: msg }
|
|
177
|
+
end
|
|
178
|
+
rescue StandardError => e
|
|
179
|
+
msg = "legionio binary check failed: #{e.message}"
|
|
180
|
+
warns << msg
|
|
181
|
+
out.warn(msg) unless options[:json]
|
|
182
|
+
{ status: :warn, message: msg }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def run_scaffold(out)
|
|
186
|
+
print_step(out, 'Scaffolding missing subsystem files')
|
|
187
|
+
silent_out = Output::Formatter.new(json: false, color: false)
|
|
188
|
+
scaffold_opts = build_scaffold_opts
|
|
189
|
+
scaffold_opts[:json] = false if options[:json]
|
|
190
|
+
ConfigScaffold.run(options[:json] ? silent_out : out, scaffold_opts)
|
|
191
|
+
:done
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def install_packs_step(pack_names, out)
|
|
195
|
+
if options[:skip_packs]
|
|
196
|
+
out.warn('Skipping pack installation (--skip-packs)') unless options[:json]
|
|
197
|
+
[]
|
|
198
|
+
else
|
|
199
|
+
print_step(out, "Installing packs: #{pack_names.join(', ')}") unless pack_names.empty?
|
|
200
|
+
install_packs(pack_names, out)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# -----------------------------------------------------------------------
|
|
205
|
+
# Scaffold options
|
|
206
|
+
# -----------------------------------------------------------------------
|
|
207
|
+
|
|
208
|
+
def build_scaffold_opts
|
|
209
|
+
{
|
|
210
|
+
force: options[:force],
|
|
211
|
+
json: options[:json],
|
|
212
|
+
only: options[:only],
|
|
213
|
+
full: options[:full],
|
|
214
|
+
dir: options[:dir]
|
|
215
|
+
}
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# -----------------------------------------------------------------------
|
|
219
|
+
# Pack installation
|
|
220
|
+
# -----------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
def install_packs(pack_names, out)
|
|
223
|
+
return [] if pack_names.empty?
|
|
224
|
+
|
|
225
|
+
gem_bin = File.join(RbConfig::CONFIG['bindir'], 'gem')
|
|
226
|
+
results = []
|
|
227
|
+
|
|
228
|
+
pack_names.each do |pack_name|
|
|
229
|
+
pack_sym = pack_name.to_sym
|
|
230
|
+
pack = Setup::PACKS[pack_sym]
|
|
231
|
+
unless pack
|
|
232
|
+
out.warn("Unknown pack: #{pack_name} (valid: #{Setup::PACKS.keys.join(', ')})") unless options[:json]
|
|
233
|
+
next
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
out.header("Installing pack: #{pack_name}") unless options[:json]
|
|
237
|
+
gem_results = install_pack_gems(pack[:gems], gem_bin, out)
|
|
238
|
+
Gem::Specification.reset
|
|
239
|
+
results << { pack: pack_name, results: gem_results }
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
results
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def install_pack_gems(gem_names, gem_bin, out)
|
|
246
|
+
already_installed = []
|
|
247
|
+
to_install = []
|
|
248
|
+
|
|
249
|
+
gem_names.each do |name|
|
|
250
|
+
Gem::Specification.find_by_name(name)
|
|
251
|
+
already_installed << name
|
|
252
|
+
rescue Gem::MissingSpecError
|
|
253
|
+
to_install << name
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
gem_results = to_install.map { |g| install_single_gem(g, gem_bin, out) }
|
|
257
|
+
|
|
258
|
+
already_installed.each do |g|
|
|
259
|
+
out.success(" #{g} already installed") unless options[:json]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
gem_results
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def install_single_gem(name, gem_bin, out)
|
|
266
|
+
puts " Installing #{name}..." unless options[:json]
|
|
267
|
+
output, success = shell_capture("#{gem_bin} install #{name} --no-document")
|
|
268
|
+
if success
|
|
269
|
+
out.success(" #{name} installed") unless options[:json]
|
|
270
|
+
{ name: name, status: 'installed' }
|
|
271
|
+
else
|
|
272
|
+
out.error(" #{name} failed") unless options[:json]
|
|
273
|
+
{ name: name, status: 'failed', error: output.strip.lines.last&.strip }
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# -----------------------------------------------------------------------
|
|
278
|
+
# Summary
|
|
279
|
+
# -----------------------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
def build_summary(config, results, warns)
|
|
282
|
+
settings_dir = ConfigImport::SETTINGS_DIR
|
|
283
|
+
subsystem_files = ConfigScaffold::SUBSYSTEMS.to_h do |s|
|
|
284
|
+
path = File.join(settings_dir, "#{s}.json")
|
|
285
|
+
[s, File.exist?(path)]
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
{
|
|
289
|
+
config_sections: config.keys.map(&:to_s),
|
|
290
|
+
packs_requested: results[:packs_requested] || [],
|
|
291
|
+
packs_installed: results[:packs_installed] || [],
|
|
292
|
+
subsystem_files: subsystem_files,
|
|
293
|
+
warnings: warns,
|
|
294
|
+
preflight: results[:preflight] || {}
|
|
295
|
+
}
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def print_summary(out, summary)
|
|
299
|
+
return if options[:json]
|
|
300
|
+
|
|
301
|
+
out.spacer
|
|
302
|
+
out.header('Bootstrap Summary')
|
|
303
|
+
out.spacer
|
|
304
|
+
|
|
305
|
+
print_config_sections(summary)
|
|
306
|
+
print_subsystem_files(summary)
|
|
307
|
+
print_packs_summary(out, summary)
|
|
308
|
+
print_warnings_section(out, summary)
|
|
309
|
+
print_next_steps(out)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def print_config_sections(summary)
|
|
313
|
+
puts " Config sections: #{summary[:config_sections].join(', ')}" if summary[:config_sections].any?
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def print_subsystem_files(summary)
|
|
317
|
+
present = summary[:subsystem_files].select { |_, v| v }.keys
|
|
318
|
+
absent = summary[:subsystem_files].reject { |_, v| v }.keys
|
|
319
|
+
puts " Subsystem files present: #{present.join(', ')}" if present.any?
|
|
320
|
+
puts " Subsystem files missing: #{absent.join(', ')}" if absent.any?
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def print_packs_summary(out, summary)
|
|
324
|
+
summary[:packs_installed].each do |pack_result|
|
|
325
|
+
successes = (pack_result[:results] || []).count { |r| r[:status] == 'installed' }
|
|
326
|
+
failures = (pack_result[:results] || []).count { |r| r[:status] == 'failed' }
|
|
327
|
+
if failures.zero?
|
|
328
|
+
out.success("Pack #{pack_result[:pack]}: #{successes} gem(s) installed")
|
|
329
|
+
else
|
|
330
|
+
out.warn("Pack #{pack_result[:pack]}: #{successes} installed, #{failures} failed")
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
out.warn('Pack installation skipped') if options[:skip_packs]
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def print_warnings_section(out, summary)
|
|
337
|
+
return unless summary[:warnings].any?
|
|
338
|
+
|
|
339
|
+
out.spacer
|
|
340
|
+
out.header('Attention')
|
|
341
|
+
summary[:warnings].each { |w| out.warn(w) }
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def print_next_steps(out)
|
|
345
|
+
return if options[:start]
|
|
346
|
+
|
|
347
|
+
out.spacer
|
|
348
|
+
puts ' Next steps:'
|
|
349
|
+
puts ' brew services start redis && brew services start legionio'
|
|
350
|
+
puts ' legion'
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# -----------------------------------------------------------------------
|
|
354
|
+
# Service startup (--start)
|
|
355
|
+
# -----------------------------------------------------------------------
|
|
356
|
+
|
|
357
|
+
def start_services(out)
|
|
358
|
+
redis_ok = run_brew_service('redis', out)
|
|
359
|
+
legion_ok = run_brew_service('legionio', out)
|
|
360
|
+
poll_daemon_ready(out) if redis_ok && legion_ok
|
|
361
|
+
{ redis: redis_ok, legionio: legion_ok }
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def run_brew_service(service, out)
|
|
365
|
+
output, success = shell_capture("brew services start #{service}")
|
|
366
|
+
if success
|
|
367
|
+
out.success("#{service} started") unless options[:json]
|
|
368
|
+
true
|
|
369
|
+
else
|
|
370
|
+
out.warn("#{service} failed to start: #{output.strip.lines.last&.strip}") unless options[:json]
|
|
371
|
+
false
|
|
372
|
+
end
|
|
373
|
+
rescue StandardError => e
|
|
374
|
+
out.warn("brew services start #{service} raised: #{e.message}") unless options[:json]
|
|
375
|
+
false
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def poll_daemon_ready(out, port: 4567, timeout: 30)
|
|
379
|
+
require 'net/http'
|
|
380
|
+
deadline = ::Time.now + timeout
|
|
381
|
+
until ::Time.now > deadline
|
|
382
|
+
begin
|
|
383
|
+
resp = Net::HTTP.get_response(URI("http://localhost:#{port}/api/ready"))
|
|
384
|
+
if resp.is_a?(Net::HTTPSuccess)
|
|
385
|
+
out.success("Daemon ready on port #{port}") unless options[:json]
|
|
386
|
+
return true
|
|
387
|
+
end
|
|
388
|
+
rescue StandardError
|
|
389
|
+
# not ready yet — keep polling
|
|
390
|
+
end
|
|
391
|
+
sleep 1
|
|
392
|
+
end
|
|
393
|
+
out.warn("Daemon did not become ready within #{timeout}s") unless options[:json]
|
|
394
|
+
false
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
end
|
|
@@ -61,6 +61,7 @@ module Legion
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
parts << cognitive_awareness(directory)
|
|
64
|
+
parts << self_awareness_hint
|
|
64
65
|
|
|
65
66
|
extra_dirs.each do |dir|
|
|
66
67
|
expanded = File.expand_path(dir)
|
|
@@ -181,6 +182,16 @@ module Legion
|
|
|
181
182
|
end
|
|
182
183
|
nil
|
|
183
184
|
end
|
|
185
|
+
|
|
186
|
+
def self.self_awareness_hint
|
|
187
|
+
return nil unless defined?(Legion::Extensions::Agentic::Self::Metacognition::Runners::Metacognition)
|
|
188
|
+
|
|
189
|
+
result = Legion::Extensions::Agentic::Self::Metacognition::Runners::Metacognition.self_narrative
|
|
190
|
+
narrative = result[:prose] if result.is_a?(Hash) && result[:prose]
|
|
191
|
+
narrative ? "\nCurrent self-awareness:\n#{narrative}" : nil
|
|
192
|
+
rescue StandardError
|
|
193
|
+
nil
|
|
194
|
+
end
|
|
184
195
|
end
|
|
185
196
|
end
|
|
186
197
|
end
|
data/lib/legion/cli.rb
CHANGED
|
@@ -65,6 +65,7 @@ module Legion
|
|
|
65
65
|
autoload :Features, 'legion/cli/features_command'
|
|
66
66
|
autoload :Debug, 'legion/cli/debug_command'
|
|
67
67
|
autoload :CodegenCommand, 'legion/cli/codegen_command'
|
|
68
|
+
autoload :Bootstrap, 'legion/cli/bootstrap_command'
|
|
68
69
|
|
|
69
70
|
module Groups
|
|
70
71
|
autoload :Ai, 'legion/cli/groups/ai_group'
|
|
@@ -236,6 +237,9 @@ module Legion
|
|
|
236
237
|
desc 'setup SUBCOMMAND', 'Install feature packs and configure IDE integrations'
|
|
237
238
|
subcommand 'setup', Legion::CLI::Setup
|
|
238
239
|
|
|
240
|
+
desc 'bootstrap SOURCE', 'One-command setup: fetch config, scaffold, and install packs'
|
|
241
|
+
subcommand 'bootstrap', Legion::CLI::Bootstrap
|
|
242
|
+
|
|
239
243
|
desc 'update', 'Update Legion gems to latest versions'
|
|
240
244
|
subcommand 'update', Legion::CLI::Update
|
|
241
245
|
|
data/lib/legion/version.rb
CHANGED
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.6.
|
|
4
|
+
version: 1.6.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -512,6 +512,7 @@ files:
|
|
|
512
512
|
- lib/legion/cli/apollo_command.rb
|
|
513
513
|
- lib/legion/cli/audit_command.rb
|
|
514
514
|
- lib/legion/cli/auth_command.rb
|
|
515
|
+
- lib/legion/cli/bootstrap_command.rb
|
|
515
516
|
- lib/legion/cli/chain.rb
|
|
516
517
|
- lib/legion/cli/chain_command.rb
|
|
517
518
|
- lib/legion/cli/chat/agent_delegator.rb
|