lex-cognitive-synesthesia 0.1.0
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 +7 -0
- data/.github/workflows/ci.yml +16 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.rubocop.yml +64 -0
- data/CLAUDE.md +119 -0
- data/Gemfile +11 -0
- data/README.md +76 -0
- data/lex-cognitive-synesthesia.gemspec +30 -0
- data/lib/legion/extensions/cognitive_synesthesia/client.rb +25 -0
- data/lib/legion/extensions/cognitive_synesthesia/helpers/constants.rb +48 -0
- data/lib/legion/extensions/cognitive_synesthesia/helpers/sensory_mapping.rb +66 -0
- data/lib/legion/extensions/cognitive_synesthesia/helpers/synesthesia_engine.rb +173 -0
- data/lib/legion/extensions/cognitive_synesthesia/helpers/synesthetic_event.rb +45 -0
- data/lib/legion/extensions/cognitive_synesthesia/runners/cognitive_synesthesia.rb +79 -0
- data/lib/legion/extensions/cognitive_synesthesia/version.rb +9 -0
- data/lib/legion/extensions/cognitive_synesthesia.rb +16 -0
- metadata +77 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f5b3932afe6397f2043ffad49ca9d50f526274cf5f6fa1a14b9ee7089570e3f9
|
|
4
|
+
data.tar.gz: 3498493819add151ac7d8432da16511f09d01b1b762999f75c4f7b10471a0407
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 3c7a1a08186b1fed2821e1c275b1b3b50ddb63e0b0efeb9a26c95046e8d3b3e36020d628eec2edac547701a645e89d4dee9989005fd9381c4caa4eb491943d02
|
|
7
|
+
data.tar.gz: 69a277b2cd7741a4bfc07bb6aed1d10ed278fe6681daac47bd5aed452010ec55c476ae690d6a8c8f280c32868449f754dd698bab59141f93f4c4fcad77b880bc
|
|
@@ -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
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
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: 25
|
|
18
|
+
|
|
19
|
+
Metrics/ClassLength:
|
|
20
|
+
Max: 150
|
|
21
|
+
|
|
22
|
+
Metrics/ModuleLength:
|
|
23
|
+
Max: 1500
|
|
24
|
+
|
|
25
|
+
Metrics/BlockLength:
|
|
26
|
+
Max: 40
|
|
27
|
+
Exclude:
|
|
28
|
+
- 'spec/**/*'
|
|
29
|
+
|
|
30
|
+
Metrics/AbcSize:
|
|
31
|
+
Max: 25
|
|
32
|
+
|
|
33
|
+
Metrics/CyclomaticComplexity:
|
|
34
|
+
Max: 15
|
|
35
|
+
|
|
36
|
+
Metrics/PerceivedComplexity:
|
|
37
|
+
Max: 17
|
|
38
|
+
|
|
39
|
+
Metrics/ParameterLists:
|
|
40
|
+
Max: 8
|
|
41
|
+
MaxOptionalParameters: 8
|
|
42
|
+
|
|
43
|
+
Style/Documentation:
|
|
44
|
+
Enabled: false
|
|
45
|
+
|
|
46
|
+
Style/SymbolArray:
|
|
47
|
+
Enabled: true
|
|
48
|
+
|
|
49
|
+
Style/FrozenStringLiteralComment:
|
|
50
|
+
Enabled: true
|
|
51
|
+
EnforcedStyle: always
|
|
52
|
+
|
|
53
|
+
Style/OneClassPerFile:
|
|
54
|
+
Exclude:
|
|
55
|
+
- 'spec/spec_helper.rb'
|
|
56
|
+
|
|
57
|
+
Naming/FileName:
|
|
58
|
+
Enabled: false
|
|
59
|
+
|
|
60
|
+
Naming/PredicateMethod:
|
|
61
|
+
Enabled: false
|
|
62
|
+
|
|
63
|
+
Naming/PredicatePrefix:
|
|
64
|
+
Enabled: false
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# lex-cognitive-synesthesia
|
|
2
|
+
|
|
3
|
+
**Level 3 Leaf Documentation**
|
|
4
|
+
- **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
|
|
5
|
+
- **Gem**: `lex-cognitive-synesthesia`
|
|
6
|
+
- **Version**: 0.1.0
|
|
7
|
+
- **Namespace**: `Legion::Extensions::CognitiveSynesthesia`
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Models cross-modal cognitive associations inspired by synesthesia — the neurological phenomenon where stimulation of one sense involuntarily triggers another. Sensory mappings link a source modality to a target modality via trigger and response patterns. When a source modality is activated, all active mappings for it fire, producing `SynestheticEvent` records. Mappings strengthen with use and decay over time. This models how cognitive input in one domain (e.g., emotional) automatically invokes representations in another (e.g., spatial or visual).
|
|
12
|
+
|
|
13
|
+
## Gem Info
|
|
14
|
+
|
|
15
|
+
- **Gemspec**: `lex-cognitive-synesthesia.gemspec`
|
|
16
|
+
- **Require**: `lex-cognitive-synesthesia`
|
|
17
|
+
- **Ruby**: >= 3.4
|
|
18
|
+
- **License**: MIT
|
|
19
|
+
- **Homepage**: https://github.com/LegionIO/lex-cognitive-synesthesia
|
|
20
|
+
|
|
21
|
+
## File Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
lib/legion/extensions/cognitive_synesthesia/
|
|
25
|
+
version.rb
|
|
26
|
+
helpers/
|
|
27
|
+
constants.rb # Modalities, strength/richness/intensity label tables, thresholds
|
|
28
|
+
sensory_mapping.rb # SensoryMapping class — one cross-modal association
|
|
29
|
+
synesthetic_event.rb # SynestheticEvent class — one fired cross-modal experience
|
|
30
|
+
synesthesia_engine.rb # SynesthesiaEngine — manages mappings and event log
|
|
31
|
+
runners/
|
|
32
|
+
cognitive_synesthesia.rb # Runner module — public API
|
|
33
|
+
client.rb
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Key Constants
|
|
37
|
+
|
|
38
|
+
| Constant | Value | Meaning |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `MAX_MAPPINGS` | 200 | Hard cap; weakest mappings pruned when exceeded |
|
|
41
|
+
| `MAX_EVENTS` | 500 | Event log ring size |
|
|
42
|
+
| `DEFAULT_STRENGTH` | 0.5 | Starting strength for new mappings |
|
|
43
|
+
| `STRENGTH_BOOST` | 0.1 | Strength increase per `activate!` call |
|
|
44
|
+
| `STRENGTH_DECAY` | 0.02 | Strength decrease per `decay!` call |
|
|
45
|
+
| `TRIGGER_THRESHOLD` | 0.3 | Minimum strength for a mapping to fire |
|
|
46
|
+
|
|
47
|
+
`MODALITIES`: `[:visual, :auditory, :tactile, :emotional, :semantic, :temporal, :spatial, :abstract]`
|
|
48
|
+
|
|
49
|
+
Strength labels: `0.8+` = `:dominant`, `0.6..0.8` = `:strong`, `0.4..0.6` = `:moderate`, `0.2..0.4` = `:faint`, `<0.2` = `:trace`
|
|
50
|
+
|
|
51
|
+
Richness labels (for `cross_modal_richness`): `0.8+` = `:synesthetic`, `0.6..0.8` = `:vivid`, `0.4..0.6` = `:partial`, `0.2..0.4` = `:sparse`, `<0.2` = `:amodal`
|
|
52
|
+
|
|
53
|
+
Intensity labels (for events): `0.8+` = `:overwhelming`, `0.6..0.8` = `:intense`, `0.4..0.6` = `:moderate`, `0.2..0.4` = `:subtle`, `<0.2` = `:subliminal`
|
|
54
|
+
|
|
55
|
+
## Key Classes
|
|
56
|
+
|
|
57
|
+
### `Helpers::SensoryMapping`
|
|
58
|
+
|
|
59
|
+
One cross-modal association with source/target modalities and pattern descriptors.
|
|
60
|
+
|
|
61
|
+
- `activate!` — increments `activation_count`, boosts strength by `STRENGTH_BOOST`
|
|
62
|
+
- `decay!` — reduces strength by `STRENGTH_DECAY`
|
|
63
|
+
- `active?` — strength >= `TRIGGER_THRESHOLD`
|
|
64
|
+
- `strength_label` — one of the `STRENGTH_LABELS` values
|
|
65
|
+
- Fields: `id` (UUID), `source_modality`, `target_modality`, `trigger_pattern` (caller-defined), `response_pattern` (caller-defined), `strength`, `activation_count`, `created_at`, `last_activated_at`
|
|
66
|
+
|
|
67
|
+
### `Helpers::SynestheticEvent`
|
|
68
|
+
|
|
69
|
+
A single fired cross-modal experience.
|
|
70
|
+
|
|
71
|
+
- `intensity_label` — one of the `INTENSITY_LABELS` values
|
|
72
|
+
- `involuntary` — always true (default); reflects the automatic nature of synesthetic responses
|
|
73
|
+
- `target_output` — hash with `{ modality:, response_pattern:, source_input:, source_modality: }`
|
|
74
|
+
- Fields: `id` (UUID), `mapping_id`, `source_input`, `target_output`, `intensity`, `triggered_at`
|
|
75
|
+
|
|
76
|
+
### `Helpers::SynesthesiaEngine`
|
|
77
|
+
|
|
78
|
+
Registry and event processor for all mappings and events.
|
|
79
|
+
|
|
80
|
+
- `register_mapping(source_modality:, target_modality:, trigger_pattern:, response_pattern:, strength:)` — validates modalities; prunes weakest mappings if over `MAX_MAPPINGS`
|
|
81
|
+
- `trigger(source_modality:, input:)` — finds all active mappings for source, fires each with `fire_event`, returns event list
|
|
82
|
+
- `decay_mappings!` — decays all mappings; removes those with strength <= 0.0
|
|
83
|
+
- `cross_modal_richness` — active unique pairs / `(8 * 7)` possible pairs; range `[0.0, 1.0]`
|
|
84
|
+
- `dominant_modality_pairs(limit:)` — top pairs by cumulative activation count
|
|
85
|
+
- `event_history(limit:)` — most recent events from ring buffer
|
|
86
|
+
- `modality_coverage` — set of modalities appearing in active mappings
|
|
87
|
+
|
|
88
|
+
## Runners
|
|
89
|
+
|
|
90
|
+
Module: `Legion::Extensions::CognitiveSynesthesia::Runners::CognitiveSynesthesia`
|
|
91
|
+
|
|
92
|
+
| Runner | Key Args | Returns |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `register_mapping` | `source_modality:`, `target_modality:`, `trigger_pattern:`, `response_pattern:`, `strength:` | `{ success:, mapping_id:, source_modality:, target_modality: }` or `{ success: false, error: :invalid_modality }` |
|
|
95
|
+
| `trigger` | `source_modality:`, `input:` | `{ success:, events:, triggered_count: }` |
|
|
96
|
+
| `decay_mappings` | — | `{ success:, mappings_removed:, mappings_remaining: }` |
|
|
97
|
+
| `cross_modal_richness` | — | `{ success:, richness:, richness_label: }` |
|
|
98
|
+
| `dominant_modality_pairs` | `limit:` | `{ success:, pairs:, count: }` |
|
|
99
|
+
| `event_history` | `limit:` | `{ success:, events:, count: }` |
|
|
100
|
+
| `modality_coverage` | — | `{ success:, covered_modalities:, coverage_count:, total_modalities: }` |
|
|
101
|
+
| `synesthesia_report` | — | aggregate report with richness, active count, dominant pairs, coverage |
|
|
102
|
+
|
|
103
|
+
All runners accept optional `engine:` keyword for test injection.
|
|
104
|
+
|
|
105
|
+
## Integration Points
|
|
106
|
+
|
|
107
|
+
- No actors defined; `decay_mappings` should be called periodically (e.g., from `lex-tick`)
|
|
108
|
+
- `trigger_pattern` and `response_pattern` are caller-defined — can be strings, symbols, or hashes
|
|
109
|
+
- Can represent cross-extension stimulus linkages: e.g., emotional triggers spatial patterns, or temporal triggers semantic associations
|
|
110
|
+
- `cross_modal_richness` measures the breadth of cross-modal connections — a proxy for cognitive integration
|
|
111
|
+
- All state is in-memory per `SynesthesiaEngine` instance
|
|
112
|
+
|
|
113
|
+
## Development Notes
|
|
114
|
+
|
|
115
|
+
- Both `trigger_pattern` and `response_pattern` are stored opaquely; the engine does not parse them
|
|
116
|
+
- `fire_event` uses the mapping's current strength (after `activate!`) as event intensity
|
|
117
|
+
- `prune_mappings!` evicts weakest by strength; `prune_weak_mappings!` removes strength-zero mappings after decay
|
|
118
|
+
- The event log is a ring buffer: `@events.shift while @events.size > MAX_EVENTS`
|
|
119
|
+
- `cross_modal_richness` counts unique source->target pairs (directional), not bidirectional
|
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# lex-cognitive-synesthesia
|
|
2
|
+
|
|
3
|
+
A LegionIO cognitive architecture extension that models cross-modal cognitive associations inspired by synesthesia. Sensory mappings link one cognitive modality to another, firing automatically when the source modality is stimulated.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
Registers **sensory mappings** between eight cognitive modalities:
|
|
8
|
+
|
|
9
|
+
`visual`, `auditory`, `tactile`, `emotional`, `semantic`, `temporal`, `spatial`, `abstract`
|
|
10
|
+
|
|
11
|
+
When a source modality is triggered, all active mappings for it fire — generating `SynestheticEvent` records that carry the target response. Mappings strengthen with repeated activation and fade through decay, modeling the learned and habituated nature of associative responses.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
require 'lex-cognitive-synesthesia'
|
|
17
|
+
|
|
18
|
+
client = Legion::Extensions::CognitiveSynesthesia::Client.new
|
|
19
|
+
|
|
20
|
+
# Register a mapping: emotional input evokes spatial response
|
|
21
|
+
client.register_mapping(
|
|
22
|
+
source_modality: :emotional,
|
|
23
|
+
target_modality: :spatial,
|
|
24
|
+
trigger_pattern: 'high_arousal',
|
|
25
|
+
response_pattern: 'open_wide_space'
|
|
26
|
+
)
|
|
27
|
+
# => { success: true, mapping_id: "uuid...", source_modality: :emotional, target_modality: :spatial }
|
|
28
|
+
|
|
29
|
+
# Register another mapping for the same source
|
|
30
|
+
client.register_mapping(
|
|
31
|
+
source_modality: :emotional,
|
|
32
|
+
target_modality: :visual,
|
|
33
|
+
trigger_pattern: 'high_arousal',
|
|
34
|
+
response_pattern: 'bright_warm_colors'
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Trigger all active mappings for emotional input
|
|
38
|
+
result = client.trigger(source_modality: :emotional, input: { valence: 0.8, arousal: 0.9 })
|
|
39
|
+
# => { success: true, triggered_count: 2, events: [ { intensity: 0.6, target_output: { modality: :spatial, ... } }, ... ] }
|
|
40
|
+
|
|
41
|
+
# Check cross-modal richness (breadth of coverage)
|
|
42
|
+
client.cross_modal_richness
|
|
43
|
+
# => { success: true, richness: 0.036, richness_label: :amodal }
|
|
44
|
+
|
|
45
|
+
# Which modality pairs are most activated?
|
|
46
|
+
client.dominant_modality_pairs(limit: 3)
|
|
47
|
+
# => { success: true, pairs: [{ pair: "emotional->spatial", activation_count: 1 }, ...], count: 2 }
|
|
48
|
+
|
|
49
|
+
# What modalities are covered by active mappings?
|
|
50
|
+
client.modality_coverage
|
|
51
|
+
# => { success: true, covered_modalities: [:emotional, :spatial, :visual], coverage_count: 3, total_modalities: 8 }
|
|
52
|
+
|
|
53
|
+
# Decay all mappings (removes those that fall to 0)
|
|
54
|
+
client.decay_mappings
|
|
55
|
+
# => { success: true, mappings_removed: 0, mappings_remaining: 2 }
|
|
56
|
+
|
|
57
|
+
# Recent event history
|
|
58
|
+
client.event_history(limit: 10)
|
|
59
|
+
# => { success: true, events: [...], count: 2 }
|
|
60
|
+
|
|
61
|
+
# Full report
|
|
62
|
+
client.synesthesia_report
|
|
63
|
+
# => { success: true, mapping_count: 2, active_mapping_count: 2, event_count: 2, cross_modal_richness: 0.036, ... }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Development
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
bundle install
|
|
70
|
+
bundle exec rspec
|
|
71
|
+
bundle exec rubocop
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
MIT
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/cognitive_synesthesia/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-cognitive-synesthesia'
|
|
7
|
+
spec.version = Legion::Extensions::CognitiveSynesthesia::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX Cognitive Synesthesia'
|
|
12
|
+
spec.description = 'Cross-modal synesthetic mapping for LegionIO agents — automatic ' \
|
|
13
|
+
'cross-domain associations that enrich understanding through sensory translation'
|
|
14
|
+
spec.homepage = 'https://github.com/LegionIO/lex-cognitive-synesthesia'
|
|
15
|
+
spec.license = 'MIT'
|
|
16
|
+
spec.required_ruby_version = '>= 3.4'
|
|
17
|
+
|
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-cognitive-synesthesia'
|
|
20
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-cognitive-synesthesia'
|
|
21
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-cognitive-synesthesia'
|
|
22
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-cognitive-synesthesia/issues'
|
|
23
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
24
|
+
|
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
26
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
27
|
+
end
|
|
28
|
+
spec.require_paths = ['lib']
|
|
29
|
+
spec.add_development_dependency 'legion-gaia'
|
|
30
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/cognitive_synesthesia/helpers/constants'
|
|
4
|
+
require 'legion/extensions/cognitive_synesthesia/helpers/sensory_mapping'
|
|
5
|
+
require 'legion/extensions/cognitive_synesthesia/helpers/synesthetic_event'
|
|
6
|
+
require 'legion/extensions/cognitive_synesthesia/helpers/synesthesia_engine'
|
|
7
|
+
require 'legion/extensions/cognitive_synesthesia/runners/cognitive_synesthesia'
|
|
8
|
+
|
|
9
|
+
module Legion
|
|
10
|
+
module Extensions
|
|
11
|
+
module CognitiveSynesthesia
|
|
12
|
+
class Client
|
|
13
|
+
include Runners::CognitiveSynesthesia
|
|
14
|
+
|
|
15
|
+
def initialize(engine: nil, **)
|
|
16
|
+
@synesthesia_engine = engine || Helpers::SynesthesiaEngine.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
attr_reader :synesthesia_engine
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveSynesthesia
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
MAX_MAPPINGS = 200
|
|
9
|
+
MAX_EVENTS = 500
|
|
10
|
+
DEFAULT_STRENGTH = 0.5
|
|
11
|
+
STRENGTH_BOOST = 0.1
|
|
12
|
+
STRENGTH_DECAY = 0.02
|
|
13
|
+
TRIGGER_THRESHOLD = 0.3
|
|
14
|
+
|
|
15
|
+
MODALITIES = %i[visual auditory tactile emotional semantic temporal spatial abstract].freeze
|
|
16
|
+
|
|
17
|
+
STRENGTH_LABELS = {
|
|
18
|
+
(0.8..) => :dominant,
|
|
19
|
+
(0.6...0.8) => :strong,
|
|
20
|
+
(0.4...0.6) => :moderate,
|
|
21
|
+
(0.2...0.4) => :faint,
|
|
22
|
+
(..0.2) => :trace
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
RICHNESS_LABELS = {
|
|
26
|
+
(0.8..) => :synesthetic,
|
|
27
|
+
(0.6...0.8) => :vivid,
|
|
28
|
+
(0.4...0.6) => :partial,
|
|
29
|
+
(0.2...0.4) => :sparse,
|
|
30
|
+
(..0.2) => :amodal
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
INTENSITY_LABELS = {
|
|
34
|
+
(0.8..) => :overwhelming,
|
|
35
|
+
(0.6...0.8) => :intense,
|
|
36
|
+
(0.4...0.6) => :moderate,
|
|
37
|
+
(0.2...0.4) => :subtle,
|
|
38
|
+
(..0.2) => :subliminal
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
def self.label_for(labels_hash, value)
|
|
42
|
+
labels_hash.find { |range, _| range.cover?(value) }&.last || :unknown
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitiveSynesthesia
|
|
8
|
+
module Helpers
|
|
9
|
+
class SensoryMapping
|
|
10
|
+
include Constants
|
|
11
|
+
|
|
12
|
+
attr_reader :id, :source_modality, :target_modality,
|
|
13
|
+
:trigger_pattern, :response_pattern,
|
|
14
|
+
:strength, :activation_count, :created_at, :last_activated_at
|
|
15
|
+
|
|
16
|
+
def initialize(source_modality:, target_modality:, trigger_pattern:, response_pattern:,
|
|
17
|
+
strength: DEFAULT_STRENGTH)
|
|
18
|
+
@id = SecureRandom.uuid
|
|
19
|
+
@source_modality = source_modality
|
|
20
|
+
@target_modality = target_modality
|
|
21
|
+
@trigger_pattern = trigger_pattern
|
|
22
|
+
@response_pattern = response_pattern
|
|
23
|
+
@strength = strength.clamp(0.0, 1.0)
|
|
24
|
+
@activation_count = 0
|
|
25
|
+
@created_at = Time.now.utc
|
|
26
|
+
@last_activated_at = nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def activate!
|
|
30
|
+
@activation_count += 1
|
|
31
|
+
@last_activated_at = Time.now.utc
|
|
32
|
+
@strength = (@strength + STRENGTH_BOOST).clamp(0.0, 1.0).round(10)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def decay!
|
|
36
|
+
@strength = (@strength - STRENGTH_DECAY).clamp(0.0, 1.0).round(10)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def active?
|
|
40
|
+
@strength >= TRIGGER_THRESHOLD
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def strength_label
|
|
44
|
+
Constants.label_for(STRENGTH_LABELS, @strength)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_h
|
|
48
|
+
{
|
|
49
|
+
id: @id,
|
|
50
|
+
source_modality: @source_modality,
|
|
51
|
+
target_modality: @target_modality,
|
|
52
|
+
trigger_pattern: @trigger_pattern,
|
|
53
|
+
response_pattern: @response_pattern,
|
|
54
|
+
strength: @strength,
|
|
55
|
+
activation_count: @activation_count,
|
|
56
|
+
strength_label: strength_label,
|
|
57
|
+
active: active?,
|
|
58
|
+
created_at: @created_at,
|
|
59
|
+
last_activated_at: @last_activated_at
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveSynesthesia
|
|
6
|
+
module Helpers
|
|
7
|
+
class SynesthesiaEngine
|
|
8
|
+
include Constants
|
|
9
|
+
|
|
10
|
+
attr_reader :mappings, :events
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@mappings = {}
|
|
14
|
+
@events = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def register_mapping(source_modality:, target_modality:, trigger_pattern:,
|
|
18
|
+
response_pattern:, strength: DEFAULT_STRENGTH, **)
|
|
19
|
+
return invalid_modality_error(:source_modality) unless MODALITIES.include?(source_modality)
|
|
20
|
+
return invalid_modality_error(:target_modality) unless MODALITIES.include?(target_modality)
|
|
21
|
+
|
|
22
|
+
mapping = SensoryMapping.new(
|
|
23
|
+
source_modality: source_modality,
|
|
24
|
+
target_modality: target_modality,
|
|
25
|
+
trigger_pattern: trigger_pattern,
|
|
26
|
+
response_pattern: response_pattern,
|
|
27
|
+
strength: strength
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
@mappings[mapping.id] = mapping
|
|
31
|
+
prune_mappings! if @mappings.size > MAX_MAPPINGS
|
|
32
|
+
|
|
33
|
+
Legion::Logging.debug "[cognitive_synesthesia] mapping registered id=#{mapping.id[0..7]} " \
|
|
34
|
+
"#{source_modality}->#{target_modality} strength=#{strength.round(2)}"
|
|
35
|
+
|
|
36
|
+
{ success: true, mapping_id: mapping.id, source_modality: source_modality,
|
|
37
|
+
target_modality: target_modality }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def trigger(source_modality:, input:, **)
|
|
41
|
+
active = active_mappings_for(source_modality)
|
|
42
|
+
return { success: true, events: [], triggered_count: 0 } if active.empty?
|
|
43
|
+
|
|
44
|
+
triggered_events = active.map do |mapping|
|
|
45
|
+
fire_event(mapping, input)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
{ success: true, events: triggered_events.map(&:to_h), triggered_count: triggered_events.size }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def decay_mappings!(**)
|
|
52
|
+
@mappings.each_value(&:decay!)
|
|
53
|
+
removed = prune_weak_mappings!
|
|
54
|
+
|
|
55
|
+
Legion::Logging.debug "[cognitive_synesthesia] decay_mappings! removed=#{removed} remaining=#{@mappings.size}"
|
|
56
|
+
{ success: true, mappings_removed: removed, mappings_remaining: @mappings.size }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def cross_modal_richness(**)
|
|
60
|
+
return 0.0 if @mappings.empty?
|
|
61
|
+
|
|
62
|
+
pairs = active_mapping_pairs
|
|
63
|
+
total_possible = (MODALITIES.size * (MODALITIES.size - 1)).to_f
|
|
64
|
+
return 0.0 if total_possible.zero?
|
|
65
|
+
|
|
66
|
+
(pairs.size.to_f / total_possible).clamp(0.0, 1.0).round(10)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def dominant_modality_pairs(limit: 5, **)
|
|
70
|
+
pair_counts = Hash.new(0)
|
|
71
|
+
@mappings.each_value do |m|
|
|
72
|
+
next unless m.active?
|
|
73
|
+
|
|
74
|
+
pair_counts["#{m.source_modality}->#{m.target_modality}"] += m.activation_count
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
sorted = pair_counts.sort_by { |_, count| -count }.first(limit)
|
|
78
|
+
sorted.map { |pair, count| { pair: pair, activation_count: count } }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def event_history(limit: 20, **)
|
|
82
|
+
recent = @events.last(limit)
|
|
83
|
+
{ success: true, events: recent.map(&:to_h), count: recent.size }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def modality_coverage(**)
|
|
87
|
+
covered = Set.new
|
|
88
|
+
@mappings.each_value do |m|
|
|
89
|
+
next unless m.active?
|
|
90
|
+
|
|
91
|
+
covered << m.source_modality
|
|
92
|
+
covered << m.target_modality
|
|
93
|
+
end
|
|
94
|
+
{
|
|
95
|
+
covered_modalities: covered.to_a.sort,
|
|
96
|
+
coverage_count: covered.size,
|
|
97
|
+
total_modalities: MODALITIES.size
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def synesthesia_report(**)
|
|
102
|
+
richness = cross_modal_richness
|
|
103
|
+
{
|
|
104
|
+
mapping_count: @mappings.size,
|
|
105
|
+
active_mapping_count: @mappings.values.count(&:active?),
|
|
106
|
+
event_count: @events.size,
|
|
107
|
+
cross_modal_richness: richness,
|
|
108
|
+
richness_label: Constants.label_for(RICHNESS_LABELS, richness),
|
|
109
|
+
dominant_modality_pairs: dominant_modality_pairs,
|
|
110
|
+
modality_coverage: modality_coverage
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def invalid_modality_error(field)
|
|
117
|
+
{ success: false, error: :invalid_modality, field: field, valid_modalities: MODALITIES }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def active_mappings_for(source_modality)
|
|
121
|
+
@mappings.values.select { |m| m.source_modality == source_modality && m.active? }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def fire_event(mapping, input)
|
|
125
|
+
mapping.activate!
|
|
126
|
+
intensity = mapping.strength.clamp(0.0, 1.0)
|
|
127
|
+
event = SynestheticEvent.new(
|
|
128
|
+
mapping_id: mapping.id,
|
|
129
|
+
source_input: input,
|
|
130
|
+
target_output: build_target_output(mapping, input),
|
|
131
|
+
intensity: intensity
|
|
132
|
+
)
|
|
133
|
+
@events << event
|
|
134
|
+
@events.shift while @events.size > MAX_EVENTS
|
|
135
|
+
|
|
136
|
+
Legion::Logging.debug "[cognitive_synesthesia] event fired id=#{event.id[0..7]} " \
|
|
137
|
+
"mapping=#{mapping.id[0..7]} intensity=#{intensity.round(2)}"
|
|
138
|
+
event
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def build_target_output(mapping, input)
|
|
142
|
+
{
|
|
143
|
+
modality: mapping.target_modality,
|
|
144
|
+
response_pattern: mapping.response_pattern,
|
|
145
|
+
source_input: input,
|
|
146
|
+
source_modality: mapping.source_modality
|
|
147
|
+
}
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def active_mapping_pairs
|
|
151
|
+
@mappings.values.select(&:active?).map do |m|
|
|
152
|
+
[m.source_modality, m.target_modality]
|
|
153
|
+
end.uniq
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def prune_mappings!
|
|
157
|
+
overflow = @mappings.size - MAX_MAPPINGS
|
|
158
|
+
return if overflow <= 0
|
|
159
|
+
|
|
160
|
+
ids_to_prune = @mappings.min_by(overflow) { |_, m| m.strength }.map(&:first)
|
|
161
|
+
ids_to_prune.each { |id| @mappings.delete(id) }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def prune_weak_mappings!
|
|
165
|
+
before = @mappings.size
|
|
166
|
+
@mappings.reject! { |_, m| m.strength <= 0.0 }
|
|
167
|
+
before - @mappings.size
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitiveSynesthesia
|
|
8
|
+
module Helpers
|
|
9
|
+
class SynestheticEvent
|
|
10
|
+
include Constants
|
|
11
|
+
|
|
12
|
+
attr_reader :id, :mapping_id, :source_input, :target_output,
|
|
13
|
+
:intensity, :involuntary, :triggered_at
|
|
14
|
+
|
|
15
|
+
def initialize(mapping_id:, source_input:, target_output:, intensity:, involuntary: true)
|
|
16
|
+
@id = SecureRandom.uuid
|
|
17
|
+
@mapping_id = mapping_id
|
|
18
|
+
@source_input = source_input
|
|
19
|
+
@target_output = target_output
|
|
20
|
+
@intensity = intensity.clamp(0.0, 1.0)
|
|
21
|
+
@involuntary = involuntary
|
|
22
|
+
@triggered_at = Time.now.utc
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def intensity_label
|
|
26
|
+
Constants.label_for(INTENSITY_LABELS, @intensity)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_h
|
|
30
|
+
{
|
|
31
|
+
id: @id,
|
|
32
|
+
mapping_id: @mapping_id,
|
|
33
|
+
source_input: @source_input,
|
|
34
|
+
target_output: @target_output,
|
|
35
|
+
intensity: @intensity,
|
|
36
|
+
intensity_label: intensity_label,
|
|
37
|
+
involuntary: @involuntary,
|
|
38
|
+
triggered_at: @triggered_at
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveSynesthesia
|
|
6
|
+
module Runners
|
|
7
|
+
module CognitiveSynesthesia
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
def register_mapping(source_modality:, target_modality:, trigger_pattern:,
|
|
12
|
+
response_pattern:, strength: Helpers::Constants::DEFAULT_STRENGTH,
|
|
13
|
+
engine: nil, **)
|
|
14
|
+
target = engine || synesthesia_engine
|
|
15
|
+
Legion::Logging.debug "[cognitive_synesthesia] runner register_mapping #{source_modality}->#{target_modality}"
|
|
16
|
+
target.register_mapping(
|
|
17
|
+
source_modality: source_modality,
|
|
18
|
+
target_modality: target_modality,
|
|
19
|
+
trigger_pattern: trigger_pattern,
|
|
20
|
+
response_pattern: response_pattern,
|
|
21
|
+
strength: strength
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def trigger(source_modality:, input:, engine: nil, **)
|
|
26
|
+
target = engine || synesthesia_engine
|
|
27
|
+
Legion::Logging.debug "[cognitive_synesthesia] runner trigger modality=#{source_modality}"
|
|
28
|
+
target.trigger(source_modality: source_modality, input: input)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def decay_mappings(engine: nil, **)
|
|
32
|
+
target = engine || synesthesia_engine
|
|
33
|
+
Legion::Logging.debug '[cognitive_synesthesia] runner decay_mappings'
|
|
34
|
+
target.decay_mappings!
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def cross_modal_richness(engine: nil, **)
|
|
38
|
+
target = engine || synesthesia_engine
|
|
39
|
+
richness = target.cross_modal_richness
|
|
40
|
+
label = Helpers::Constants.label_for(Helpers::Constants::RICHNESS_LABELS, richness)
|
|
41
|
+
Legion::Logging.debug "[cognitive_synesthesia] runner cross_modal_richness=#{richness.round(4)} label=#{label}"
|
|
42
|
+
{ success: true, richness: richness, richness_label: label }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def dominant_modality_pairs(limit: 5, engine: nil, **)
|
|
46
|
+
target = engine || synesthesia_engine
|
|
47
|
+
pairs = target.dominant_modality_pairs(limit: limit)
|
|
48
|
+
Legion::Logging.debug "[cognitive_synesthesia] runner dominant_modality_pairs limit=#{limit}"
|
|
49
|
+
{ success: true, pairs: pairs, count: pairs.size }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def event_history(limit: 20, engine: nil, **)
|
|
53
|
+
target = engine || synesthesia_engine
|
|
54
|
+
Legion::Logging.debug "[cognitive_synesthesia] runner event_history limit=#{limit}"
|
|
55
|
+
target.event_history(limit: limit)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def modality_coverage(engine: nil, **)
|
|
59
|
+
target = engine || synesthesia_engine
|
|
60
|
+
Legion::Logging.debug '[cognitive_synesthesia] runner modality_coverage'
|
|
61
|
+
{ success: true }.merge(target.modality_coverage)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def synesthesia_report(engine: nil, **)
|
|
65
|
+
target = engine || synesthesia_engine
|
|
66
|
+
Legion::Logging.debug '[cognitive_synesthesia] runner synesthesia_report'
|
|
67
|
+
{ success: true }.merge(target.synesthesia_report)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def synesthesia_engine
|
|
73
|
+
@synesthesia_engine ||= Helpers::SynesthesiaEngine.new
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/cognitive_synesthesia/version'
|
|
4
|
+
require 'legion/extensions/cognitive_synesthesia/helpers/constants'
|
|
5
|
+
require 'legion/extensions/cognitive_synesthesia/helpers/sensory_mapping'
|
|
6
|
+
require 'legion/extensions/cognitive_synesthesia/helpers/synesthetic_event'
|
|
7
|
+
require 'legion/extensions/cognitive_synesthesia/helpers/synesthesia_engine'
|
|
8
|
+
require 'legion/extensions/cognitive_synesthesia/runners/cognitive_synesthesia'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module Extensions
|
|
12
|
+
module CognitiveSynesthesia
|
|
13
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-cognitive-synesthesia
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
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
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: legion-gaia
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: Cross-modal synesthetic mapping for LegionIO agents — automatic cross-domain
|
|
27
|
+
associations that enrich understanding through sensory translation
|
|
28
|
+
email:
|
|
29
|
+
- matthewdiverson@gmail.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- ".github/workflows/ci.yml"
|
|
35
|
+
- ".gitignore"
|
|
36
|
+
- ".rspec"
|
|
37
|
+
- ".rubocop.yml"
|
|
38
|
+
- CLAUDE.md
|
|
39
|
+
- Gemfile
|
|
40
|
+
- README.md
|
|
41
|
+
- lex-cognitive-synesthesia.gemspec
|
|
42
|
+
- lib/legion/extensions/cognitive_synesthesia.rb
|
|
43
|
+
- lib/legion/extensions/cognitive_synesthesia/client.rb
|
|
44
|
+
- lib/legion/extensions/cognitive_synesthesia/helpers/constants.rb
|
|
45
|
+
- lib/legion/extensions/cognitive_synesthesia/helpers/sensory_mapping.rb
|
|
46
|
+
- lib/legion/extensions/cognitive_synesthesia/helpers/synesthesia_engine.rb
|
|
47
|
+
- lib/legion/extensions/cognitive_synesthesia/helpers/synesthetic_event.rb
|
|
48
|
+
- lib/legion/extensions/cognitive_synesthesia/runners/cognitive_synesthesia.rb
|
|
49
|
+
- lib/legion/extensions/cognitive_synesthesia/version.rb
|
|
50
|
+
homepage: https://github.com/LegionIO/lex-cognitive-synesthesia
|
|
51
|
+
licenses:
|
|
52
|
+
- MIT
|
|
53
|
+
metadata:
|
|
54
|
+
homepage_uri: https://github.com/LegionIO/lex-cognitive-synesthesia
|
|
55
|
+
source_code_uri: https://github.com/LegionIO/lex-cognitive-synesthesia
|
|
56
|
+
documentation_uri: https://github.com/LegionIO/lex-cognitive-synesthesia
|
|
57
|
+
changelog_uri: https://github.com/LegionIO/lex-cognitive-synesthesia
|
|
58
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-synesthesia/issues
|
|
59
|
+
rubygems_mfa_required: 'true'
|
|
60
|
+
rdoc_options: []
|
|
61
|
+
require_paths:
|
|
62
|
+
- lib
|
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.4'
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '0'
|
|
73
|
+
requirements: []
|
|
74
|
+
rubygems_version: 3.6.9
|
|
75
|
+
specification_version: 4
|
|
76
|
+
summary: LEX Cognitive Synesthesia
|
|
77
|
+
test_files: []
|