brute 4.3.2 → 5.0.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/brute/completion/async_faraday.rb +38 -0
  3. data/lib/brute/completion/lang_chain.rb +185 -0
  4. data/lib/brute/completion/llmrb.rb +182 -0
  5. data/lib/brute/completion/open_router.rb +54 -42
  6. data/lib/brute/completion/ruby_llm.rb +198 -0
  7. data/lib/brute/contrib/otel.rb +208 -0
  8. data/lib/brute/hooks.rb +149 -31
  9. data/lib/brute/message_transport/lang_chain.rb +36 -0
  10. data/lib/brute/message_transport/llm.rb +6 -0
  11. data/lib/brute/message_transport/open_router.rb +6 -0
  12. data/lib/brute/message_transport/ruby_llm.rb +6 -0
  13. data/lib/brute/message_transport.rb +8 -0
  14. data/lib/brute/middleware/000_base.rb +61 -0
  15. data/lib/brute/middleware/002_session_log.rb +1 -1
  16. data/lib/brute/middleware/004_summarize.rb +1 -1
  17. data/lib/brute/middleware/005_tracing.rb +1 -1
  18. data/lib/brute/middleware/006_loop.rb +1 -1
  19. data/lib/brute/middleware/008_checkpoint.rb +1 -1
  20. data/lib/brute/middleware/010_max_iterations.rb +1 -1
  21. data/lib/brute/middleware/020_system_prompt.rb +1 -1
  22. data/lib/brute/middleware/025_skills.rb +1 -1
  23. data/lib/brute/middleware/040_compaction_check.rb +1 -1
  24. data/lib/brute/middleware/060_questions.rb +1 -1
  25. data/lib/brute/middleware/070_tool_pipeline.rb +60 -47
  26. data/lib/brute/middleware/event_handler.rb +1 -1
  27. data/lib/brute/middleware/user_queue.rb +1 -1
  28. data/lib/brute/turn/agent_pipeline.rb +3 -4
  29. data/lib/brute/turn/pipeline.rb +151 -2
  30. data/lib/brute/usage_detection/lang_chain.rb +44 -0
  31. data/lib/brute/usage_detection/llmrb.rb +53 -0
  32. data/lib/brute/usage_detection/open_router.rb +62 -0
  33. data/lib/brute/usage_detection/ruby_llm.rb +55 -0
  34. data/lib/brute/usage_detection/usage.rb +51 -0
  35. data/lib/brute/version.rb +1 -1
  36. data/lib/brute.rb +95 -3
  37. metadata +83 -8
  38. data/lib/brute/changelog.rb +0 -322
  39. data/lib/brute/deprecate.rb +0 -132
  40. data/lib/brute/middleware/001_otel_span.rb +0 -79
  41. data/lib/brute/middleware/015_otel_token_usage.rb +0 -44
  42. data/lib/brute/middleware/073_otel_tool_call.rb +0 -51
  43. data/lib/brute/middleware/075_otel_tool_results.rb +0 -48
  44. data/lib/brute/middleware/open_router.rb +0 -56
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "brute"
5
+ require "brute/usage_detection/usage"
6
+
7
+ module Brute
8
+ module UsageDetection
9
+ # llm.rb models usage as its own LLM::Usage value object. Note it answers
10
+ # 0, not nil, for anything the provider left out — that is llm.rb's
11
+ # reading of the response and it is reported as given.
12
+ module LLMrb
13
+ def self.detect(response)
14
+ return nil unless response.respond_to?(:usage)
15
+
16
+ usage = response.usage
17
+ return nil if usage.nil?
18
+
19
+ Usage.new(
20
+ input: read(usage, :input_tokens),
21
+ output: read(usage, :output_tokens),
22
+ total: read(usage, :total_tokens),
23
+ reasoning: read(usage, :reasoning_tokens),
24
+ cache_read: read(usage, :cache_read_tokens),
25
+ cache_write: read(usage, :cache_write_tokens),
26
+ raw: usage,
27
+ )
28
+ end
29
+
30
+ def self.read(usage, name) = usage.respond_to?(name) ? usage.public_send(name) : nil
31
+ end
32
+ end
33
+ end
34
+
35
+ __END__
36
+
37
+ describe "brute/usage_detection/llmrb" do
38
+ LLMrbUsage = Struct.new(:input_tokens, :output_tokens, :total_tokens, :reasoning_tokens,
39
+ :cache_read_tokens, :cache_write_tokens) unless defined?(LLMrbUsage)
40
+ LLMrbResponse = Struct.new(:usage) unless defined?(LLMrbResponse)
41
+
42
+ it "reports LLM::Usage as given, and answers nil when there is no usage to read" do
43
+ usage = Brute::UsageDetection::LLMrb.detect(LLMrbResponse.new(LLMrbUsage.new(10, 5, 15, 0, 0, 0)))
44
+
45
+ usage.input.should == 10
46
+ usage.output.should == 5
47
+ usage.total.should == 15
48
+ usage.reasoning.should == 0 # llm.rb's own zero, reported as given
49
+
50
+ Brute::UsageDetection::LLMrb.detect(LLMrbResponse.new(nil)).should.be.nil
51
+ Brute::UsageDetection::LLMrb.detect(Object.new).should.be.nil
52
+ end
53
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "brute"
5
+ require "brute/usage_detection/usage"
6
+
7
+ module Brute
8
+ module UsageDetection
9
+ # OpenRouter reports usage as the provider's raw hash on the response,
10
+ # in OpenAI's wire shape. Reasoning tokens, when the model reports them,
11
+ # arrive nested under completion_tokens_details.
12
+ module OpenRouter
13
+ def self.detect(response)
14
+ return nil unless response.respond_to?(:usage)
15
+
16
+ usage = response.usage
17
+ return nil if usage.nil?
18
+
19
+ usage = usage.to_h
20
+ details = (usage["completion_tokens_details"] || usage[:completion_tokens_details] || {}).to_h
21
+
22
+ Usage.new(
23
+ input: field(usage, "prompt_tokens"),
24
+ output: field(usage, "completion_tokens"),
25
+ total: field(usage, "total_tokens"),
26
+ reasoning: field(details, "reasoning_tokens"),
27
+ cache_read: field(usage, "cached_tokens"),
28
+ cost: field(usage, "cost"),
29
+ raw: usage,
30
+ )
31
+ end
32
+
33
+ # OpenRouter hands back string keys; a hand-built response may not.
34
+ def self.field(hash, key) = hash[key] || hash[key.to_sym]
35
+ end
36
+ end
37
+ end
38
+
39
+ __END__
40
+
41
+ describe "brute/usage_detection/open_router" do
42
+ Response = Struct.new(:usage) unless defined?(Response)
43
+
44
+ it "reads the OpenAI-shaped hash, string or symbol keys, and answers nil for no usage" do
45
+ usage = Brute::UsageDetection::OpenRouter.detect(Response.new({
46
+ "prompt_tokens" => 10, "completion_tokens" => 5, "total_tokens" => 15,
47
+ "completion_tokens_details" => { "reasoning_tokens" => 4 },
48
+ }))
49
+ usage.input.should == 10
50
+ usage.output.should == 5
51
+ usage.total.should == 15
52
+ usage.reasoning.should == 4
53
+
54
+ symbols = Brute::UsageDetection::OpenRouter.detect(Response.new({ prompt_tokens: 1, completion_tokens: 2 }))
55
+ symbols.input.should == 1
56
+ symbols.output.should == 2
57
+ symbols.total.should.be.nil
58
+
59
+ Brute::UsageDetection::OpenRouter.detect(Response.new(nil)).should.be.nil
60
+ Brute::UsageDetection::OpenRouter.detect(Object.new).should.be.nil
61
+ end
62
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "brute"
5
+ require "brute/usage_detection/usage"
6
+
7
+ module Brute
8
+ module UsageDetection
9
+ # ruby_llm hangs usage off the *message*, not the response: a Tokens
10
+ # object with input/output/cache_read/cache_write/thinking, plus the
11
+ # provider's own reported_cost when it gives one.
12
+ module RubyLLM
13
+ def self.detect(message)
14
+ return nil unless message.respond_to?(:tokens)
15
+
16
+ tokens = message.tokens
17
+ return nil if tokens.nil?
18
+
19
+ Usage.new(
20
+ input: read(tokens, :input),
21
+ output: read(tokens, :output),
22
+ reasoning: read(tokens, :thinking),
23
+ cache_read: read(tokens, :cache_read),
24
+ cache_write: read(tokens, :cache_write),
25
+ cost: read(tokens, :reported_cost),
26
+ raw: tokens,
27
+ )
28
+ end
29
+
30
+ def self.read(tokens, name) = tokens.respond_to?(name) ? tokens.public_send(name) : nil
31
+ end
32
+ end
33
+ end
34
+
35
+ __END__
36
+
37
+ describe "brute/usage_detection/ruby_llm" do
38
+ FakeTokens = Struct.new(:input, :output, :cache_read, :cache_write, :thinking, :reported_cost) unless defined?(FakeTokens)
39
+ FakeMessage = Struct.new(:tokens) unless defined?(FakeMessage)
40
+
41
+ it "reads Tokens off the message, maps thinking to reasoning, and answers nil for no usage" do
42
+ usage = Brute::UsageDetection::RubyLLM.detect(FakeMessage.new(FakeTokens.new(10, 5, 2, 1, 4, 0.002)))
43
+
44
+ usage.input.should == 10
45
+ usage.output.should == 5
46
+ usage.total.should.be.nil # ruby_llm reports parts, not a total
47
+ usage.reasoning.should == 4
48
+ usage.cache_read.should == 2
49
+ usage.cache_write.should == 1
50
+ usage.cost.should == 0.002
51
+
52
+ Brute::UsageDetection::RubyLLM.detect(FakeMessage.new(nil)).should.be.nil
53
+ Brute::UsageDetection::RubyLLM.detect(Object.new).should.be.nil
54
+ end
55
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "brute"
5
+
6
+ module Brute
7
+ # What the provider reported, normalised. Every library reports usage
8
+ # differently —
9
+ # OpenRouter as a raw hash on the response, ruby_llm as a Tokens object on
10
+ # the *message*, llm.rb as an LLM::Usage, langchainrb as per-provider
11
+ # methods digging their own raw shape — so each strategy here reads one of
12
+ # them and answers the same Usage.
13
+ #
14
+ # Brute::UsageDetection::OpenRouter.detect(response)
15
+ # # => #<data Brute::UsageDetection::Usage input=10, output=5, total=15, ...>
16
+ #
17
+ # A strategy reads one response and answers what that provider said —
18
+ # nothing more. It does not derive, sum or accumulate: running totals for a
19
+ # turn are the env's business, not the reader's. A strategy answers nil when
20
+ # the provider reported nothing at all.
21
+ module UsageDetection
22
+ Usage = Data.define(:input, :output, :total, :reasoning, :cache_read, :cache_write, :cost, :raw) do
23
+ def initialize(input: nil, output: nil, total: nil, reasoning: nil,
24
+ cache_read: nil, cache_write: nil, cost: nil, raw: nil)
25
+ super
26
+ end
27
+
28
+ # What a provider did not report stays out, so a reader sees the counts
29
+ # that are real rather than a row of nils.
30
+ def to_h = super.compact
31
+
32
+ def empty? = [input, output, total].all?(&:nil?)
33
+ end
34
+ end
35
+ end
36
+
37
+ __END__
38
+
39
+ describe "brute/usage_detection/usage" do
40
+ it "reports only what it was given, and drops what was never reported" do
41
+ # No derivation: a provider that reports parts and no total has no total.
42
+ usage = Brute::UsageDetection::Usage.new(input: 10, output: 5)
43
+ usage.total.should.be.nil
44
+ usage.to_h.should == { input: 10, output: 5 }
45
+
46
+ Brute::UsageDetection::Usage.new(input: 10, output: 5, total: 99).total.should == 99
47
+
48
+ Brute::UsageDetection::Usage.new.empty?.should.be.true
49
+ Brute::UsageDetection::Usage.new(total: 3).empty?.should.be.false
50
+ end
51
+ end
data/lib/brute/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Brute
4
- VERSION = "4.3.2"
4
+ VERSION = "5.0.0"
5
5
  end
data/lib/brute.rb CHANGED
@@ -87,8 +87,100 @@ module Brute
87
87
  end
88
88
  end
89
89
 
90
- Dir.glob("#{__dir__}/brute/**/*.rb").sort.each do |path|
91
- require path
92
- end
90
+ # Explicit, in dependency order: Hooks and Middleware::Base are included and
91
+ # inherited at load time, so they cannot be left to a sorted glob that happens
92
+ # to reach completion/ before hooks.
93
+ require_relative "brute/version"
94
+ require_relative "brute/hooks"
95
+ require_relative "brute/messages"
96
+ require_relative "brute/middleware/000_base"
97
+ require_relative "brute/completion/async_faraday"
98
+ require_relative "brute/completion/open_router"
99
+ require_relative "brute/completion/lang_chain"
100
+ require_relative "brute/completion/llmrb"
101
+ require_relative "brute/completion/ruby_llm"
102
+ require_relative "brute/usage_detection/usage"
103
+ require_relative "brute/usage_detection/open_router"
104
+ require_relative "brute/usage_detection/ruby_llm"
105
+ require_relative "brute/usage_detection/llmrb"
106
+ require_relative "brute/usage_detection/lang_chain"
107
+ require_relative "brute/contrib/log_file"
108
+ require_relative "brute/contrib/otel"
109
+ require_relative "brute/events/handler"
110
+ require_relative "brute/events/prefixed_terminal_output"
111
+ require_relative "brute/events/terminal_output_handler"
112
+ require_relative "brute/message_transport"
113
+ require_relative "brute/message_transport/anthropic"
114
+ require_relative "brute/message_transport/llm"
115
+ require_relative "brute/message_transport/open_router"
116
+ require_relative "brute/message_transport/openai"
117
+ require_relative "brute/message_transport/ruby_llm"
118
+ require_relative "brute/message_transport/ruby_open_ai"
119
+ require_relative "brute/message_transport/lang_chain"
120
+ require_relative "brute/middleware/002_session_log"
121
+ require_relative "brute/middleware/004_summarize"
122
+ require_relative "brute/middleware/005_tracing"
123
+ require_relative "brute/middleware/006_loop"
124
+ require_relative "brute/middleware/008_checkpoint"
125
+ require_relative "brute/middleware/010_max_iterations"
126
+ require_relative "brute/middleware/020_system_prompt"
127
+ require_relative "brute/middleware/025_skills"
128
+ require_relative "brute/middleware/040_compaction_check"
129
+ require_relative "brute/middleware/060_questions"
130
+ require_relative "brute/middleware/070_tool_pipeline"
131
+ require_relative "brute/middleware/event_handler"
132
+ require_relative "brute/middleware/user_queue"
133
+ require_relative "brute/prompt_template"
134
+ require_relative "brute/prompts"
135
+ require_relative "brute/prompts/autonomy"
136
+ require_relative "brute/prompts/base"
137
+ require_relative "brute/prompts/build_switch"
138
+ require_relative "brute/prompts/code_references"
139
+ require_relative "brute/prompts/code_style"
140
+ require_relative "brute/prompts/conventions"
141
+ require_relative "brute/prompts/doing_tasks"
142
+ require_relative "brute/prompts/editing_approach"
143
+ require_relative "brute/prompts/editing_constraints"
144
+ require_relative "brute/prompts/environment"
145
+ require_relative "brute/prompts/frontend_tasks"
146
+ require_relative "brute/prompts/git_safety"
147
+ require_relative "brute/prompts/identity"
148
+ require_relative "brute/prompts/instructions"
149
+ require_relative "brute/prompts/max_steps"
150
+ require_relative "brute/prompts/objectivity"
151
+ require_relative "brute/prompts/plan_reminder"
152
+ require_relative "brute/prompts/proactiveness"
153
+ require_relative "brute/prompts/security_and_safety"
154
+ require_relative "brute/prompts/skills"
155
+ require_relative "brute/prompts/task_management"
156
+ require_relative "brute/prompts/tone_and_style"
157
+ require_relative "brute/prompts/tool_usage"
158
+ require_relative "brute/rack/adapter"
159
+ require_relative "brute/skill"
160
+ require_relative "brute/system_prompt"
161
+ require_relative "brute/tool"
162
+ require_relative "brute/tools"
163
+ require_relative "brute/tools/adapter"
164
+ require_relative "brute/tools/fs/file_mutation_queue"
165
+ require_relative "brute/tools/fs/snapshot_store"
166
+ require_relative "brute/tools/fs_patch"
167
+ require_relative "brute/tools/fs_read"
168
+ require_relative "brute/tools/fs_remove"
169
+ require_relative "brute/tools/fs_search"
170
+ require_relative "brute/tools/fs_undo"
171
+ require_relative "brute/tools/fs_write"
172
+ require_relative "brute/tools/net_fetch"
173
+ require_relative "brute/tools/question"
174
+ require_relative "brute/tools/shell"
175
+ require_relative "brute/tools/skill_load"
176
+ require_relative "brute/tools/sub_agent"
177
+ require_relative "brute/tools/todo_list/store"
178
+ require_relative "brute/tools/todo_read"
179
+ require_relative "brute/tools/todo_write"
180
+ require_relative "brute/truncation"
181
+ require_relative "brute/turn/agent_pipeline"
182
+ require_relative "brute/turn/pipeline"
183
+ require_relative "brute/turn/tool_pipeline"
184
+ require_relative "brute/utils/diff"
93
185
 
94
186
  # gangsta g-dogg bruh...
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brute
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.2
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brute Contributors
@@ -107,6 +107,20 @@ dependencies:
107
107
  - - ">="
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: async-http-faraday
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
110
124
  - !ruby/object:Gem::Dependency
111
125
  name: json_schemer
112
126
  requirement: !ruby/object:Gem::Requirement
@@ -149,6 +163,62 @@ dependencies:
149
163
  - - "~>"
150
164
  - !ruby/object:Gem::Version
151
165
  version: '0.2'
166
+ - !ruby/object:Gem::Dependency
167
+ name: ruby_llm
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: open_router_enhanced
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '0'
187
+ type: :development
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ - !ruby/object:Gem::Dependency
195
+ name: llm.rb
196
+ requirement: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ type: :development
202
+ prerelease: false
203
+ version_requirements: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
208
+ - !ruby/object:Gem::Dependency
209
+ name: langchainrb
210
+ requirement: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - ">="
213
+ - !ruby/object:Gem::Version
214
+ version: '0'
215
+ type: :development
216
+ prerelease: false
217
+ version_requirements: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - ">="
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
152
222
  - !ruby/object:Gem::Dependency
153
223
  name: rake
154
224
  requirement: !ruby/object:Gem::Requirement
@@ -212,39 +282,39 @@ extensions: []
212
282
  extra_rdoc_files: []
213
283
  files:
214
284
  - lib/brute.rb
215
- - lib/brute/changelog.rb
285
+ - lib/brute/completion/async_faraday.rb
286
+ - lib/brute/completion/lang_chain.rb
287
+ - lib/brute/completion/llmrb.rb
216
288
  - lib/brute/completion/open_router.rb
289
+ - lib/brute/completion/ruby_llm.rb
217
290
  - lib/brute/contrib/log_file.rb
218
- - lib/brute/deprecate.rb
291
+ - lib/brute/contrib/otel.rb
219
292
  - lib/brute/events/handler.rb
220
293
  - lib/brute/events/prefixed_terminal_output.rb
221
294
  - lib/brute/events/terminal_output_handler.rb
222
295
  - lib/brute/hooks.rb
223
296
  - lib/brute/message_transport.rb
224
297
  - lib/brute/message_transport/anthropic.rb
298
+ - lib/brute/message_transport/lang_chain.rb
225
299
  - lib/brute/message_transport/llm.rb
226
300
  - lib/brute/message_transport/open_router.rb
227
301
  - lib/brute/message_transport/openai.rb
228
302
  - lib/brute/message_transport/ruby_llm.rb
229
303
  - lib/brute/message_transport/ruby_open_ai.rb
230
304
  - lib/brute/messages.rb
231
- - lib/brute/middleware/001_otel_span.rb
305
+ - lib/brute/middleware/000_base.rb
232
306
  - lib/brute/middleware/002_session_log.rb
233
307
  - lib/brute/middleware/004_summarize.rb
234
308
  - lib/brute/middleware/005_tracing.rb
235
309
  - lib/brute/middleware/006_loop.rb
236
310
  - lib/brute/middleware/008_checkpoint.rb
237
311
  - lib/brute/middleware/010_max_iterations.rb
238
- - lib/brute/middleware/015_otel_token_usage.rb
239
312
  - lib/brute/middleware/020_system_prompt.rb
240
313
  - lib/brute/middleware/025_skills.rb
241
314
  - lib/brute/middleware/040_compaction_check.rb
242
315
  - lib/brute/middleware/060_questions.rb
243
316
  - lib/brute/middleware/070_tool_pipeline.rb
244
- - lib/brute/middleware/073_otel_tool_call.rb
245
- - lib/brute/middleware/075_otel_tool_results.rb
246
317
  - lib/brute/middleware/event_handler.rb
247
- - lib/brute/middleware/open_router.rb
248
318
  - lib/brute/middleware/user_queue.rb
249
319
  - lib/brute/prompt_template.rb
250
320
  - lib/brute/prompts.rb
@@ -316,6 +386,11 @@ files:
316
386
  - lib/brute/turn/agent_pipeline.rb
317
387
  - lib/brute/turn/pipeline.rb
318
388
  - lib/brute/turn/tool_pipeline.rb
389
+ - lib/brute/usage_detection/lang_chain.rb
390
+ - lib/brute/usage_detection/llmrb.rb
391
+ - lib/brute/usage_detection/open_router.rb
392
+ - lib/brute/usage_detection/ruby_llm.rb
393
+ - lib/brute/usage_detection/usage.rb
319
394
  - lib/brute/utils/diff.rb
320
395
  - lib/brute/version.rb
321
396
  - lib/brute/version.rb.erb