brute 2.0.6 → 3.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 (83) 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/messages.rb +47 -0
  6. data/lib/brute/middleware/001_otel_span.rb +4 -2
  7. data/lib/brute/middleware/002_session_log.rb +100 -0
  8. data/lib/brute/middleware/004_summarize.rb +14 -12
  9. data/lib/brute/middleware/006_loop.rb +158 -0
  10. data/lib/brute/middleware/010_max_iterations.rb +8 -6
  11. data/lib/brute/middleware/015_otel_token_usage.rb +3 -1
  12. data/lib/brute/middleware/020_system_prompt.rb +5 -3
  13. data/lib/brute/middleware/040_compaction_check.rb +3 -1
  14. data/lib/brute/middleware/060_questions.rb +3 -1
  15. data/lib/brute/middleware/{070_tool_call.rb → 070_tool_pipeline.rb} +31 -20
  16. data/lib/brute/middleware/073_otel_tool_call.rb +3 -1
  17. data/lib/brute/middleware/075_otel_tool_results.rb +3 -1
  18. data/lib/brute/middleware/event_handler.rb +3 -1
  19. data/lib/brute/middleware/user_queue.rb +3 -1
  20. data/lib/brute/prompts/autonomy.rb +3 -0
  21. data/lib/brute/prompts/base.rb +3 -0
  22. data/lib/brute/prompts/build_switch.rb +3 -1
  23. data/lib/brute/prompts/code_references.rb +3 -0
  24. data/lib/brute/prompts/code_style.rb +3 -0
  25. data/lib/brute/prompts/conventions.rb +3 -0
  26. data/lib/brute/prompts/doing_tasks.rb +3 -0
  27. data/lib/brute/prompts/editing_approach.rb +3 -0
  28. data/lib/brute/prompts/editing_constraints.rb +3 -0
  29. data/lib/brute/prompts/environment.rb +3 -1
  30. data/lib/brute/prompts/frontend_tasks.rb +3 -0
  31. data/lib/brute/prompts/git_safety.rb +3 -0
  32. data/lib/brute/prompts/identity.rb +3 -1
  33. data/lib/brute/prompts/instructions.rb +3 -1
  34. data/lib/brute/prompts/max_steps.rb +3 -1
  35. data/lib/brute/prompts/objectivity.rb +3 -0
  36. data/lib/brute/prompts/plan_reminder.rb +3 -1
  37. data/lib/brute/prompts/proactiveness.rb +3 -0
  38. data/lib/brute/prompts/security_and_safety.rb +3 -0
  39. data/lib/brute/prompts/skills.rb +6 -2
  40. data/lib/brute/prompts/task_management.rb +3 -0
  41. data/lib/brute/prompts/tone_and_style.rb +3 -0
  42. data/lib/brute/prompts/tool_usage.rb +3 -0
  43. data/lib/brute/prompts.rb +5 -0
  44. data/lib/brute/rack/adapter.rb +237 -0
  45. data/lib/brute/skill.rb +192 -15
  46. data/lib/brute/system_prompt.rb +3 -1
  47. data/lib/brute/tools/adapter.rb +296 -0
  48. data/lib/brute/tools/fs/file_mutation_queue.rb +107 -0
  49. data/lib/brute/tools/fs/snapshot_store.rb +41 -0
  50. data/lib/brute/tools/fs_patch.rb +5 -3
  51. data/lib/brute/tools/fs_read.rb +4 -2
  52. data/lib/brute/tools/fs_remove.rb +2 -2
  53. data/lib/brute/tools/fs_search.rb +3 -1
  54. data/lib/brute/tools/fs_undo.rb +2 -2
  55. data/lib/brute/tools/fs_write.rb +5 -3
  56. data/lib/brute/tools/shell.rb +3 -1
  57. data/lib/brute/tools/skill_load.rb +156 -0
  58. data/lib/brute/tools/sub_agent.rb +118 -0
  59. data/lib/brute/tools/todo_list/store.rb +36 -0
  60. data/lib/brute/tools/todo_read.rb +1 -1
  61. data/lib/brute/tools/todo_write.rb +1 -1
  62. data/lib/brute/tools.rb +2 -1
  63. data/lib/brute/truncation.rb +3 -1
  64. data/lib/brute/turn/agent_pipeline.rb +168 -0
  65. data/lib/brute/turn/pipeline.rb +95 -0
  66. data/lib/brute/turn/tool_pipeline.rb +106 -0
  67. data/lib/brute/utils/diff.rb +3 -1
  68. data/lib/brute/version.rb +1 -1
  69. data/lib/brute.rb +46 -71
  70. data/lib/{brute → brute_cli}/providers/shell.rb +4 -1
  71. data/lib/{brute → brute_cli}/providers/shell_response.rb +3 -5
  72. data/lib/ruby_llm/message_transport.rb +117 -0
  73. metadata +34 -30
  74. data/lib/brute/agent.rb +0 -82
  75. data/lib/brute/middleware/003_tool_result_loop.rb +0 -103
  76. data/lib/brute/middleware/100_llm_call.rb +0 -63
  77. data/lib/brute/pipeline.rb +0 -97
  78. data/lib/brute/queue/file_mutation_queue.rb +0 -102
  79. data/lib/brute/session.rb +0 -51
  80. data/lib/brute/store/snapshot_store.rb +0 -36
  81. data/lib/brute/store/todo_store.rb +0 -30
  82. data/lib/brute/sub_agent.rb +0 -106
  83. data/lib/brute/tool.rb +0 -107
data/lib/brute/tool.rb DELETED
@@ -1,107 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/setup"
4
- require "brute"
5
- require 'brute/pipeline'
6
-
7
- module Brute
8
- # A Tool is a Pipeline configured for tool execution. The terminal app
9
- # does the work; middleware wraps it with concerns like file mutation
10
- # queueing, snapshotting, validation, logging.
11
- #
12
- # Coexists with Brute::Tools::* (which inherit from RubyLLM::Tool).
13
- # Use Brute::Tool when you want middleware. Use RubyLLM::Tool subclasses
14
- # for simple cases.
15
- #
16
- # Usage:
17
- #
18
- # read = Brute::Tool.new(
19
- # name: "read",
20
- # description: "Read a file's contents",
21
- # params: { file_path: { type: "string", required: true } },
22
- # ) do
23
- # use Brute::Middleware::Tool::ValidateParams
24
- # run ->(env) {
25
- # env[:result] = File.read(File.expand_path(env[:arguments][:file_path]))
26
- # }
27
- # end
28
- #
29
- # read.call(file_path: "lib/brute.rb")
30
- #
31
- class Tool < Pipeline
32
- attr_reader :name, :description, :params
33
-
34
- def initialize(name:, description:, params: {}, &block)
35
- @name = name.to_s
36
- @description = description
37
- @params = params
38
- super(&block)
39
- end
40
-
41
- # Execute the tool. Arguments come in as kwargs; result is whatever
42
- # the terminal app puts into env[:result].
43
- def call(events: NullSink.new, **arguments)
44
- env = {
45
- name: @name,
46
- arguments: arguments,
47
- result: nil,
48
- events: events,
49
- metadata: {},
50
- }
51
- super(env)
52
- env[:result]
53
- end
54
-
55
- # Adapter so the LLM can call this tool through ruby_llm.
56
- # ToolCall middleware checks for to_ruby_llm and uses it if present.
57
- def to_ruby_llm
58
- tool = self
59
- Class.new(RubyLLM::Tool) do
60
- description tool.description
61
- tool.params.each { |k, opts| param k, **opts }
62
- define_method(:name) { tool.name }
63
- define_method(:execute) { |**args| tool.call(**args) }
64
- end.new
65
- end
66
- end
67
- end
68
-
69
- test do
70
- it "exposes name, description, params" do
71
- t = Brute::Tool.new(name: "echo", description: "echo input") do
72
- run ->(env) { env[:result] = env[:arguments][:msg] }
73
- end
74
-
75
- t.name.should == "echo"
76
- t.description.should == "echo input"
77
- t.call(msg: "hi").should == "hi"
78
- end
79
-
80
- it "passes arguments through env to the terminal app" do
81
- captured = nil
82
- t = Brute::Tool.new(name: "x", description: "x") do
83
- run ->(env) { captured = env[:arguments]; env[:result] = nil }
84
- end
85
-
86
- t.call(a: 1, b: 2)
87
- captured.should == { a: 1, b: 2 }
88
- end
89
-
90
- it "runs middleware around the terminal app" do
91
- log = []
92
- wrap = Class.new do
93
- def initialize(app, tag:); @app = app; @tag = tag; end
94
- def call(env); (env[:metadata][:log] ||= []) << "in-#{@tag}"; @app.call(env); env[:metadata][:log] << "out-#{@tag}"; end
95
- end
96
-
97
- t = Brute::Tool.new(name: "x", description: "x") do
98
- use wrap, tag: "a"
99
- run ->(env) { env[:metadata][:log] << "core"; env[:result] = :ok }
100
- end
101
-
102
- # Pre-seed the log on the env that gets built — tool builds its own env,
103
- # so we capture via the middleware metadata channel
104
- result = t.call(input: 1)
105
- result.should == :ok
106
- end
107
- end