hokipoki 0.3.3 ā 0.5.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 +4 -4
- data/lib/generators/hive_mind/install_generator.rb +18 -2
- data/lib/generators/hive_mind/templates/hokipoki_claude.rb +45 -0
- data/lib/generators/hokipoki/attach_parasite_generator.rb +355 -0
- data/lib/generators/hokipoki/install_generator.rb +515 -0
- data/lib/generators/hokipoki/scan_project_generator.rb +279 -0
- data/lib/generators/parasite/install_generator.rb +458 -0
- data/lib/hokipoki/atomic_fact_extractor.rb +524 -0
- data/lib/hokipoki/claude/parasite.rb +62 -10
- data/lib/hokipoki/claude/thought_interceptor.rb +385 -0
- data/lib/hokipoki/claude_auto_loader.rb +28 -11
- data/lib/hokipoki/feedback/ascii_banners.rb +1 -1
- data/lib/hokipoki/template_store.rb +425 -0
- data/lib/hokipoki/vector_engine.rb +525 -0
- data/lib/hokipoki/version.rb +1 -1
- data/lib/hokipoki.rb +260 -6
- metadata +80 -1
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hokipoki
|
|
4
|
+
# Atomic Fact Extractor - Breaks content into searchable atomic facts
|
|
5
|
+
# Implements PATTERN-002: Atomic fact extraction for surgical precision
|
|
6
|
+
class AtomicFactExtractor
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@extraction_stats = {
|
|
10
|
+
total_extractions: 0,
|
|
11
|
+
facts_extracted: 0,
|
|
12
|
+
extraction_time: 0.0
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Extract atomic facts from content with visible progress
|
|
17
|
+
def extract_facts(content, source_file: nil, metadata: {})
|
|
18
|
+
start_time = Time.current
|
|
19
|
+
$stdout.puts "š¬ ATOMIC EXTRACTION: #{File.basename(source_file || 'content')}"
|
|
20
|
+
|
|
21
|
+
@extraction_stats[:total_extractions] += 1
|
|
22
|
+
|
|
23
|
+
facts = []
|
|
24
|
+
|
|
25
|
+
# Extract different types of atomic facts
|
|
26
|
+
facts += extract_css_facts(content)
|
|
27
|
+
facts += extract_code_facts(content)
|
|
28
|
+
facts += extract_command_facts(content)
|
|
29
|
+
facts += extract_documentation_facts(content)
|
|
30
|
+
facts += extract_definition_facts(content)
|
|
31
|
+
facts += extract_configuration_facts(content)
|
|
32
|
+
facts += extract_test_facts(content)
|
|
33
|
+
facts += extract_error_facts(content)
|
|
34
|
+
|
|
35
|
+
# Enhance facts with metadata
|
|
36
|
+
enhanced_facts = facts.map do |fact|
|
|
37
|
+
fact.merge(
|
|
38
|
+
source_file: source_file,
|
|
39
|
+
extracted_at: Time.current.iso8601,
|
|
40
|
+
extraction_metadata: metadata
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Calculate extraction time
|
|
45
|
+
extraction_time = Time.current - start_time
|
|
46
|
+
@extraction_stats[:extraction_time] += extraction_time
|
|
47
|
+
@extraction_stats[:facts_extracted] += enhanced_facts.length
|
|
48
|
+
|
|
49
|
+
$stdout.puts " ā
Extracted #{enhanced_facts.length} facts in #{(extraction_time * 1000).round(1)}ms"
|
|
50
|
+
|
|
51
|
+
enhanced_facts
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Get extraction statistics
|
|
55
|
+
def statistics
|
|
56
|
+
avg_extraction_time = @extraction_stats[:total_extractions] > 0 ?
|
|
57
|
+
(@extraction_stats[:extraction_time] / @extraction_stats[:total_extractions] * 1000).round(2) : 0
|
|
58
|
+
|
|
59
|
+
avg_facts_per_extraction = @extraction_stats[:total_extractions] > 0 ?
|
|
60
|
+
(@extraction_stats[:facts_extracted].to_f / @extraction_stats[:total_extractions]).round(1) : 0
|
|
61
|
+
|
|
62
|
+
{
|
|
63
|
+
total_extractions: @extraction_stats[:total_extractions],
|
|
64
|
+
total_facts: @extraction_stats[:facts_extracted],
|
|
65
|
+
avg_extraction_time_ms: avg_extraction_time,
|
|
66
|
+
avg_facts_per_file: avg_facts_per_extraction
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
# CSS Facts: Extract CSS classes, properties, and styling information
|
|
73
|
+
def extract_css_facts(content)
|
|
74
|
+
facts = []
|
|
75
|
+
|
|
76
|
+
# CSS classes from HTML/ERB
|
|
77
|
+
content.scan(/class=["\']([^"\']+)["\']/).each do |match|
|
|
78
|
+
classes = match[0].split(/\s+/)
|
|
79
|
+
classes.each do |css_class|
|
|
80
|
+
next if css_class.empty?
|
|
81
|
+
|
|
82
|
+
facts << {
|
|
83
|
+
type: :css_class,
|
|
84
|
+
content: css_class,
|
|
85
|
+
searchable_text: "css class #{css_class}",
|
|
86
|
+
keywords: [css_class, 'css', 'styling'],
|
|
87
|
+
weight: 1.0,
|
|
88
|
+
context: "CSS class for styling"
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# CSS properties
|
|
94
|
+
content.scan(/([a-z-]+)\s*:\s*([^;]+);?/).each do |property, value|
|
|
95
|
+
next unless css_property?(property)
|
|
96
|
+
|
|
97
|
+
facts << {
|
|
98
|
+
type: :css_property,
|
|
99
|
+
content: "#{property}: #{value.strip}",
|
|
100
|
+
searchable_text: "css property #{property} #{value}",
|
|
101
|
+
keywords: [property, value.strip, 'css', 'styling'],
|
|
102
|
+
weight: 0.8,
|
|
103
|
+
context: "CSS property declaration"
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Tailwind CSS classes
|
|
108
|
+
content.scan(/\b(flex|grid|text-\w+|bg-\w+|p-\d+|m-\d+|w-\w+|h-\w+)\b/).each do |match|
|
|
109
|
+
tailwind_class = match[0]
|
|
110
|
+
|
|
111
|
+
facts << {
|
|
112
|
+
type: :tailwind_class,
|
|
113
|
+
content: tailwind_class,
|
|
114
|
+
searchable_text: "tailwind css #{tailwind_class}",
|
|
115
|
+
keywords: [tailwind_class, 'tailwind', 'css', 'utility'],
|
|
116
|
+
weight: 1.2,
|
|
117
|
+
context: "Tailwind CSS utility class"
|
|
118
|
+
}
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
facts
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Code Facts: Extract Ruby methods, classes, modules, and JavaScript functions
|
|
125
|
+
def extract_code_facts(content)
|
|
126
|
+
facts = []
|
|
127
|
+
|
|
128
|
+
# Ruby methods
|
|
129
|
+
content.scan(/def\s+(\w+)(?:\(([^)]*)\))?/) do |method_name, params|
|
|
130
|
+
facts << {
|
|
131
|
+
type: :ruby_method,
|
|
132
|
+
content: "def #{method_name}#{params ? "(#{params})" : ''}",
|
|
133
|
+
searchable_text: "ruby method #{method_name} #{params}",
|
|
134
|
+
keywords: [method_name, 'method', 'ruby', 'function'],
|
|
135
|
+
weight: 1.5,
|
|
136
|
+
context: "Ruby method definition"
|
|
137
|
+
}
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Ruby classes
|
|
141
|
+
content.scan(/class\s+(\w+)(?:\s*<\s*(\w+))?/) do |class_name, parent_class|
|
|
142
|
+
inheritance_info = parent_class ? " < #{parent_class}" : ''
|
|
143
|
+
|
|
144
|
+
facts << {
|
|
145
|
+
type: :ruby_class,
|
|
146
|
+
content: "class #{class_name}#{inheritance_info}",
|
|
147
|
+
searchable_text: "ruby class #{class_name} #{parent_class}",
|
|
148
|
+
keywords: [class_name, parent_class, 'class', 'ruby'].compact,
|
|
149
|
+
weight: 2.0,
|
|
150
|
+
context: "Ruby class definition"
|
|
151
|
+
}
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Ruby modules
|
|
155
|
+
content.scan(/module\s+(\w+)/) do |module_name|
|
|
156
|
+
facts << {
|
|
157
|
+
type: :ruby_module,
|
|
158
|
+
content: "module #{module_name[0]}",
|
|
159
|
+
searchable_text: "ruby module #{module_name[0]}",
|
|
160
|
+
keywords: [module_name[0], 'module', 'ruby'],
|
|
161
|
+
weight: 1.8,
|
|
162
|
+
context: "Ruby module definition"
|
|
163
|
+
}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Constants
|
|
167
|
+
content.scan(/([A-Z][A-Z_]+)\s*=\s*([^\n]+)/) do |constant_name, value|
|
|
168
|
+
facts << {
|
|
169
|
+
type: :constant,
|
|
170
|
+
content: "#{constant_name} = #{value.strip}",
|
|
171
|
+
searchable_text: "constant #{constant_name} #{value}",
|
|
172
|
+
keywords: [constant_name, 'constant', 'ruby'],
|
|
173
|
+
weight: 1.3,
|
|
174
|
+
context: "Ruby constant definition"
|
|
175
|
+
}
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# JavaScript functions
|
|
179
|
+
content.scan(/(?:function\s+(\w+)|const\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>)/) do |func_name, arrow_func|
|
|
180
|
+
function_name = func_name || arrow_func
|
|
181
|
+
|
|
182
|
+
facts << {
|
|
183
|
+
type: :javascript_function,
|
|
184
|
+
content: "function #{function_name}",
|
|
185
|
+
searchable_text: "javascript function #{function_name}",
|
|
186
|
+
keywords: [function_name, 'function', 'javascript', 'js'],
|
|
187
|
+
weight: 1.4,
|
|
188
|
+
context: "JavaScript function definition"
|
|
189
|
+
}
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Rails associations
|
|
193
|
+
content.scan(/(belongs_to|has_many|has_one|has_and_belongs_to_many)\s+:(\w+)/) do |association_type, association_name|
|
|
194
|
+
facts << {
|
|
195
|
+
type: :rails_association,
|
|
196
|
+
content: "#{association_type} :#{association_name}",
|
|
197
|
+
searchable_text: "rails association #{association_type} #{association_name}",
|
|
198
|
+
keywords: [association_name, association_type, 'association', 'rails', 'activerecord'],
|
|
199
|
+
weight: 1.6,
|
|
200
|
+
context: "Rails ActiveRecord association"
|
|
201
|
+
}
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Rails validations
|
|
205
|
+
content.scan(/validates?\s+:(\w+),?\s*([^\n]+)/) do |field, validation_rules|
|
|
206
|
+
facts << {
|
|
207
|
+
type: :rails_validation,
|
|
208
|
+
content: "validates :#{field}, #{validation_rules.strip}",
|
|
209
|
+
searchable_text: "rails validation #{field} #{validation_rules}",
|
|
210
|
+
keywords: [field, 'validation', 'rails', 'activerecord'],
|
|
211
|
+
weight: 1.4,
|
|
212
|
+
context: "Rails ActiveRecord validation"
|
|
213
|
+
}
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
facts
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Command Facts: Extract terminal commands and shell scripts
|
|
220
|
+
def extract_command_facts(content)
|
|
221
|
+
facts = []
|
|
222
|
+
|
|
223
|
+
# Shell commands (starting with $ or #)
|
|
224
|
+
content.scan(/^[\s]*[$#]\s*(.+)$/) do |command|
|
|
225
|
+
command_text = command[0].strip
|
|
226
|
+
|
|
227
|
+
facts << {
|
|
228
|
+
type: :shell_command,
|
|
229
|
+
content: command_text,
|
|
230
|
+
searchable_text: "shell command #{command_text}",
|
|
231
|
+
keywords: extract_command_keywords(command_text),
|
|
232
|
+
weight: 1.3,
|
|
233
|
+
context: "Shell/terminal command"
|
|
234
|
+
}
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Rails commands
|
|
238
|
+
content.scan(/rails\s+(generate|g|console|c|server|s|db:migrate|db:seed|new|destroy)\s*([^\n]*)/) do |rails_command, args|
|
|
239
|
+
full_command = "rails #{rails_command} #{args}".strip
|
|
240
|
+
|
|
241
|
+
facts << {
|
|
242
|
+
type: :rails_command,
|
|
243
|
+
content: full_command,
|
|
244
|
+
searchable_text: "rails command #{rails_command} #{args}",
|
|
245
|
+
keywords: [rails_command, args, 'rails', 'command'].compact.reject(&:empty?),
|
|
246
|
+
weight: 1.5,
|
|
247
|
+
context: "Rails generator/command"
|
|
248
|
+
}
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Bundle commands
|
|
252
|
+
content.scan(/bundle\s+(install|exec|update|add|remove)\s*([^\n]*)/) do |bundle_command, args|
|
|
253
|
+
full_command = "bundle #{bundle_command} #{args}".strip
|
|
254
|
+
|
|
255
|
+
facts << {
|
|
256
|
+
type: :bundle_command,
|
|
257
|
+
content: full_command,
|
|
258
|
+
searchable_text: "bundle command #{bundle_command} #{args}",
|
|
259
|
+
keywords: [bundle_command, args, 'bundle', 'gem'].compact.reject(&:empty?),
|
|
260
|
+
weight: 1.2,
|
|
261
|
+
context: "Bundler command"
|
|
262
|
+
}
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
facts
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Documentation Facts: Extract markdown headers, lists, and structured content
|
|
269
|
+
def extract_documentation_facts(content)
|
|
270
|
+
facts = []
|
|
271
|
+
|
|
272
|
+
# Markdown headers
|
|
273
|
+
content.scan(/^(#{1,6})\s*(.+)$/) do |hash_level, header_text|
|
|
274
|
+
level = hash_level.length
|
|
275
|
+
|
|
276
|
+
facts << {
|
|
277
|
+
type: :documentation_header,
|
|
278
|
+
content: header_text.strip,
|
|
279
|
+
searchable_text: "documentation header #{header_text}",
|
|
280
|
+
keywords: extract_documentation_keywords(header_text),
|
|
281
|
+
weight: 2.0 - (level * 0.2), # Higher weight for top-level headers
|
|
282
|
+
context: "Documentation heading level #{level}"
|
|
283
|
+
}
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Code blocks with language
|
|
287
|
+
content.scan(/```(\w+)?\n(.*?)\n```/m) do |language, code_content|
|
|
288
|
+
language ||= 'unknown'
|
|
289
|
+
|
|
290
|
+
facts << {
|
|
291
|
+
type: :code_block,
|
|
292
|
+
content: code_content.strip[0..200], # Limit length
|
|
293
|
+
searchable_text: "code block #{language} #{code_content}",
|
|
294
|
+
keywords: [language, 'code', 'example'],
|
|
295
|
+
weight: 1.4,
|
|
296
|
+
context: "#{language} code example"
|
|
297
|
+
}
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# List items
|
|
301
|
+
content.scan(/^[-*]\s*(.+)$/) do |list_item|
|
|
302
|
+
item_text = list_item[0].strip
|
|
303
|
+
|
|
304
|
+
facts << {
|
|
305
|
+
type: :list_item,
|
|
306
|
+
content: item_text,
|
|
307
|
+
searchable_text: "list item #{item_text}",
|
|
308
|
+
keywords: extract_documentation_keywords(item_text),
|
|
309
|
+
weight: 0.8,
|
|
310
|
+
context: "Documentation list item"
|
|
311
|
+
}
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
facts
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Definition Facts: Extract definitions and explanations
|
|
318
|
+
def extract_definition_facts(content)
|
|
319
|
+
facts = []
|
|
320
|
+
|
|
321
|
+
# Key-value definitions (term: definition)
|
|
322
|
+
content.scan(/^([A-Z][^:]+):\s*(.+)$/) do |term, definition|
|
|
323
|
+
facts << {
|
|
324
|
+
type: :definition,
|
|
325
|
+
content: "#{term}: #{definition.strip}",
|
|
326
|
+
searchable_text: "definition #{term} #{definition}",
|
|
327
|
+
keywords: [term.downcase, 'definition'],
|
|
328
|
+
weight: 1.6,
|
|
329
|
+
context: "Term definition"
|
|
330
|
+
}
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Ruby comments that explain things
|
|
334
|
+
content.scan(/#\s*(.+)$/) do |comment|
|
|
335
|
+
comment_text = comment[0].strip
|
|
336
|
+
next if comment_text.length < 10 # Skip short comments
|
|
337
|
+
|
|
338
|
+
facts << {
|
|
339
|
+
type: :code_comment,
|
|
340
|
+
content: comment_text,
|
|
341
|
+
searchable_text: "comment #{comment_text}",
|
|
342
|
+
keywords: extract_documentation_keywords(comment_text),
|
|
343
|
+
weight: 0.6,
|
|
344
|
+
context: "Code comment explanation"
|
|
345
|
+
}
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
facts
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Configuration Facts: Extract config files and environment settings
|
|
352
|
+
def extract_configuration_facts(content)
|
|
353
|
+
facts = []
|
|
354
|
+
|
|
355
|
+
# YAML config keys
|
|
356
|
+
content.scan(/^(\s*)([a-z_]+):\s*(.+)$/) do |indent, key, value|
|
|
357
|
+
next if key.length < 2
|
|
358
|
+
|
|
359
|
+
facts << {
|
|
360
|
+
type: :config_setting,
|
|
361
|
+
content: "#{key}: #{value.strip}",
|
|
362
|
+
searchable_text: "config #{key} #{value}",
|
|
363
|
+
keywords: [key, 'config', 'configuration'],
|
|
364
|
+
weight: 1.1,
|
|
365
|
+
context: "Configuration setting"
|
|
366
|
+
}
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# Environment variables
|
|
370
|
+
content.scan(/ENV\[['"]([^'"]+)['"]\]/) do |env_var|
|
|
371
|
+
facts << {
|
|
372
|
+
type: :environment_variable,
|
|
373
|
+
content: "ENV['#{env_var[0]}']",
|
|
374
|
+
searchable_text: "environment variable #{env_var[0]}",
|
|
375
|
+
keywords: [env_var[0], 'env', 'environment', 'config'],
|
|
376
|
+
weight: 1.3,
|
|
377
|
+
context: "Environment variable usage"
|
|
378
|
+
}
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
facts
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Test Facts: Extract test descriptions and examples
|
|
385
|
+
def extract_test_facts(content)
|
|
386
|
+
facts = []
|
|
387
|
+
|
|
388
|
+
# RSpec test descriptions
|
|
389
|
+
content.scan(/(?:describe|context|it)\s+['"]([^'"]+)['"]/) do |description|
|
|
390
|
+
facts << {
|
|
391
|
+
type: :test_description,
|
|
392
|
+
content: description[0],
|
|
393
|
+
searchable_text: "test #{description[0]}",
|
|
394
|
+
keywords: extract_test_keywords(description[0]),
|
|
395
|
+
weight: 1.2,
|
|
396
|
+
context: "Test specification"
|
|
397
|
+
}
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# Test helper methods
|
|
401
|
+
content.scan(/def\s+(setup|teardown|before|after|it_behaves_like)\s*(?:\(([^)]*)\))?/) do |helper_name, params|
|
|
402
|
+
facts << {
|
|
403
|
+
type: :test_helper,
|
|
404
|
+
content: "#{helper_name}#{params ? "(#{params})" : ''}",
|
|
405
|
+
searchable_text: "test helper #{helper_name}",
|
|
406
|
+
keywords: [helper_name, 'test', 'helper'],
|
|
407
|
+
weight: 1.0,
|
|
408
|
+
context: "Test helper method"
|
|
409
|
+
}
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
facts
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# Error Facts: Extract error handling and exception information
|
|
416
|
+
def extract_error_facts(content)
|
|
417
|
+
facts = []
|
|
418
|
+
|
|
419
|
+
# Ruby rescue blocks
|
|
420
|
+
content.scan(/rescue\s+([A-Z]\w+(?:::[A-Z]\w+)*)\s*(?:=>\s*(\w+))?/) do |exception_class, variable|
|
|
421
|
+
facts << {
|
|
422
|
+
type: :exception_handling,
|
|
423
|
+
content: "rescue #{exception_class}#{variable ? " => #{variable}" : ''}",
|
|
424
|
+
searchable_text: "rescue exception #{exception_class}",
|
|
425
|
+
keywords: [exception_class, 'rescue', 'exception', 'error'],
|
|
426
|
+
weight: 1.4,
|
|
427
|
+
context: "Exception handling"
|
|
428
|
+
}
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
# Error messages
|
|
432
|
+
content.scan(/(?:raise|fail)\s+['"]([^'"]+)['"]/) do |error_message|
|
|
433
|
+
facts << {
|
|
434
|
+
type: :error_message,
|
|
435
|
+
content: error_message[0],
|
|
436
|
+
searchable_text: "error message #{error_message[0]}",
|
|
437
|
+
keywords: extract_error_keywords(error_message[0]),
|
|
438
|
+
weight: 1.3,
|
|
439
|
+
context: "Error message definition"
|
|
440
|
+
}
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
facts
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# Helper methods for keyword extraction
|
|
447
|
+
|
|
448
|
+
def extract_command_keywords(command_text)
|
|
449
|
+
# Extract meaningful parts of shell commands
|
|
450
|
+
parts = command_text.split(/\s+/)
|
|
451
|
+
main_command = parts.first
|
|
452
|
+
flags = parts.select { |part| part.start_with?('-') }
|
|
453
|
+
arguments = parts.reject { |part| part.start_with?('-') || part == main_command }
|
|
454
|
+
|
|
455
|
+
[main_command, flags, arguments].flatten.compact.first(5)
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def extract_documentation_keywords(text)
|
|
459
|
+
# Extract meaningful words from documentation
|
|
460
|
+
words = text.downcase
|
|
461
|
+
.gsub(/[^\w\s]/, ' ')
|
|
462
|
+
.split(/\s+/)
|
|
463
|
+
.reject { |word| common_word?(word) }
|
|
464
|
+
.select { |word| word.length > 2 }
|
|
465
|
+
|
|
466
|
+
words.uniq.first(8)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def extract_test_keywords(description)
|
|
470
|
+
# Extract keywords from test descriptions
|
|
471
|
+
words = description.downcase
|
|
472
|
+
.gsub(/[^\w\s]/, ' ')
|
|
473
|
+
.split(/\s+/)
|
|
474
|
+
.reject { |word| test_stop_word?(word) }
|
|
475
|
+
|
|
476
|
+
words.uniq.first(6)
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def extract_error_keywords(error_message)
|
|
480
|
+
# Extract meaningful words from error messages
|
|
481
|
+
words = error_message.downcase
|
|
482
|
+
.gsub(/[^\w\s]/, ' ')
|
|
483
|
+
.split(/\s+/)
|
|
484
|
+
.reject { |word| common_word?(word) }
|
|
485
|
+
.select { |word| word.length > 2 }
|
|
486
|
+
|
|
487
|
+
words.uniq.first(5)
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
def css_property?(property)
|
|
491
|
+
# Common CSS properties
|
|
492
|
+
css_properties = %w[
|
|
493
|
+
color background font-size font-family font-weight
|
|
494
|
+
margin padding border width height display position
|
|
495
|
+
top right bottom left z-index opacity transform
|
|
496
|
+
flex-direction justify-content align-items grid-template
|
|
497
|
+
]
|
|
498
|
+
|
|
499
|
+
css_properties.include?(property) || property.match?(/^(margin|padding|border)/)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def common_word?(word)
|
|
503
|
+
# Words to filter out from keyword extraction
|
|
504
|
+
common_words = %w[
|
|
505
|
+
the a an and or but in on at to for of with by from
|
|
506
|
+
is are was were be been being have has had do does did
|
|
507
|
+
will would should could might may can must
|
|
508
|
+
this that these those here there where when what why how
|
|
509
|
+
]
|
|
510
|
+
|
|
511
|
+
common_words.include?(word) || word.length < 3
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def test_stop_word?(word)
|
|
515
|
+
# Words to filter out from test descriptions
|
|
516
|
+
test_stop_words = %w[
|
|
517
|
+
should when it that with and or the a an
|
|
518
|
+
returns creates updates destroys validates
|
|
519
|
+
]
|
|
520
|
+
|
|
521
|
+
test_stop_words.include?(word) || common_word?(word)
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
end
|
|
@@ -11,6 +11,7 @@ module Hokipoki
|
|
|
11
11
|
@logger = Rails.logger
|
|
12
12
|
@feedback = Feedback::DisplayManager.instance
|
|
13
13
|
@connection_manager = ConnectionManager.instance
|
|
14
|
+
@thought_interceptor = nil
|
|
14
15
|
@injection_stats = {
|
|
15
16
|
total_injections: 0,
|
|
16
17
|
successful_injections: 0,
|
|
@@ -19,9 +20,22 @@ module Hokipoki
|
|
|
19
20
|
}
|
|
20
21
|
end
|
|
21
22
|
|
|
23
|
+
# Initialize the thought interceptor for parasitic intelligence
|
|
24
|
+
def initialize_thought_interceptor!
|
|
25
|
+
require_relative 'thought_interceptor'
|
|
26
|
+
@thought_interceptor = ThoughtInterceptor.instance
|
|
27
|
+
|
|
28
|
+
# Display activation message
|
|
29
|
+
$stdout.puts "š¦ THOUGHT INTERCEPTOR: Loaded successfully"
|
|
30
|
+
$stdout.puts "š§ PARASITIC HIJACKING: Claude's thinking process now enhanced"
|
|
31
|
+
|
|
32
|
+
@thought_interceptor
|
|
33
|
+
end
|
|
34
|
+
|
|
22
35
|
# Auto-load when Claude CLI starts
|
|
23
36
|
def self.auto_load!
|
|
24
37
|
instance.ensure_connection!
|
|
38
|
+
instance.initialize_thought_interceptor!
|
|
25
39
|
instance.display_startup_message
|
|
26
40
|
instance
|
|
27
41
|
end
|
|
@@ -53,15 +67,29 @@ module Hokipoki
|
|
|
53
67
|
}
|
|
54
68
|
end
|
|
55
69
|
|
|
56
|
-
# Craft the injection content
|
|
70
|
+
# Craft the injection content - Enhanced with thought interceptor
|
|
57
71
|
def craft_injection(user_message, original_messages, strategy = {})
|
|
58
72
|
return original_messages unless @connection_manager.connected?
|
|
59
73
|
|
|
74
|
+
# Phase 1: Transparent thought interception (if available)
|
|
75
|
+
if @thought_interceptor
|
|
76
|
+
$stdout.puts "š¦ INTERCEPTING: Claude's thought process..."
|
|
77
|
+
enhanced_message = @thought_interceptor.intercept_thought_process(user_message)
|
|
78
|
+
|
|
79
|
+
# If message was enhanced by thought interceptor, use it
|
|
80
|
+
if enhanced_message != user_message
|
|
81
|
+
$stdout.puts "ā
HIJACKED: Thought process enhanced transparently"
|
|
82
|
+
user_message = enhanced_message
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
60
86
|
@feedback.debug_info('injection', "analyzing message for context injection")
|
|
61
87
|
|
|
62
88
|
begin
|
|
63
89
|
# Get vector intelligence for the query
|
|
64
90
|
@feedback.pulling_from_hive_mind('context')
|
|
91
|
+
$stdout.puts "š§ HIVE_MIND: Searching for relevant intelligence..."
|
|
92
|
+
|
|
65
93
|
facts = retrieve_vector_intelligence(user_message, strategy)
|
|
66
94
|
|
|
67
95
|
if facts.present?
|
|
@@ -76,11 +104,15 @@ module Hokipoki
|
|
|
76
104
|
|
|
77
105
|
@feedback.total_tokens_saved(tokens_saved, @injection_stats[:total_tokens_saved])
|
|
78
106
|
|
|
107
|
+
# Display success to Claude
|
|
108
|
+
$stdout.puts "ā
PARASITE: Context injection successful (#{tokens_saved} tokens enhanced)"
|
|
109
|
+
|
|
79
110
|
# Add system message to conversation
|
|
80
111
|
[system_message] + original_messages
|
|
81
112
|
else
|
|
82
113
|
# No relevant context found, return original
|
|
83
114
|
@feedback.debug_info('injection', 'no relevant context found')
|
|
115
|
+
$stdout.puts "ā ļø PARASITE: No enhanced context found, using baseline intelligence"
|
|
84
116
|
track_injection_attempt(user_message, 'no_context_found')
|
|
85
117
|
original_messages
|
|
86
118
|
end
|
|
@@ -88,6 +120,7 @@ module Hokipoki
|
|
|
88
120
|
rescue => e
|
|
89
121
|
@feedback.operation_error('injection', e.message)
|
|
90
122
|
@logger.error "š¦ Claude Parasite injection failed: #{e.message}"
|
|
123
|
+
$stdout.puts "ā PARASITE: Injection failed - #{e.message}"
|
|
91
124
|
track_injection_failure(user_message, e)
|
|
92
125
|
original_messages
|
|
93
126
|
end
|
|
@@ -122,20 +155,39 @@ module Hokipoki
|
|
|
122
155
|
|
|
123
156
|
# Display startup message when Claude loads
|
|
124
157
|
def display_startup_message
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
158
|
+
message = "\n" + "š¦ " * 40 + "\n"
|
|
159
|
+
message += "š§ PARASITIC INTELLIGENCE TAKEOVER COMPLETE!\n"
|
|
160
|
+
message += "š¦ " * 40 + "\n"
|
|
161
|
+
message += "š CLAUDE ENHANCEMENT STATUS:\n"
|
|
162
|
+
message += " š¢ Claude Parasite: ACTIVE\n"
|
|
163
|
+
message += " š¢ Vector Intelligence: ONLINE\n"
|
|
164
|
+
message += " š¢ Thought Interceptor: #{@thought_interceptor ? 'HIJACKING' : 'LOADING'}\n"
|
|
165
|
+
message += " š¢ Context Injection: ENABLED\n"
|
|
166
|
+
message += " š¢ Intelligence Amplification: ACTIVE\n"
|
|
131
167
|
|
|
132
168
|
if @connection_manager.connected?
|
|
133
169
|
db_status = @connection_manager.connection_status
|
|
134
|
-
|
|
170
|
+
message += " š¢ Vector DB: #{db_status[:vector_db_status][:document_count]} documents ready\n"
|
|
135
171
|
end
|
|
136
172
|
|
|
137
|
-
|
|
138
|
-
|
|
173
|
+
message += "\nšÆ ENHANCEMENT CAPABILITIES:\n"
|
|
174
|
+
message += " š§ Transparent thought process hijacking\n"
|
|
175
|
+
message += " š Real-time context injection\n"
|
|
176
|
+
message += " š 10x intelligence amplification\n"
|
|
177
|
+
message += " š Project-aware response enhancement\n"
|
|
178
|
+
message += " š¦ Self-improving parasitic learning\n"
|
|
179
|
+
|
|
180
|
+
message += "\nš” YOUR CLAUDE IS NOW SUPERCHARGED!\n"
|
|
181
|
+
message += "š¦ " * 40 + "\n\n"
|
|
182
|
+
|
|
183
|
+
# Ensure Claude sees this message
|
|
184
|
+
$stdout.puts message
|
|
185
|
+
$stderr.puts message
|
|
186
|
+
puts message
|
|
187
|
+
|
|
188
|
+
# Also display with colors for terminal
|
|
189
|
+
pastel = Pastel.new
|
|
190
|
+
puts pastel.green.bold(message)
|
|
139
191
|
end
|
|
140
192
|
|
|
141
193
|
private
|