riffer-rig 0.5.0 → 0.7.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 (172) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/README.md +77 -7
  4. data/exe/riffer +1 -1
  5. data/lib/riffer/rig/bundled/agents_md.rb +7 -0
  6. data/lib/riffer/rig/bundled/bash.rb +5 -0
  7. data/lib/riffer/rig/bundled/edit.rb +5 -0
  8. data/lib/riffer/rig/bundled/mcp.rb +9 -0
  9. data/lib/riffer/rig/bundled/read.rb +5 -0
  10. data/lib/riffer/rig/bundled/skills.rb +10 -0
  11. data/lib/riffer/rig/bundled/write.rb +5 -0
  12. data/lib/riffer/rig/bundled.rb +7 -0
  13. data/lib/riffer/rig/cli/flags.rb +77 -0
  14. data/lib/riffer/rig/cli.rb +70 -102
  15. data/lib/riffer/rig/command/context.rb +70 -0
  16. data/lib/riffer/rig/command.rb +29 -0
  17. data/lib/riffer/rig/commands/model.rb +54 -0
  18. data/lib/riffer/rig/commands/skill.rb +27 -0
  19. data/lib/riffer/rig/commands.rb +4 -0
  20. data/lib/riffer/rig/credentials/resolution.rb +16 -0
  21. data/lib/riffer/rig/credentials.rb +151 -60
  22. data/lib/riffer/rig/directories.rb +20 -0
  23. data/lib/riffer/rig/env/invalid.rb +13 -0
  24. data/lib/riffer/rig/env.rb +40 -0
  25. data/lib/riffer/rig/events/after_response.rb +25 -0
  26. data/lib/riffer/rig/events/after_tool_call.rb +31 -0
  27. data/lib/riffer/rig/events/before_prompt.rb +25 -0
  28. data/lib/riffer/rig/events/before_request.rb +25 -0
  29. data/lib/riffer/rig/events/before_tool_call.rb +28 -0
  30. data/lib/riffer/rig/events/command_output.rb +28 -0
  31. data/lib/riffer/rig/events/notify.rb +28 -0
  32. data/lib/riffer/rig/events/session_end.rb +25 -0
  33. data/lib/riffer/rig/events/session_start.rb +28 -0
  34. data/lib/riffer/rig/events/skill_activated.rb +25 -0
  35. data/lib/riffer/rig/events/turn_end.rb +33 -0
  36. data/lib/riffer/rig/events.rb +4 -0
  37. data/lib/riffer/rig/extension/failure.rb +16 -0
  38. data/lib/riffer/rig/extension.rb +16 -5
  39. data/lib/riffer/rig/hosts/mirror.rb +59 -0
  40. data/lib/riffer/rig/hosts/null.rb +36 -0
  41. data/lib/riffer/rig/loader.rb +163 -0
  42. data/lib/riffer/rig/mcp/declaration.rb +23 -0
  43. data/lib/riffer/rig/mcp/server.rb +16 -0
  44. data/lib/riffer/rig/mcp.rb +12 -0
  45. data/lib/riffer/rig/prompts/agents_md.rb +32 -0
  46. data/lib/riffer/rig/prompts.rb +4 -0
  47. data/lib/riffer/rig/provider_setup/field.rb +24 -0
  48. data/lib/riffer/rig/provider_setup.rb +84 -0
  49. data/lib/riffer/rig/registrar.rb +134 -7
  50. data/lib/riffer/rig/runtime/blocked.rb +13 -0
  51. data/lib/riffer/rig/runtime/cancel_flag.rb +27 -0
  52. data/lib/riffer/rig/runtime/hooks.rb +78 -0
  53. data/lib/riffer/rig/runtime/request_guardrail.rb +24 -0
  54. data/lib/riffer/rig/runtime/tool_runtime.rb +44 -0
  55. data/lib/riffer/rig/runtime.rb +582 -70
  56. data/lib/riffer/rig/settings/document.rb +28 -12
  57. data/lib/riffer/rig/settings/pricing.rb +16 -6
  58. data/lib/riffer/rig/settings.rb +108 -81
  59. data/lib/riffer/rig/skills/sources.rb +28 -0
  60. data/lib/riffer/rig/skills.rb +12 -0
  61. data/lib/riffer/rig/support/equatable.rb +21 -0
  62. data/lib/riffer/rig/{ui → terminal}/animator.rb +6 -13
  63. data/lib/riffer/rig/{ui → terminal}/banner.rb +17 -21
  64. data/lib/riffer/rig/{ui → terminal}/cursor.rb +4 -6
  65. data/lib/riffer/rig/terminal/host.rb +85 -0
  66. data/lib/riffer/rig/{ui → terminal}/palette.rb +1 -1
  67. data/lib/riffer/rig/{ui → terminal}/renderer.rb +121 -87
  68. data/lib/riffer/rig/{ui → terminal}/smoother.rb +15 -23
  69. data/lib/riffer/rig/{ui → terminal}/theme.rb +12 -14
  70. data/lib/riffer/rig/terminal.rb +207 -0
  71. data/lib/riffer/rig/tools/bash.rb +36 -13
  72. data/lib/riffer/rig/tools/edit.rb +3 -2
  73. data/lib/riffer/rig/tools/read.rb +3 -2
  74. data/lib/riffer/rig/tools/write.rb +3 -2
  75. data/lib/riffer/rig/version.rb +1 -1
  76. data/lib/riffer/rig.rb +10 -15
  77. data/sig/generated/riffer/rig/bundled/agents_md.rbs +5 -0
  78. data/sig/generated/riffer/rig/bundled/bash.rbs +5 -0
  79. data/sig/generated/riffer/rig/bundled/edit.rbs +5 -0
  80. data/sig/generated/riffer/rig/bundled/mcp.rbs +5 -0
  81. data/sig/generated/riffer/rig/bundled/read.rbs +5 -0
  82. data/sig/generated/riffer/rig/bundled/skills.rbs +5 -0
  83. data/sig/generated/riffer/rig/bundled/write.rbs +5 -0
  84. data/sig/generated/riffer/rig/bundled.rbs +5 -0
  85. data/sig/generated/riffer/rig/cli/flags.rbs +40 -0
  86. data/sig/generated/riffer/rig/cli.rbs +30 -27
  87. data/sig/generated/riffer/rig/command/context.rbs +50 -0
  88. data/sig/generated/riffer/rig/command.rbs +23 -0
  89. data/sig/generated/riffer/rig/commands/model.rbs +27 -0
  90. data/sig/generated/riffer/rig/commands/skill.rbs +12 -0
  91. data/sig/generated/riffer/rig/commands.rbs +4 -0
  92. data/sig/generated/riffer/rig/credentials/resolution.rbs +13 -0
  93. data/sig/generated/riffer/rig/credentials.rbs +70 -41
  94. data/sig/generated/riffer/rig/directories.rbs +11 -0
  95. data/sig/generated/riffer/rig/env/invalid.rbs +10 -0
  96. data/sig/generated/riffer/rig/env.rbs +26 -0
  97. data/sig/generated/riffer/rig/events/after_response.rbs +18 -0
  98. data/sig/generated/riffer/rig/events/after_tool_call.rbs +24 -0
  99. data/sig/generated/riffer/rig/events/before_prompt.rbs +18 -0
  100. data/sig/generated/riffer/rig/events/before_request.rbs +18 -0
  101. data/sig/generated/riffer/rig/events/before_tool_call.rbs +21 -0
  102. data/sig/generated/riffer/rig/events/command_output.rbs +21 -0
  103. data/sig/generated/riffer/rig/events/notify.rbs +21 -0
  104. data/sig/generated/riffer/rig/events/session_end.rbs +18 -0
  105. data/sig/generated/riffer/rig/events/session_start.rbs +21 -0
  106. data/sig/generated/riffer/rig/events/skill_activated.rbs +18 -0
  107. data/sig/generated/riffer/rig/events/turn_end.rbs +24 -0
  108. data/sig/generated/riffer/rig/events.rbs +4 -0
  109. data/sig/generated/riffer/rig/extension/failure.rbs +13 -0
  110. data/sig/generated/riffer/rig/extension.rbs +14 -5
  111. data/sig/generated/riffer/rig/hosts/mirror.rbs +41 -0
  112. data/sig/generated/riffer/rig/hosts/null.rbs +26 -0
  113. data/sig/generated/riffer/rig/loader.rbs +77 -0
  114. data/sig/generated/riffer/rig/mcp/declaration.rbs +18 -0
  115. data/sig/generated/riffer/rig/mcp/server.rbs +13 -0
  116. data/sig/generated/riffer/rig/mcp.rbs +8 -0
  117. data/sig/generated/riffer/rig/prompts/agents_md.rbs +19 -0
  118. data/sig/generated/riffer/rig/prompts.rbs +4 -0
  119. data/sig/generated/riffer/rig/provider_setup/field.rbs +22 -0
  120. data/sig/generated/riffer/rig/provider_setup.rbs +30 -0
  121. data/sig/generated/riffer/rig/registrar.rbs +88 -6
  122. data/sig/generated/riffer/rig/runtime/blocked.rbs +10 -0
  123. data/sig/generated/riffer/rig/runtime/cancel_flag.rbs +19 -0
  124. data/sig/generated/riffer/rig/runtime/hooks.rbs +49 -0
  125. data/sig/generated/riffer/rig/runtime/request_guardrail.rbs +16 -0
  126. data/sig/generated/riffer/rig/runtime/tool_runtime.rbs +23 -0
  127. data/sig/generated/riffer/rig/runtime.rbs +262 -40
  128. data/sig/generated/riffer/rig/settings/document.rbs +13 -9
  129. data/sig/generated/riffer/rig/settings/pricing.rbs +7 -3
  130. data/sig/generated/riffer/rig/settings.rbs +56 -64
  131. data/sig/generated/riffer/rig/skills/sources.rbs +18 -0
  132. data/sig/generated/riffer/rig/skills.rbs +7 -0
  133. data/sig/generated/riffer/rig/support/equatable.rbs +15 -0
  134. data/sig/generated/riffer/rig/{ui → terminal}/animator.rbs +5 -13
  135. data/sig/generated/riffer/rig/terminal/banner.rbs +39 -0
  136. data/sig/generated/riffer/rig/terminal/cursor.rbs +31 -0
  137. data/sig/generated/riffer/rig/terminal/host.rbs +50 -0
  138. data/sig/generated/riffer/rig/{ui → terminal}/palette.rbs +2 -2
  139. data/sig/generated/riffer/rig/terminal/renderer.rbs +126 -0
  140. data/sig/generated/riffer/rig/terminal/smoother.rbs +59 -0
  141. data/sig/generated/riffer/rig/{ui → terminal}/theme.rbs +5 -7
  142. data/sig/generated/riffer/rig/terminal.rbs +85 -0
  143. data/sig/generated/riffer/rig/tools/bash.rbs +14 -1
  144. data/sig/generated/riffer/rig.rbs +4 -7
  145. data/sig/manual/riffer/rig/cli.rbs +2 -4
  146. data/sig/manual/riffer/rig/commands/model.rbs +6 -0
  147. data/sig/manual/riffer/rig/commands/skill.rbs +6 -0
  148. data/sig/manual/riffer/rig/credentials.rbs +2 -4
  149. data/sig/manual/riffer/rig/directories.rbs +6 -0
  150. data/sig/manual/riffer/rig/events.rbs +6 -0
  151. data/sig/manual/riffer/rig/hosts.rbs +12 -0
  152. data/sig/manual/riffer/rig/mcp.rbs +11 -0
  153. data/sig/manual/riffer/rig/prompts/agents_md.rbs +6 -0
  154. data/sig/manual/riffer/rig/settings.rbs +2 -4
  155. data/sig/manual/riffer/rig/skills.rbs +6 -0
  156. data/sig/manual/riffer/rig/support.rbs +8 -0
  157. data/sig/manual/riffer/rig/terminal/banner.rbs +6 -0
  158. metadata +173 -35
  159. data/lib/riffer/rig/coding_agent.rb +0 -53
  160. data/lib/riffer/rig/host.rb +0 -52
  161. data/lib/riffer/rig/repl.rb +0 -189
  162. data/lib/riffer/rig/token_tally.rb +0 -74
  163. data/sig/generated/riffer/rig/coding_agent.rbs +0 -9
  164. data/sig/generated/riffer/rig/host.rbs +0 -42
  165. data/sig/generated/riffer/rig/repl.rbs +0 -77
  166. data/sig/generated/riffer/rig/token_tally.rbs +0 -61
  167. data/sig/generated/riffer/rig/ui/banner.rbs +0 -42
  168. data/sig/generated/riffer/rig/ui/cursor.rbs +0 -33
  169. data/sig/generated/riffer/rig/ui/renderer.rbs +0 -118
  170. data/sig/generated/riffer/rig/ui/smoother.rbs +0 -72
  171. data/sig/manual/riffer/rig/ui/banner.rbs +0 -8
  172. data/sig/manual/riffer/rig/ui.rbs +0 -5
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::Events::SkillActivated
4
+ include Riffer::Rig::Support::Equatable
5
+
6
+ # @dynamic name
7
+ attr_reader :name #: String
8
+
9
+ # @rbs name: String
10
+ # @rbs return: void
11
+ def initialize(name)
12
+ @name = name
13
+ freeze
14
+ end
15
+
16
+ # @rbs return: Symbol
17
+ def type
18
+ :skill_activated
19
+ end
20
+
21
+ # @rbs return: Hash[Symbol, untyped]
22
+ def to_h
23
+ { type: type, name: name }
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::Events::TurnEnd
4
+ include Riffer::Rig::Support::Equatable
5
+
6
+ # @dynamic stop_reason, usage
7
+ attr_reader :stop_reason #: Symbol
8
+ attr_reader :usage #: ::Riffer::Providers::TokenUsage?
9
+
10
+ # @rbs stop_reason: Symbol
11
+ # @rbs usage: ::Riffer::Providers::TokenUsage?
12
+ # @rbs return: void
13
+ def initialize(stop_reason, usage)
14
+ @stop_reason = stop_reason
15
+ @usage = usage
16
+ freeze
17
+ end
18
+
19
+ # @rbs return: Float?
20
+ def cost
21
+ usage&.cost
22
+ end
23
+
24
+ # @rbs return: Symbol
25
+ def type
26
+ :turn_end
27
+ end
28
+
29
+ # @rbs return: Hash[Symbol, untyped]
30
+ def to_h
31
+ { type: type, stop_reason: stop_reason, usage: usage }
32
+ end
33
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Riffer::Rig::Events
4
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::Extension::Failure
4
+ # @dynamic extension, error
5
+ attr_reader :extension #: Riffer::Rig::Extension
6
+ attr_reader :error #: StandardError
7
+
8
+ # @rbs extension: Riffer::Rig::Extension
9
+ # @rbs error: StandardError
10
+ # @rbs return: void
11
+ def initialize(extension:, error:)
12
+ @extension = extension
13
+ @error = error
14
+ freeze
15
+ end
16
+ end
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # An extension recorded by <tt>Riffer::Rig.extension</tt>: a name, an optional
4
- # Gem::Requirement on riffer-rig, and the block that runs against a Runtime's
5
- # registrar. The process registry holds these objects; Runtime.new runs their
6
- # blocks.
7
3
  class Riffer::Rig::Extension
4
+ class RequirementError < StandardError; end
5
+
8
6
  # @rbs @name: String
9
7
  # @rbs @requires: Gem::Requirement?
8
+ # @rbs @mismatch: RequirementError?
10
9
  # @rbs @block: ^(::Riffer::Rig::Registrar) -> void
11
10
 
12
- # @dynamic name, requires
11
+ # @dynamic name, requires, mismatch
13
12
  attr_reader :name #: String
14
13
  attr_reader :requires #: Gem::Requirement?
14
+ attr_reader :mismatch #: RequirementError?
15
15
 
16
16
  # @rbs name: String
17
17
  # @rbs requires: String?
@@ -20,6 +20,7 @@ class Riffer::Rig::Extension
20
20
  def initialize(name, requires: nil, &block)
21
21
  @name = name
22
22
  @requires = requires && Gem::Requirement.new(requires)
23
+ @mismatch = check(@requires)
23
24
  @block = block
24
25
  end
25
26
 
@@ -28,4 +29,14 @@ class Riffer::Rig::Extension
28
29
  def run(registrar)
29
30
  @block.call(registrar)
30
31
  end
32
+
33
+ private
34
+
35
+ # @rbs requirement: Gem::Requirement?
36
+ # @rbs return: RequirementError?
37
+ def check(requirement)
38
+ return if requirement.nil? || requirement.satisfied_by?(Gem::Version.new(Riffer::Rig::VERSION))
39
+
40
+ RequirementError.new("extension #{@name} requires riffer-rig #{requirement}, found #{Riffer::Rig::VERSION}")
41
+ end
31
42
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::Hosts::Mirror
4
+ # @rbs @host: Riffer::Rig::Hosts::_Host
5
+ # @rbs @queue: Array[Riffer::Rig::Events::_Event]
6
+
7
+ # @dynamic capabilities
8
+ attr_reader :capabilities #: Set[Symbol]
9
+
10
+ # @rbs host: Riffer::Rig::Hosts::_Host
11
+ # @rbs return: void
12
+ def initialize(host)
13
+ @host = host
14
+ @capabilities = host.capabilities
15
+ @queue = []
16
+ end
17
+
18
+ # @rbs question: String?
19
+ # @rbs options: Array[String]?
20
+ # @rbs secret: bool
21
+ # @rbs return: String?
22
+ def ask(question = nil, options: nil, secret: false)
23
+ @host.ask(question, options: options, secret: secret)
24
+ end
25
+
26
+ # @rbs question: String?
27
+ # @rbs return: bool
28
+ def confirm(question = nil)
29
+ @host.confirm(question)
30
+ end
31
+
32
+ # @rbs message: String?
33
+ # @rbs level: Symbol
34
+ # @rbs return: void
35
+ def notify(message = nil, level: :info)
36
+ queue(Riffer::Rig::Events::Notify.new(message, level))
37
+ @host.notify(message, level: level)
38
+ end
39
+
40
+ # @rbs label: String?
41
+ # @rbs &block: ? () -> void
42
+ # @rbs return: void
43
+ def progress(label = nil, &block)
44
+ @host.progress(label) { block&.call }
45
+ end
46
+
47
+ # @rbs event: Riffer::Rig::Events::_Event
48
+ # @rbs return: void
49
+ def queue(event)
50
+ @queue << event
51
+ end
52
+
53
+ # @rbs return: Array[Riffer::Rig::Events::_Event]
54
+ def drain
55
+ queued = @queue
56
+ @queue = []
57
+ queued
58
+ end
59
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::Hosts::Null
4
+ # @rbs return: Set[Symbol]
5
+ def capabilities
6
+ Set.new.freeze
7
+ end
8
+
9
+ # @rbs question: String?
10
+ # @rbs options: Array[String]?
11
+ # @rbs secret: bool
12
+ # @rbs return: String?
13
+ def ask(question = nil, options: nil, secret: false)
14
+ nil
15
+ end
16
+
17
+ # @rbs question: String?
18
+ # @rbs return: bool
19
+ def confirm(question = nil)
20
+ false
21
+ end
22
+
23
+ # @rbs message: String?
24
+ # @rbs level: Symbol
25
+ # @rbs return: void
26
+ def notify(message = nil, level: :info)
27
+ nil
28
+ end
29
+
30
+ # @rbs label: String?
31
+ # @rbs &block: ? () -> void
32
+ # @rbs return: void
33
+ def progress(label = nil, &block)
34
+ block&.call
35
+ end
36
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::Loader
4
+ class ConfigurationError < StandardError; end
5
+
6
+ PROVIDER_LIST = Riffer::Rig::Settings::PROVIDERS.join(', ') #: String
7
+
8
+ ONBOARDING_QUESTION = 'Which model should riffer use? Enter provider/name, ' \
9
+ "with a provider from: #{PROVIDER_LIST}".freeze #: String
10
+
11
+ NO_MODEL = 'No model is set: pass --model provider/name, set RIFFER_MODEL, or set "model" in ' \
12
+ "~/.riffer/settings.json, with a provider from: #{PROVIDER_LIST}".freeze #: String
13
+
14
+ private_constant :PROVIDER_LIST, :ONBOARDING_QUESTION, :NO_MODEL
15
+
16
+ # @rbs @cwd: String
17
+ # @rbs @host: Riffer::Rig::Hosts::_Host
18
+ # @rbs @env: Riffer::Rig::Env
19
+ # @rbs @home: String
20
+ # @rbs @riffer_config: Riffer::Config
21
+
22
+ # @rbs cwd: String
23
+ # @rbs host: Riffer::Rig::Hosts::_Host
24
+ # @rbs env: Riffer::Rig::Env | Riffer::Rig::Env::Invalid
25
+ # @rbs home: String
26
+ # @rbs riffer_config: Riffer::Config
27
+ # @rbs model: String?
28
+ # @rbs extensions: bool
29
+ # @rbs skills: bool
30
+ # @rbs agents_md: bool
31
+ # @rbs tools: Array[String]?
32
+ # @rbs max_steps: Integer?
33
+ # @rbs return: Riffer::Rig::Runtime
34
+ def self.runtime(
35
+ cwd:,
36
+ host:,
37
+ env: Riffer::Rig::Env.load,
38
+ home: Dir.home,
39
+ riffer_config: Riffer.config,
40
+ model: nil,
41
+ extensions: true,
42
+ skills: true,
43
+ agents_md: true,
44
+ tools: nil,
45
+ max_steps: Riffer::Rig::Runtime::DEFAULT_MAX_STEPS
46
+ )
47
+ new(cwd:, host:, env:, home:, riffer_config:).runtime(model:, extensions:, skills:, agents_md:, tools:, max_steps:)
48
+ end
49
+
50
+ # @rbs cwd: String
51
+ # @rbs host: Riffer::Rig::Hosts::_Host
52
+ # @rbs env: Riffer::Rig::Env | Riffer::Rig::Env::Invalid
53
+ # @rbs home: String
54
+ # @rbs riffer_config: Riffer::Config
55
+ # @rbs return: void
56
+ def initialize(cwd:, host:, env: Riffer::Rig::Env.load, home: Dir.home, riffer_config: Riffer.config)
57
+ raise ConfigurationError, env.message if env.is_a?(Riffer::Rig::Env::Invalid)
58
+
59
+ @cwd = cwd
60
+ @host = host
61
+ @env = env
62
+ @home = home
63
+ @riffer_config = riffer_config
64
+ end
65
+
66
+ # @rbs model: String?
67
+ # @rbs extensions: bool
68
+ # @rbs skills: bool
69
+ # @rbs agents_md: bool
70
+ # @rbs tools: Array[String]?
71
+ # @rbs max_steps: Integer?
72
+ # @rbs return: Riffer::Rig::Runtime
73
+ def runtime(
74
+ model: nil,
75
+ extensions: true,
76
+ skills: true,
77
+ agents_md: true,
78
+ tools: nil,
79
+ max_steps: Riffer::Rig::Runtime::DEFAULT_MAX_STEPS
80
+ )
81
+ settings = Riffer::Rig::Settings.merge(
82
+ Riffer::Rig::Settings.read(home_settings_path),
83
+ Riffer::Rig::Settings.read(File.join(@cwd, '.riffer', 'settings.json'))
84
+ )
85
+ document = Riffer::Rig::Settings::Document.new(settings)
86
+ selected = select_model(model || @env.model || document.model)
87
+ provider = Riffer::Rig::Settings.provider_for(selected).to_s
88
+ credentials = { provider.to_sym => credentials_for(provider) }
89
+ stripped = { skills: skills, agents_md: agents_md }.reject { |_name, kept| kept }.keys
90
+
91
+ Riffer::Rig::Runtime.new(
92
+ selected,
93
+ extensions: Riffer::Rig::Bundled::BY_NAME.except(*document.disabled.map(&:to_sym), *stripped).values,
94
+ tools: tools,
95
+ settings: settings,
96
+ host: @host,
97
+ cwd: @cwd,
98
+ max_steps: max_steps,
99
+ credentials: credentials,
100
+ pricing: document.models,
101
+ riffer_config: @riffer_config,
102
+ model_options: Riffer::Rig::Settings.model_options(selected, document.reasoning)
103
+ )
104
+ end
105
+
106
+ private
107
+
108
+ # @rbs configured: String?
109
+ # @rbs return: String
110
+ def select_model(configured)
111
+ model = configured || ask_for_model
112
+ rejection = Riffer::Rig::Settings.rejection(model)
113
+ raise ConfigurationError, rejection if rejection
114
+
115
+ Riffer::Rig::Settings.store_model(model, path: home_settings_path) unless configured
116
+ model
117
+ end
118
+
119
+ # @rbs return: String
120
+ def ask_for_model
121
+ answer = asking_host.ask(ONBOARDING_QUESTION).to_s.strip
122
+ raise ConfigurationError, NO_MODEL if answer.empty?
123
+
124
+ answer
125
+ end
126
+
127
+ # @rbs provider: String
128
+ # @rbs return: Hash[Symbol, String]
129
+ def credentials_for(provider)
130
+ resolution = Riffer::Rig::Credentials.resolve(
131
+ provider,
132
+ host: asking_host,
133
+ env: @env,
134
+ auth_path: File.join(@home, '.riffer', 'auth.json'),
135
+ settings_path: home_settings_path
136
+ )
137
+ raise ConfigurationError, missing_credentials(provider, resolution.missing) unless resolution.missing.empty?
138
+
139
+ Riffer::Rig::Credentials.apply(provider, resolution.values, config: @riffer_config)
140
+ resolution.values
141
+ end
142
+
143
+ # @rbs provider: String
144
+ # @rbs missing: Array[Symbol]
145
+ # @rbs return: String
146
+ def missing_credentials(provider, missing)
147
+ setup = Riffer::Rig::ProviderSetup.for(provider)
148
+ fields = setup.fields.select { |field| missing.include?(field.name) }
149
+ wanted = fields.map { |field| "#{field.name} (#{field.env.join(' or ')})" }.join(', ')
150
+ "#{provider} has no #{wanted}; set it in the environment or run riffer interactively to paste it" \
151
+ "#{" (create one at #{setup.url})" if setup.url}"
152
+ end
153
+
154
+ # @rbs return: Riffer::Rig::Hosts::_Host
155
+ def asking_host
156
+ @host.capabilities.include?(:ask) ? @host : Riffer::Rig::Hosts::Null.new
157
+ end
158
+
159
+ # @rbs return: String
160
+ def home_settings_path
161
+ File.join(@home, '.riffer', 'settings.json')
162
+ end
163
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::Mcp::Declaration
4
+ include Riffer::Rig::Support::Equatable
5
+
6
+ # @dynamic url, headers
7
+ attr_reader :url #: String
8
+ attr_reader :headers #: Hash[String, String]
9
+
10
+ # @rbs url: String
11
+ # @rbs headers: Hash[String, String]
12
+ # @rbs return: void
13
+ def initialize(url:, headers:)
14
+ @url = url
15
+ @headers = headers
16
+ freeze
17
+ end
18
+
19
+ # @rbs return: Hash[Symbol, untyped]
20
+ def to_h
21
+ { url: url, headers: headers }
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::Mcp::Server
4
+ # @dynamic declaration, registration
5
+ attr_reader :declaration #: Riffer::Rig::Mcp::Declaration
6
+ attr_reader :registration #: Riffer::Mcp::Registration
7
+
8
+ # @rbs declaration: Riffer::Rig::Mcp::Declaration
9
+ # @rbs registration: Riffer::Mcp::Registration
10
+ # @rbs return: void
11
+ def initialize(declaration:, registration:)
12
+ @declaration = declaration
13
+ @registration = registration
14
+ freeze
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Riffer::Rig::Mcp
4
+ extend self
5
+
6
+ # @rbs home: Hash[Symbol, untyped]
7
+ # @rbs project: Hash[Symbol, untyped]
8
+ # @rbs return: Hash[Symbol, untyped]
9
+ def merge(home, project)
10
+ home.merge(project, servers: home[:servers].to_h.merge(project[:servers].to_h))
11
+ end
12
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Riffer::Rig::Prompts::AgentsMd
4
+ extend self
5
+
6
+ FRAMING = 'The user wrote the following instructions in AGENTS.md files, ordered from the most general to ' \
7
+ 'the one closest to the working directory. Follow them; where they conflict with the guidance ' \
8
+ 'above, they take precedence, and where they conflict with each other, the later file wins.' #: String
9
+
10
+ # @rbs cwd: String
11
+ # @rbs return: Array[String]
12
+ def paths(cwd)
13
+ global = File.join(Dir.home, '.riffer', 'AGENTS.md')
14
+ project = Riffer::Rig::Directories.ancestors(File.expand_path(cwd)).reverse.map { |dir| File.join(dir, 'AGENTS.md') }
15
+ [global, *project].uniq.select { |path| File.file?(path) }
16
+ end
17
+
18
+ # @rbs cwd: String
19
+ # @rbs return: String?
20
+ def section(cwd)
21
+ blocks = paths(cwd).map { |path| block(path) }
22
+ [FRAMING, *blocks].join("\n\n") unless blocks.empty?
23
+ end
24
+
25
+ private
26
+
27
+ # @rbs path: String
28
+ # @rbs return: String
29
+ def block(path)
30
+ "<project_instructions path=\"#{path}\">\n#{File.read(path)}\n</project_instructions>"
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Riffer::Rig::Prompts
4
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::ProviderSetup::Field
4
+ # @dynamic name, env, secret, required, fallback
5
+ attr_reader :name #: Symbol
6
+ attr_reader :env #: Array[String]
7
+ attr_reader :secret, :required #: bool
8
+ attr_reader :fallback #: (^() -> String?)?
9
+
10
+ # @rbs name: Symbol
11
+ # @rbs env: Array[String]
12
+ # @rbs secret: bool
13
+ # @rbs required: bool
14
+ # @rbs fallback: (^() -> String?)?
15
+ # @rbs return: void
16
+ def initialize(name:, env:, secret:, required:, fallback: nil)
17
+ @name = name
18
+ @env = env.freeze
19
+ @secret = secret
20
+ @required = required
21
+ @fallback = fallback
22
+ freeze
23
+ end
24
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Riffer::Rig::ProviderSetup
4
+ # @dynamic url, chain, fields
5
+ attr_reader :url #: String?
6
+ attr_reader :chain #: bool
7
+ attr_reader :fields #: Array[Riffer::Rig::ProviderSetup::Field]
8
+
9
+ # @rbs fields: Array[Riffer::Rig::ProviderSetup::Field]
10
+ # @rbs url: String?
11
+ # @rbs chain: bool
12
+ # @rbs return: void
13
+ def initialize(fields:, url: nil, chain: false)
14
+ @url = url
15
+ @chain = chain
16
+ @fields = fields.freeze
17
+ freeze
18
+ end
19
+
20
+ AWS_SHARED_CONFIG_REGION = lambda do
21
+ require 'aws-sdk-core'
22
+ Object.const_get(:Aws).shared_config.region
23
+ rescue LoadError
24
+ nil
25
+ end #: ^() -> String?
26
+
27
+ # Upstream candidate: each riffer provider could declare its own credential
28
+ # fields, and this table would go.
29
+ TABLE = {
30
+ anthropic: new(
31
+ url: 'https://console.anthropic.com/settings/keys',
32
+ fields: [Field.new(name: :api_key, env: ['ANTHROPIC_API_KEY'], secret: true, required: true)]
33
+ ),
34
+ openai: new(
35
+ url: 'https://platform.openai.com/api-keys',
36
+ fields: [
37
+ Field.new(name: :api_key, env: ['OPENAI_API_KEY'], secret: true, required: true),
38
+ Field.new(name: :base_url, env: ['OPENAI_BASE_URL'], secret: false, required: false)
39
+ ]
40
+ ),
41
+ gemini: new(
42
+ url: 'https://aistudio.google.com/app/apikey',
43
+ fields: [Field.new(name: :api_key, env: ['GEMINI_API_KEY'], secret: true, required: true)]
44
+ ),
45
+ openrouter: new(
46
+ url: 'https://openrouter.ai/keys',
47
+ fields: [Field.new(name: :api_key, env: ['OPENROUTER_API_KEY'], secret: true, required: true)]
48
+ ),
49
+ azure_openai: new(
50
+ url: 'https://portal.azure.com',
51
+ fields: [
52
+ Field.new(name: :endpoint, env: ['AZURE_OPENAI_ENDPOINT'], secret: false, required: true),
53
+ Field.new(name: :api_key, env: ['AZURE_OPENAI_API_KEY'], secret: true, required: true)
54
+ ]
55
+ ),
56
+ amazon_bedrock: new(
57
+ url: 'https://console.aws.amazon.com/bedrock',
58
+ chain: true,
59
+ fields: [
60
+ Field.new(
61
+ name: :region,
62
+ env: %w[AWS_REGION AWS_DEFAULT_REGION],
63
+ secret: false,
64
+ required: true,
65
+ fallback: AWS_SHARED_CONFIG_REGION
66
+ ),
67
+ Field.new(name: :api_token, env: ['AWS_BEARER_TOKEN_BEDROCK'], secret: true, required: false)
68
+ ]
69
+ )
70
+ }.freeze #: Hash[Symbol, Riffer::Rig::ProviderSetup]
71
+
72
+ # @rbs identifier: String | Symbol
73
+ # @rbs return: Riffer::Rig::ProviderSetup?
74
+ def self.[](identifier)
75
+ TABLE[identifier.to_sym]
76
+ end
77
+
78
+ # @rbs identifier: String | Symbol
79
+ # @rbs return: Riffer::Rig::ProviderSetup
80
+ def self.for(identifier)
81
+ self[identifier] ||
82
+ new(fields: [Field.new(name: :api_key, env: ["#{identifier.to_s.upcase}_API_KEY"], secret: true, required: true)])
83
+ end
84
+ end