brute 2.0.6 → 3.0.1

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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/lib/brute/events/handler.rb +3 -1
  3. data/lib/brute/events/prefixed_terminal_output.rb +3 -1
  4. data/lib/brute/events/terminal_output_handler.rb +3 -1
  5. data/lib/brute/message_transport/anthropic.rb +143 -0
  6. data/lib/brute/message_transport/llm.rb +94 -0
  7. data/lib/brute/message_transport/openai.rb +113 -0
  8. data/lib/brute/message_transport/ruby_llm.rb +78 -0
  9. data/lib/brute/message_transport.rb +133 -0
  10. data/lib/brute/messages.rb +120 -0
  11. data/lib/brute/middleware/001_otel_span.rb +4 -2
  12. data/lib/brute/middleware/002_session_log.rb +100 -0
  13. data/lib/brute/middleware/004_summarize.rb +21 -19
  14. data/lib/brute/middleware/006_loop.rb +158 -0
  15. data/lib/brute/middleware/010_max_iterations.rb +9 -7
  16. data/lib/brute/middleware/015_otel_token_usage.rb +3 -1
  17. data/lib/brute/middleware/020_system_prompt.rb +6 -4
  18. data/lib/brute/middleware/040_compaction_check.rb +5 -3
  19. data/lib/brute/middleware/060_questions.rb +3 -1
  20. data/lib/brute/middleware/{070_tool_call.rb → 070_tool_pipeline.rb} +60 -46
  21. data/lib/brute/middleware/073_otel_tool_call.rb +3 -1
  22. data/lib/brute/middleware/075_otel_tool_results.rb +3 -1
  23. data/lib/brute/middleware/event_handler.rb +3 -1
  24. data/lib/brute/middleware/user_queue.rb +3 -1
  25. data/lib/brute/prompts/autonomy.rb +3 -0
  26. data/lib/brute/prompts/base.rb +3 -0
  27. data/lib/brute/prompts/build_switch.rb +3 -1
  28. data/lib/brute/prompts/code_references.rb +3 -0
  29. data/lib/brute/prompts/code_style.rb +3 -0
  30. data/lib/brute/prompts/conventions.rb +3 -0
  31. data/lib/brute/prompts/doing_tasks.rb +3 -0
  32. data/lib/brute/prompts/editing_approach.rb +3 -0
  33. data/lib/brute/prompts/editing_constraints.rb +3 -0
  34. data/lib/brute/prompts/environment.rb +3 -1
  35. data/lib/brute/prompts/frontend_tasks.rb +3 -0
  36. data/lib/brute/prompts/git_safety.rb +3 -0
  37. data/lib/brute/prompts/identity.rb +3 -1
  38. data/lib/brute/prompts/instructions.rb +3 -1
  39. data/lib/brute/prompts/max_steps.rb +3 -1
  40. data/lib/brute/prompts/objectivity.rb +3 -0
  41. data/lib/brute/prompts/plan_reminder.rb +3 -1
  42. data/lib/brute/prompts/proactiveness.rb +3 -0
  43. data/lib/brute/prompts/security_and_safety.rb +3 -0
  44. data/lib/brute/prompts/skills.rb +6 -2
  45. data/lib/brute/prompts/task_management.rb +3 -0
  46. data/lib/brute/prompts/tone_and_style.rb +3 -0
  47. data/lib/brute/prompts/tool_usage.rb +3 -0
  48. data/lib/brute/prompts.rb +5 -0
  49. data/lib/brute/rack/adapter.rb +237 -0
  50. data/lib/brute/skill.rb +192 -15
  51. data/lib/brute/system_prompt.rb +3 -1
  52. data/lib/brute/tool.rb +92 -75
  53. data/lib/brute/tools/adapter.rb +258 -0
  54. data/lib/brute/tools/fs/file_mutation_queue.rb +107 -0
  55. data/lib/brute/tools/fs/snapshot_store.rb +41 -0
  56. data/lib/brute/tools/fs_patch.rb +6 -4
  57. data/lib/brute/tools/fs_read.rb +5 -3
  58. data/lib/brute/tools/fs_remove.rb +3 -3
  59. data/lib/brute/tools/fs_search.rb +4 -2
  60. data/lib/brute/tools/fs_undo.rb +3 -3
  61. data/lib/brute/tools/fs_write.rb +6 -4
  62. data/lib/brute/tools/net_fetch.rb +1 -1
  63. data/lib/brute/tools/question.rb +1 -1
  64. data/lib/brute/tools/shell.rb +4 -2
  65. data/lib/brute/tools/skill_load.rb +156 -0
  66. data/lib/brute/tools/sub_agent.rb +101 -0
  67. data/lib/brute/tools/todo_list/store.rb +36 -0
  68. data/lib/brute/tools/todo_read.rb +2 -2
  69. data/lib/brute/tools/todo_write.rb +2 -2
  70. data/lib/brute/tools.rb +2 -1
  71. data/lib/brute/truncation.rb +3 -1
  72. data/lib/brute/turn/agent_pipeline.rb +169 -0
  73. data/lib/brute/turn/pipeline.rb +95 -0
  74. data/lib/brute/turn/tool_pipeline.rb +96 -0
  75. data/lib/brute/utils/diff.rb +3 -1
  76. data/lib/brute/version.rb +1 -1
  77. data/lib/brute.rb +45 -71
  78. data/lib/{brute → brute_cli}/providers/shell.rb +4 -1
  79. data/lib/{brute → brute_cli}/providers/shell_response.rb +9 -15
  80. metadata +52 -43
  81. data/lib/brute/agent.rb +0 -82
  82. data/lib/brute/middleware/003_tool_result_loop.rb +0 -103
  83. data/lib/brute/middleware/100_llm_call.rb +0 -63
  84. data/lib/brute/pipeline.rb +0 -97
  85. data/lib/brute/queue/file_mutation_queue.rb +0 -102
  86. data/lib/brute/session.rb +0 -51
  87. data/lib/brute/store/snapshot_store.rb +0 -36
  88. data/lib/brute/store/todo_store.rb +0 -30
  89. data/lib/brute/sub_agent.rb +0 -106
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "brute"
5
+
6
+ module Brute
7
+ module Turn
8
+ class Pipeline < ::Rack::Builder
9
+ module Chainable
10
+ # Enables the following syntax:
11
+ #
12
+ # Brute.agent
13
+ # .use(UltraSecurity)
14
+ # .use(MaxProfit)
15
+ # .use(DontTellMom)
16
+ # .run -> (env) {
17
+ # your_llm_library.complete("How to make money?")
18
+ # }
19
+ #
20
+ def use(...) = tap { super }
21
+ def run(...) = tap { super }
22
+ end
23
+
24
+ include Chainable
25
+
26
+ # Rack's `new_from_string` evaluates the script then returns `to_app` —
27
+ # the built callable. An agent, though, is the *builder*: you `.start` it,
28
+ # and it may be re-`use`d or served through the Rack adapter. So evaluate
29
+ # the script against a fresh builder and hand back the builder itself.
30
+ # This also backs `parse_file` (→ `load_file` → here), so
31
+ # `AgentPipeline.parse_file("agent.ru").start(prompt)` works as documented.
32
+ def self.new_from_string(builder_script, path = "(rackup)", **options)
33
+ builder = new(**options)
34
+ eval(builder_script, ::Rack::BUILDER_TOPLEVEL_BINDING.call(builder), path) # rubocop:disable Security/Eval
35
+ builder
36
+ end
37
+
38
+ # Brute's name for Rack's `to_app`: nest the middleware around the
39
+ # terminal app and return the runnable callable. Raises (via `to_app`)
40
+ # when `run` was never called.
41
+ alias_method :build, :to_app
42
+
43
+ # Default null sink for env[:events] — swallows anything pushed to it.
44
+ class NullSink
45
+ def <<(_event); self; end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ __END__
52
+
53
+ describe "brute/turn/pipeline" do
54
+ it "builds and calls a chain" do
55
+ seen = []
56
+ inc = Class.new do
57
+ def initialize(app, label:); @app = app; @label = label; end
58
+ def call(env); env[:trace] << @label; @app.call(env); env[:trace] << "#{@label}-after"; end
59
+ end
60
+
61
+ pipeline = Brute::Turn::Pipeline.new do
62
+ use inc, label: "outer"
63
+ use inc, label: "inner"
64
+ run ->(env) { env[:trace] << "core" }
65
+ end
66
+
67
+ env = { trace: [] }
68
+ pipeline.call(env)
69
+ env[:trace].should == ["outer", "inner", "core", "inner-after", "outer-after"]
70
+ end
71
+
72
+ it "raises when run was never called" do
73
+ lambda { Brute::Turn::Pipeline.new.call({}) }.should.raise(RuntimeError)
74
+ end
75
+
76
+ it "accepts a callable as the terminal app" do
77
+ pipeline = Brute::Turn::Pipeline.new do
78
+ run ->(env) { env[:result] = 42 }
79
+ end
80
+ env = {}
81
+ pipeline.call(env)
82
+ env[:result].should == 42
83
+ end
84
+
85
+ it "run accepts a block terminal and stays chainable" do
86
+ pipeline = Brute::Turn::Pipeline.new.use(Class.new do
87
+ def initialize(app); @app = app; end
88
+ def call(env); env[:trace] << "mw"; @app.call(env); end
89
+ end).run { |env| env[:trace] << "core" }
90
+
91
+ env = { trace: [] }
92
+ pipeline.call(env)
93
+ env[:trace].should == ["mw", "core"]
94
+ end
95
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "brute"
5
+ require "brute/turn/pipeline"
6
+
7
+ module Brute
8
+ module Turn
9
+ # A ToolPipeline runs a tool call through a middleware stack. Like
10
+ # AgentPipeline it *composes* a Pipeline rather than inheriting: the
11
+ # definition block is instance_eval'd into the internal Pipeline, so `use`
12
+ # / `run` inside it are the builder's methods. The tool's terminal app does
13
+ # the work; middleware wraps it with concerns like file mutation queueing,
14
+ # validation, logging.
15
+ #
16
+ # Coexists with Brute::Tools::* (which inherit from Brute::Tool). Use a
17
+ # ToolPipeline when you want middleware; use Brute::Tool subclasses for
18
+ # simple cases.
19
+ #
20
+ # read = Brute::Turn::ToolPipeline.new(
21
+ # name: "read",
22
+ # description: "Read a file's contents",
23
+ # params: { file_path: { type: "string", required: true } },
24
+ # ) do
25
+ # use Brute::Middleware::Tool::ValidateParams
26
+ # run ->(env) {
27
+ # env[:result] = File.read(File.expand_path(env[:arguments][:file_path]))
28
+ # }
29
+ # end
30
+ #
31
+ # read.call(file_path: "lib/brute.rb")
32
+ #
33
+ class ToolPipeline
34
+ attr_reader :name, :description, :params
35
+
36
+ def initialize(name:, description:, params: {}, &block)
37
+ @name = name.to_s
38
+ @description = description
39
+ @params = params
40
+ @pipeline = Pipeline.new
41
+ @pipeline.instance_eval(&block) if block
42
+ end
43
+
44
+ def call(events: Pipeline::NullSink.new, **arguments)
45
+ env = {
46
+ name: @name,
47
+ arguments: arguments,
48
+ result: nil,
49
+ events: events,
50
+ metadata: {},
51
+ }
52
+ @pipeline.call(env)
53
+ env[:result]
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+
60
+ __END__
61
+
62
+ describe "brute/turn/tool_pipeline" do
63
+ it "exposes name, description, params" do
64
+ t = Brute::Turn::ToolPipeline.new(name: "echo", description: "echo input") do
65
+ run ->(env) { env[:result] = env[:arguments][:msg] }
66
+ end
67
+
68
+ t.name.should == "echo"
69
+ t.description.should == "echo input"
70
+ t.call(msg: "hi").should == "hi"
71
+ end
72
+
73
+ it "passes arguments through env to the terminal app" do
74
+ captured = nil
75
+ t = Brute::Turn::ToolPipeline.new(name: "x", description: "x") do
76
+ run ->(env) { captured = env[:arguments]; env[:result] = nil }
77
+ end
78
+
79
+ t.call(a: 1, b: 2)
80
+ captured.should == { a: 1, b: 2 }
81
+ end
82
+
83
+ it "runs middleware around the terminal app (instance_eval'd block)" do
84
+ wrap = Class.new do
85
+ def initialize(app, tag:); @app = app; @tag = tag; end
86
+ def call(env); (env[:metadata][:log] ||= []) << "in-#{@tag}"; @app.call(env); env[:metadata][:log] << "out-#{@tag}"; end
87
+ end
88
+
89
+ t = Brute::Turn::ToolPipeline.new(name: "x", description: "x") do
90
+ use wrap, tag: "a"
91
+ run ->(env) { env[:metadata][:log] << "core"; env[:result] = :ok }
92
+ end
93
+
94
+ t.call(input: 1).should == :ok
95
+ end
96
+ end
@@ -27,7 +27,9 @@ module Brute
27
27
  end
28
28
  end
29
29
 
30
- test do
30
+ __END__
31
+
32
+ describe "brute/utils/diff" do
31
33
  it "generates a unified diff for changed content" do
32
34
  Brute::Diff.unified("line1\nold\nline3\n", "line1\nnew\nline3\n").should =~ /\-old/
33
35
  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 = "2.0.6"
4
+ VERSION = "3.0.1"
5
5
  end
data/lib/brute.rb CHANGED
@@ -1,22 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ruby_llm'
3
+ require "bundler/setup"
4
+
4
5
  require "rack"
5
6
  require 'timeout'
6
7
  require 'logger'
7
- require 'scampi/kernel_ext'
8
8
  require 'colorize_extended'
9
9
  require 'active_support/all'
10
10
 
11
- # Brute — a coding agent built on ruby_llm
12
- #
13
- # Cross-cutting concerns are implemented as Rack-style middleware in a
14
- # Pipeline that wraps every LLM call:
15
- #
16
- # Tracing → Retry → Session → Tokens → Compaction → ToolErrors → DoomLoop → Reasoning → [LLM Call]
17
- #
18
-
19
- require 'brute/version'
11
+ require_relative 'brute/version'
20
12
 
21
13
  module Brute
22
14
  LOGO = <<-LOGO
@@ -29,70 +21,51 @@ module Brute
29
21
  `Y8bod8P' d888b `V88V"V8P' "888" `Y8bod8P'
30
22
  LOGO
31
23
 
32
- def self.config
33
- @config ||= begin
34
- RubyLLM.configure do |config|
35
- # Anthropic
36
- config.anthropic_api_key = ENV['ANTHROPIC_API_KEY']
37
- config.anthropic_api_base = ENV['ANTHROPIC_API_BASE'] # Available in v1.13.0+ (optional custom Anthropic endpoint)
38
-
39
- # Azure
40
- config.azure_api_base = ENV['AZURE_API_BASE'] # Microsoft Foundry project endpoint
41
- config.azure_api_key = ENV['AZURE_API_KEY'] # use this or
42
- config.azure_ai_auth_token = ENV['AZURE_AI_AUTH_TOKEN'] # this
43
-
44
- # Bedrock
45
- config.bedrock_api_key = ENV['AWS_ACCESS_KEY_ID']
46
- config.bedrock_secret_key = ENV['AWS_SECRET_ACCESS_KEY']
47
- config.bedrock_region = ENV['AWS_REGION'] # Required for Bedrock
48
- config.bedrock_session_token = ENV['AWS_SESSION_TOKEN'] # For temporary credentials
49
-
50
- # DeepSeek
51
- config.deepseek_api_key = ENV['DEEPSEEK_API_KEY']
52
- config.deepseek_api_base = ENV['DEEPSEEK_API_BASE'] # Available in v1.13.0+ (optional custom DeepSeek endpoint)
53
-
54
- # Gemini
55
- config.gemini_api_key = ENV['GEMINI_API_KEY']
56
- config.gemini_api_base = ENV['GEMINI_API_BASE'] # Available in v1.9.0+ (optional API version override)
57
-
58
- # GPUStack
59
- config.gpustack_api_base = ENV['GPUSTACK_API_BASE']
60
- config.gpustack_api_key = ENV['GPUSTACK_API_KEY']
61
-
62
- # Mistral
63
- config.mistral_api_key = ENV['MISTRAL_API_KEY']
64
-
65
- # Ollama
66
- config.ollama_api_base = 'http://localhost:11434/v1'
67
- config.ollama_api_key = ENV['OLLAMA_API_KEY'] # Available in v1.13.0+ (optional for authenticated/remote Ollama endpoints)
68
-
69
- # OpenAI
70
- config.openai_api_key = ENV['OPENAI_API_KEY']
71
- config.openai_api_base = ENV['OPENAI_API_BASE'] # Optional custom OpenAI-compatible endpoint
72
-
73
- # OpenRouter
74
- config.openrouter_api_key = ENV['OPENROUTER_API_KEY']
75
- config.openrouter_api_base = ENV['OPENROUTER_API_BASE'] # Available in v1.13.0+ (optional custom OpenRouter endpoint)
76
-
77
- # Perplexity
78
- config.perplexity_api_key = ENV['PERPLEXITY_API_KEY']
79
-
80
- # Vertex AI
81
- config.vertexai_project_id = ENV['GOOGLE_CLOUD_PROJECT'] # Available in v1.7.0+
82
- config.vertexai_location = ENV['GOOGLE_CLOUD_LOCATION']
83
- config.vertexai_service_account_key = ENV['VERTEXAI_SERVICE_ACCOUNT_KEY'] # Optional: service account JSON key
84
-
85
- # xAI
86
- config.xai_api_key = ENV['XAI_API_KEY'] # Available in v1.11.0+
87
- end
88
- RubyLLM.config
89
- end
90
- end
24
+ # NOTE: Brute owns no LLM configuration and no LLM library. All
25
+ # provider/model/credential config lives in the pipeline's terminal `run`
26
+ # proc, which the user writes with whatever LLM library they prefer
27
+ # (ruby_llm, llm.rb, openai, anthropic, raw HTTP, ...). The proc converts
28
+ # env[:messages] (Brute::Message values — see Brute.log) to the library's
29
+ # format, makes the call, and appends the response back as Brute::Message
30
+ # values — the MessageTransport pattern. See examples/ruby_llm.rb,
31
+ # examples/llm.rb, examples/openai.rb and examples/anthropic.rb.
91
32
 
92
33
  def self.provider
93
34
  @provider ||= :anthropic
94
35
  end
95
-
36
+
37
+ # Start building an agent turn. Returns an AgentPipeline — a rack-style
38
+ # builder that is also the runnable Agent: chain `.use` for middleware and
39
+ # `.run` for the terminal LLM-call proc (both return the AgentPipeline), then
40
+ # invoke it with `#start`. It takes no config — LLM config lives in the `run`
41
+ # proc, tools go to the ToolPipeline middleware, the log to SessionLog.
42
+ #
43
+ # agent = Brute.agent
44
+ # .use(Brute::Middleware::SystemPrompt)
45
+ # .use(Brute::Middleware::ToolPipeline, tools: Brute::Tools::ALL)
46
+ # .run ->(env) { ... } # the LLM-call proc (provider/model/creds here)
47
+ #
48
+ # agent.start("what changed?")
49
+ #
50
+ # A block form is equivalent (evaluated in the AgentPipeline's context):
51
+ #
52
+ # Brute.agent do
53
+ # use Brute::Middleware::SystemPrompt
54
+ # run ->(env) { ... }
55
+ # end
56
+ def self.agent(&block)
57
+ Brute::Turn::AgentPipeline.new(&block)
58
+ end
59
+
60
+ # Adapt any Brute tools (hashes, Brute::Tool, Brute::Turn::ToolPipeline,
61
+ # SubAgent …) into a { name_sym => Brute::Tools::Adapter } hash. Each
62
+ # adapter exposes #to_h — a neutral JSON-Schema-ish definition the inline
63
+ # `run` proc converts to whatever its LLM library expects.
64
+ def self.tools(tools)
65
+ Brute::Tools::Adapter.wrap_all(tools || [])
66
+ end
67
+
68
+
96
69
  def self.provider=(p)
97
70
  @provider = p.to_sym
98
71
  end
@@ -102,3 +75,4 @@ Dir.glob("#{__dir__}/brute/**/*.rb").sort.each do |path|
102
75
  require path
103
76
  end
104
77
 
78
+ # gangsta g-dogg bruh...
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "bundler/setup"
4
+ require "brute"
5
+
3
6
  require "shellwords"
4
7
 
5
- module Brute
8
+ module BruteCLI
6
9
  module Providers
7
10
  # A pseudo-LLM provider that executes user input as code via the
8
11
  # existing Brute::Tools::Shell tool.
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- if __FILE__ == $0
4
- require "bundler/setup"
5
- require "brute"
6
- end
3
+ require "bundler/setup"
4
+ require "brute"
7
5
 
8
6
  require "securerandom"
9
7
 
10
- module Brute
8
+ module BruteCLI
11
9
  module Providers
12
10
  # Synthetic completion response returned by Brute::Providers::Shell.
13
11
  #
@@ -30,15 +28,15 @@ module Brute
30
28
  return [empty_assistant] if @command.nil?
31
29
 
32
30
  call_id = "shell_#{SecureRandom.hex(8)}"
33
- tool_calls = {
34
- call_id => RubyLLM::ToolCall.new(
31
+ tool_calls = [
32
+ Brute::ToolCall.new(
35
33
  id: call_id,
36
34
  name: "shell",
37
35
  arguments: { "command" => @command },
38
36
  )
39
- }
37
+ ]
40
38
 
41
- [RubyLLM::Message.new(
39
+ [Brute::Message.new(
42
40
  role: :assistant,
43
41
  content: "",
44
42
  tool_calls: tool_calls,
@@ -80,17 +78,13 @@ module Brute
80
78
  end
81
79
 
82
80
  def usage
83
- RubyLLM::Tokens.new(
84
- input: 0,
85
- output: 0,
86
- reasoning: 0,
87
- )
81
+ { input: 0, output: 0, reasoning: 0 }
88
82
  end
89
83
 
90
84
  private
91
85
 
92
86
  def empty_assistant
93
- RubyLLM::Message.new(role: :assistant, content: "")
87
+ Brute::Message.new(role: :assistant, content: "")
94
88
  end
95
89
  end
96
90
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brute
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.6
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brute Contributors
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
10
+ date: 1980-01-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: async
@@ -38,7 +38,7 @@ dependencies:
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.5'
40
40
  - !ruby/object:Gem::Dependency
41
- name: ruby_llm
41
+ name: activesupport
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - ">="
@@ -52,7 +52,7 @@ dependencies:
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
54
  - !ruby/object:Gem::Dependency
55
- name: scampi
55
+ name: colorize-extended
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - ">="
@@ -66,21 +66,21 @@ dependencies:
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0'
68
68
  - !ruby/object:Gem::Dependency
69
- name: activesupport
69
+ name: rack
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
- - - ">="
72
+ - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '0'
74
+ version: '3.0'
75
75
  type: :runtime
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - ">="
79
+ - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '0'
81
+ version: '3.0'
82
82
  - !ruby/object:Gem::Dependency
83
- name: colorize-extended
83
+ name: net-http-persistent
84
84
  requirement: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - ">="
@@ -94,75 +94,75 @@ dependencies:
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
96
  - !ruby/object:Gem::Dependency
97
- name: rack
97
+ name: json_schemer
98
98
  requirement: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '3.0'
102
+ version: '2.5'
103
103
  type: :runtime
104
104
  prerelease: false
105
105
  version_requirements: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: '3.0'
109
+ version: '2.5'
110
110
  - !ruby/object:Gem::Dependency
111
- name: net-http-persistent
111
+ name: google-protobuf
112
112
  requirement: !ruby/object:Gem::Requirement
113
113
  requirements:
114
- - - ">="
114
+ - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: '0'
116
+ version: '4.34'
117
117
  type: :runtime
118
118
  prerelease: false
119
119
  version_requirements: !ruby/object:Gem::Requirement
120
120
  requirements:
121
- - - ">="
121
+ - - "~>"
122
122
  - !ruby/object:Gem::Version
123
- version: '0'
123
+ version: '4.34'
124
124
  - !ruby/object:Gem::Dependency
125
- name: json_schemer
125
+ name: rake
126
126
  requirement: !ruby/object:Gem::Requirement
127
127
  requirements:
128
128
  - - "~>"
129
129
  - !ruby/object:Gem::Version
130
- version: '2.5'
131
- type: :runtime
130
+ version: '13.0'
131
+ type: :development
132
132
  prerelease: false
133
133
  version_requirements: !ruby/object:Gem::Requirement
134
134
  requirements:
135
135
  - - "~>"
136
136
  - !ruby/object:Gem::Version
137
- version: '2.5'
137
+ version: '13.0'
138
138
  - !ruby/object:Gem::Dependency
139
- name: google-protobuf
139
+ name: scampi
140
140
  requirement: !ruby/object:Gem::Requirement
141
141
  requirements:
142
142
  - - "~>"
143
143
  - !ruby/object:Gem::Version
144
- version: '4.34'
145
- type: :runtime
144
+ version: '1.0'
145
+ type: :development
146
146
  prerelease: false
147
147
  version_requirements: !ruby/object:Gem::Requirement
148
148
  requirements:
149
149
  - - "~>"
150
150
  - !ruby/object:Gem::Version
151
- version: '4.34'
151
+ version: '1.0'
152
152
  - !ruby/object:Gem::Dependency
153
- name: rake
153
+ name: lefthook
154
154
  requirement: !ruby/object:Gem::Requirement
155
155
  requirements:
156
156
  - - "~>"
157
157
  - !ruby/object:Gem::Version
158
- version: '13.0'
158
+ version: '2.1'
159
159
  type: :development
160
160
  prerelease: false
161
161
  version_requirements: !ruby/object:Gem::Requirement
162
162
  requirements:
163
163
  - - "~>"
164
164
  - !ruby/object:Gem::Version
165
- version: '13.0'
165
+ version: '2.1'
166
166
  description: Production-grade coding agent with tool execution, middleware pipeline,
167
167
  context compaction, session persistence, and multi-provider LLM support.
168
168
  executables: []
@@ -170,26 +170,30 @@ extensions: []
170
170
  extra_rdoc_files: []
171
171
  files:
172
172
  - lib/brute.rb
173
- - lib/brute/agent.rb
174
173
  - lib/brute/events/handler.rb
175
174
  - lib/brute/events/prefixed_terminal_output.rb
176
175
  - lib/brute/events/terminal_output_handler.rb
176
+ - lib/brute/message_transport.rb
177
+ - lib/brute/message_transport/anthropic.rb
178
+ - lib/brute/message_transport/llm.rb
179
+ - lib/brute/message_transport/openai.rb
180
+ - lib/brute/message_transport/ruby_llm.rb
181
+ - lib/brute/messages.rb
177
182
  - lib/brute/middleware/001_otel_span.rb
178
- - lib/brute/middleware/003_tool_result_loop.rb
183
+ - lib/brute/middleware/002_session_log.rb
179
184
  - lib/brute/middleware/004_summarize.rb
180
185
  - lib/brute/middleware/005_tracing.rb
186
+ - lib/brute/middleware/006_loop.rb
181
187
  - lib/brute/middleware/010_max_iterations.rb
182
188
  - lib/brute/middleware/015_otel_token_usage.rb
183
189
  - lib/brute/middleware/020_system_prompt.rb
184
190
  - lib/brute/middleware/040_compaction_check.rb
185
191
  - lib/brute/middleware/060_questions.rb
186
- - lib/brute/middleware/070_tool_call.rb
192
+ - lib/brute/middleware/070_tool_pipeline.rb
187
193
  - lib/brute/middleware/073_otel_tool_call.rb
188
194
  - lib/brute/middleware/075_otel_tool_results.rb
189
- - lib/brute/middleware/100_llm_call.rb
190
195
  - lib/brute/middleware/event_handler.rb
191
196
  - lib/brute/middleware/user_queue.rb
192
- - lib/brute/pipeline.rb
193
197
  - lib/brute/prompts.rb
194
198
  - lib/brute/prompts/autonomy.rb
195
199
  - lib/brute/prompts/base.rb
@@ -232,17 +236,14 @@ files:
232
236
  - lib/brute/prompts/text/tool_usage/google.txt
233
237
  - lib/brute/prompts/tone_and_style.rb
234
238
  - lib/brute/prompts/tool_usage.rb
235
- - lib/brute/providers/shell.rb
236
- - lib/brute/providers/shell_response.rb
237
- - lib/brute/queue/file_mutation_queue.rb
238
- - lib/brute/session.rb
239
+ - lib/brute/rack/adapter.rb
239
240
  - lib/brute/skill.rb
240
- - lib/brute/store/snapshot_store.rb
241
- - lib/brute/store/todo_store.rb
242
- - lib/brute/sub_agent.rb
243
241
  - lib/brute/system_prompt.rb
244
242
  - lib/brute/tool.rb
245
243
  - lib/brute/tools.rb
244
+ - lib/brute/tools/adapter.rb
245
+ - lib/brute/tools/fs/file_mutation_queue.rb
246
+ - lib/brute/tools/fs/snapshot_store.rb
246
247
  - lib/brute/tools/fs_patch.rb
247
248
  - lib/brute/tools/fs_read.rb
248
249
  - lib/brute/tools/fs_remove.rb
@@ -252,11 +253,19 @@ files:
252
253
  - lib/brute/tools/net_fetch.rb
253
254
  - lib/brute/tools/question.rb
254
255
  - lib/brute/tools/shell.rb
256
+ - lib/brute/tools/skill_load.rb
257
+ - lib/brute/tools/sub_agent.rb
258
+ - lib/brute/tools/todo_list/store.rb
255
259
  - lib/brute/tools/todo_read.rb
256
260
  - lib/brute/tools/todo_write.rb
257
261
  - lib/brute/truncation.rb
262
+ - lib/brute/turn/agent_pipeline.rb
263
+ - lib/brute/turn/pipeline.rb
264
+ - lib/brute/turn/tool_pipeline.rb
258
265
  - lib/brute/utils/diff.rb
259
266
  - lib/brute/version.rb
267
+ - lib/brute_cli/providers/shell.rb
268
+ - lib/brute_cli/providers/shell_response.rb
260
269
  homepage: https://github.com/general-intelligence-systems/brute
261
270
  licenses:
262
271
  - MIT
@@ -276,7 +285,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
276
285
  - !ruby/object:Gem::Version
277
286
  version: '0'
278
287
  requirements: []
279
- rubygems_version: 4.0.6
288
+ rubygems_version: 3.7.2
280
289
  specification_version: 4
281
- summary: A coding agent built on ruby_llm
290
+ summary: A framework-agnostic coding agent
282
291
  test_files: []