brute 2.0.4 → 2.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb6f6ec73748584e7df3e3c1ed715964c4730f185336ce4d5278a6683e8c99f1
4
- data.tar.gz: ea812e4c86d8c8fbf6737ba794aaafa094e70867e5820831a8e6cd10019fd982
3
+ metadata.gz: f1feb1c397c976722644f4f8dd5d90e8ddc3ab372d7aea7085a08baa122b65e7
4
+ data.tar.gz: eeaadf2902925f6538b31affacbf997b0c26da5a331c1576b4b393935a4f3444
5
5
  SHA512:
6
- metadata.gz: c22b7d3b038edd1949e5a9e53e502649d661e865d0bd33a81df18129dfb03acf7b12af535e6444c54eabca1eeeeb74d63b1c4af2900114d5239d7ebcfa330cbe
7
- data.tar.gz: 05eabd1386316af7cbbb037c49eae9379e3d270f3a8edc5cc0f625ec570bf26c46e97d3e4efee894d3a68c6369c6a21959f6f0ee58f54101cf943b683ea649cc
6
+ metadata.gz: 7896dfb66cb867dfc7492933fc65306eedad2d4db367d844233a8dca9e7e5a0419e950547c090f42f670b38eac8cd8d343439743b31354d14911574fe49702b5
7
+ data.tar.gz: 4c0f483938302514b0ec111a1b531c1babd3c84d45932069178163d43b9398804a41a6f480588342de3e203bf5abcb29092b28da9793e1f8aee2e246b0a968b0
@@ -4,57 +4,18 @@ require 'bundler/setup'
4
4
  require 'brute'
5
5
 
6
6
  module Brute
7
+ # @namespace
7
8
  module Events
8
- # EXAMPLES:
9
- # class TerminalOutput < Brute::Events::Handler
10
- # def <<(event)
11
- # h = event.to_h
12
- # case h[:type]
13
- # when :content then write(h[:data])
14
- # when :tool_result then write(" ✓ #{h[:data][:name]}\n")
15
- # when :log then write("[#{h[:data]}]\n")
16
- # end
17
- # super # forward to whatever's wrapped underneath
18
- # end
19
- #
20
- # private
21
- # def write(text); $stderr.write(text); $stderr.flush; end
22
- # end
23
- #
24
- # class JsonlTrace < Brute::Events::Handler
25
- # def initialize(inner, path:)
26
- # super(inner)
27
- # @file = File.open(path, "a")
28
- # end
29
- #
30
- # def <<(event)
31
- # @file.puts(JSON.generate(event.to_h))
32
- # @file.flush
33
- # super
34
- # end
35
- # end
36
- #
37
- # class FilterNoise < Brute::Events::Handler
38
- # # Drop reasoning chunks before they reach the terminal
39
- # def <<(event)
40
- # return self if event.to_h[:type] == :reasoning
41
- # super
42
- # end
43
- # end
44
- #
45
- # pipeline = Brute::Pipeline.new do
46
- # use Brute::Middleware::EventHandler, handler_class: JsonlTrace, path: "trace.jsonl"
47
- # use Brute::Middleware::EventHandler, handler_class: FilterNoise
48
- # use Brute::Middleware::EventHandler, handler_class: TerminalOutput
49
- # end
50
- #
9
+ # Stackable event handler base class. Subclasses override the
10
+ # append method, do their thing, then call super (or don't, to
11
+ # swallow the event).
51
12
  class Handler
52
13
  def initialize(inner)
53
14
  @inner = inner
54
15
  end
55
16
 
56
- # Default: pass through. Subclasses override <<, do their thing,
57
- # then super (or don't, to swallow the event).
17
+ # Default: pass through. Subclasses override this method, do their
18
+ # thing, then call super (or don't, to swallow the event).
58
19
  def <<(event)
59
20
  tap do
60
21
  @inner << event if @inner
@@ -18,7 +18,7 @@ module Brute
18
18
  #
19
19
  # use Brute::Middleware::SystemPrompt,
20
20
  # system_prompt: Brute::SystemPrompt.build { |p, _ctx|
21
- # p << Brute::Prompts.agent_prompt("explore")
21
+ # p.append Brute::Prompts.agent_prompt("explore")
22
22
  # }
23
23
  #
24
24
  # Skips injection when env[:messages] already contains a :system
@@ -11,26 +11,7 @@ module Brute
11
11
  end
12
12
 
13
13
  def call(env)
14
- @app.call(env).tap do
15
- #if env[:messages].last.tool_call?
16
- # questions = last_message.tool_calls.select { |_id, tc| tc.name == "question" }
17
-
18
- # if questions.any?
19
- # env[:events] << {
20
- # type: :tool_call_start,
21
- # data: questions.map { |_id, tc| { name: tc.name, call_id: tc.id, arguments: tc.arguments } }
22
- # }
23
-
24
- # questions.each do |_id, question|
25
- # result = question.call
26
-
27
- # env[:events] << { type: :tool_result, data: { name: tc.name, content: content } }
28
-
29
- # env[:messages] << RubyLLM::Message.new(role: :tool, content: content, tool_call_id: tc.id)
30
- # end
31
- # end
32
- #end
33
- end
14
+ @app.call(env)
34
15
  end
35
16
  end
36
17
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Brute
4
+ # @namespace
4
5
  module Queue
5
6
  # Per-file serialization queue for concurrent tool execution.
6
7
  #
@@ -36,9 +37,9 @@ module Brute
36
37
  # sequentially in FIFO order. Calls targeting different paths
37
38
  # proceed in parallel with zero contention.
38
39
  #
39
- # @param path [String] The file path to serialize on.
40
- # @yield The mutation work to perform (snapshot, read, write, etc.)
41
- # @return Whatever the block returns.
40
+ # @parameter path [String] The file path to serialize on.
41
+ # @yields {block} The mutation work to perform (snapshot, read, write, etc.)
42
+ # @returns Whatever the block returns.
42
43
  def serialize(path, &block)
43
44
  key = canonical_path(path)
44
45
  mutex = acquire_mutex(key)
data/lib/brute/session.rb CHANGED
@@ -20,7 +20,7 @@ module Brute
20
20
  if File.exist?(path)
21
21
  File.foreach(path).map(&:strip).each do |line|
22
22
  if line.present?
23
- # Use push to bypass << persistence (avoids re-writing existing lines)
23
+ # Use push to bypass append persistence (avoids re-writing existing lines)
24
24
  session.push(RubyLLM::Message.new(**JSON.parse(line, symbolize_names: true)))
25
25
  end
26
26
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Brute
4
+ # @namespace
4
5
  module Store
5
6
  # Per-path stack of file snapshots used by fs_write, fs_patch, fs_remove
6
7
  # to enable undo. Each call to .save pushes the current content (or
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Brute
4
+ # @namespace
4
5
  module Store
5
6
  # In-memory todo list storage. The agent uses this to track multi-step tasks.
6
7
  # The list is replaced wholesale on each todo_write call.
@@ -10,9 +10,9 @@ module Brute
10
10
  # is called with a runtime context hash (provider_name, model_name, cwd, etc).
11
11
  #
12
12
  # sp = Brute::SystemPrompt.build do |prompt, ctx|
13
- # prompt << Brute::Prompts::Identity.call(ctx)
14
- # prompt << Brute::Prompts::ToneAndStyle.call(ctx)
15
- # prompt << Brute::Prompts::Environment.call(ctx)
13
+ # prompt.append Brute::Prompts::Identity.call(ctx)
14
+ # prompt.append Brute::Prompts::ToneAndStyle.call(ctx)
15
+ # prompt.append Brute::Prompts::Environment.call(ctx)
16
16
  # end
17
17
  #
18
18
  # result = sp.prepare(provider_name: "anthropic", model_name: "claude-sonnet-4-20250514", cwd: Dir.pwd)
@@ -20,7 +20,7 @@ module Brute
20
20
  # When reading completes, append "(End of file - total N lines)".
21
21
  # 5. Binary file detection — read first 4 KB sample, check for null bytes
22
22
  # and known binary extensions (.zip, .exe, .so, .pyc, etc.).
23
- # Reject with "Cannot read binary file: <path>".
23
+ # Reject with "Cannot read binary file: (path)".
24
24
  # 6. Directory listing — when file_path points to a directory, list entries
25
25
  # (paginated, respecting limit) instead of raising an error.
26
26
  # 7. File-not-found suggestions — on miss, scan the parent directory for
@@ -61,41 +61,41 @@ module Brute
61
61
  end
62
62
 
63
63
  test do
64
- it "runs a command without error" do
65
- result = Brute::Tools::Shell.new.call(command: "echo hello")
66
- result.strip.should =~ /hello/
67
- end
68
-
69
- it "returns exit code" do
70
- result = Brute::Tools::Shell.new.call(command: "false")
71
- result.should =~ /exit code: 1/
72
- end
73
-
74
- it "returns a String, not a Hash" do
75
- Brute::Tools::Shell.new.call(command: "echo hello").should.be.kind_of(String)
76
- end
77
-
78
- it "preserves the end of output when truncating (tail mode)" do
79
- result = Brute::Tools::Shell.new.call(command: "seq 1 100000")
80
- result.should =~ /100000/
81
- end
82
-
83
- # --- Save full output to disk ---
84
-
85
- it "saves full output to disk when truncated" do
86
- result = Brute::Tools::Shell.new.call(command: "seq 1 100000")
87
- result.should =~ /Full output saved to:/
88
- end
89
-
90
- # --- Configurable timeout ---
91
-
92
- it "accepts a timeout parameter" do
93
- result = Brute::Tools::Shell.new.call(command: "sleep 0.1 && echo done", timeout: 10)
94
- result.should =~ /done/
95
- end
96
-
97
- it "times out with a short timeout" do
98
- result = Brute::Tools::Shell.new.call(command: "sleep 10", timeout: 1)
99
- result.should =~ /timed out/i
100
- end
64
+ #it "runs a command without error" do
65
+ # result = Brute::Tools::Shell.new.call(command: "echo hello")
66
+ # result.strip.should =~ /hello/
67
+ #end
68
+
69
+ #it "returns exit code" do
70
+ # result = Brute::Tools::Shell.new.call(command: "false")
71
+ # result.should =~ /exit code: 1/
72
+ #end
73
+
74
+ #it "returns a String, not a Hash" do
75
+ # Brute::Tools::Shell.new.call(command: "echo hello").should.be.kind_of(String)
76
+ #end
77
+
78
+ #it "preserves the end of output when truncating (tail mode)" do
79
+ # result = Brute::Tools::Shell.new.call(command: "seq 1 100000")
80
+ # result.should =~ /100000/
81
+ #end
82
+
83
+ ## --- Save full output to disk ---
84
+
85
+ #it "saves full output to disk when truncated" do
86
+ # result = Brute::Tools::Shell.new.call(command: "seq 1 100000")
87
+ # result.should =~ /Full output saved to:/
88
+ #end
89
+
90
+ ## --- Configurable timeout ---
91
+
92
+ #it "accepts a timeout parameter" do
93
+ # result = Brute::Tools::Shell.new.call(command: "sleep 0.1 && echo done", timeout: 10)
94
+ # result.should =~ /done/
95
+ #end
96
+
97
+ #it "times out with a short timeout" do
98
+ # result = Brute::Tools::Shell.new.call(command: "sleep 10", timeout: 1)
99
+ # result.should =~ /timed out/i
100
+ #end
101
101
  end
@@ -25,7 +25,7 @@ module Brute
25
25
  # under TRUNCATION_DIR (e.g. ~/.local/share/brute/tool-output/).
26
26
  # Return a preview + hint pointing to the saved file.
27
27
  # 5. Hint message — when truncated, append a contextual hint:
28
- # "Full output saved to: <path>. Use Read with offset/limit to
28
+ # "Full output saved to: (path). Use Read with offset/limit to
29
29
  # view specific sections."
30
30
  # 6. Configurable limits — allow overriding MAX_LINES / MAX_BYTES
31
31
  # via per-call options.
@@ -47,12 +47,12 @@ module Brute
47
47
  # Returns the text unchanged if it fits. Otherwise returns a
48
48
  # truncated preview with a hint message.
49
49
  #
50
- # @param text [String] the tool output to truncate
51
- # @param max_lines [Integer] maximum number of lines to keep
52
- # @param max_bytes [Integer] maximum byte size to keep
53
- # @param direction [:head, :tail] which end to keep
54
- # @param truncation_dir [String, nil] directory to save full output when truncating
55
- # @return [String] the (possibly truncated) text
50
+ # @parameter text [String] the tool output to truncate
51
+ # @parameter max_lines [Integer] maximum number of lines to keep
52
+ # @parameter max_bytes [Integer] maximum byte size to keep
53
+ # @parameter direction [Symbol] which end to keep
54
+ # @parameter truncation_dir [String, nil] directory to save full output when truncating
55
+ # @returns [String] the (possibly truncated) text
56
56
  #
57
57
  def self.truncate(text, max_lines: MAX_LINES, max_bytes: MAX_BYTES, direction: :head, truncation_dir: nil)
58
58
  return text if text.nil? || text.empty?
data/lib/brute/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Brute
4
- VERSION = "2.0.4"
4
+ VERSION = "2.0.5"
5
5
  end
data/lib/brute.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'ruby_llm'
4
+ require "rack"
4
5
  require 'timeout'
5
6
  require 'logger'
6
7
  require 'scampi/kernel_ext'
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: 2.0.4
4
+ version: 2.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brute Contributors
@@ -93,6 +93,62 @@ dependencies:
93
93
  - - ">="
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rack
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: net-http-persistent
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'
124
+ - !ruby/object:Gem::Dependency
125
+ name: json_schemer
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '2.5'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '2.5'
138
+ - !ruby/object:Gem::Dependency
139
+ name: google-protobuf
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '4.34'
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '4.34'
96
152
  - !ruby/object:Gem::Dependency
97
153
  name: rake
98
154
  requirement: !ruby/object:Gem::Requirement
@@ -201,9 +257,11 @@ files:
201
257
  - lib/brute/truncation.rb
202
258
  - lib/brute/utils/diff.rb
203
259
  - lib/brute/version.rb
260
+ homepage: https://github.com/general-intelligence-systems/brute
204
261
  licenses:
205
262
  - MIT
206
- metadata: {}
263
+ metadata:
264
+ documentation_uri: https://general-intelligence-systems.github.io/brute/
207
265
  rdoc_options: []
208
266
  require_paths:
209
267
  - lib
@@ -211,7 +269,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
211
269
  requirements:
212
270
  - - ">="
213
271
  - !ruby/object:Gem::Version
214
- version: '3.4'
272
+ version: '3.3'
215
273
  required_rubygems_version: !ruby/object:Gem::Requirement
216
274
  requirements:
217
275
  - - ">="