heathrow 0.7.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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +58 -0
  3. data/README.md +205 -0
  4. data/bin/heathrow +42 -0
  5. data/bin/heathrowd +283 -0
  6. data/docs/ARCHITECTURE.md +1172 -0
  7. data/docs/DATABASE_SCHEMA.md +685 -0
  8. data/docs/DEVELOPMENT_WORKFLOW.md +867 -0
  9. data/docs/DISCORD_SETUP.md +142 -0
  10. data/docs/GMAIL_OAUTH_SETUP.md +120 -0
  11. data/docs/PLUGIN_SYSTEM.md +1370 -0
  12. data/docs/PROJECT_PLAN.md +1022 -0
  13. data/docs/README.md +417 -0
  14. data/docs/REDDIT_SETUP.md +174 -0
  15. data/docs/REPLY_FORWARD.md +182 -0
  16. data/docs/WHATSAPP_TELEGRAM_SETUP.md +306 -0
  17. data/heathrow.gemspec +34 -0
  18. data/heathrowd.service +21 -0
  19. data/img/heathrow.svg +95 -0
  20. data/img/rss_threaded.png +0 -0
  21. data/img/sources.png +0 -0
  22. data/lib/heathrow/address_book.rb +42 -0
  23. data/lib/heathrow/config.rb +332 -0
  24. data/lib/heathrow/database.rb +731 -0
  25. data/lib/heathrow/database_new.rb +392 -0
  26. data/lib/heathrow/event_bus.rb +175 -0
  27. data/lib/heathrow/logger.rb +122 -0
  28. data/lib/heathrow/message.rb +176 -0
  29. data/lib/heathrow/message_composer.rb +399 -0
  30. data/lib/heathrow/message_organizer.rb +774 -0
  31. data/lib/heathrow/migrations/001_initial_schema.rb +248 -0
  32. data/lib/heathrow/notmuch.rb +45 -0
  33. data/lib/heathrow/oauth2_smtp.rb +254 -0
  34. data/lib/heathrow/plugin/base.rb +212 -0
  35. data/lib/heathrow/plugin_manager.rb +141 -0
  36. data/lib/heathrow/poller.rb +93 -0
  37. data/lib/heathrow/smtp_sender.rb +204 -0
  38. data/lib/heathrow/source.rb +39 -0
  39. data/lib/heathrow/sources/base.rb +74 -0
  40. data/lib/heathrow/sources/discord.rb +357 -0
  41. data/lib/heathrow/sources/gmail.rb +294 -0
  42. data/lib/heathrow/sources/imap.rb +198 -0
  43. data/lib/heathrow/sources/instagram.rb +307 -0
  44. data/lib/heathrow/sources/instagram_fetch.py +101 -0
  45. data/lib/heathrow/sources/instagram_send.py +55 -0
  46. data/lib/heathrow/sources/instagram_send_marionette.py +104 -0
  47. data/lib/heathrow/sources/maildir.rb +606 -0
  48. data/lib/heathrow/sources/messenger.rb +212 -0
  49. data/lib/heathrow/sources/messenger_fetch.js +297 -0
  50. data/lib/heathrow/sources/messenger_fetch_marionette.py +138 -0
  51. data/lib/heathrow/sources/messenger_send.js +32 -0
  52. data/lib/heathrow/sources/messenger_send.py +100 -0
  53. data/lib/heathrow/sources/reddit.rb +461 -0
  54. data/lib/heathrow/sources/rss.rb +299 -0
  55. data/lib/heathrow/sources/slack.rb +375 -0
  56. data/lib/heathrow/sources/source_manager.rb +328 -0
  57. data/lib/heathrow/sources/telegram.rb +498 -0
  58. data/lib/heathrow/sources/webpage.rb +207 -0
  59. data/lib/heathrow/sources/weechat.rb +479 -0
  60. data/lib/heathrow/sources/whatsapp.rb +474 -0
  61. data/lib/heathrow/ui/application.rb +8098 -0
  62. data/lib/heathrow/ui/navigation.rb +8 -0
  63. data/lib/heathrow/ui/panes.rb +8 -0
  64. data/lib/heathrow/ui/source_wizard.rb +567 -0
  65. data/lib/heathrow/ui/threaded_view.rb +780 -0
  66. data/lib/heathrow/ui/views.rb +8 -0
  67. data/lib/heathrow/version.rb +3 -0
  68. data/lib/heathrow/wizards/discord_wizard.rb +193 -0
  69. data/lib/heathrow/wizards/slack_wizard.rb +140 -0
  70. data/lib/heathrow.rb +55 -0
  71. metadata +147 -0
@@ -0,0 +1,8 @@
1
+ # View management for Heathrow
2
+ module Heathrow
3
+ module UI
4
+ module Views
5
+ # View filtering and management
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Heathrow
2
+ VERSION = '0.7.0'
3
+ end
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'net/http'
5
+ require 'json'
6
+ require 'io/console'
7
+
8
+ module Heathrow
9
+ module Wizards
10
+ class DiscordWizard
11
+ def self.run(source = nil)
12
+ new(source).run
13
+ end
14
+
15
+ def initialize(source = nil)
16
+ @source = source
17
+ @config = source ? source.config : {}
18
+ end
19
+
20
+ def run
21
+ puts "\n=== Discord Configuration Wizard ==="
22
+ puts
23
+
24
+ # Get token
25
+ token = get_token
26
+ return nil unless token
27
+
28
+ # Test token
29
+ is_bot = test_token(token)
30
+ return nil unless is_bot
31
+
32
+ @config['token'] = token
33
+ @config['is_bot'] = is_bot
34
+
35
+ # Discover servers and channels
36
+ puts "\nDiscovering servers and channels..."
37
+ discover_and_select_channels(token, is_bot)
38
+
39
+ # Set other options
40
+ @config['fetch_limit'] ||= 50
41
+ @config['polling_interval'] ||= 300
42
+
43
+ puts "\n=== Configuration Complete ==="
44
+ puts "Token: #{token[0..20]}..."
45
+ puts "Bot account: #{is_bot ? 'Yes' : 'No'}"
46
+ puts "Channels: #{@config['channels'].split(',').length} selected" if @config['channels']
47
+ puts "Guilds: #{@config['guilds'].split(',').length} selected" if @config['guilds'] && !@config['guilds'].empty?
48
+
49
+ @config
50
+ end
51
+
52
+ private
53
+
54
+ def get_token
55
+ puts "Discord Token Setup"
56
+ puts "-" * 40
57
+ puts "You can use either:"
58
+ puts " 1. Bot token (recommended) - Create at https://discord.com/developers/applications"
59
+ puts " 2. User token (against ToS) - Found in browser DevTools"
60
+ puts
61
+
62
+ existing = @config['token']
63
+ if existing
64
+ print "Current token: #{existing[0..20]}...\nKeep this token? (Y/n): "
65
+ keep = gets.chomp.downcase
66
+ return existing unless keep == 'n'
67
+ end
68
+
69
+ print "Enter Discord token: "
70
+ token = gets.chomp.strip
71
+
72
+ if token.empty?
73
+ puts "Error: Token cannot be empty"
74
+ return nil
75
+ end
76
+
77
+ token
78
+ end
79
+
80
+ def test_token(token)
81
+ # Try as bot token first
82
+ uri = URI("https://discord.com/api/v10/users/@me")
83
+
84
+ [true, false].each do |is_bot|
85
+ request = Net::HTTP::Get.new(uri)
86
+ request['Authorization'] = is_bot ? "Bot #{token}" : token
87
+
88
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
89
+ http.request(request)
90
+ end
91
+
92
+ if response.is_a?(Net::HTTPSuccess)
93
+ user_data = JSON.parse(response.body)
94
+ puts "✓ Connected as: #{user_data['username']}##{user_data['discriminator']}"
95
+ puts " Account type: #{is_bot ? 'Bot' : 'User'}"
96
+ return is_bot
97
+ end
98
+ end
99
+
100
+ puts "✗ Failed to authenticate with Discord"
101
+ false
102
+ end
103
+
104
+ def discover_and_select_channels(token, is_bot)
105
+ # Get user's guilds
106
+ uri = URI("https://discord.com/api/v10/users/@me/guilds")
107
+ request = Net::HTTP::Get.new(uri)
108
+ request['Authorization'] = is_bot ? "Bot #{token}" : token
109
+
110
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
111
+ http.request(request)
112
+ end
113
+
114
+ unless response.is_a?(Net::HTTPSuccess)
115
+ puts "Could not fetch guilds. You can manually add channel IDs later."
116
+ return
117
+ end
118
+
119
+ guilds = JSON.parse(response.body)
120
+ puts "\nFound #{guilds.length} servers:"
121
+
122
+ # Show guilds and their channels
123
+ selected_channels = []
124
+ selected_guilds = []
125
+
126
+ guilds.each_with_index do |guild, index|
127
+ puts "\n#{index + 1}. #{guild['name']}"
128
+
129
+ # Get channels for this guild
130
+ channels_uri = URI("https://discord.com/api/v10/guilds/#{guild['id']}/channels")
131
+ channels_request = Net::HTTP::Get.new(channels_uri)
132
+ channels_request['Authorization'] = is_bot ? "Bot #{token}" : token
133
+
134
+ channels_response = Net::HTTP.start(channels_uri.hostname, channels_uri.port, use_ssl: true) do |http|
135
+ http.request(channels_request)
136
+ end
137
+
138
+ if channels_response.is_a?(Net::HTTPSuccess)
139
+ channels = JSON.parse(channels_response.body)
140
+ text_channels = channels.select { |c| c['type'] == 0 } # Type 0 = text channel
141
+
142
+ puts " Channels:"
143
+ text_channels.first(10).each do |channel|
144
+ puts " - ##{channel['name']} (ID: #{channel['id']})"
145
+ end
146
+ puts " ... and #{text_channels.length - 10} more" if text_channels.length > 10
147
+ else
148
+ puts " Could not fetch channels for this server"
149
+ end
150
+ end
151
+
152
+ puts "\n" + "=" * 40
153
+ puts "Channel Selection Options:"
154
+ puts " 1. Select all channels from specific servers"
155
+ puts " 2. Select individual channels (enter IDs)"
156
+ puts " 3. Use existing configuration"
157
+ puts " 4. Skip for now"
158
+ print "\nChoice (1-4): "
159
+
160
+ choice = gets.chomp
161
+
162
+ case choice
163
+ when '1'
164
+ print "Enter server numbers (comma-separated, e.g., 1,3,5): "
165
+ numbers = gets.chomp.split(',').map(&:strip)
166
+
167
+ numbers.each do |num|
168
+ idx = num.to_i - 1
169
+ if idx >= 0 && idx < guilds.length
170
+ selected_guilds << guilds[idx]['id']
171
+ puts " Added: #{guilds[idx]['name']}"
172
+ end
173
+ end
174
+
175
+ @config['guilds'] = selected_guilds.join(',') unless selected_guilds.empty?
176
+
177
+ when '2'
178
+ puts "Enter channel IDs (comma-separated):"
179
+ puts "Tip: Right-click channel in Discord > Copy ID"
180
+ print "> "
181
+ channels = gets.chomp
182
+ @config['channels'] = channels unless channels.empty?
183
+
184
+ when '3'
185
+ puts "Keeping existing configuration"
186
+
187
+ else
188
+ puts "Skipping channel configuration"
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'json'
5
+ require_relative '../sources/slack'
6
+
7
+ module Heathrow
8
+ module Wizards
9
+ # Setup wizard for Slack integration
10
+ class SlackWizard
11
+ def self.run
12
+ puts "\n=== Slack Setup Wizard ==="
13
+ puts "This will help you configure Slack integration for Heathrow.\n\n"
14
+
15
+ puts "You'll need a Slack API token. You can get one by:"
16
+ puts "1. Creating a Slack App at https://api.slack.com/apps"
17
+ puts "2. Installing it to your workspace"
18
+ puts "3. Getting a User OAuth Token (xoxp-...) or Bot Token (xoxb-...)"
19
+ puts "\nFor full access to all channels and DMs, use a User Token."
20
+ puts "For limited bot access, use a Bot Token.\n\n"
21
+
22
+ config = {}
23
+
24
+ # Get API token
25
+ print "Enter your Slack API token (xoxp-... or xoxb-...): "
26
+ config['api_token'] = gets.chomp
27
+
28
+ # Test the token
29
+ print "\nTesting connection..."
30
+ test_source = Heathrow::Sources::Slack.new(config)
31
+
32
+ unless test_source.test_connection
33
+ puts "\nFailed to connect. Please check your token and try again."
34
+ return nil
35
+ end
36
+
37
+ puts " Success!\n\n"
38
+
39
+ # Get workspace name (optional)
40
+ print "Enter a name for this workspace (optional, for display): "
41
+ workspace = gets.chomp
42
+ config['workspace'] = workspace unless workspace.empty?
43
+
44
+ # Ask about channel configuration
45
+ puts "\nChannel Configuration:"
46
+ puts "1. Monitor all channels I have access to (default)"
47
+ puts "2. Monitor specific channels only"
48
+ print "Choose option [1]: "
49
+
50
+ choice = gets.chomp
51
+ choice = '1' if choice.empty?
52
+
53
+ if choice == '2'
54
+ puts "\nEnter channel IDs to monitor (one per line, empty line to finish):"
55
+ puts "Example: C1234567890"
56
+ puts "You can find channel IDs in Slack by right-clicking a channel."
57
+
58
+ channels = []
59
+ loop do
60
+ print "> "
61
+ channel = gets.chomp
62
+ break if channel.empty?
63
+ channels << channel
64
+ end
65
+ config['channel_ids'] = channels unless channels.empty?
66
+ end
67
+
68
+ # Ask about DM configuration
69
+ puts "\nDirect Message Configuration:"
70
+ puts "1. Monitor all DMs (default)"
71
+ puts "2. Monitor specific users only"
72
+ print "Choose option [1]: "
73
+
74
+ choice = gets.chomp
75
+ choice = '1' if choice.empty?
76
+
77
+ if choice == '2'
78
+ puts "\nEnter user IDs to monitor DMs with (one per line, empty line to finish):"
79
+ puts "Example: U1234567890"
80
+
81
+ users = []
82
+ loop do
83
+ print "> "
84
+ user = gets.chomp
85
+ break if user.empty?
86
+ users << user
87
+ end
88
+ config['dm_user_ids'] = users unless users.empty?
89
+ end
90
+
91
+ # Ask about fetch interval
92
+ print "\nHow often to fetch messages (in seconds) [300]: "
93
+ interval = gets.chomp
94
+ config['fetch_interval'] = interval.empty? ? 300 : interval.to_i
95
+
96
+ # Save configuration
97
+ source_name = workspace.empty? ? 'slack' : "slack_#{workspace.downcase.gsub(/\s+/, '_')}"
98
+ config_file = File.join(Dir.home, '.config', 'heathrow', 'sources', "#{source_name}.json")
99
+
100
+ # Create directory if it doesn't exist
101
+ FileUtils.mkdir_p(File.dirname(config_file))
102
+
103
+ # Write config
104
+ File.write(config_file, JSON.pretty_generate(config))
105
+
106
+ puts "\n✓ Slack configuration saved to: #{config_file}"
107
+ puts "\nYou can now use this source in Heathrow!"
108
+ puts "The source will appear as: #{source_name}"
109
+
110
+ config
111
+ end
112
+
113
+ def self.run_oauth_flow
114
+ puts "\n=== Slack OAuth Setup (Alternative) ==="
115
+ puts "This method uses OAuth authentication flow.\n\n"
116
+
117
+ puts "1. Go to: https://api.slack.com/apps"
118
+ puts "2. Create a new app or select existing"
119
+ puts "3. Add these OAuth scopes:"
120
+ puts " - channels:history"
121
+ puts " - channels:read"
122
+ puts " - groups:history"
123
+ puts " - groups:read"
124
+ puts " - im:history"
125
+ puts " - im:read"
126
+ puts " - mpim:history"
127
+ puts " - mpim:read"
128
+ puts " - users:read"
129
+ puts " - chat:write"
130
+ puts "4. Install the app to your workspace"
131
+ puts "5. Copy the User OAuth Token\n\n"
132
+
133
+ print "Press Enter when ready to continue..."
134
+ gets
135
+
136
+ run
137
+ end
138
+ end
139
+ end
140
+ end
data/lib/heathrow.rb ADDED
@@ -0,0 +1,55 @@
1
+ # Main Heathrow module
2
+ require 'fileutils'
3
+ require 'yaml'
4
+ require 'json'
5
+ require 'sqlite3'
6
+ require 'time'
7
+
8
+ # Create Heathrow home directory structure
9
+ HEATHROW_HOME = File.expand_path('~/.heathrow')
10
+ HEATHROW_DB = File.join(HEATHROW_HOME, 'heathrow.db')
11
+ HEATHROW_CONFIG = File.join(HEATHROW_HOME, 'config.yml')
12
+ HEATHROW_SOURCES = File.join(HEATHROW_HOME, 'sources')
13
+ HEATHROW_VIEWS = File.join(HEATHROW_HOME, 'views')
14
+ HEATHROW_ATTACHMENTS = File.join(HEATHROW_HOME, 'attachments')
15
+ HEATHROW_PLUGINS = File.join(HEATHROW_HOME, 'plugins')
16
+ HEATHROW_LOGS = File.join(HEATHROW_HOME, 'logs')
17
+
18
+ # Create directory structure
19
+ [HEATHROW_HOME, HEATHROW_SOURCES, HEATHROW_VIEWS, HEATHROW_ATTACHMENTS, HEATHROW_PLUGINS, HEATHROW_LOGS].each do |dir|
20
+ FileUtils.mkdir_p(dir)
21
+ end
22
+
23
+ # Load all components
24
+ require_relative 'heathrow/version'
25
+
26
+ # Core infrastructure (Phase 0)
27
+ require_relative 'heathrow/logger'
28
+ require_relative 'heathrow/event_bus'
29
+ require_relative 'heathrow/database'
30
+ require_relative 'heathrow/config'
31
+
32
+ # Plugin system
33
+ require_relative 'heathrow/plugin/base'
34
+ require_relative 'heathrow/plugin_manager'
35
+
36
+ # Models
37
+ require_relative 'heathrow/message'
38
+ require_relative 'heathrow/source'
39
+
40
+ # Sources
41
+ require_relative 'heathrow/sources/source_manager'
42
+
43
+ # Background polling
44
+ require_relative 'heathrow/poller'
45
+
46
+ # UI components
47
+ require_relative 'heathrow/ui/application'
48
+ require_relative 'heathrow/ui/source_wizard'
49
+ require_relative 'heathrow/ui/panes'
50
+ require_relative 'heathrow/ui/navigation'
51
+ require_relative 'heathrow/ui/views'
52
+
53
+ module Heathrow
54
+ class Error < StandardError; end
55
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: heathrow
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ platform: ruby
6
+ authors:
7
+ - Geir Isene
8
+ - Claude Code
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2026-03-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rcurses
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '5.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '5.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: sqlite3
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '1.4'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '1.4'
42
+ description: A unified TUI application for managing all your communication sources
43
+ in one place. Brings together emails, WhatsApp, Discord, Reddit, RSS feeds, and
44
+ more into a single, efficient terminal interface.
45
+ email:
46
+ - g@isene.com
47
+ executables:
48
+ - heathrow
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - ".gitignore"
53
+ - README.md
54
+ - bin/heathrow
55
+ - bin/heathrowd
56
+ - docs/ARCHITECTURE.md
57
+ - docs/DATABASE_SCHEMA.md
58
+ - docs/DEVELOPMENT_WORKFLOW.md
59
+ - docs/DISCORD_SETUP.md
60
+ - docs/GMAIL_OAUTH_SETUP.md
61
+ - docs/PLUGIN_SYSTEM.md
62
+ - docs/PROJECT_PLAN.md
63
+ - docs/README.md
64
+ - docs/REDDIT_SETUP.md
65
+ - docs/REPLY_FORWARD.md
66
+ - docs/WHATSAPP_TELEGRAM_SETUP.md
67
+ - heathrow.gemspec
68
+ - heathrowd.service
69
+ - img/heathrow.svg
70
+ - img/rss_threaded.png
71
+ - img/sources.png
72
+ - lib/heathrow.rb
73
+ - lib/heathrow/address_book.rb
74
+ - lib/heathrow/config.rb
75
+ - lib/heathrow/database.rb
76
+ - lib/heathrow/database_new.rb
77
+ - lib/heathrow/event_bus.rb
78
+ - lib/heathrow/logger.rb
79
+ - lib/heathrow/message.rb
80
+ - lib/heathrow/message_composer.rb
81
+ - lib/heathrow/message_organizer.rb
82
+ - lib/heathrow/migrations/001_initial_schema.rb
83
+ - lib/heathrow/notmuch.rb
84
+ - lib/heathrow/oauth2_smtp.rb
85
+ - lib/heathrow/plugin/base.rb
86
+ - lib/heathrow/plugin_manager.rb
87
+ - lib/heathrow/poller.rb
88
+ - lib/heathrow/smtp_sender.rb
89
+ - lib/heathrow/source.rb
90
+ - lib/heathrow/sources/base.rb
91
+ - lib/heathrow/sources/discord.rb
92
+ - lib/heathrow/sources/gmail.rb
93
+ - lib/heathrow/sources/imap.rb
94
+ - lib/heathrow/sources/instagram.rb
95
+ - lib/heathrow/sources/instagram_fetch.py
96
+ - lib/heathrow/sources/instagram_send.py
97
+ - lib/heathrow/sources/instagram_send_marionette.py
98
+ - lib/heathrow/sources/maildir.rb
99
+ - lib/heathrow/sources/messenger.rb
100
+ - lib/heathrow/sources/messenger_fetch.js
101
+ - lib/heathrow/sources/messenger_fetch_marionette.py
102
+ - lib/heathrow/sources/messenger_send.js
103
+ - lib/heathrow/sources/messenger_send.py
104
+ - lib/heathrow/sources/reddit.rb
105
+ - lib/heathrow/sources/rss.rb
106
+ - lib/heathrow/sources/slack.rb
107
+ - lib/heathrow/sources/source_manager.rb
108
+ - lib/heathrow/sources/telegram.rb
109
+ - lib/heathrow/sources/webpage.rb
110
+ - lib/heathrow/sources/weechat.rb
111
+ - lib/heathrow/sources/whatsapp.rb
112
+ - lib/heathrow/ui/application.rb
113
+ - lib/heathrow/ui/navigation.rb
114
+ - lib/heathrow/ui/panes.rb
115
+ - lib/heathrow/ui/source_wizard.rb
116
+ - lib/heathrow/ui/threaded_view.rb
117
+ - lib/heathrow/ui/views.rb
118
+ - lib/heathrow/version.rb
119
+ - lib/heathrow/wizards/discord_wizard.rb
120
+ - lib/heathrow/wizards/slack_wizard.rb
121
+ homepage: https://github.com/isene/heathrow
122
+ licenses:
123
+ - Unlicense
124
+ metadata:
125
+ homepage_uri: https://github.com/isene/heathrow
126
+ source_code_uri: https://github.com/isene/heathrow
127
+ changelog_uri: https://github.com/isene/heathrow/blob/master/CHANGELOG.md
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: 2.7.0
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubygems_version: 3.4.20
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Communication Hub In The Terminal
147
+ test_files: []