lex-detect 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0f221abbf1751de26290f2d11137ec31338bb77dfb64b033ce09ebffbcd73d9d
4
+ data.tar.gz: fac9bfec6e9275b5b48d8d1e253edf98e1fa504b48f90300a44604fe79bd5cc9
5
+ SHA512:
6
+ metadata.gz: '0259374ea1f54906333dba2cbb9de76c6c1081a1e24082956a86b055cfdaa942a442c8d48eb3f49f373276c91bf2901e21720b669205ddc90a3fe97001976d41'
7
+ data.tar.gz: b6a5d1afa9da7d4f7a6feecd24e49ae2bced0123e79df54d368fa6f1e610189a72e8991866768ff0f4881bd056404b6208318764fb38e66bf57ab2f30835277e
@@ -0,0 +1,16 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+
7
+ jobs:
8
+ ci:
9
+ uses: LegionIO/.github/.github/workflows/ci.yml@main
10
+
11
+ release:
12
+ needs: ci
13
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
14
+ uses: LegionIO/.github/.github/workflows/release.yml@main
15
+ secrets:
16
+ rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,56 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.4
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+
6
+ Layout/LineLength:
7
+ Max: 160
8
+
9
+ Layout/SpaceAroundEqualsInParameterDefault:
10
+ EnforcedStyle: space
11
+
12
+ Layout/HashAlignment:
13
+ EnforcedHashRocketStyle: table
14
+ EnforcedColonStyle: table
15
+
16
+ Metrics/MethodLength:
17
+ Max: 50
18
+
19
+ Metrics/ClassLength:
20
+ Max: 1500
21
+
22
+ Metrics/ModuleLength:
23
+ Max: 1500
24
+
25
+ Metrics/BlockLength:
26
+ Max: 40
27
+ Exclude:
28
+ - 'spec/**/*'
29
+
30
+ Metrics/ParameterLists:
31
+ Max: 8
32
+
33
+ Metrics/AbcSize:
34
+ Max: 60
35
+
36
+ Metrics/CyclomaticComplexity:
37
+ Max: 15
38
+
39
+ Metrics/PerceivedComplexity:
40
+ Max: 17
41
+
42
+ Style/Documentation:
43
+ Enabled: false
44
+
45
+ Style/SymbolArray:
46
+ Enabled: true
47
+
48
+ Style/FrozenStringLiteralComment:
49
+ Enabled: true
50
+ EnforcedStyle: always
51
+
52
+ Naming/FileName:
53
+ Enabled: false
54
+
55
+ Gemspec/DevelopmentDependencies:
56
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## [0.1.1] - 2026-03-18
4
+
5
+ ### Changed
6
+ - Switch CI to reusable workflows from LegionIO/.github
7
+
8
+ ## [0.1.0] - 2026-03-18
9
+
10
+ ### Added
11
+ - Detection catalog with 20 rules covering AI providers, communication, productivity, developer tools, HashiCorp, infrastructure, and operations
12
+ - Scanner module: probes macOS apps, Homebrew formulas/casks, environment variables, TCP ports, and config files
13
+ - Installer module: `Gem.install` wrapper with dry_run support
14
+ - Public API: `scan`, `missing`, `install_missing!`, `catalog`
15
+ - Parallel port scanning with 1-second connect timeout
16
+ - Zero runtime dependencies
data/CLAUDE.md ADDED
@@ -0,0 +1,67 @@
1
+ # lex-detect: Environment-Aware Extension Discovery
2
+
3
+ **Repository Level 3 Documentation**
4
+ - **Parent (Level 2)**: `/Users/miverso2/rubymine/legion/extensions/CLAUDE.md`
5
+ - **Parent (Level 1)**: `/Users/miverso2/rubymine/legion/CLAUDE.md`
6
+
7
+ ## Purpose
8
+
9
+ Legion Extension that scans the local environment and recommends which `lex-*` extensions to install. Detects macOS apps, Homebrew formulas/casks, environment variables, open TCP ports, and config files, then maps them to recommended extensions via a declarative catalog.
10
+
11
+ **GitHub**: https://github.com/LegionIO/lex-detect
12
+ **License**: MIT
13
+ **Version**: 0.1.0
14
+
15
+ ## Architecture
16
+
17
+ ```
18
+ Legion::Extensions::Detect
19
+ ├── CATALOG (constant) # Frozen array of 20 detection rules
20
+ ├── Scanner # Probes environment, matches against catalog
21
+ ├── Installer # Gem.install wrapper with dry_run support
22
+ └── Entry Point # Public API: scan, missing, install_missing!, catalog
23
+ ```
24
+
25
+ This is NOT a typical LEX — no runners, no actors, no AMQP transport, no Faraday client. It is a local-only utility gem with zero runtime dependencies.
26
+
27
+ - `data_required? false` — no database needed
28
+ - `remote_invocable? false` — no AMQP queue creation
29
+
30
+ ## Signal Types
31
+
32
+ | Type | Source | Platform |
33
+ |------|--------|----------|
34
+ | `:app` | `/Applications/*.app` | macOS |
35
+ | `:brew_formula` | `brew list --formula` | macOS/Linux |
36
+ | `:brew_cask` | `brew list --cask` | macOS |
37
+ | `:env` | `ENV.keys` | all |
38
+ | `:port` | TCP connect probe (1s timeout) | all |
39
+ | `:file` | `File.exist?` with `~` expansion | all |
40
+
41
+ ## Public API
42
+
43
+ ```ruby
44
+ Legion::Extensions::Detect.scan # full scan results
45
+ Legion::Extensions::Detect.missing # uninstalled gem names
46
+ Legion::Extensions::Detect.install_missing! # install missing gems
47
+ Legion::Extensions::Detect.catalog # raw CATALOG constant
48
+ ```
49
+
50
+ ## Design Docs
51
+
52
+ - Design: `docs/plans/2026-03-18-lex-detect-design.md`
53
+ - Implementation: `docs/plans/2026-03-18-lex-detect-implementation.md`
54
+
55
+ ## Testing
56
+
57
+ 37 specs across 4 spec files.
58
+
59
+ ```bash
60
+ bundle install
61
+ bundle exec rspec
62
+ bundle exec rubocop
63
+ ```
64
+
65
+ ---
66
+
67
+ **Maintained By**: Matthew Iverson (@Esity)
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ gem 'rspec'
9
+ gem 'rspec_junit_formatter'
10
+ gem 'rubocop'
11
+ gem 'simplecov'
12
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LegionIO
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # lex-detect
2
+
3
+ Environment-aware extension discovery for [LegionIO](https://github.com/LegionIO).
4
+
5
+ Scans your local environment (macOS apps, Homebrew packages, environment variables, open ports, config files) and recommends which `lex-*` extensions to install.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ gem install lex-detect
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ruby
16
+ require 'legion/extensions/detect'
17
+
18
+ # Full scan — returns array of detection results
19
+ results = Legion::Extensions::Detect.scan
20
+ # => [{ name: 'Claude', extensions: ['lex-claude'],
21
+ # matched_signals: ['app:Claude.app'], installed: { 'lex-claude' => true } }, ...]
22
+
23
+ # Just the missing gems
24
+ missing = Legion::Extensions::Detect.missing
25
+ # => ['lex-slack', 'lex-todoist']
26
+
27
+ # Install all missing
28
+ Legion::Extensions::Detect.install_missing!
29
+ # => { installed: ['lex-slack', 'lex-todoist'], failed: [] }
30
+
31
+ # Dry run
32
+ Legion::Extensions::Detect.install_missing!(dry_run: true)
33
+
34
+ # View the catalog
35
+ Legion::Extensions::Detect.catalog
36
+ ```
37
+
38
+ ## Detection Catalog
39
+
40
+ | Name | Extensions | Signal Types |
41
+ |------|-----------|-------------|
42
+ | Claude | lex-claude | app, brew_cask, env |
43
+ | OpenAI | lex-openai | app, brew_cask, env |
44
+ | Ollama | lex-openai | brew_formula, port |
45
+ | Slack | lex-slack | app, brew_cask |
46
+ | Microsoft Teams | lex-microsoft_teams | app, brew_cask |
47
+ | Todoist | lex-todoist | app, brew_cask |
48
+ | GitHub | lex-github | app, brew_formula, brew_cask, env |
49
+ | Consul | lex-consul | brew_formula, port |
50
+ | Vault | lex-vault | brew_formula, port, env |
51
+ | Nomad | lex-nomad | brew_formula, port |
52
+ | Terraform | lex-tfe | brew_formula, file |
53
+ | RabbitMQ | legion-transport | brew_formula, port, env |
54
+ | Redis | lex-redis, legion-cache | brew_formula, port, env |
55
+ | Memcached | legion-cache | brew_formula, port |
56
+ | PostgreSQL | legion-data | brew_formula, port, env |
57
+ | MySQL | legion-data | brew_formula, port |
58
+ | Chef | lex-chef | app, file |
59
+ | S3 / AWS | lex-s3 | brew_formula, env, file |
60
+ | Elasticsearch | lex-elasticsearch | brew_formula, port |
61
+ | InfluxDB | lex-influxdb | brew_formula, port |
62
+
63
+ ## License
64
+
65
+ MIT
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/detect/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-detect'
7
+ spec.version = Legion::Extensions::Detect::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX Detect'
12
+ spec.description = 'Environment-aware extension discovery for LegionIO'
13
+ spec.homepage = 'https://github.com/LegionIO/lex-detect'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.4'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-detect'
19
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-detect'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-detect'
21
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-detect/issues'
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
23
+
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ end
27
+ spec.require_paths = ['lib']
28
+ end
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Detect
6
+ CATALOG = [
7
+ # AI Providers
8
+ {
9
+ name: 'Claude',
10
+ extensions: ['lex-claude'],
11
+ signals: [
12
+ { type: :app, match: 'Claude.app' },
13
+ { type: :brew_cask, match: 'claude' },
14
+ { type: :brew_cask, match: 'claude-code' },
15
+ { type: :brew_formula, match: 'claude-code' },
16
+ { type: :env, match: 'ANTHROPIC_API_KEY' }
17
+ ]
18
+ },
19
+ {
20
+ name: 'OpenAI',
21
+ extensions: ['lex-openai'],
22
+ signals: [
23
+ { type: :app, match: 'ChatGPT.app' },
24
+ { type: :brew_cask, match: 'chatgpt' },
25
+ { type: :brew_cask, match: 'codex' },
26
+ { type: :env, match: 'OPENAI_API_KEY' }
27
+ ]
28
+ },
29
+ {
30
+ name: 'Ollama',
31
+ extensions: ['lex-openai'],
32
+ signals: [
33
+ { type: :brew_formula, match: 'ollama' },
34
+ { type: :port, match: 11_434 }
35
+ ]
36
+ },
37
+
38
+ # Communication
39
+ {
40
+ name: 'Slack',
41
+ extensions: ['lex-slack'],
42
+ signals: [
43
+ { type: :app, match: 'Slack.app' },
44
+ { type: :brew_cask, match: 'slack' }
45
+ ]
46
+ },
47
+ {
48
+ name: 'Microsoft Teams',
49
+ extensions: ['lex-microsoft_teams'],
50
+ signals: [
51
+ { type: :app, match: 'Microsoft Teams.app' },
52
+ { type: :brew_cask, match: 'microsoft-teams' }
53
+ ]
54
+ },
55
+
56
+ # Productivity
57
+ {
58
+ name: 'Todoist',
59
+ extensions: ['lex-todoist'],
60
+ signals: [
61
+ { type: :app, match: 'Todoist.app' },
62
+ { type: :brew_cask, match: 'todoist' },
63
+ { type: :brew_cask, match: 'todoist-app' }
64
+ ]
65
+ },
66
+
67
+ # Developer Tools
68
+ {
69
+ name: 'GitHub',
70
+ extensions: ['lex-github'],
71
+ signals: [
72
+ { type: :app, match: 'GitHub Desktop.app' },
73
+ { type: :brew_formula, match: 'gh' },
74
+ { type: :brew_cask, match: 'github' },
75
+ { type: :env, match: 'GITHUB_TOKEN' }
76
+ ]
77
+ },
78
+
79
+ # HashiCorp
80
+ {
81
+ name: 'Consul',
82
+ extensions: ['lex-consul'],
83
+ signals: [
84
+ { type: :brew_formula, match: /^consul/ },
85
+ { type: :port, match: 8500 }
86
+ ]
87
+ },
88
+ {
89
+ name: 'Vault',
90
+ extensions: ['lex-vault'],
91
+ signals: [
92
+ { type: :brew_formula, match: /^vault/ },
93
+ { type: :port, match: 8200 },
94
+ { type: :env, match: 'VAULT_ADDR' }
95
+ ]
96
+ },
97
+ {
98
+ name: 'Nomad',
99
+ extensions: ['lex-nomad'],
100
+ signals: [
101
+ { type: :brew_formula, match: /^nomad/ },
102
+ { type: :port, match: 4646 }
103
+ ]
104
+ },
105
+ {
106
+ name: 'Terraform',
107
+ extensions: ['lex-tfe'],
108
+ signals: [
109
+ { type: :brew_formula, match: 'terraform' },
110
+ { type: :brew_formula, match: 'tfenv' },
111
+ { type: :file, match: '~/.terraform.d/credentials.tfrc.json' }
112
+ ]
113
+ },
114
+
115
+ # Infrastructure
116
+ {
117
+ name: 'RabbitMQ',
118
+ extensions: ['legion-transport'],
119
+ signals: [
120
+ { type: :brew_formula, match: 'rabbitmq' },
121
+ { type: :port, match: 5672 },
122
+ { type: :env, match: 'AMQP_URL' }
123
+ ]
124
+ },
125
+ {
126
+ name: 'Redis',
127
+ extensions: %w[lex-redis legion-cache],
128
+ signals: [
129
+ { type: :brew_formula, match: 'redis' },
130
+ { type: :port, match: 6379 },
131
+ { type: :env, match: 'REDIS_URL' }
132
+ ]
133
+ },
134
+ {
135
+ name: 'Memcached',
136
+ extensions: ['legion-cache'],
137
+ signals: [
138
+ { type: :brew_formula, match: 'memcached' },
139
+ { type: :port, match: 11_211 }
140
+ ]
141
+ },
142
+ {
143
+ name: 'PostgreSQL',
144
+ extensions: ['legion-data'],
145
+ signals: [
146
+ { type: :brew_formula, match: /^postgresql/ },
147
+ { type: :port, match: 5432 },
148
+ { type: :env, match: 'DATABASE_URL' }
149
+ ]
150
+ },
151
+ {
152
+ name: 'MySQL',
153
+ extensions: ['legion-data'],
154
+ signals: [
155
+ { type: :brew_formula, match: /^mysql/ },
156
+ { type: :port, match: 3306 }
157
+ ]
158
+ },
159
+
160
+ # Operations
161
+ {
162
+ name: 'Chef',
163
+ extensions: ['lex-chef'],
164
+ signals: [
165
+ { type: :app, match: 'Chef Workstation App.app' },
166
+ { type: :file, match: '~/.chef/config.rb' },
167
+ { type: :file, match: '~/.chef/credentials' }
168
+ ]
169
+ },
170
+ {
171
+ name: 'S3 / AWS',
172
+ extensions: ['lex-s3'],
173
+ signals: [
174
+ { type: :brew_formula, match: 'awscli' },
175
+ { type: :env, match: 'AWS_ACCESS_KEY_ID' },
176
+ { type: :file, match: '~/.aws/credentials' }
177
+ ]
178
+ },
179
+ {
180
+ name: 'Elasticsearch',
181
+ extensions: ['lex-elasticsearch'],
182
+ signals: [
183
+ { type: :brew_formula, match: 'elasticsearch' },
184
+ { type: :brew_formula, match: 'opensearch' },
185
+ { type: :port, match: 9200 }
186
+ ]
187
+ },
188
+ {
189
+ name: 'InfluxDB',
190
+ extensions: ['lex-influxdb'],
191
+ signals: [
192
+ { type: :brew_formula, match: 'influxdb' },
193
+ { type: :port, match: 8086 }
194
+ ]
195
+ }
196
+ ].freeze
197
+
198
+ SIGNAL_TYPES = %i[app brew_formula brew_cask env port file].freeze
199
+
200
+ SCAN_PORTS = [5672, 6379, 11_211, 8200, 5432, 3306, 8500, 4646, 9200, 8086, 11_434].freeze
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Detect
6
+ module Installer
7
+ class << self
8
+ def install(gem_names, dry_run: false)
9
+ return { installed: gem_names, failed: [] } if dry_run
10
+
11
+ installed = []
12
+ failed = []
13
+
14
+ gem_names.each do |name|
15
+ Gem.install(name)
16
+ installed << name
17
+ rescue StandardError => e
18
+ failed << { name: name, error: e.message }
19
+ end
20
+
21
+ { installed: installed, failed: failed }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Detect
8
+ module Scanner
9
+ class << self
10
+ def scan
11
+ env = gather_environment
12
+ CATALOG.each_with_object([]) do |rule, results|
13
+ matched_signals = rule[:signals].select { |s| signal_present?(s, env) }
14
+ next if matched_signals.empty?
15
+
16
+ results << {
17
+ name: rule[:name],
18
+ extensions: rule[:extensions],
19
+ matched_signals: matched_signals.map { |s| "#{s[:type]}:#{s[:match]}" },
20
+ installed: rule[:extensions].to_h { |e| [e, gem_installed?(e)] }
21
+ }
22
+ end
23
+ end
24
+
25
+ def gather_environment
26
+ {
27
+ apps: scan_applications,
28
+ brew_formulas: scan_brew_formulas,
29
+ brew_casks: scan_brew_casks,
30
+ env_vars: ENV.keys,
31
+ ports: scan_ports,
32
+ files: nil
33
+ }
34
+ end
35
+
36
+ def reset!
37
+ @environment = nil
38
+ end
39
+
40
+ private
41
+
42
+ def scan_applications
43
+ Dir.glob('/Applications/*.app').map { |p| File.basename(p) }
44
+ rescue StandardError
45
+ []
46
+ end
47
+
48
+ def scan_brew_formulas
49
+ `brew list --formula -1 2>/dev/null`.split("\n")
50
+ rescue StandardError
51
+ []
52
+ end
53
+
54
+ def scan_brew_casks
55
+ `brew list --cask -1 2>/dev/null`.split("\n")
56
+ rescue StandardError
57
+ []
58
+ end
59
+
60
+ def scan_ports
61
+ threads = SCAN_PORTS.map do |port|
62
+ Thread.new(port) do |p|
63
+ Socket.tcp('127.0.0.1', p, connect_timeout: 1) { true }
64
+ rescue StandardError
65
+ false
66
+ end
67
+ end
68
+
69
+ SCAN_PORTS.zip(threads.map(&:value)).select { |_port, open| open }.map(&:first)
70
+ end
71
+
72
+ def signal_present?(signal, env)
73
+ case signal[:type]
74
+ when :app then env[:apps].any? { |a| match_value?(a, signal[:match]) }
75
+ when :brew_formula then env[:brew_formulas].any? { |f| match_value?(f, signal[:match]) }
76
+ when :brew_cask then env[:brew_casks].any? { |c| match_value?(c, signal[:match]) }
77
+ when :env then env[:env_vars].include?(signal[:match])
78
+ when :port then env[:ports].include?(signal[:match])
79
+ when :file then File.exist?(File.expand_path(signal[:match]))
80
+ else false
81
+ end
82
+ end
83
+
84
+ def match_value?(value, pattern)
85
+ pattern.is_a?(Regexp) ? value.match?(pattern) : value == pattern
86
+ end
87
+
88
+ def gem_installed?(name)
89
+ Gem::Specification.find_by_name(name)
90
+ true
91
+ rescue Gem::MissingSpecError
92
+ false
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Detect
6
+ VERSION = '0.1.1'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/detect/version'
4
+ require 'legion/extensions/detect/catalog'
5
+ require 'legion/extensions/detect/scanner'
6
+ require 'legion/extensions/detect/installer'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module Detect
11
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined?(:Core)
12
+
13
+ class << self
14
+ def data_required?
15
+ false
16
+ end
17
+
18
+ def remote_invocable?
19
+ false
20
+ end
21
+
22
+ def scan
23
+ Scanner.scan
24
+ end
25
+
26
+ def missing
27
+ results = scan
28
+ results.each_with_object([]) do |detection, gems|
29
+ detection[:installed].each do |gem_name, installed|
30
+ gems << gem_name unless installed || gems.include?(gem_name)
31
+ end
32
+ end
33
+ end
34
+
35
+ def install_missing!(dry_run: false)
36
+ Installer.install(missing, dry_run: dry_run)
37
+ end
38
+
39
+ def catalog
40
+ CATALOG
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-detect
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Esity
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Environment-aware extension discovery for LegionIO
13
+ email:
14
+ - matthewdiverson@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - ".github/workflows/ci.yml"
20
+ - ".gitignore"
21
+ - ".rspec"
22
+ - ".rubocop.yml"
23
+ - CHANGELOG.md
24
+ - CLAUDE.md
25
+ - Gemfile
26
+ - LICENSE
27
+ - README.md
28
+ - lex-detect.gemspec
29
+ - lib/legion/extensions/detect.rb
30
+ - lib/legion/extensions/detect/catalog.rb
31
+ - lib/legion/extensions/detect/installer.rb
32
+ - lib/legion/extensions/detect/scanner.rb
33
+ - lib/legion/extensions/detect/version.rb
34
+ homepage: https://github.com/LegionIO/lex-detect
35
+ licenses:
36
+ - MIT
37
+ metadata:
38
+ homepage_uri: https://github.com/LegionIO/lex-detect
39
+ source_code_uri: https://github.com/LegionIO/lex-detect
40
+ documentation_uri: https://github.com/LegionIO/lex-detect
41
+ changelog_uri: https://github.com/LegionIO/lex-detect
42
+ bug_tracker_uri: https://github.com/LegionIO/lex-detect/issues
43
+ rubygems_mfa_required: 'true'
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '3.4'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.6.9
59
+ specification_version: 4
60
+ summary: LEX Detect
61
+ test_files: []