brute 4.1.0 → 4.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 428a37a7f0f2e0930d1b0c901fc75606686c25e07046b54c6dc4964fbbb88433
4
- data.tar.gz: 33f0b4a2956004b7e4d26790f981718fea46d2ae7cf1f54deaea250a7951edaf
3
+ metadata.gz: 6016495cc66cb0d8c5b7b33aaef14eee694960355bb2d772c2d75b1aaacca844
4
+ data.tar.gz: 137446fd33a37736993e2d067d75169d8ca1ce83c109511054b73e768550fda5
5
5
  SHA512:
6
- metadata.gz: 7c382ae2d9229bdd7524cbdb87614f94b8c54e25ae2f76ee68892198bcf8d901d35398b3555f712b07253f17c3dddd1d8d66907c44ff9eaa28dd5adca3441582
7
- data.tar.gz: 1c3816fff0d6664a25a9bfcb0c44df62c6833c2ba5066b3cbec0772fd24ea1ab4e078b7e1faca3f577297842fdd92c3aca59368f376993ba88956aaf3c02d500
6
+ metadata.gz: 4098f01a58d7d6946fb4f362aab7d37db86cb1f6e77def51f96e08b86eddbaa8c5462e8a613d9e74ff84731baf5ed65076abd010c2ab854b5e838279b3801ee3
7
+ data.tar.gz: 25c4986d4beed8103eb6c0f527ad737b98de3f7c3d1e562c83f956afcc3b3267eb5c6cb669ac488cf9ffdf97c6a446888f9363a2d01bd6f3913701d7cc46c313
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "date"
4
4
 
5
- require_relative "deprecate"
5
+ require "gem_kit"
6
6
 
7
7
  module Brute
8
8
  # A parser and linter for CHANGELOG.md, which follows
@@ -34,8 +34,8 @@ module Brute
34
34
  # Deprecated. This moved out to the gem_kit-release gem, where the rest of
35
35
  # the release toolchain now lives — `gem kit changelog` is the CLI over it.
36
36
  # The implementation stays here, working, until the deadline.
37
- extend Brute::Deprecate
38
- brute_deprecate_constant "GemKit::Release::Changelog", "5.0"
37
+ extend GemKit::Deprecate
38
+ superseded_by "GemKit::Release::Changelog", "5.0"
39
39
 
40
40
  # The six change types Keep a Changelog defines. Anything else under a
41
41
  # version is a typo or an invention, and both are worth catching.
@@ -1,190 +1,91 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rubygems/deprecate"
4
-
5
- # gem_kit-release is a *development* dependency: the release tooling reads the
6
- # registry below, but the library itself must not depend on release tooling to
7
- # load. So mirror into its registry when it happens to be there, and carry on
8
- # when it is not.
9
- begin
10
- require "gem_kit/release/deprecate"
11
- rescue LoadError
12
- # Not installed — this is a normal production install of Brute.
13
- end
3
+ require "gem_kit"
14
4
 
15
5
  module Brute
16
- # Brute's deprecation policy, built on Gem::Deprecate.
17
- #
18
- # Rubygems' own convention is the one worth copying: a deprecation names its
19
- # replacement and the *version it will be removed in*, so "deprecated" is a
20
- # dated promise rather than an open-ended apology. Brute adds one thing on
21
- # top — a registry. Every declaration records itself, which turns the set of
22
- # outstanding deprecations into data the tooling can act on:
23
- #
24
- # bin/deprecations # list everything still outstanding
25
- # bin/increment-version major # refuses to bump past a removal deadline
26
- #
27
- # Deprecate a method:
6
+ # Deprecated. This is [`GemKit::Deprecate`](https://rubygems.org/gems/gem_kit)
7
+ # now — the same code, extracted so that other gems could use it, and so that
8
+ # `gem kit deprecations` could read one registry rather than two.
28
9
  #
29
- # class Session
30
- # extend Brute::Deprecate
10
+ # extend Brute::Deprecate -> extend GemKit::Deprecate
11
+ # brute_deprecate -> deprecate
12
+ # brute_deprecate_constant -> superseded_by
31
13
  #
32
- # def old_reset; new_reset; end
33
- # brute_deprecate :old_reset, "Session#new_reset", "5.0"
34
- # end
35
- #
36
- # Deprecate a whole class — a renamed or moved constant. Leave the old name
37
- # in place as a subclass of the new one, then declare it:
38
- #
39
- # class Completion < Brute::Completion::OpenRouter
40
- # extend Brute::Deprecate
41
- # brute_deprecate_constant "Brute::Completion::OpenRouter", "5.0"
42
- # end
43
- #
44
- # Both warn on use, naming the caller. Gem::Deprecate.skip_during { ... }
45
- # silences them, so a test suite can exercise the old path in quiet.
14
+ # Extending this module still works and still registers: it warns, then
15
+ # extends GemKit::Deprecate for you and aliases the old method names onto the
16
+ # new ones. It will stop working in 5.0.
46
17
  module Deprecate
47
- extend Gem::Deprecate
48
-
49
- # One outstanding deprecation. `removed_in` is the version the old name
50
- # stops existing in the deadline both CLI commands read.
51
- Entry = Struct.new(:name, :replacement, :removed_in, :declared_at, keyword_init: true) do
52
- def to_s
53
- "#{name} -> #{replacement == :none ? "(no replacement)" : replacement}"
54
- end
55
- end
56
-
57
- class << self
58
- # Every deprecation declared in the loaded library, in declaration order.
59
- def registry
60
- @registry ||= []
61
- end
62
-
63
- # Record a deprecation. Returns the Entry.
64
- def register(name:, replacement:, removed_in:, declared_at: nil)
65
- entry = Entry.new(
66
- name: name.to_s,
67
- replacement: replacement,
68
- removed_in: Gem::Version.new(removed_in.to_s),
69
- declared_at: declared_at || caller_locations(1, 1)&.first&.then { |l| "#{l.path}:#{l.lineno}" },
70
- )
71
- registry << entry
72
-
73
- # Same shape (name, replacement, removed_in, declared_at), so
74
- # `gem kit deprecations` can read Brute's entries directly.
75
- if defined?(::GemKit::Release::Deprecate)
76
- ::GemKit::Release::Deprecate.registry << entry
77
- end
78
-
79
- entry
80
- end
81
-
82
- # The deprecations that come due at `version` — everything whose removal
83
- # deadline has arrived or passed. This is the gate: releasing `version`
84
- # with any of these still present breaks the promise the warning made.
85
- def pending(version)
86
- target = Gem::Version.new(version.to_s)
87
- registry.select { |entry| entry.removed_in <= target }
88
- end
89
-
90
- # Deprecations still in their grace period at `version`.
91
- def upcoming(version)
92
- target = Gem::Version.new(version.to_s)
93
- registry.reject { |entry| entry.removed_in <= target }
94
- end
95
-
96
- # The default deadline: the next major version after the current one.
97
- def next_major_version
98
- Gem::Version.new(Brute::VERSION.split(".").first).bump.to_s
99
- end
100
-
101
- # Single funnel for every warning, so Gem::Deprecate.skip_during works
102
- # across all of them and specs have one place to listen.
103
- def warn(message)
104
- Kernel.warn(message) unless Gem::Deprecate.skip
105
- end
106
-
107
- # Build the Gem::Deprecate-shaped message body. `origin` must be
108
- # computed at the call site — one frame deeper and it names this file
109
- # rather than the code that needs changing.
110
- def message(target, replacement, removed_in, origin)
111
- [
112
- "NOTE: #{target} is deprecated",
113
- replacement == :none ? " with no replacement" : "; use #{replacement} instead",
114
- ". It will be removed in Brute #{removed_in}",
115
- "\n#{target} called from #{origin}.",
116
- ].join
117
- end
118
- end
119
-
120
- # Deprecate one method. Mirrors Gem::Deprecate#rubygems_deprecate, but the
121
- # deadline is explicit rather than "the next major" — a deprecation added
122
- # late in a cycle usually wants the major after next.
123
- def brute_deprecate(name, replacement = :none, removed_in = Brute::Deprecate.next_major_version)
124
- label = singleton_class? ? "#{attached_object}.#{name}" : "#{self}##{name}"
125
- Brute::Deprecate.register(
126
- name: label,
127
- replacement: replacement,
128
- removed_in: removed_in,
129
- declared_at: caller_locations(1, 1)&.first&.then { |l| "#{l.path}:#{l.lineno}" },
18
+ REPLACEMENT = "GemKit::Deprecate"
19
+ REMOVED_IN = "5.0"
20
+
21
+ # The names Brute used before the extraction. `deprecate` collides with
22
+ # Gem::Deprecate's own, which is why Brute's carried a prefix; GemKit's
23
+ # namespace does that job instead.
24
+ ALIASES = { brute_deprecate: :deprecate, brute_deprecate_constant: :superseded_by }.freeze
25
+
26
+ def self.extended(base)
27
+ origin = Gem.location_of_caller.join(":")
28
+ GemKit::Deprecate.warn(
29
+ GemKit::Deprecate.message("Brute::Deprecate", REPLACEMENT, REMOVED_IN, origin),
130
30
  )
131
31
 
132
- class_eval do
133
- old = "_deprecated_#{name}"
134
- alias_method old, name
135
- define_method name do |*args, &block|
136
- target = is_a?(Module) ? "#{self}.#{name}" : "#{self.class}##{name}"
137
- origin = Gem.location_of_caller.join(":")
138
- Brute::Deprecate.warn(Brute::Deprecate.message(target, replacement, removed_in, origin))
139
- send(old, *args, &block)
140
- end
141
- ruby2_keywords name if respond_to?(:ruby2_keywords, true)
142
- end
32
+ base.extend(GemKit::Deprecate)
33
+ ALIASES.each { |old, new| base.singleton_class.alias_method(old, new) }
143
34
  end
144
35
 
145
- # Deprecate a whole constant the renamed-or-moved case. Call it in the
146
- # body of the old name (kept as a subclass of the new one); it registers
147
- # the rename and warns whenever the old name is instantiated.
148
- def brute_deprecate_constant(replacement, removed_in = Brute::Deprecate.next_major_version)
149
- Brute::Deprecate.register(
150
- name: name || to_s,
151
- replacement: replacement,
152
- removed_in: removed_in,
153
- declared_at: caller_locations(1, 1)&.first&.then { |l| "#{l.path}:#{l.lineno}" },
154
- )
155
-
156
- return unless respond_to?(:new)
157
-
158
- define_singleton_method(:new) do |*args, **options, &block|
159
- origin = Gem.location_of_caller.join(":")
160
- Brute::Deprecate.warn(Brute::Deprecate.message(name || to_s, replacement, removed_in, origin))
161
- super(*args, **options, &block)
162
- end
36
+ # The registry moved with the DSL. Delegated rather than mirrored: two
37
+ # registries meant two answers to "what is still outstanding".
38
+ class << self
39
+ def registry = GemKit::Deprecate.registry
40
+ def pending(version) = GemKit::Deprecate.pending(version)
41
+ def upcoming(version) = GemKit::Deprecate.upcoming(version)
42
+ def register(...) = GemKit::Deprecate.register(...)
43
+ def warn(message) = GemKit::Deprecate.warn(message)
163
44
  end
164
45
  end
165
46
  end
166
47
 
48
+ # The module itself is deprecated, and a module cannot announce that the way a
49
+ # class can — there is no `new` to wrap. Registering it by hand is what puts it
50
+ # in `gem kit deprecations` alongside everything else.
51
+ GemKit::Deprecate.register(
52
+ name: "Brute::Deprecate",
53
+ replacement: Brute::Deprecate::REPLACEMENT,
54
+ removed_in: Brute::Deprecate::REMOVED_IN,
55
+ declared_at: "#{__FILE__}:#{__LINE__ - 5}",
56
+ )
57
+
167
58
  __END__
168
59
 
169
60
  describe "brute/deprecate" do
170
- # Capture what Brute::Deprecate.warn emits, and keep the shared registry
171
- # clean — these specs declare throwaway deprecations.
172
61
  captured = []
173
- around_each = lambda do |&block|
174
- saved = Brute::Deprecate.registry.dup
175
- original = Brute::Deprecate.method(:warn)
62
+ # Capture what the shim emits, and keep the shared registry clean.
63
+ isolated = lambda do |&block|
64
+ saved = GemKit::Deprecate.registry.dup
65
+ original = GemKit::Deprecate.method(:warn)
176
66
  captured.clear
177
- Brute::Deprecate.define_singleton_method(:warn) { |message| captured << message }
67
+ GemKit::Deprecate.define_singleton_method(:warn) { |message| captured << message }
178
68
  begin
179
69
  block.call
180
70
  ensure
181
- Brute::Deprecate.define_singleton_method(:warn, original)
182
- Brute::Deprecate.registry.replace(saved)
71
+ GemKit::Deprecate.define_singleton_method(:warn, original)
72
+ GemKit::Deprecate.registry.replace(saved)
73
+ end
74
+ end
75
+
76
+ it "warns when extended, naming GemKit::Deprecate" do
77
+ isolated.call do
78
+ Class.new { extend Brute::Deprecate }
79
+
80
+ captured.size.should == 1
81
+ captured.first.should.match(/Brute::Deprecate is deprecated/)
82
+ captured.first.should.match(/use GemKit::Deprecate instead/)
83
+ captured.first.should.match(/removed in 5\.0/)
183
84
  end
184
85
  end
185
86
 
186
- it "warns on a deprecated method, naming the replacement, version and caller" do
187
- around_each.call do
87
+ it "still declares a method deprecation under the old name" do
88
+ isolated.call do
188
89
  klass = Class.new do
189
90
  extend Brute::Deprecate
190
91
  def new_name = :result
@@ -192,17 +93,13 @@ describe "brute/deprecate" do
192
93
  brute_deprecate :old_name, "Thing#new_name", "9.0"
193
94
  end
194
95
 
195
- klass.new.old_name.should == :result # still works
196
- captured.size.should == 1
197
- captured.first.should.match(/is deprecated/)
198
- captured.first.should.match(/use Thing#new_name instead/)
199
- captured.first.should.match(/removed in Brute 9\.0/)
200
- captured.first.should.match(/called from /)
96
+ klass.new.old_name.should == :result
97
+ GemKit::Deprecate.registry.last.replacement.should == "Thing#new_name"
201
98
  end
202
99
  end
203
100
 
204
- it "warns on a deprecated constant but keeps it working" do
205
- around_each.call do
101
+ it "still declares a constant deprecation under the old name" do
102
+ isolated.call do
206
103
  modern = Class.new { def initialize(x); @x = x; end; attr_reader :x }
207
104
  legacy = Class.new(modern) do
208
105
  extend Brute::Deprecate
@@ -210,116 +107,26 @@ describe "brute/deprecate" do
210
107
  brute_deprecate_constant "New::Name", "9.0"
211
108
  end
212
109
 
213
- legacy.new(42).x.should == 42 # still works
214
- captured.size.should == 1
215
- captured.first.should.match(/Old::Name is deprecated; use New::Name instead/)
216
- end
217
- end
218
-
219
- it "names the caller, not the deprecation machinery" do
220
- around_each.call do
221
- klass = Class.new do
222
- extend Brute::Deprecate
223
- def old_name = :result
224
- brute_deprecate :old_name, "Thing#new_name", "9.0"
225
- end
226
-
227
- # These specs live in this file's __END__, so "the caller" is a line in
228
- # deprecate.rb either way — pin the exact line to tell them apart.
229
- klass.new.old_name; call_line = __LINE__
230
- captured.first.should.match(/called from .*deprecate\.rb:#{call_line}\./)
231
- end
232
- end
233
-
234
- it "labels a class-method deprecation by the class, not its singleton" do
235
- around_each.call do
236
- Class.new do
237
- def self.to_s = "Demo"
238
- def self.old_thing = :ok
239
- class << self
240
- extend Brute::Deprecate
241
- brute_deprecate :old_thing, "Other.new_thing", "9.0"
242
- end
243
- end
244
-
245
- Brute::Deprecate.registry.last.name.should == "Demo.old_thing"
246
- end
247
- end
248
-
249
- it "registers each declaration with its deadline and source" do
250
- around_each.call do
251
- Class.new do
252
- extend Brute::Deprecate
253
- def gone = nil
254
- brute_deprecate :gone, "Other#kept", "9.0"
255
- end
256
-
257
- entry = Brute::Deprecate.registry.last
258
- entry.replacement.should == "Other#kept"
259
- entry.removed_in.should == Gem::Version.new("9.0")
260
- entry.declared_at.should.match(/deprecate\.rb:\d+/)
261
- end
262
- end
263
-
264
- it "mirrors registrations into gem_kit-release's registry when it is present" do
265
- # Conditional by design: gem_kit-release is a development dependency, so
266
- # in a production install of Brute there is nothing to mirror into. (Also
267
- # true inside a pre-commit hook, whose flake snapshot is HEAD's.)
268
- next unless defined?(::GemKit::Release::Deprecate)
269
-
270
- around_each.call do
271
- saved = GemKit::Release::Deprecate.registry.dup
272
- begin
273
- Brute::Deprecate.register(name: "Mirrored", replacement: "New", removed_in: "9.0")
274
-
275
- GemKit::Release::Deprecate.registry.last.name.should == "Mirrored"
276
- GemKit::Release::Deprecate.registry.last.removed_in.should == Gem::Version.new("9.0")
277
- ensure
278
- GemKit::Release::Deprecate.registry.replace(saved)
279
- end
110
+ legacy.new(42).x.should == 42
111
+ GemKit::Deprecate.registry.last.name.should == "Old::Name"
280
112
  end
281
113
  end
282
114
 
283
- it "splits the registry into pending (due) and upcoming at a version" do
284
- around_each.call do
285
- Brute::Deprecate.registry.clear
286
- Brute::Deprecate.register(name: "A", replacement: "A2", removed_in: "5.0")
287
- Brute::Deprecate.register(name: "B", replacement: "B2", removed_in: "6.0")
288
-
289
- Brute::Deprecate.pending("5.0.0").map(&:name).should == ["A"]
290
- Brute::Deprecate.upcoming("5.0.0").map(&:name).should == ["B"]
291
- Brute::Deprecate.pending("4.9.0").should.be.empty
292
- Brute::Deprecate.pending("6.1.0").map(&:name).should == ["A", "B"]
293
- end
294
- end
115
+ it "delegates the registry rather than keeping one of its own" do
116
+ isolated.call do
117
+ Brute::Deprecate.registry.should.equal?(GemKit::Deprecate.registry)
295
118
 
296
- it "defaults the deadline to the next major version" do
297
- around_each.call do
298
- Brute::Deprecate.next_major_version.should == Gem::Version.new(Brute::VERSION.split(".").first).bump.to_s
119
+ GemKit::Deprecate.register(name: "A", replacement: "A2", removed_in: "5.0")
120
+ Brute::Deprecate.pending("5.0.0").map(&:name).should.include?("A")
121
+ Brute::Deprecate.upcoming("4.0.0").map(&:name).should.include?("A")
299
122
  end
300
123
  end
301
124
 
302
- it "stays quiet inside Gem::Deprecate.skip_during" do
303
- saved = Brute::Deprecate.registry.dup
304
- begin
305
- klass = Class.new do
306
- extend Brute::Deprecate
307
- def quiet = :ok
308
- brute_deprecate :quiet, "Other#loud", "9.0"
309
- end
310
-
311
- warned = []
312
- original = Kernel.method(:warn)
313
- Kernel.define_singleton_method(:warn) { |*args| warned << args.join }
314
- begin
315
- Gem::Deprecate.skip_during { klass.new.quiet.should == :ok }
316
- ensure
317
- Kernel.define_singleton_method(:warn, original)
318
- end
125
+ it "registers itself, so `gem kit deprecations` lists it" do
126
+ entry = GemKit::Deprecate.registry.find { |e| e.name == "Brute::Deprecate" }
319
127
 
320
- warned.should.be.empty
321
- ensure
322
- Brute::Deprecate.registry.replace(saved)
323
- end
128
+ entry.should.not.be.nil
129
+ entry.replacement.should == "GemKit::Deprecate"
130
+ entry.removed_in.should == Gem::Version.new("5.0")
324
131
  end
325
132
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../deprecate"
3
+ require "gem_kit"
4
+
4
5
  require_relative "../completion/open_router"
5
6
 
6
7
  module Brute
@@ -13,10 +14,10 @@ module Brute
13
14
  # Brute::Middleware::OpenRouter::Completion -> Brute::Completion::OpenRouter
14
15
  #
15
16
  # The old name stays a working subclass of the new one until the deadline
16
- # below; see Brute::Deprecate and `bin/deprecations`.
17
+ # below; see DEPRECATIONS.md and `gem kit deprecations`.
17
18
  class Completion < Brute::Completion::OpenRouter
18
- extend Brute::Deprecate
19
- brute_deprecate_constant "Brute::Completion::OpenRouter", "5.0"
19
+ extend GemKit::Deprecate
20
+ superseded_by "Brute::Completion::OpenRouter", "5.0"
20
21
  end
21
22
  end
22
23
  end
@@ -33,22 +34,22 @@ describe "brute/middleware/open_router" do
33
34
 
34
35
  it "warns on use, naming the replacement and the removal version" do
35
36
  captured = []
36
- original = Brute::Deprecate.method(:warn)
37
- Brute::Deprecate.define_singleton_method(:warn) { |message| captured << message }
37
+ original = GemKit::Deprecate.method(:warn)
38
+ GemKit::Deprecate.define_singleton_method(:warn) { |message| captured << message }
38
39
  begin
39
40
  Brute::Middleware::OpenRouter::Completion.new(->(env) { env })
40
41
  ensure
41
- Brute::Deprecate.define_singleton_method(:warn, original)
42
+ GemKit::Deprecate.define_singleton_method(:warn, original)
42
43
  end
43
44
 
44
45
  captured.size.should == 1
45
46
  captured.first.should.match(/Brute::Middleware::OpenRouter::Completion is deprecated/)
46
47
  captured.first.should.match(/use Brute::Completion::OpenRouter instead/)
47
- captured.first.should.match(/removed in Brute 5\.0/)
48
+ captured.first.should.match(/removed in 5\.0/)
48
49
  end
49
50
 
50
51
  it "is registered with its removal deadline" do
51
- entry = Brute::Deprecate.registry.find { |e| e.name == "Brute::Middleware::OpenRouter::Completion" }
52
+ entry = GemKit::Deprecate.registry.find { |e| e.name == "Brute::Middleware::OpenRouter::Completion" }
52
53
  entry.should.not.be.nil
53
54
  entry.removed_in.should == Gem::Version.new("5.0")
54
55
  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.1.0"
4
+ VERSION = "4.2.0"
5
5
  end
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.1.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brute Contributors
@@ -135,6 +135,20 @@ dependencies:
135
135
  - - "~>"
136
136
  - !ruby/object:Gem::Version
137
137
  version: '4.34'
138
+ - !ruby/object:Gem::Dependency
139
+ name: gem_kit
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.2'
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '0.2'
138
152
  - !ruby/object:Gem::Dependency
139
153
  name: rake
140
154
  requirement: !ruby/object:Gem::Requirement
@@ -183,14 +197,14 @@ dependencies:
183
197
  requirements:
184
198
  - - "~>"
185
199
  - !ruby/object:Gem::Version
186
- version: '0.1'
200
+ version: '0.2'
187
201
  type: :development
188
202
  prerelease: false
189
203
  version_requirements: !ruby/object:Gem::Requirement
190
204
  requirements:
191
205
  - - "~>"
192
206
  - !ruby/object:Gem::Version
193
- version: '0.1'
207
+ version: '0.2'
194
208
  description: Production-grade coding agent with tool execution, middleware pipeline,
195
209
  context compaction, session persistence, and multi-provider LLM support.
196
210
  executables: []