rubik 0.3.1 → 0.3.2
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/ATTRIBUTION.md +1 -1
- data/CHANGELOG.md +50 -0
- data/lib/rubik/ast/cache_store.rb +4 -2
- data/lib/rubik/ast/graph.rb +49 -94
- data/lib/rubik/ast/graph_extensions.rb +202 -0
- data/lib/rubik/ast/ruby_extractor.rb +8 -3
- data/lib/rubik/ast/symbol.rb +7 -0
- data/lib/rubik/ast/treesitter/base_walker.rb +4 -3
- data/lib/rubik/ast/treesitter/c_walker.rb +10 -8
- data/lib/rubik/ast/treesitter/go_walker.rb +4 -2
- data/lib/rubik/ast/treesitter/rust_walker.rb +9 -7
- data/lib/rubik/ast/treesitter/typescript_walker.rb +7 -5
- data/lib/rubik/ast/treesitter/zig_walker.rb +44 -6
- data/lib/rubik/lsp/client.rb +102 -24
- data/lib/rubik/lsp/manager.rb +136 -83
- data/lib/rubik/orchestrator/tool_circuit_breaker.rb +15 -5
- data/lib/rubik/prompt/token_estimator.rb +15 -3
- data/lib/rubik/repl/ast_warmup.rb +35 -4
- data/lib/rubik/repl/shell.rb +1 -1
- data/lib/rubik/tools/builtins/symbols.rb +56 -20
- data/lib/rubik/tools/symbol_tool_support.rb +113 -0
- data/lib/rubik/version.rb +1 -1
- metadata +3 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "output"
|
|
4
|
+
require_relative "diagnostic_reporter"
|
|
5
|
+
require_relative "fuzzy_matcher"
|
|
6
|
+
require_relative "truncator"
|
|
7
|
+
|
|
8
|
+
module Rubik
|
|
9
|
+
module Tools
|
|
10
|
+
module SymbolToolSupport
|
|
11
|
+
def self.string_argument(args, primary, aliases: [], label: primary)
|
|
12
|
+
return [nil, ToolOutput.error("Tool arguments must be an object.")] unless args.is_a?(Hash)
|
|
13
|
+
|
|
14
|
+
values = [primary, *aliases].flat_map do |key|
|
|
15
|
+
[key.to_s, key.to_sym].uniq.filter_map { |actual| args[actual] if args.key?(actual) }
|
|
16
|
+
end
|
|
17
|
+
values.compact!
|
|
18
|
+
return [nil, nil] if values.empty?
|
|
19
|
+
return [nil, ToolOutput.error("#{label} must be a string.")] unless values.all?(String)
|
|
20
|
+
|
|
21
|
+
[values.first, nil]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.boolean_argument(args, key, label: key)
|
|
25
|
+
return [nil, ToolOutput.error("Tool arguments must be an object.")] unless args.is_a?(Hash)
|
|
26
|
+
|
|
27
|
+
values = [key.to_s, key.to_sym].uniq.filter_map { |actual| args[actual] if args.key?(actual) }
|
|
28
|
+
values.compact!
|
|
29
|
+
return [false, nil] if values.empty?
|
|
30
|
+
return [nil, ToolOutput.error("#{label} must be a boolean.")] unless values.all? { |value| [true, false].include?(value) }
|
|
31
|
+
|
|
32
|
+
[values.first, nil]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.warmup_output(graph)
|
|
36
|
+
return nil unless graph.respond_to?(:warmup_status)
|
|
37
|
+
|
|
38
|
+
status = graph.warmup_status
|
|
39
|
+
return nil unless status
|
|
40
|
+
|
|
41
|
+
state = status[:state] || status["state"]
|
|
42
|
+
if [:warming, "warming"].include?(state)
|
|
43
|
+
indexed = status[:indexed] || status["indexed"]
|
|
44
|
+
total = status[:total] || status["total"]
|
|
45
|
+
progress = if indexed.is_a?(Numeric) && total.is_a?(Numeric)
|
|
46
|
+
" (#{indexed}/#{total} files indexed)"
|
|
47
|
+
else
|
|
48
|
+
""
|
|
49
|
+
end
|
|
50
|
+
ToolOutput.error("AST index warming; retry this request shortly#{progress}.")
|
|
51
|
+
elsif [:failed, "failed"].include?(state)
|
|
52
|
+
ToolOutput.error("AST index warmup failed; retry after running '!context scan'.")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.rank_matches(matches, query, graph)
|
|
57
|
+
symbol_map = graph.respond_to?(:symbols) ? graph.symbols : nil
|
|
58
|
+
qualified = symbol_map.respond_to?(:qualified_symbols) ? symbol_map.qualified_symbols : {}
|
|
59
|
+
keys_by_id = {}.compare_by_identity
|
|
60
|
+
qualified.sort_by { |key, _symbol| key }.each do |(key, symbol)|
|
|
61
|
+
keys_by_id[symbol] ||= key
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
matches.each_with_index.sort_by do |symbol, index|
|
|
65
|
+
name = symbol.name.to_s.downcase
|
|
66
|
+
lowered_query = query.downcase
|
|
67
|
+
rank = if name == lowered_query
|
|
68
|
+
0
|
|
69
|
+
elsif name.start_with?(lowered_query)
|
|
70
|
+
1
|
|
71
|
+
else
|
|
72
|
+
2
|
|
73
|
+
end
|
|
74
|
+
key = keys_by_id[symbol] || "#{symbol.file}##{symbol.name}"
|
|
75
|
+
[rank, key, index]
|
|
76
|
+
end.map(&:first)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.render_search(query, matches)
|
|
80
|
+
shown_count = [matches.length, 50].min
|
|
81
|
+
loop do
|
|
82
|
+
omitted = matches.length - shown_count
|
|
83
|
+
lines = ["Found #{matches.length} matching symbol(s) for '#{query}':"]
|
|
84
|
+
matches.first(shown_count).each do |sym|
|
|
85
|
+
lines << "• #{sym.kind.to_s.upcase} #{sym.name} (#{sym.file}:#{sym.line_start}-#{sym.line_end})"
|
|
86
|
+
lines << " Signature: #{sym.signature}"
|
|
87
|
+
lines << " Calls: #{sym.calls.join(', ')}"
|
|
88
|
+
end
|
|
89
|
+
lines << "... #{omitted} additional matches omitted." if omitted.positive?
|
|
90
|
+
|
|
91
|
+
truncated = Truncator.truncate(lines.join("\n"))
|
|
92
|
+
return truncated[:text] if !truncated[:truncated] || shown_count <= 1
|
|
93
|
+
|
|
94
|
+
shown_count -= 1
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.fuzzy_candidate_names(graph)
|
|
99
|
+
symbols = graph.respond_to?(:symbols) ? graph.symbols : nil
|
|
100
|
+
return [] unless symbols
|
|
101
|
+
|
|
102
|
+
names = if symbols.respond_to?(:values)
|
|
103
|
+
symbols.values.filter_map { |sym| sym.name if sym.respond_to?(:name) }.uniq
|
|
104
|
+
else
|
|
105
|
+
[]
|
|
106
|
+
end
|
|
107
|
+
return names unless names.empty?
|
|
108
|
+
|
|
109
|
+
symbols.respond_to?(:keys) ? symbols.keys : []
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
data/lib/rubik/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubik
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Özgür Ulukır
|
|
@@ -249,6 +249,7 @@ files:
|
|
|
249
249
|
- lib/rubik/ast/fallback_cleaner.rb
|
|
250
250
|
- lib/rubik/ast/go_extractor.rb
|
|
251
251
|
- lib/rubik/ast/graph.rb
|
|
252
|
+
- lib/rubik/ast/graph_extensions.rb
|
|
252
253
|
- lib/rubik/ast/python_extractor.rb
|
|
253
254
|
- lib/rubik/ast/ruby_extractor.rb
|
|
254
255
|
- lib/rubik/ast/rust_extractor.rb
|
|
@@ -757,6 +758,7 @@ files:
|
|
|
757
758
|
- lib/rubik/tools/registry.rb
|
|
758
759
|
- lib/rubik/tools/result.rb
|
|
759
760
|
- lib/rubik/tools/services.rb
|
|
761
|
+
- lib/rubik/tools/symbol_tool_support.rb
|
|
760
762
|
- lib/rubik/tools/truncator.rb
|
|
761
763
|
- lib/rubik/tools/uri_resolver.rb
|
|
762
764
|
- lib/rubik/ui.rb
|