riffer-rig 0.7.0 → 0.9.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +13 -3
  4. data/lib/riffer/rig/cli/flags.rb +16 -2
  5. data/lib/riffer/rig/cli.rb +2 -1
  6. data/lib/riffer/rig/commands/auth.rb +79 -0
  7. data/lib/riffer/rig/commands/model.rb +70 -21
  8. data/lib/riffer/rig/commands/reload.rb +23 -0
  9. data/lib/riffer/rig/credentials.rb +13 -1
  10. data/lib/riffer/rig/extension.rb +12 -0
  11. data/lib/riffer/rig/loader.rb +327 -23
  12. data/lib/riffer/rig/provider_setup.rb +36 -5
  13. data/lib/riffer/rig/providers.rb +48 -0
  14. data/lib/riffer/rig/registrar.rb +8 -0
  15. data/lib/riffer/rig/runtime.rb +110 -21
  16. data/lib/riffer/rig/sdk.rb +62 -0
  17. data/lib/riffer/rig/settings/document.rb +9 -1
  18. data/lib/riffer/rig/settings.rb +5 -4
  19. data/lib/riffer/rig/stores/jsonl.rb +115 -0
  20. data/lib/riffer/rig/stores/recorder.rb +119 -0
  21. data/lib/riffer/rig/terminal/renderer.rb +86 -13
  22. data/lib/riffer/rig/terminal/wrapper.rb +103 -0
  23. data/lib/riffer/rig/trust.rb +37 -0
  24. data/lib/riffer/rig/version.rb +1 -1
  25. data/lib/riffer/rig.rb +7 -1
  26. data/sig/generated/riffer/rig/cli/flags.rbs +5 -2
  27. data/sig/generated/riffer/rig/commands/auth.rbs +35 -0
  28. data/sig/generated/riffer/rig/commands/model.rbs +21 -9
  29. data/sig/generated/riffer/rig/commands/reload.rbs +12 -0
  30. data/sig/generated/riffer/rig/credentials.rbs +8 -0
  31. data/sig/generated/riffer/rig/extension.rbs +4 -0
  32. data/sig/generated/riffer/rig/loader.rbs +142 -5
  33. data/sig/generated/riffer/rig/provider_setup.rbs +15 -4
  34. data/sig/generated/riffer/rig/providers.rbs +28 -0
  35. data/sig/generated/riffer/rig/registrar.rbs +6 -0
  36. data/sig/generated/riffer/rig/runtime.rbs +63 -9
  37. data/sig/generated/riffer/rig/sdk.rbs +33 -0
  38. data/sig/generated/riffer/rig/settings/document.rbs +7 -1
  39. data/sig/generated/riffer/rig/settings.rbs +2 -3
  40. data/sig/generated/riffer/rig/stores/jsonl.rbs +64 -0
  41. data/sig/generated/riffer/rig/stores/recorder.rbs +67 -0
  42. data/sig/generated/riffer/rig/terminal/renderer.rbs +41 -1
  43. data/sig/generated/riffer/rig/terminal/wrapper.rbs +43 -0
  44. data/sig/generated/riffer/rig/trust.rbs +20 -0
  45. data/sig/generated/riffer/rig.rbs +4 -0
  46. data/sig/manual/riffer/rig/commands/auth.rbs +6 -0
  47. data/sig/manual/riffer/rig/commands/reload.rbs +6 -0
  48. data/sig/manual/riffer/rig/providers.rbs +6 -0
  49. data/sig/manual/riffer/rig/sdk.rbs +6 -0
  50. data/sig/manual/riffer/rig/stores.rbs +11 -0
  51. data/sig/manual/riffer/rig/trust.rbs +6 -0
  52. metadata +23 -29
@@ -1,32 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'io/console'
3
4
  require 'json'
4
5
 
5
6
  class Riffer::Rig::Terminal::Renderer
6
7
  RESULT_PREVIEW_LIMIT = 200 #: Integer
7
8
 
9
+ ARGUMENT_PREVIEW_LIMIT = 60 #: Integer
10
+
11
+ PROSE_INDENT = 2 #: Integer
12
+
13
+ TOOL_CALL_INDENT = 4 #: Integer
14
+
15
+ TOOL_RESULT_INDENT = 6 #: Integer
16
+
8
17
  # @rbs @io: IO
9
18
  # @rbs @theme: Riffer::Rig::Terminal::Theme
10
19
  # @rbs @smoother: Riffer::Rig::Terminal::Smoother | PassThroughSmoother
11
20
  # @rbs @cursor: Riffer::Rig::Terminal::Cursor
12
21
  # @rbs @prose_gap_pending: bool
22
+ # @rbs @width: Integer?
23
+ # @rbs @wrapper: Riffer::Rig::Terminal::Wrapper
13
24
 
14
25
  # @rbs io: IO
15
26
  # @rbs theme: Riffer::Rig::Terminal::Theme
16
27
  # @rbs smoother: Riffer::Rig::Terminal::Smoother | PassThroughSmoother
17
28
  # @rbs cursor: Riffer::Rig::Terminal::Cursor
29
+ # @rbs width: Integer?
18
30
  # @rbs return: void
19
31
  def initialize(
20
32
  io: $stdout,
21
33
  theme: Riffer::Rig::Terminal::Theme.for(io),
22
34
  smoother: PassThroughSmoother.new(io),
23
- cursor: Riffer::Rig::Terminal::Cursor.new(io: io, theme: theme)
35
+ cursor: Riffer::Rig::Terminal::Cursor.new(io: io, theme: theme),
36
+ width: nil
24
37
  )
25
38
  @io = io
26
39
  @theme = theme
27
40
  @smoother = smoother
28
41
  @cursor = cursor
29
42
  @prose_gap_pending = false
43
+ @width = width
44
+ @wrapper = new_wrapper
30
45
  end
31
46
 
32
47
  # @rbs event: Riffer::StreamEvents::TextDelta | Riffer::StreamEvents::ToolCallDone | Riffer::StreamEvents::Interrupt
@@ -36,7 +51,7 @@ class Riffer::Rig::Terminal::Renderer
36
51
  when Riffer::StreamEvents::TextDelta
37
52
  render_prose(event.content)
38
53
  when Riffer::StreamEvents::ToolCallDone
39
- render_tool_activity(2) { @theme.cyan("⚙ #{event.name}(#{format_arguments(event.arguments)})") }
54
+ render_tool_activity(TOOL_CALL_INDENT) { "⚙ #{event.name}(#{format_arguments(event.arguments)})" }
40
55
  when Riffer::StreamEvents::Interrupt
41
56
  render_block(0) { @theme.dim("[interrupted: #{event.reason}]") }
42
57
  end
@@ -73,7 +88,12 @@ class Riffer::Rig::Terminal::Renderer
73
88
  def usage(usage, session)
74
89
  return unless usage && session
75
90
 
76
- render_block(0) { @theme.dim(usage_line(usage, session)) }
91
+ flush_prose
92
+ @prose_gap_pending = true
93
+ @io.puts
94
+ text = (@wrapper << usage_line(usage, session)) + @wrapper.flush
95
+ @io.print(@theme.dim(text)) unless text.empty?
96
+ @io.flush
77
97
  end
78
98
 
79
99
  # @rbs return: void
@@ -115,13 +135,14 @@ class Riffer::Rig::Terminal::Renderer
115
135
 
116
136
  # @rbs return: void
117
137
  def end_turn
138
+ flush_prose
118
139
  @smoother.finish
119
140
  @cursor.show
120
141
  end
121
142
 
122
143
  # @rbs return: void
123
144
  def drain
124
- @smoother.drain
145
+ flush_prose
125
146
  end
126
147
 
127
148
  # @rbs message: Riffer::Messages::Base
@@ -130,9 +151,9 @@ class Riffer::Rig::Terminal::Renderer
130
151
  return unless message.is_a?(Riffer::Messages::Tool)
131
152
 
132
153
  open_tool_activity
133
- line = "↳ #{preview(message.content)}"
154
+ line = fit("↳ #{preview(message.content)}", TOOL_RESULT_INDENT)
134
155
  styled = message.error? ? @theme.red(line) : @theme.dim(line)
135
- @io.print(" #{styled}\n")
156
+ @io.print("#{' ' * TOOL_RESULT_INDENT}#{styled}\n")
136
157
  @io.flush
137
158
  end
138
159
 
@@ -167,7 +188,7 @@ class Riffer::Rig::Terminal::Renderer
167
188
  # @rbs &block: () -> String
168
189
  # @rbs return: void
169
190
  def render_block(indent, &)
170
- drain
191
+ flush_prose
171
192
  @io.puts
172
193
  @prose_gap_pending = true
173
194
  @io.puts((' ' * indent) + yield)
@@ -182,8 +203,10 @@ class Riffer::Rig::Terminal::Renderer
182
203
  if @prose_gap_pending
183
204
  @prose_gap_pending = false
184
205
  @io.print("\n")
206
+ @wrapper = new_wrapper
185
207
  end
186
- @smoother << content
208
+ emit = @wrapper << content
209
+ @smoother << emit unless emit.empty?
187
210
  end
188
211
 
189
212
  # @rbs indent: Integer
@@ -191,7 +214,7 @@ class Riffer::Rig::Terminal::Renderer
191
214
  # @rbs return: void
192
215
  def render_tool_activity(indent, &)
193
216
  open_tool_activity
194
- @io.puts((' ' * indent) + yield)
217
+ @io.puts((' ' * indent) + @theme.cyan(fit(yield, indent)))
195
218
  @io.flush
196
219
  end
197
220
 
@@ -201,7 +224,7 @@ class Riffer::Rig::Terminal::Renderer
201
224
  # group; the group stays open so the prose after it pays the closing gap.
202
225
  return if @prose_gap_pending
203
226
 
204
- drain
227
+ flush_prose
205
228
  @io.puts
206
229
  @prose_gap_pending = true
207
230
  end
@@ -224,7 +247,7 @@ class Riffer::Rig::Terminal::Renderer
224
247
  # @rbs return: String
225
248
  def format_arguments(arguments)
226
249
  parsed = JSON.parse(arguments)
227
- parsed.map { |key, value| "#{key}: #{value.inspect}" }.join(', ')
250
+ parsed.map { |key, value| "#{key}: #{elide(value.inspect, ARGUMENT_PREVIEW_LIMIT)}" }.join(', ')
228
251
  rescue JSON::ParserError
229
252
  arguments
230
253
  end
@@ -233,8 +256,58 @@ class Riffer::Rig::Terminal::Renderer
233
256
  # @rbs return: String
234
257
  def preview(content)
235
258
  first_line = content.to_s.lines.first.to_s.chomp
236
- return first_line if first_line.length <= RESULT_PREVIEW_LIMIT
259
+ elide(first_line, RESULT_PREVIEW_LIMIT)
260
+ end
261
+
262
+ # @rbs text: String
263
+ # @rbs limit: Integer
264
+ # @rbs return: String
265
+ def elide(text, limit)
266
+ return text if text.length <= limit
267
+ return '…' if limit < 1
268
+
269
+ "#{text[0, limit - 1]}…"
270
+ end
271
+
272
+ # Truncation is what keeps an indented line from wrapping: a wrapped
273
+ # continuation starts at column zero and destroys the prose > tool nesting.
274
+ # @rbs text: String
275
+ # @rbs indent: Integer
276
+ # @rbs return: String
277
+ def fit(text, indent)
278
+ width = detect_width
279
+ return text unless width
280
+
281
+ elide(text, width - indent)
282
+ end
283
+
284
+ # @rbs return: void
285
+ def flush_prose
286
+ emit = @wrapper.flush
287
+ @smoother << emit unless emit.empty?
288
+ @smoother.drain
289
+ end
290
+
291
+ # @rbs return: Riffer::Rig::Terminal::Wrapper
292
+ def new_wrapper
293
+ Riffer::Rig::Terminal::Wrapper.new(width: detect_width, indent: PROSE_INDENT)
294
+ end
295
+
296
+ # @rbs return: Integer?
297
+ def detect_width
298
+ @width || detected_width
299
+ end
237
300
 
238
- "#{first_line[0, RESULT_PREVIEW_LIMIT]}…"
301
+ # winsize raises on ttys with no queryable window size and some pty
302
+ # wrappers report zero columns; either way the real width is unknowable,
303
+ # and nil leaves every consumer un-fitted rather than mis-fitted.
304
+ # @rbs return: Integer?
305
+ def detected_width
306
+ return nil unless @io.tty?
307
+
308
+ width = @io.winsize[1]
309
+ width.positive? ? width : nil
310
+ rescue SystemCallError
311
+ nil
239
312
  end
240
313
  end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::Terminal::Wrapper
4
+ # @rbs @width: Integer?
5
+ # @rbs @indent: Integer
6
+ # @rbs @line: String
7
+
8
+ # @rbs width: Integer?
9
+ # @rbs indent: Integer
10
+ # @rbs return: void
11
+ def initialize(width:, indent:)
12
+ @width = width
13
+ @indent = indent
14
+ @line = +''
15
+ end
16
+
17
+ # @rbs text: String
18
+ # @rbs return: String
19
+ def <<(text)
20
+ @line << text
21
+ out = +''
22
+ while (logical = take_line)
23
+ out << physical_lines(logical)
24
+ end
25
+ out
26
+ end
27
+
28
+ # @rbs return: String
29
+ def flush
30
+ out = +''
31
+ out << physical_lines(@line) unless @line.strip.empty?
32
+ @line = +''
33
+ out
34
+ end
35
+
36
+ private
37
+
38
+ # @rbs return: String?
39
+ def take_line
40
+ if (index = @line.index("\n"))
41
+ @line.slice!(0, index + 1).to_s.chomp
42
+ elsif overfull?
43
+ take_overfull_head
44
+ end
45
+ end
46
+
47
+ # @rbs return: bool
48
+ def overfull?
49
+ width = @width
50
+ return false unless width
51
+
52
+ @line.length > width - @indent
53
+ end
54
+
55
+ # @rbs return: String
56
+ def take_overfull_head
57
+ limit = (@width || 0) - @indent
58
+ head = @line[0, limit].to_s
59
+ if (space = head.rindex(' '))
60
+ @line.slice!(0, space + 1)
61
+ head[0, space].to_s
62
+ else
63
+ @line.slice!(0, limit).to_s
64
+ end
65
+ end
66
+
67
+ # @rbs logical: String
68
+ # @rbs return: String
69
+ def physical_lines(logical)
70
+ return "\n" if logical.empty?
71
+
72
+ wrap_words(logical).map { |line| "#{indent}#{line}\n" }.join
73
+ end
74
+
75
+ # @rbs return: String
76
+ def indent
77
+ ' ' * @indent
78
+ end
79
+
80
+ # @rbs text: String
81
+ # @rbs return: Array[String]
82
+ def wrap_words(text)
83
+ width = @width
84
+ limit = width ? width - @indent : nil
85
+ return [text] unless limit
86
+
87
+ out = [] #: Array[String]
88
+ current = nil #: String?
89
+ text.split.each do |word|
90
+ if current
91
+ if current.length + 1 + word.length <= limit
92
+ current = "#{current} #{word}"
93
+ next
94
+ end
95
+ out << current
96
+ end
97
+ out << word.slice!(0, limit).to_s while word.length > limit
98
+ current = word unless word.empty?
99
+ end
100
+ out << current if current
101
+ out
102
+ end
103
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'fileutils'
5
+
6
+ module Riffer::Rig::Trust
7
+ extend self
8
+
9
+ # @rbs path: String
10
+ # @rbs return: Hash[String, bool]
11
+ def read(path)
12
+ return {} unless File.file?(path)
13
+
14
+ source = JSON.parse(File.read(path))
15
+ source.is_a?(Hash) ? source.select { |_file, decision| [true, false].include?(decision) } : {}
16
+ rescue JSON::ParserError
17
+ {}
18
+ end
19
+
20
+ # @rbs path: String
21
+ # @rbs file: String
22
+ # @rbs decision: bool
23
+ # @rbs return: void
24
+ def store(path, file, decision)
25
+ write(path, read(path).merge(file => decision))
26
+ end
27
+
28
+ private
29
+
30
+ # @rbs path: String
31
+ # @rbs document: Hash[String, bool]
32
+ # @rbs return: void
33
+ def write(path, document)
34
+ FileUtils.mkdir_p(File.dirname(path), mode: 0o700)
35
+ File.write(path, JSON.pretty_generate(document))
36
+ end
37
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Riffer
4
4
  module Rig
5
- VERSION = '0.7.0'
5
+ VERSION = '0.9.0'
6
6
  end
7
7
  end
data/lib/riffer/rig.rb CHANGED
@@ -6,7 +6,7 @@ require_relative 'rig/version'
6
6
 
7
7
  loader = Zeitwerk::Loader.for_gem_extension(Riffer)
8
8
  loader.ignore("#{__dir__}/rig/version.rb")
9
- loader.inflector.inflect('cli' => 'CLI')
9
+ loader.inflector.inflect('cli' => 'CLI', 'sdk' => 'SDK', 'jsonl' => 'JSONL')
10
10
  loader.setup
11
11
 
12
12
  module Riffer::Rig
@@ -28,6 +28,12 @@ module Riffer::Rig
28
28
  @extensions.dup
29
29
  end
30
30
 
31
+ # @rbs identifier: String | Symbol
32
+ # @rbs return: Hash[Symbol, String]?
33
+ def self.credentials(identifier)
34
+ Credentials.read(identifier)
35
+ end
36
+
31
37
  # @rbs name: Symbol?
32
38
  # @rbs return: Riffer::Rig::Extension | Array[Riffer::Rig::Extension]
33
39
  def self.bundled(name = nil)
@@ -3,7 +3,7 @@
3
3
  class Riffer::Rig::CLI::Flags
4
4
  BANNER: String
5
5
 
6
- # @dynamic model, extensions, skills, agents_md, tools, max_steps, help
6
+ # @dynamic model, extensions, skills, agents_md, tools, max_steps, help, save
7
7
  attr_reader model: String?
8
8
 
9
9
  attr_reader extensions: bool
@@ -18,6 +18,8 @@ class Riffer::Rig::CLI::Flags
18
18
 
19
19
  attr_reader max_steps: Integer?
20
20
 
21
+ attr_reader save: bool
22
+
21
23
  # @rbs argv: Array[String]
22
24
  # @rbs return: Riffer::Rig::CLI::Flags | String
23
25
  def self.parse: (Array[String] argv) -> (Riffer::Rig::CLI::Flags | String)
@@ -34,7 +36,8 @@ class Riffer::Rig::CLI::Flags
34
36
  # @rbs agents_md: bool
35
37
  # @rbs tools: Array[String]?
36
38
  # @rbs max_steps: Integer?
39
+ # @rbs save: bool
37
40
  # @rbs help: bool
38
41
  # @rbs return: void
39
- def initialize: (?model: String?, ?extensions: bool, ?skills: bool, ?agents_md: bool, ?tools: Array[String]?, ?max_steps: Integer?, ?help: bool) -> void
42
+ def initialize: (?model: String?, ?extensions: bool, ?skills: bool, ?agents_md: bool, ?tools: Array[String]?, ?max_steps: Integer?, ?save: bool, ?help: bool) -> void
40
43
  end
@@ -0,0 +1,35 @@
1
+ # Generated from lib/riffer/rig/commands/auth.rb with RBS::Inline
2
+
3
+ module Riffer::Rig::Commands::Auth
4
+ USAGE: String
5
+
6
+ # @rbs return: Riffer::Rig::Command
7
+ def command: () -> Riffer::Rig::Command
8
+
9
+ # @rbs ctx: Riffer::Rig::Command::Context
10
+ # @rbs return: void
11
+ def run: (Riffer::Rig::Command::Context ctx) -> void
12
+
13
+ private
14
+
15
+ # @rbs return: String
16
+ def hint: () -> String
17
+
18
+ # @rbs return: String
19
+ def list: () -> String
20
+
21
+ # @rbs ctx: Riffer::Rig::Command::Context
22
+ # @rbs identifier: String
23
+ # @rbs return: void
24
+ def rerun: (Riffer::Rig::Command::Context ctx, String identifier) -> void
25
+
26
+ # @rbs ctx: Riffer::Rig::Command::Context
27
+ # @rbs identifier: String
28
+ # @rbs return: void
29
+ def remove: (Riffer::Rig::Command::Context ctx, String identifier) -> void
30
+
31
+ # @rbs provider: Symbol
32
+ # @rbs missing: Array[Symbol]
33
+ # @rbs return: String
34
+ def refusal: (Symbol provider, Array[Symbol] missing) -> String
35
+ end
@@ -1,9 +1,7 @@
1
1
  # Generated from lib/riffer/rig/commands/model.rb with RBS::Inline
2
2
 
3
3
  module Riffer::Rig::Commands::Model
4
- HINT: String
5
-
6
- SAVE_UNAVAILABLE: ::String
4
+ SAVE: String
7
5
 
8
6
  # @rbs return: Riffer::Rig::Command
9
7
  def command: () -> Riffer::Rig::Command
@@ -14,14 +12,28 @@ module Riffer::Rig::Commands::Model
14
12
 
15
13
  private
16
14
 
15
+ # @rbs return: String
16
+ def hint: () -> String
17
+
18
+ # The same setup flows the Loader runs when it builds a Runtime: the
19
+ # provider's SDK gem, then its credentials, asked for when the host can ask.
20
+ # @rbs ctx: Riffer::Rig::Command::Context
17
21
  # @rbs model: String
18
22
  # @rbs provider: String
19
- # @rbs missing: Array[Symbol]
20
- # @rbs return: String
21
- def refusal: (String model, String provider, Array[Symbol] missing) -> String
23
+ # @rbs return: bool
24
+ def ready?: (Riffer::Rig::Command::Context ctx, String model, String provider) -> bool
25
+
26
+ # @rbs ctx: Riffer::Rig::Command::Context
27
+ # @rbs model: String
28
+ # @rbs provider: String
29
+ # @rbs setup: Riffer::Rig::ProviderSetup
30
+ # @rbs return: bool
31
+ def credentials_ready?: (Riffer::Rig::Command::Context ctx, String model, String provider, Riffer::Rig::ProviderSetup setup) -> bool
22
32
 
33
+ # @rbs ctx: Riffer::Rig::Command::Context
34
+ # @rbs model: String
23
35
  # @rbs provider: String
24
- # @rbs credentials: Hash[Symbol, Hash[Symbol, String]]
25
- # @rbs return: Array[Symbol]
26
- def missing_credentials: (String provider, Hash[Symbol, Hash[Symbol, String]] credentials) -> Array[Symbol]
36
+ # @rbs missing: Array[Symbol]
37
+ # @rbs return: void
38
+ def refuse: (Riffer::Rig::Command::Context ctx, String model, String provider, Array[Symbol] missing) -> void
27
39
  end
@@ -0,0 +1,12 @@
1
+ # Generated from lib/riffer/rig/commands/reload.rb with RBS::Inline
2
+
3
+ module Riffer::Rig::Commands::Reload
4
+ # @rbs loader: Riffer::Rig::Loader
5
+ # @rbs return: Riffer::Rig::Command
6
+ def command: (Riffer::Rig::Loader loader) -> Riffer::Rig::Command
7
+
8
+ # @rbs loader: Riffer::Rig::Loader
9
+ # @rbs ctx: Riffer::Rig::Command::Context
10
+ # @rbs return: void
11
+ def run: (Riffer::Rig::Loader loader, Riffer::Rig::Command::Context ctx) -> void
12
+ end
@@ -5,6 +5,8 @@ module Riffer::Rig::Credentials
5
5
 
6
6
  ENTRY_TYPE: ::String
7
7
 
8
+ @applied: Hash[Symbol, Hash[Symbol, String]]
9
+
8
10
  # @rbs identifier: String | Symbol
9
11
  # @rbs host: Riffer::Rig::Hosts::_Host
10
12
  # @rbs setup: Riffer::Rig::ProviderSetup
@@ -14,12 +16,18 @@ module Riffer::Rig::Credentials
14
16
  # @rbs return: Resolution
15
17
  def resolve: (String | Symbol identifier, host: Riffer::Rig::Hosts::_Host, ?setup: Riffer::Rig::ProviderSetup, ?env: Riffer::Rig::Env, ?auth_path: String, ?settings_path: String) -> Resolution
16
18
 
19
+ # A registered extension provider has no Riffer::Config member, so the
20
+ # assignment stays built-ins-only.
17
21
  # @rbs identifier: String | Symbol
18
22
  # @rbs values: Hash[Symbol, String]
19
23
  # @rbs config: Riffer::Config
20
24
  # @rbs return: void
21
25
  def apply: (String | Symbol identifier, Hash[Symbol, String] values, ?config: Riffer::Config) -> void
22
26
 
27
+ # @rbs identifier: String | Symbol
28
+ # @rbs return: Hash[Symbol, String]?
29
+ def read: (String | Symbol identifier) -> Hash[Symbol, String]?
30
+
23
31
  # @rbs identifier: String | Symbol
24
32
  # @rbs values: Hash[Symbol, String]
25
33
  # @rbs setup: Riffer::Rig::ProviderSetup
@@ -29,6 +29,10 @@ class Riffer::Rig::Extension
29
29
  # @rbs return: void
30
30
  def run: (Riffer::Rig::Registrar registrar) -> void
31
31
 
32
+ # @rbs registrar: Riffer::Rig::Registrar
33
+ # @rbs return: StandardError?
34
+ def load_into: (Riffer::Rig::Registrar registrar) -> StandardError?
35
+
32
36
  private
33
37
 
34
38
  # @rbs requirement: Gem::Requirement?