legionio 1.4.101 → 1.4.102

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: 4a5e67604f3f5f8bf4b49bea289800d891fc4073ac2947adef44760cf1eca2ff
4
- data.tar.gz: af9fc49a94f6babfde4a69905d3c1da3c3d4cac2856ba71429a4720daadc0334
3
+ metadata.gz: 4d271552f43fd7cd244b7e22845d5f08b0a8adba77c159ae6054ab913f2cbea1
4
+ data.tar.gz: 761a64ecc11ca421b45c8d618e8d2e7dc4dcf7d53c231b79e9bae805580d0ce5
5
5
  SHA512:
6
- metadata.gz: 2e64d067a8e1dcc4005b2c7640aaf04112b94b0499a661003a8bcd56b80023e7543a6bfcfd81f3642f74fe041ef77bcb3a8dead6216873f94329e3e2c72b32fd
7
- data.tar.gz: ffe6519b87c9ff5e891ddfeb8f785ace26729f48e840c3bc5fad409dae8b2997b5cd6b074bfb6171447625ea72b1f11a4a31a23747c187171c2c8b750ff7fe21
6
+ metadata.gz: 6fa6a1c07d50d1283608f175016ff4833f6518269bb5a5b5c9e0a5b8484bb5784918115648fd8ec64762a05676b3593f5c5fc27585ba5f6d1940b39bfc0466e7
7
+ data.tar.gz: 03ab72c8a44c4c2a83146d343c8824bd9eaae5cd3cd4eb1bd6e31c843f9e2622e7e4dce306c19d34769e3e703ab6315cf469336c2a0515fcc7e604b0b7ab33fd
data/.rubocop.yml CHANGED
@@ -42,6 +42,7 @@ Metrics/BlockLength:
42
42
  - 'lib/legion/cli/auth_command.rb'
43
43
  - 'lib/legion/cli/detect_command.rb'
44
44
  - 'lib/legion/cli/prompt_command.rb'
45
+ - 'lib/legion/cli/image_command.rb'
45
46
  - 'lib/legion/api/acp.rb'
46
47
 
47
48
  Metrics/AbcSize:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Legion Changelog
2
2
 
3
+ ## [1.4.102] - 2026-03-21
4
+
5
+ ### Added
6
+ - `legion image analyze PATH` — analyze an image file via LLM; supports `--prompt`, `--model`, `--provider`, `--format text|json`
7
+ - `legion image compare PATH1 PATH2` — compare two images side by side via LLM with same options
8
+ - Supports png, jpg, jpeg, gif, webp; base64-encodes image data and builds multimodal content blocks for the LLM message
9
+
3
10
  ## [1.4.101] - 2026-03-21
4
11
 
5
12
  ### Fixed
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require 'base64'
5
+
6
+ module Legion
7
+ module CLI
8
+ class Image < Thor
9
+ def self.exit_on_failure?
10
+ true
11
+ end
12
+
13
+ SUPPORTED_TYPES = %w[png jpg jpeg gif webp].freeze
14
+
15
+ MIME_TYPES = {
16
+ 'png' => 'image/png',
17
+ 'jpg' => 'image/jpeg',
18
+ 'jpeg' => 'image/jpeg',
19
+ 'gif' => 'image/gif',
20
+ 'webp' => 'image/webp'
21
+ }.freeze
22
+
23
+ class_option :json, type: :boolean, default: false, desc: 'Output as JSON'
24
+ class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
25
+ class_option :verbose, type: :boolean, default: false, aliases: ['-V'], desc: 'Verbose logging'
26
+ class_option :config_dir, type: :string, desc: 'Config directory path'
27
+
28
+ desc 'analyze PATH', 'Analyze an image file using an LLM'
29
+ option :prompt, type: :string, aliases: ['-p'],
30
+ desc: 'Custom question to ask about the image',
31
+ default: 'Describe this image in detail'
32
+ option :model, type: :string, aliases: ['-m'], desc: 'LLM model override'
33
+ option :provider, type: :string, desc: 'LLM provider override'
34
+ option :format, type: :string, default: 'text', desc: 'Output format: text or json'
35
+ def analyze(path)
36
+ out = formatter
37
+ setup_connection(out)
38
+
39
+ image_data = load_image(path, out)
40
+ return unless image_data
41
+
42
+ messages = [build_image_message([image_data], options[:prompt])]
43
+ response = call_llm(messages, out)
44
+ return unless response
45
+
46
+ render_response(out, response, { path: path, prompt: options[:prompt] })
47
+ rescue CLI::Error => e
48
+ formatter.error(e.message)
49
+ raise SystemExit, 1
50
+ ensure
51
+ Connection.shutdown
52
+ end
53
+
54
+ desc 'compare PATH1 PATH2', 'Compare two images side by side using an LLM'
55
+ option :prompt, type: :string, aliases: ['-p'],
56
+ desc: 'Custom comparison question',
57
+ default: 'Compare these two images and describe the differences'
58
+ option :model, type: :string, aliases: ['-m'], desc: 'LLM model override'
59
+ option :provider, type: :string, desc: 'LLM provider override'
60
+ option :format, type: :string, default: 'text', desc: 'Output format: text or json'
61
+ def compare(path1, path2)
62
+ out = formatter
63
+ setup_connection(out)
64
+
65
+ image1 = load_image(path1, out)
66
+ return unless image1
67
+
68
+ image2 = load_image(path2, out)
69
+ return unless image2
70
+
71
+ messages = [build_image_message([image1, image2], options[:prompt])]
72
+ response = call_llm(messages, out)
73
+ return unless response
74
+
75
+ render_response(out, response, { path1: path1, path2: path2, prompt: options[:prompt] })
76
+ rescue CLI::Error => e
77
+ formatter.error(e.message)
78
+ raise SystemExit, 1
79
+ ensure
80
+ Connection.shutdown
81
+ end
82
+
83
+ no_commands do
84
+ def formatter
85
+ @formatter ||= Output::Formatter.new(
86
+ json: options[:json],
87
+ color: !options[:no_color]
88
+ )
89
+ end
90
+
91
+ def setup_connection(out)
92
+ Connection.config_dir = options[:config_dir] if options[:config_dir]
93
+ Connection.log_level = options[:verbose] ? 'debug' : 'error'
94
+ Connection.ensure_llm
95
+ rescue CLI::Error => e
96
+ out.error(e.message)
97
+ raise SystemExit, 1
98
+ end
99
+
100
+ def load_image(path, out)
101
+ unless File.exist?(path)
102
+ out.error("File not found: #{path}")
103
+ raise SystemExit, 1
104
+ end
105
+
106
+ ext = File.extname(path).delete_prefix('.').downcase
107
+ unless SUPPORTED_TYPES.include?(ext)
108
+ out.error("Unsupported image type '.#{ext}'. Supported: #{SUPPORTED_TYPES.join(', ')}")
109
+ raise SystemExit, 1
110
+ end
111
+
112
+ {
113
+ path: path,
114
+ mime_type: MIME_TYPES[ext],
115
+ data: Base64.strict_encode64(File.binread(path))
116
+ }
117
+ end
118
+
119
+ def build_image_message(images, prompt_text)
120
+ content = images.map do |img|
121
+ {
122
+ type: 'image',
123
+ source: {
124
+ type: 'base64',
125
+ media_type: img[:mime_type],
126
+ data: img[:data]
127
+ }
128
+ }
129
+ end
130
+ content << { type: 'text', text: prompt_text }
131
+ { role: 'user', content: content }
132
+ end
133
+
134
+ def call_llm(messages, out)
135
+ llm_kwargs = {}
136
+ llm_kwargs[:model] = options[:model] if options[:model]
137
+ llm_kwargs[:provider] = options[:provider].to_sym if options[:provider]
138
+
139
+ Legion::LLM.chat(messages: messages, **llm_kwargs)
140
+ rescue StandardError => e
141
+ out.error("LLM call failed: #{e.message}")
142
+ raise SystemExit, 1
143
+ end
144
+
145
+ def render_response(out, response, meta)
146
+ content = response[:content].to_s
147
+ usage = response[:usage] || {}
148
+
149
+ if options[:format] == 'json' || options[:json]
150
+ out.json(meta.merge(response: content, usage: usage))
151
+ else
152
+ out.header('Analysis')
153
+ out.spacer
154
+ puts content
155
+ return if usage.nil? || usage.empty?
156
+
157
+ out.spacer
158
+ out.detail(usage)
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
data/lib/legion/cli.rb CHANGED
@@ -42,6 +42,7 @@ module Legion
42
42
  autoload :Init, 'legion/cli/init_command'
43
43
  autoload :Skill, 'legion/cli/skill_command'
44
44
  autoload :Prompt, 'legion/cli/prompt_command'
45
+ autoload :Image, 'legion/cli/image_command'
45
46
  autoload :Dataset, 'legion/cli/dataset_command'
46
47
  autoload :Cost, 'legion/cli/cost_command'
47
48
  autoload :Marketplace, 'legion/cli/marketplace_command'
@@ -267,6 +268,9 @@ module Legion
267
268
  desc 'observe SUBCOMMAND', 'MCP tool observation stats'
268
269
  subcommand 'observe', Legion::CLI::ObserveCommand
269
270
 
271
+ desc 'image SUBCOMMAND', 'Multimodal image analysis and comparison'
272
+ subcommand 'image', Legion::CLI::Image
273
+
270
274
  desc 'payroll SUBCOMMAND', 'Workforce cost and labor economics'
271
275
  subcommand 'payroll', Legion::CLI::Payroll
272
276
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.4.101'
4
+ VERSION = '1.4.102'
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.101
4
+ version: 1.4.102
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -472,6 +472,7 @@ files:
472
472
  - lib/legion/cli/gaia_command.rb
473
473
  - lib/legion/cli/generate_command.rb
474
474
  - lib/legion/cli/graph_command.rb
475
+ - lib/legion/cli/image_command.rb
475
476
  - lib/legion/cli/init/config_generator.rb
476
477
  - lib/legion/cli/init/environment_detector.rb
477
478
  - lib/legion/cli/init_command.rb