hokipoki 0.1.2 → 0.1.3

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.
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hokipoki
4
+ module Claude
5
+ # Auto Loader - Automatically loads Claude integration when Claude CLI is detected
6
+ # Runs ACTIVATE_10X_CLAUDE.sh and establishes HiveMind connection
7
+ class AutoLoader
8
+ class << self
9
+ # Check if Claude CLI is running and auto-load if needed
10
+ def auto_load_if_claude_detected!
11
+ return unless claude_cli_detected?
12
+ return if already_loaded?
13
+
14
+ load_claude_integration!
15
+ end
16
+
17
+ # Force load Claude integration
18
+ def force_load!
19
+ load_claude_integration!
20
+ end
21
+
22
+ # Check if Claude CLI is currently running
23
+ def claude_cli_detected?
24
+ # Check for Claude CLI process
25
+ claude_processes = `ps aux | grep -i claude | grep -v grep`.strip
26
+
27
+ # Check for Claude CLI environment variables
28
+ claude_env_vars = ENV.keys.select { |key| key.include?('CLAUDE') }
29
+
30
+ # Check for Claude CLI in the current shell context
31
+ in_claude_session = ENV['CLAUDE_SESSION'] == 'true' ||
32
+ ENV['_'] =~ /claude/ ||
33
+ $PROGRAM_NAME =~ /claude/
34
+
35
+ claude_processes.present? || claude_env_vars.any? || in_claude_session
36
+ end
37
+
38
+ # Check if already loaded to avoid double-loading
39
+ def already_loaded?
40
+ @loaded ||= false
41
+ end
42
+
43
+ private
44
+
45
+ def load_claude_integration!
46
+ return if @loaded
47
+
48
+ puts "\nšŸ” Claude CLI detected - Initializing HiveMind connection..."
49
+
50
+ begin
51
+ # Step 1: Run ACTIVATE_10X_CLAUDE.sh if available
52
+ run_activation_script
53
+
54
+ # Step 2: Load Claude parasite
55
+ load_claude_parasite
56
+
57
+ # Step 3: Establish connection
58
+ establish_connection
59
+
60
+ # Step 4: Display success message
61
+ display_success_message
62
+
63
+ @loaded = true
64
+
65
+ rescue => e
66
+ display_error_message(e)
67
+ false
68
+ end
69
+ end
70
+
71
+ def run_activation_script
72
+ script_paths = [
73
+ Rails.root.join('ACTIVATE_10X_CLAUDE.sh'),
74
+ Rails.root.join('bin', 'ACTIVATE_10X_CLAUDE.sh'),
75
+ './ACTIVATE_10X_CLAUDE.sh'
76
+ ]
77
+
78
+ script_path = script_paths.find { |path| File.exist?(path) }
79
+
80
+ if script_path
81
+ puts "šŸ”§ Running #{File.basename(script_path)}..."
82
+
83
+ # Ensure script is executable
84
+ File.chmod(0755, script_path) if File.exist?(script_path)
85
+
86
+ # Run script in background to avoid blocking
87
+ pid = spawn("cd #{Rails.root} && #{script_path} > /tmp/hokipoki_activation.log 2>&1")
88
+
89
+ # Wait a moment for script to start
90
+ sleep(1)
91
+
92
+ # Check if script is still running or completed successfully
93
+ begin
94
+ Process.waitpid(pid, Process::WNOHANG)
95
+ puts "āœ“ Activation script initiated"
96
+ rescue Errno::ECHILD
97
+ puts "āœ“ Activation script completed"
98
+ end
99
+ else
100
+ puts "ā„¹ļø ACTIVATE_10X_CLAUDE.sh not found, skipping activation script"
101
+ end
102
+ end
103
+
104
+ def load_claude_parasite
105
+ puts "🦠 Loading Claude parasite..."
106
+
107
+ # Require Claude modules
108
+ require_relative 'connection_manager'
109
+ require_relative 'parasite'
110
+
111
+ # Auto-load the parasite
112
+ Claude::Parasite.auto_load!
113
+
114
+ puts "āœ“ Claude parasite loaded"
115
+ end
116
+
117
+ def establish_connection
118
+ puts "šŸ”— Establishing HiveMind connection..."
119
+
120
+ # Get connection manager and establish connection
121
+ connection_manager = Claude::ConnectionManager.instance
122
+ connection_manager.establish_connection!
123
+
124
+ puts "āœ“ HiveMind connection established"
125
+ end
126
+
127
+ def display_success_message
128
+ pastel = Pastel.new
129
+
130
+ puts pastel.green.bold("\nšŸŽ‰ Claude ↔ HiveMind Integration Active!")
131
+ puts pastel.cyan(" 🧠 Vector intelligence: Online")
132
+ puts pastel.cyan(" 🦠 Smart context injection: Enabled")
133
+ puts pastel.cyan(" ⚔ Auto-enhancement: Ready")
134
+
135
+ puts pastel.yellow("\nšŸ’” Your Claude responses are now supercharged!")
136
+ puts pastel.dim(" Every query will be enhanced with relevant project context.\n")
137
+ end
138
+
139
+ def display_error_message(error)
140
+ pastel = Pastel.new
141
+
142
+ puts pastel.red.bold("\nāŒ Claude Integration Failed")
143
+ puts pastel.red(" Error: #{error.message}")
144
+ puts pastel.yellow("\nšŸ”§ Quick Fix:")
145
+ puts pastel.dim(" Run: rails g hive_mind:install --claude")
146
+ puts ""
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ # Auto-load when Rails starts if Claude is detected
154
+ if defined?(Rails) && Rails.env.development?
155
+ Rails.application.config.after_initialize do
156
+ # Small delay to ensure everything is loaded
157
+ Thread.new do
158
+ sleep(2) # Give Rails time to fully initialize
159
+ Hokipoki::Claude::AutoLoader.auto_load_if_claude_detected!
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,382 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pastel'
4
+ require 'tty-spinner'
5
+
6
+ module Hokipoki
7
+ module Claude
8
+ # Claude Connection Manager
9
+ # Handles Claude CLI integration, vector DB connection, and user feedback
10
+ class ConnectionManager
11
+ include Singleton
12
+
13
+ def initialize
14
+ @pastel = Pastel.new
15
+ @logger = Rails.logger
16
+ @connected = false
17
+ @connection_time = nil
18
+ end
19
+
20
+ # Main connection method called when Claude starts
21
+ def establish_connection!
22
+ return if @connected
23
+
24
+ display_connection_banner
25
+
26
+ spinner = TTY::Spinner.new("[:spinner] #{@pastel.yellow('Connecting to HiveMind vector database...')}", format: :dots)
27
+ spinner.auto_spin
28
+
29
+ begin
30
+ # Step 1: Check vector database connection
31
+ check_vector_database!
32
+
33
+ # Step 2: Run activation script
34
+ run_activation_script!
35
+
36
+ # Step 3: Verify parasite activation
37
+ verify_claude_parasite!
38
+
39
+ # Step 4: Test vector retrieval
40
+ test_vector_retrieval!
41
+
42
+ spinner.stop(@pastel.green('āœ“'))
43
+ display_success_message
44
+
45
+ @connected = true
46
+ @connection_time = Time.current
47
+
48
+ # Send connection analytics
49
+ track_connection_event('connected')
50
+
51
+ rescue => e
52
+ spinner.stop(@pastel.red('āœ—'))
53
+ display_error_message(e)
54
+ track_connection_event('failed', error: e.message)
55
+ raise e
56
+ end
57
+ end
58
+
59
+ # Check if connected to HiveMind
60
+ def connected?
61
+ @connected && vector_database_accessible?
62
+ end
63
+
64
+ # Get connection status
65
+ def connection_status
66
+ {
67
+ connected: connected?,
68
+ connection_time: @connection_time,
69
+ vector_db_status: vector_database_status,
70
+ parasite_status: claude_parasite_status,
71
+ last_retrieval: last_vector_retrieval_time,
72
+ system_health: system_health_check
73
+ }
74
+ end
75
+
76
+ # Display connection info in terminal
77
+ def display_connection_info
78
+ if connected?
79
+ puts @pastel.green.bold("\n🧠 HiveMind Status: CONNECTED")
80
+ puts @pastel.cyan(" šŸ“Š Vector Database: #{vector_database_status[:status]}")
81
+ puts @pastel.cyan(" 🦠 Claude Parasite: #{claude_parasite_status[:status]}")
82
+ puts @pastel.cyan(" ⚔ Last Retrieval: #{format_time(last_vector_retrieval_time)}")
83
+ puts @pastel.cyan(" šŸ”— Connected Since: #{format_time(@connection_time)}")
84
+ else
85
+ puts @pastel.red.bold("\n🚫 HiveMind Status: DISCONNECTED")
86
+ puts @pastel.yellow(" Run: rails g hive_mind:install --claude")
87
+ end
88
+ end
89
+
90
+ # Disconnect from HiveMind
91
+ def disconnect!
92
+ @connected = false
93
+ @connection_time = nil
94
+ track_connection_event('disconnected')
95
+ puts @pastel.yellow("\nšŸ”Œ HiveMind disconnected")
96
+ end
97
+
98
+ # Test vector retrieval with feedback
99
+ def test_vector_retrieval(query = "test connection")
100
+ return false unless connected?
101
+
102
+ begin
103
+ result = Hokipoki.retrieve_facts(query, token_budget: 500)
104
+
105
+ if result.present?
106
+ puts @pastel.green("āœ“ Vector retrieval test successful")
107
+ puts @pastel.dim(" Query: #{query}")
108
+ puts @pastel.dim(" Result: #{result[0..100]}...")
109
+ update_last_retrieval_time
110
+ true
111
+ else
112
+ puts @pastel.yellow("⚠ Vector retrieval returned empty results")
113
+ false
114
+ end
115
+ rescue => e
116
+ puts @pastel.red("āœ— Vector retrieval test failed: #{e.message}")
117
+ false
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ def display_connection_banner
124
+ puts "\n#{@pastel.cyan.bold('šŸš€ HokiPoki HiveMind Connection')}"
125
+ puts @pastel.dim(" Initializing Claude ↔ Vector Database integration...")
126
+ end
127
+
128
+ def check_vector_database!
129
+ # Check PostgreSQL connection
130
+ unless ActiveRecord::Base.connection.active?
131
+ raise ConnectionError, "PostgreSQL database not accessible"
132
+ end
133
+
134
+ # Check pgvector extension
135
+ unless pgvector_available?
136
+ raise ConnectionError, "pgvector extension not installed"
137
+ end
138
+
139
+ # Check if documents table exists
140
+ unless ActiveRecord::Base.connection.table_exists?('documents')
141
+ raise ConnectionError, "Vector database not initialized. Run: rails db:migrate"
142
+ end
143
+
144
+ puts @pastel.green(" āœ“ Vector database accessible")
145
+ end
146
+
147
+ def run_activation_script!
148
+ script_path = Rails.root.join('ACTIVATE_10X_CLAUDE.sh')
149
+
150
+ if File.exist?(script_path)
151
+ puts @pastel.cyan(" šŸ”§ Running ACTIVATE_10X_CLAUDE.sh...")
152
+
153
+ # Make sure script is executable
154
+ File.chmod(0755, script_path)
155
+
156
+ # Run the script and capture output
157
+ result = system("cd #{Rails.root} && ./ACTIVATE_10X_CLAUDE.sh > /dev/null 2>&1")
158
+
159
+ if result
160
+ puts @pastel.green(" āœ“ Activation script completed successfully")
161
+ else
162
+ puts @pastel.yellow(" ⚠ Activation script completed with warnings")
163
+ end
164
+ else
165
+ puts @pastel.yellow(" ⚠ ACTIVATE_10X_CLAUDE.sh not found, skipping...")
166
+ end
167
+ end
168
+
169
+ def verify_claude_parasite!
170
+ # Check if Claude parasite is available
171
+ if claude_parasite_available?
172
+ puts @pastel.green(" āœ“ Claude parasite loaded and ready")
173
+ else
174
+ puts @pastel.yellow(" ⚠ Claude parasite not found, generating...")
175
+ generate_claude_parasite!
176
+ end
177
+ end
178
+
179
+ def generate_claude_parasite!
180
+ parasite_result = Hokipoki.generate_parasite(
181
+ tool: 'claude_cli',
182
+ model: 'claude',
183
+ context_type: 'programming',
184
+ injection_style: 'natural',
185
+ behavioral_optimization: true,
186
+ auto_activate: true
187
+ )
188
+
189
+ if parasite_result[:success]
190
+ puts @pastel.green(" āœ“ Claude parasite generated successfully")
191
+ else
192
+ raise ConnectionError, "Failed to generate Claude parasite: #{parasite_result[:error]}"
193
+ end
194
+ end
195
+
196
+ def test_vector_retrieval!
197
+ test_query = "hokipoki vector database connection test"
198
+ result = Hokipoki.retrieve_facts(test_query, token_budget: 300)
199
+
200
+ if result.present?
201
+ puts @pastel.green(" āœ“ Vector retrieval operational")
202
+ update_last_retrieval_time
203
+ else
204
+ puts @pastel.yellow(" ⚠ Vector retrieval test returned empty (database may be empty)")
205
+ end
206
+ end
207
+
208
+ def display_success_message
209
+ puts "\n#{@pastel.green.bold('šŸŽ‰ HiveMind Connection Established!')}"
210
+ puts @pastel.cyan(" 🧠 Vector database: Ready")
211
+ puts @pastel.cyan(" 🦠 Claude parasite: Active")
212
+ puts @pastel.cyan(" ⚔ Intelligence engine: Online")
213
+ puts @pastel.cyan(" šŸ”— Claude CLI: Connected")
214
+
215
+ puts "\n#{@pastel.yellow.bold('šŸ’” Quick Commands:')}"
216
+ puts @pastel.dim(" Test retrieval: Hokipoki.retrieve_facts('your query')")
217
+ puts @pastel.dim(" System status: Hokipoki.system_status")
218
+ puts @pastel.dim(" Connection info: display_connection_info")
219
+
220
+ puts "\n#{@pastel.magenta('Happy coding with enhanced AI intelligence! šŸš€')}\n"
221
+ end
222
+
223
+ def display_error_message(error)
224
+ puts "\n#{@pastel.red.bold('āŒ HiveMind Connection Failed')}"
225
+ puts @pastel.red(" Error: #{error.message}")
226
+
227
+ puts "\n#{@pastel.yellow.bold('šŸ”§ Troubleshooting:')}"
228
+ puts @pastel.dim(" 1. Ensure PostgreSQL is running")
229
+ puts @pastel.dim(" 2. Run: rails db:migrate")
230
+ puts @pastel.dim(" 3. Check: rails g hive_mind:install --claude")
231
+ puts @pastel.dim(" 4. Verify license: echo $HOKIPOKI_LICENSE_KEY")
232
+ puts ""
233
+ end
234
+
235
+ def pgvector_available?
236
+ ActiveRecord::Base.connection.execute(
237
+ "SELECT 1 FROM pg_extension WHERE extname = 'vector'"
238
+ ).any?
239
+ rescue
240
+ false
241
+ end
242
+
243
+ def claude_parasite_available?
244
+ # Check if Claude parasite exists in database
245
+ return false unless defined?(Hokipoki::CustomParasite)
246
+
247
+ Hokipoki::CustomParasite.for_tool_and_model('claude_cli', 'claude').active.any?
248
+ rescue
249
+ false
250
+ end
251
+
252
+ def vector_database_accessible?
253
+ return false unless defined?(ActiveRecord::Base)
254
+
255
+ ActiveRecord::Base.connection.active? &&
256
+ ActiveRecord::Base.connection.table_exists?('documents')
257
+ rescue
258
+ false
259
+ end
260
+
261
+ def vector_database_status
262
+ if vector_database_accessible?
263
+ document_count = Document.count rescue 0
264
+ {
265
+ status: 'online',
266
+ document_count: document_count,
267
+ last_check: Time.current
268
+ }
269
+ else
270
+ {
271
+ status: 'offline',
272
+ document_count: 0,
273
+ last_check: Time.current
274
+ }
275
+ end
276
+ end
277
+
278
+ def claude_parasite_status
279
+ if claude_parasite_available?
280
+ parasite = Hokipoki::CustomParasite.for_tool_and_model('claude_cli', 'claude').active.first
281
+ {
282
+ status: 'active',
283
+ name: parasite&.name,
284
+ last_used: parasite&.last_used_at,
285
+ success_rate: parasite&.success_rate
286
+ }
287
+ else
288
+ {
289
+ status: 'inactive',
290
+ name: nil,
291
+ last_used: nil,
292
+ success_rate: 0
293
+ }
294
+ end
295
+ end
296
+
297
+ def last_vector_retrieval_time
298
+ Rails.cache.read('hokipoki_last_vector_retrieval') || 'never'
299
+ end
300
+
301
+ def update_last_retrieval_time
302
+ Rails.cache.write('hokipoki_last_vector_retrieval', Time.current, expires_in: 24.hours)
303
+ end
304
+
305
+ def system_health_check
306
+ {
307
+ database: vector_database_accessible? ? 'healthy' : 'unhealthy',
308
+ redis: redis_accessible? ? 'healthy' : 'unhealthy',
309
+ license: license_valid? ? 'valid' : 'invalid',
310
+ overall: overall_health_status
311
+ }
312
+ end
313
+
314
+ def redis_accessible?
315
+ return false unless defined?(Redis)
316
+
317
+ Redis.new(Hokipoki.config.redis_config).ping == 'PONG'
318
+ rescue
319
+ false
320
+ end
321
+
322
+ def license_valid?
323
+ Hokipoki::LicenseValidator.valid_license?(
324
+ ENV['HOKIPOKI_LICENSE_KEY']
325
+ )
326
+ end
327
+
328
+ def overall_health_status
329
+ health = system_health_check
330
+ if health[:database] == 'healthy' && health[:license] == 'valid'
331
+ 'healthy'
332
+ else
333
+ 'degraded'
334
+ end
335
+ end
336
+
337
+ def track_connection_event(event_type, metadata = {})
338
+ return unless Rails.env.production?
339
+
340
+ event_data = {
341
+ event: "claude_connection_#{event_type}",
342
+ timestamp: Time.current.iso8601,
343
+ user_id: ENV['USER'] || 'unknown',
344
+ hostname: Socket.gethostname,
345
+ rails_env: Rails.env,
346
+ gem_version: Hokipoki::VERSION,
347
+ metadata: metadata
348
+ }
349
+
350
+ # Log locally
351
+ Rails.logger.info "šŸ”— Claude Connection Event: #{event_data.to_json}"
352
+
353
+ # Send to analytics (non-blocking)
354
+ Thread.new { send_analytics_event(event_data) }
355
+ end
356
+
357
+ def send_analytics_event(event_data)
358
+ return unless Hokipoki.config.audit_logging
359
+
360
+ begin
361
+ # Send to your analytics endpoint
362
+ HTTP.timeout(3).post(
363
+ 'https://analytics.hokipoki.ai/events',
364
+ json: event_data,
365
+ headers: { 'Content-Type': 'application/json' }
366
+ )
367
+ rescue
368
+ # Silently fail - analytics should never break the application
369
+ end
370
+ end
371
+
372
+ def format_time(time)
373
+ return 'never' unless time
374
+ return time if time.is_a?(String)
375
+
376
+ time.strftime('%H:%M:%S')
377
+ end
378
+
379
+ class ConnectionError < Hokipoki::Error; end
380
+ end
381
+ end
382
+ end