riffer-rig 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/README.md +37 -4
- data/exe/riffer +1 -1
- data/lib/riffer/rig/bundled/agents_md.rb +7 -0
- data/lib/riffer/rig/bundled/bash.rb +5 -0
- data/lib/riffer/rig/bundled/edit.rb +5 -0
- data/lib/riffer/rig/bundled/mcp.rb +9 -0
- data/lib/riffer/rig/bundled/read.rb +5 -0
- data/lib/riffer/rig/bundled/skills.rb +10 -0
- data/lib/riffer/rig/bundled/write.rb +5 -0
- data/lib/riffer/rig/bundled.rb +7 -0
- data/lib/riffer/rig/cli/flags.rb +77 -0
- data/lib/riffer/rig/cli.rb +69 -106
- data/lib/riffer/rig/command/context.rb +70 -0
- data/lib/riffer/rig/command.rb +29 -0
- data/lib/riffer/rig/commands/model.rb +54 -0
- data/lib/riffer/rig/commands/skill.rb +27 -0
- data/lib/riffer/rig/commands.rb +4 -0
- data/lib/riffer/rig/credentials.rb +2 -2
- data/lib/riffer/rig/directories.rb +20 -0
- data/lib/riffer/rig/env/invalid.rb +13 -0
- data/lib/riffer/rig/env.rb +12 -1
- data/lib/riffer/rig/events/after_response.rb +25 -0
- data/lib/riffer/rig/events/after_tool_call.rb +31 -0
- data/lib/riffer/rig/events/before_prompt.rb +25 -0
- data/lib/riffer/rig/events/before_request.rb +25 -0
- data/lib/riffer/rig/events/before_tool_call.rb +28 -0
- data/lib/riffer/rig/events/command_output.rb +3 -2
- data/lib/riffer/rig/events/notify.rb +3 -2
- data/lib/riffer/rig/events/session_end.rb +3 -2
- data/lib/riffer/rig/events/session_start.rb +3 -2
- data/lib/riffer/rig/events/skill_activated.rb +3 -2
- data/lib/riffer/rig/events/turn_end.rb +3 -2
- data/lib/riffer/rig/extension/failure.rb +16 -0
- data/lib/riffer/rig/extension.rb +16 -1
- data/lib/riffer/rig/hosts/mirror.rb +6 -7
- data/lib/riffer/rig/hosts/null.rb +1 -1
- data/lib/riffer/rig/loader.rb +163 -0
- data/lib/riffer/rig/mcp/declaration.rb +23 -0
- data/lib/riffer/rig/mcp/server.rb +16 -0
- data/lib/riffer/rig/mcp.rb +12 -0
- data/lib/riffer/rig/prompts/agents_md.rb +32 -0
- data/lib/riffer/rig/prompts.rb +4 -0
- data/lib/riffer/rig/registrar.rb +120 -5
- data/lib/riffer/rig/runtime/blocked.rb +13 -0
- data/lib/riffer/rig/runtime/hooks.rb +78 -0
- data/lib/riffer/rig/runtime/request_guardrail.rb +24 -0
- data/lib/riffer/rig/runtime/tool_runtime.rb +44 -0
- data/lib/riffer/rig/runtime.rb +506 -57
- data/lib/riffer/rig/settings/document.rb +27 -16
- data/lib/riffer/rig/settings/pricing.rb +16 -5
- data/lib/riffer/rig/settings.rb +73 -43
- data/lib/riffer/rig/skills/sources.rb +28 -0
- data/lib/riffer/rig/skills.rb +12 -0
- data/lib/riffer/rig/{events/event.rb → support/equatable.rb} +2 -11
- data/lib/riffer/rig/{ui → terminal}/animator.rb +4 -4
- data/lib/riffer/rig/{ui → terminal}/banner.rb +17 -21
- data/lib/riffer/rig/{ui → terminal}/cursor.rb +4 -4
- data/lib/riffer/rig/terminal/host.rb +85 -0
- data/lib/riffer/rig/{ui → terminal}/palette.rb +1 -1
- data/lib/riffer/rig/{ui → terminal}/renderer.rb +115 -66
- data/lib/riffer/rig/{ui → terminal}/smoother.rb +4 -4
- data/lib/riffer/rig/{ui → terminal}/theme.rb +12 -12
- data/lib/riffer/rig/terminal.rb +207 -0
- data/lib/riffer/rig/tools/bash.rb +6 -4
- data/lib/riffer/rig/tools/edit.rb +3 -2
- data/lib/riffer/rig/tools/read.rb +3 -2
- data/lib/riffer/rig/tools/write.rb +3 -2
- data/lib/riffer/rig/version.rb +1 -1
- data/lib/riffer/rig.rb +8 -8
- data/sig/generated/riffer/rig/bundled/agents_md.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/bash.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/edit.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/mcp.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/read.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/skills.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/write.rbs +5 -0
- data/sig/generated/riffer/rig/bundled.rbs +5 -0
- data/sig/generated/riffer/rig/cli/flags.rbs +40 -0
- data/sig/generated/riffer/rig/cli.rbs +30 -35
- data/sig/generated/riffer/rig/command/context.rbs +50 -0
- data/sig/generated/riffer/rig/command.rbs +23 -0
- data/sig/generated/riffer/rig/commands/model.rbs +27 -0
- data/sig/generated/riffer/rig/commands/skill.rbs +12 -0
- data/sig/generated/riffer/rig/commands.rbs +4 -0
- data/sig/generated/riffer/rig/credentials.rbs +4 -4
- data/sig/generated/riffer/rig/directories.rbs +11 -0
- data/sig/generated/riffer/rig/env/invalid.rbs +10 -0
- data/sig/generated/riffer/rig/env.rbs +7 -1
- data/sig/generated/riffer/rig/events/after_response.rbs +18 -0
- data/sig/generated/riffer/rig/events/after_tool_call.rbs +24 -0
- data/sig/generated/riffer/rig/events/before_prompt.rbs +18 -0
- data/sig/generated/riffer/rig/events/before_request.rbs +18 -0
- data/sig/generated/riffer/rig/events/before_tool_call.rbs +21 -0
- data/sig/generated/riffer/rig/events/command_output.rbs +3 -1
- data/sig/generated/riffer/rig/events/notify.rbs +3 -1
- data/sig/generated/riffer/rig/events/session_end.rbs +3 -1
- data/sig/generated/riffer/rig/events/session_start.rbs +3 -1
- data/sig/generated/riffer/rig/events/skill_activated.rbs +3 -1
- data/sig/generated/riffer/rig/events/turn_end.rbs +3 -1
- data/sig/generated/riffer/rig/extension/failure.rbs +13 -0
- data/sig/generated/riffer/rig/extension.rbs +14 -1
- data/sig/generated/riffer/rig/hosts/mirror.rbs +9 -9
- data/sig/generated/riffer/rig/hosts/null.rbs +1 -1
- data/sig/generated/riffer/rig/loader.rbs +77 -0
- data/sig/generated/riffer/rig/mcp/declaration.rbs +18 -0
- data/sig/generated/riffer/rig/mcp/server.rbs +13 -0
- data/sig/generated/riffer/rig/mcp.rbs +8 -0
- data/sig/generated/riffer/rig/prompts/agents_md.rbs +19 -0
- data/sig/generated/riffer/rig/prompts.rbs +4 -0
- data/sig/generated/riffer/rig/registrar.rbs +78 -4
- data/sig/generated/riffer/rig/runtime/blocked.rbs +10 -0
- data/sig/generated/riffer/rig/runtime/hooks.rbs +49 -0
- data/sig/generated/riffer/rig/runtime/request_guardrail.rbs +16 -0
- data/sig/generated/riffer/rig/runtime/tool_runtime.rbs +23 -0
- data/sig/generated/riffer/rig/runtime.rbs +225 -22
- data/sig/generated/riffer/rig/settings/document.rbs +11 -11
- data/sig/generated/riffer/rig/settings/pricing.rbs +7 -2
- data/sig/generated/riffer/rig/settings.rbs +36 -23
- data/sig/generated/riffer/rig/skills/sources.rbs +18 -0
- data/sig/generated/riffer/rig/skills.rbs +7 -0
- data/sig/generated/riffer/rig/support/equatable.rbs +15 -0
- data/sig/generated/riffer/rig/{ui → terminal}/animator.rbs +5 -5
- data/sig/generated/riffer/rig/terminal/banner.rbs +39 -0
- data/sig/generated/riffer/rig/{ui → terminal}/cursor.rbs +5 -5
- data/sig/generated/riffer/rig/terminal/host.rbs +50 -0
- data/sig/generated/riffer/rig/{ui → terminal}/palette.rbs +2 -2
- data/sig/generated/riffer/rig/terminal/renderer.rbs +126 -0
- data/sig/generated/riffer/rig/{ui → terminal}/smoother.rbs +5 -5
- data/sig/generated/riffer/rig/{ui → terminal}/theme.rbs +5 -5
- data/sig/generated/riffer/rig/terminal.rbs +85 -0
- data/sig/generated/riffer/rig/tools/bash.rbs +2 -1
- data/sig/generated/riffer/rig.rbs +4 -0
- data/sig/manual/riffer/rig/commands/model.rbs +6 -0
- data/sig/manual/riffer/rig/commands/skill.rbs +6 -0
- data/sig/manual/riffer/rig/directories.rbs +6 -0
- data/sig/manual/riffer/rig/events.rbs +6 -0
- data/sig/manual/riffer/rig/hosts.rbs +7 -0
- data/sig/manual/riffer/rig/mcp.rbs +11 -0
- data/sig/manual/riffer/rig/prompts/agents_md.rbs +6 -0
- data/sig/manual/riffer/rig/{ui/banner.rbs → skills.rbs} +2 -2
- data/sig/manual/riffer/rig/support.rbs +8 -0
- data/sig/manual/riffer/rig/terminal/banner.rbs +6 -0
- metadata +140 -27
- data/lib/riffer/rig/coding_agent.rb +0 -53
- data/lib/riffer/rig/hosts/base.rb +0 -36
- data/lib/riffer/rig/repl.rb +0 -188
- data/lib/riffer/rig/token_tally.rb +0 -54
- data/sig/generated/riffer/rig/coding_agent.rbs +0 -9
- data/sig/generated/riffer/rig/events/event.rbs +0 -20
- data/sig/generated/riffer/rig/hosts/base.rbs +0 -26
- data/sig/generated/riffer/rig/repl.rbs +0 -69
- data/sig/generated/riffer/rig/token_tally.rbs +0 -42
- data/sig/generated/riffer/rig/ui/banner.rbs +0 -42
- data/sig/generated/riffer/rig/ui/renderer.rbs +0 -94
- data/sig/manual/riffer/rig/ui.rbs +0 -5
|
@@ -1,28 +1,39 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Riffer::Rig::Settings::Document
|
|
4
|
-
# @
|
|
5
|
-
# @rbs @reasoning: String?
|
|
6
|
-
# @rbs @models: Hash[String, Riffer::Rig::Settings::Pricing]
|
|
7
|
-
# @rbs @providers: Hash[String, Hash[String, String]]
|
|
8
|
-
|
|
9
|
-
# @dynamic model, reasoning, models, providers
|
|
4
|
+
# @dynamic model, reasoning, models, providers, disabled
|
|
10
5
|
attr_reader :model #: String?
|
|
11
6
|
attr_reader :reasoning #: String?
|
|
12
7
|
attr_reader :models #: Hash[String, Riffer::Rig::Settings::Pricing]
|
|
13
8
|
attr_reader :providers #: Hash[String, Hash[String, String]]
|
|
9
|
+
attr_reader :disabled #: Array[String]
|
|
14
10
|
|
|
15
|
-
# @rbs source: untyped
|
|
11
|
+
# @rbs source: Hash[Symbol, untyped]
|
|
16
12
|
# @rbs return: void
|
|
17
13
|
def initialize(source)
|
|
18
|
-
@model = source[
|
|
19
|
-
@reasoning = source[
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
@model = source[:model].is_a?(String) ? source[:model] : nil
|
|
15
|
+
@reasoning = source[:reasoning].is_a?(String) ? source[:reasoning] : nil
|
|
16
|
+
@models = hash_or_empty(source[:models]).filter_map do |name, entry|
|
|
17
|
+
[name.to_s, Riffer::Rig::Settings::Pricing.from(entry)] if entry.is_a?(Hash)
|
|
18
|
+
end.to_h.freeze
|
|
19
|
+
@providers = hash_or_empty(source[:providers]).filter_map do |identifier, fields|
|
|
20
|
+
if fields.is_a?(Hash)
|
|
21
|
+
[identifier.to_s, fields.select do |_name, value|
|
|
22
|
+
value.is_a?(String)
|
|
23
|
+
end.transform_keys(&:to_s)]
|
|
24
|
+
end
|
|
25
|
+
end.to_h.freeze
|
|
26
|
+
disabled = hash_or_empty(source[:extensions])[:disabled]
|
|
27
|
+
names = disabled.is_a?(Array) ? disabled.grep(String) : [] #: Array[String]
|
|
28
|
+
@disabled = names.freeze
|
|
29
|
+
freeze
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# @rbs value: untyped
|
|
35
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
36
|
+
def hash_or_empty(value)
|
|
37
|
+
value.is_a?(Hash) ? value : {}
|
|
27
38
|
end
|
|
28
39
|
end
|
|
@@ -4,17 +4,28 @@ class Riffer::Rig::Settings::Pricing
|
|
|
4
4
|
# @dynamic input, output, cache_write, cache_read
|
|
5
5
|
attr_reader :input, :output, :cache_write, :cache_read #: Float
|
|
6
6
|
|
|
7
|
-
# @rbs entry: Hash[
|
|
7
|
+
# @rbs entry: Hash[Symbol, untyped]
|
|
8
8
|
# @rbs return: ::Riffer::Rig::Settings::Pricing
|
|
9
9
|
def self.from(entry)
|
|
10
10
|
new(
|
|
11
|
-
input: entry.fetch(
|
|
12
|
-
output: entry.fetch(
|
|
13
|
-
cache_write: entry.fetch(
|
|
14
|
-
cache_read: entry.fetch(
|
|
11
|
+
input: entry.fetch(:input, 0).to_f,
|
|
12
|
+
output: entry.fetch(:output, 0).to_f,
|
|
13
|
+
cache_write: entry.fetch(:cache_write, 0).to_f,
|
|
14
|
+
cache_read: entry.fetch(:cache_read, 0).to_f
|
|
15
15
|
)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# @rbs table: Hash[String, ::Riffer::Rig::Settings::Pricing]
|
|
19
|
+
# @rbs registry: ::Riffer::Config::Pricing
|
|
20
|
+
# @rbs return: void
|
|
21
|
+
def self.register(table, registry)
|
|
22
|
+
table.each do |model, entry|
|
|
23
|
+
registry.set(
|
|
24
|
+
model, input: entry.input, output: entry.output, cache_read: entry.cache_read, cache_write: entry.cache_write
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
18
29
|
# @rbs input: Float
|
|
19
30
|
# @rbs output: Float
|
|
20
31
|
# @rbs cache_write: Float
|
data/lib/riffer/rig/settings.rb
CHANGED
|
@@ -8,45 +8,85 @@ module Riffer::Rig::Settings
|
|
|
8
8
|
|
|
9
9
|
PATH = File.expand_path('~/.riffer/settings.json') #: String
|
|
10
10
|
|
|
11
|
-
DEFAULT_MODEL = 'anthropic/claude-sonnet-4-6'
|
|
12
|
-
|
|
13
11
|
REASONING_LEVELS_BY_PROVIDER = {
|
|
14
12
|
'anthropic' => %w[low medium high xhigh max].freeze,
|
|
15
13
|
'openai' => %w[low medium high xhigh].freeze,
|
|
16
14
|
'openrouter' => %w[low medium high xhigh].freeze
|
|
17
15
|
}.freeze #: Hash[String, Array[String]]
|
|
18
16
|
|
|
17
|
+
MODEL_STRING = %r{\A(?<provider>[^/\s]+)/\S+\z} #: Regexp
|
|
18
|
+
|
|
19
|
+
# Upstream candidate: riffer's Repository keeps extension registrations
|
|
20
|
+
# private and lists no identifiers, so the list names riffer's built-ins.
|
|
21
|
+
PROVIDERS = (Riffer::Providers::Repository::REPO.keys - [:mock]).freeze #: Array[Symbol]
|
|
22
|
+
|
|
19
23
|
# @rbs path: String
|
|
20
|
-
# @rbs return:
|
|
21
|
-
def
|
|
22
|
-
|
|
24
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
25
|
+
def read(path)
|
|
26
|
+
return {} unless File.file?(path)
|
|
27
|
+
|
|
28
|
+
source = JSON.parse(File.read(path), symbolize_names: true)
|
|
29
|
+
source.is_a?(Hash) ? source : {}
|
|
30
|
+
rescue JSON::ParserError
|
|
31
|
+
{}
|
|
23
32
|
end
|
|
24
33
|
|
|
25
|
-
#
|
|
34
|
+
# MCP servers merge by name, whole, so a home server's headers never reach a
|
|
35
|
+
# project server's url.
|
|
36
|
+
# @rbs home: Hash[Symbol, untyped]
|
|
37
|
+
# @rbs project: Hash[Symbol, untyped]
|
|
26
38
|
# @rbs return: Hash[Symbol, untyped]
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
def merge(home, project)
|
|
40
|
+
merged = deep_merge(home, project)
|
|
41
|
+
home_mcp = home[:mcp]
|
|
42
|
+
project_mcp = project[:mcp]
|
|
43
|
+
if home_mcp.is_a?(Hash) && project_mcp.is_a?(Hash)
|
|
44
|
+
merged = merged.merge(mcp: Riffer::Rig::Mcp.merge(home_mcp, project_mcp))
|
|
45
|
+
end
|
|
46
|
+
disabled = [home, project].flat_map { |scope| Document.new(scope).disabled }.uniq
|
|
47
|
+
return merged if disabled.empty?
|
|
48
|
+
|
|
49
|
+
extensions = merged[:extensions].is_a?(Hash) ? merged[:extensions] : {} #: Hash[Symbol, untyped]
|
|
50
|
+
merged.merge(extensions: extensions.merge(disabled: disabled))
|
|
30
51
|
end
|
|
31
52
|
|
|
32
|
-
# @rbs
|
|
53
|
+
# @rbs model: String
|
|
54
|
+
# @rbs return: String?
|
|
55
|
+
def provider_for(model)
|
|
56
|
+
model[MODEL_STRING, :provider]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @rbs model: String
|
|
33
60
|
# @rbs return: String?
|
|
34
|
-
def
|
|
35
|
-
|
|
61
|
+
def rejection(model)
|
|
62
|
+
provider = provider_for(model)
|
|
63
|
+
return if provider && Riffer::Providers::Repository.find(provider)
|
|
64
|
+
|
|
65
|
+
"#{model} is not a provider/name model string; the provider is one of: #{PROVIDERS.join(', ')}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @rbs model: String
|
|
69
|
+
# @rbs reasoning: String?
|
|
70
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
71
|
+
def model_options(model, reasoning)
|
|
72
|
+
provider = provider_for(model)
|
|
73
|
+
levels = (provider && REASONING_LEVELS_BY_PROVIDER[provider]) || []
|
|
74
|
+
level = reasoning if levels.include?(reasoning)
|
|
75
|
+
base_options(provider).merge(reasoning_options(level, provider))
|
|
36
76
|
end
|
|
37
77
|
|
|
38
78
|
# @rbs model: String
|
|
39
79
|
# @rbs path: String
|
|
40
|
-
# @rbs return:
|
|
41
|
-
def
|
|
42
|
-
read(path).
|
|
80
|
+
# @rbs return: void
|
|
81
|
+
def store_model(model, path: PATH)
|
|
82
|
+
write(path, read(path).merge(model: model))
|
|
43
83
|
end
|
|
44
84
|
|
|
45
85
|
# @rbs identifier: String
|
|
46
86
|
# @rbs path: String
|
|
47
87
|
# @rbs return: Hash[String, String]
|
|
48
88
|
def provider_fields(identifier, path: PATH)
|
|
49
|
-
read(path).providers[identifier] || {}
|
|
89
|
+
Document.new(read(path)).providers[identifier] || {}
|
|
50
90
|
end
|
|
51
91
|
|
|
52
92
|
# @rbs identifier: String
|
|
@@ -68,17 +108,32 @@ module Riffer::Rig::Settings
|
|
|
68
108
|
|
|
69
109
|
private
|
|
70
110
|
|
|
111
|
+
# @rbs home: Hash[Symbol, untyped]
|
|
112
|
+
# @rbs project: Hash[Symbol, untyped]
|
|
113
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
114
|
+
def deep_merge(home, project)
|
|
115
|
+
home.merge(project) do |_key, earlier, later|
|
|
116
|
+
earlier.is_a?(Hash) && later.is_a?(Hash) ? deep_merge(earlier, later) : later
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
71
120
|
# @rbs path: String
|
|
72
121
|
# @rbs &: (Hash[String, Hash[String, String]]) -> Hash[String, Hash[String, String]]
|
|
73
122
|
# @rbs return: void
|
|
74
123
|
def update_providers(path)
|
|
75
|
-
source =
|
|
124
|
+
source = read(path)
|
|
76
125
|
current = Document.new(source).providers
|
|
77
126
|
providers = yield current
|
|
78
127
|
return if providers == current
|
|
79
128
|
|
|
129
|
+
write(path, providers.empty? ? source.except(:providers) : source.merge(providers: providers))
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @rbs path: String
|
|
133
|
+
# @rbs document: Hash[Symbol, untyped]
|
|
134
|
+
# @rbs return: void
|
|
135
|
+
def write(path, document)
|
|
80
136
|
FileUtils.mkdir_p(File.dirname(path), mode: 0o700)
|
|
81
|
-
document = providers.empty? ? source.except('providers') : source.merge('providers' => providers)
|
|
82
137
|
File.write(path, JSON.pretty_generate(document))
|
|
83
138
|
end
|
|
84
139
|
|
|
@@ -90,15 +145,6 @@ module Riffer::Rig::Settings
|
|
|
90
145
|
{}
|
|
91
146
|
end
|
|
92
147
|
|
|
93
|
-
# @rbs path: String
|
|
94
|
-
# @rbs provider: String?
|
|
95
|
-
# @rbs return: String?
|
|
96
|
-
def reasoning_for(path: PATH, provider: nil)
|
|
97
|
-
level = read(path).reasoning
|
|
98
|
-
valid_levels = (provider && REASONING_LEVELS_BY_PROVIDER[provider]) || []
|
|
99
|
-
valid_levels.include?(level) ? level : nil
|
|
100
|
-
end
|
|
101
|
-
|
|
102
148
|
# @rbs level: String?
|
|
103
149
|
# @rbs provider: String?
|
|
104
150
|
# @rbs return: Hash[Symbol, untyped]
|
|
@@ -114,20 +160,4 @@ module Riffer::Rig::Settings
|
|
|
114
160
|
{}
|
|
115
161
|
end
|
|
116
162
|
end
|
|
117
|
-
|
|
118
|
-
# @rbs path: String
|
|
119
|
-
# @rbs return: Document
|
|
120
|
-
def read(path)
|
|
121
|
-
Document.new(read_source(path))
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# @rbs path: String
|
|
125
|
-
# @rbs return: Hash[String, untyped]
|
|
126
|
-
def read_source(path)
|
|
127
|
-
return {} unless File.file?(path)
|
|
128
|
-
|
|
129
|
-
JSON.parse(File.read(path))
|
|
130
|
-
rescue JSON::ParserError
|
|
131
|
-
{}
|
|
132
|
-
end
|
|
133
163
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Upstream candidate: riffer's Skills::Config takes a single backend, so the
|
|
4
|
+
# sources every extension registers are merged into one here.
|
|
5
|
+
class Riffer::Rig::Skills::Sources < Riffer::Skills::Backend
|
|
6
|
+
# @rbs @backends: Array[Riffer::Skills::Backend]
|
|
7
|
+
|
|
8
|
+
# @rbs backends: Array[Riffer::Skills::Backend]
|
|
9
|
+
# @rbs return: void
|
|
10
|
+
def initialize(backends)
|
|
11
|
+
super()
|
|
12
|
+
@backends = backends
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @rbs return: Array[Riffer::Skills::Frontmatter]
|
|
16
|
+
def list_skills
|
|
17
|
+
@backends.flat_map(&:list_skills).uniq(&:name)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @rbs name: String
|
|
21
|
+
# @rbs return: String
|
|
22
|
+
def read_skill(name)
|
|
23
|
+
backend = @backends.find { |source| source.list_skills.any? { |skill| skill.name == name } }
|
|
24
|
+
raise Riffer::ArgumentError, "Skill not found: '#{name}'" unless backend
|
|
25
|
+
|
|
26
|
+
backend.read_skill(name)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Riffer::Rig::Skills
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
# @rbs cwd: String
|
|
7
|
+
# @rbs return: Array[String]
|
|
8
|
+
def directories(cwd)
|
|
9
|
+
project = Riffer::Rig::Directories.up_to_repo_root(cwd).map { |dir| File.join(dir, '.agents', 'skills') }
|
|
10
|
+
[*project, File.join(Dir.home, '.agents', 'skills')]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def type
|
|
6
|
-
raise NotImplementedError, "#{self.class.name} must define type"
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
# @rbs return: Hash[Symbol, untyped]
|
|
10
|
-
def to_h
|
|
11
|
-
raise NotImplementedError, "#{self.class.name} must define to_h"
|
|
12
|
-
end
|
|
13
|
-
|
|
3
|
+
# @rbs module-self Riffer::Rig::Support::_ToH
|
|
4
|
+
module Riffer::Rig::Support::Equatable
|
|
14
5
|
# @rbs other: untyped
|
|
15
6
|
# @rbs return: bool
|
|
16
7
|
def ==(other)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
class Riffer::Rig::
|
|
3
|
+
class Riffer::Rig::Terminal::Animator
|
|
4
4
|
REVEAL_FRAME_SECONDS = 0.05 #: Float
|
|
5
5
|
|
|
6
6
|
SPINNER_FRAME_SECONDS = 0.12 #: Float
|
|
@@ -22,16 +22,16 @@ class Riffer::Rig::UI::Animator
|
|
|
22
22
|
REASONING_TICK_RANGE = (1..5) #: Range[Integer]
|
|
23
23
|
|
|
24
24
|
# @rbs @io: IO
|
|
25
|
-
# @rbs @theme: Riffer::Rig::
|
|
25
|
+
# @rbs @theme: Riffer::Rig::Terminal::Theme
|
|
26
26
|
# @rbs @thread: Thread?
|
|
27
27
|
# @rbs @mode: Symbol
|
|
28
28
|
# @rbs @phrase: String?
|
|
29
29
|
# @rbs @stop: bool
|
|
30
30
|
|
|
31
31
|
# @rbs io: IO
|
|
32
|
-
# @rbs theme: Riffer::Rig::
|
|
32
|
+
# @rbs theme: Riffer::Rig::Terminal::Theme
|
|
33
33
|
# @rbs return: void
|
|
34
|
-
def initialize(io: $stdout, theme: Riffer::Rig::
|
|
34
|
+
def initialize(io: $stdout, theme: Riffer::Rig::Terminal::Theme.for(io))
|
|
35
35
|
@io = io
|
|
36
36
|
@theme = theme
|
|
37
37
|
@thread = nil
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
module Riffer::Rig::
|
|
3
|
+
module Riffer::Rig::Terminal::Banner
|
|
4
4
|
extend self
|
|
5
5
|
|
|
6
6
|
WORDMARK = [
|
|
@@ -13,57 +13,53 @@ module Riffer::Rig::UI::Banner
|
|
|
13
13
|
].freeze #: Array[String]
|
|
14
14
|
|
|
15
15
|
ROW_COLOURS = [
|
|
16
|
-
Riffer::Rig::
|
|
17
|
-
Riffer::Rig::
|
|
18
|
-
Riffer::Rig::
|
|
19
|
-
Riffer::Rig::
|
|
20
|
-
Riffer::Rig::
|
|
21
|
-
Riffer::Rig::
|
|
16
|
+
Riffer::Rig::Terminal::Palette::PINK,
|
|
17
|
+
Riffer::Rig::Terminal::Palette::MAGENTA,
|
|
18
|
+
Riffer::Rig::Terminal::Palette::PURPLE,
|
|
19
|
+
Riffer::Rig::Terminal::Palette::BLUE,
|
|
20
|
+
Riffer::Rig::Terminal::Palette::CYAN,
|
|
21
|
+
Riffer::Rig::Terminal::Palette::CYAN
|
|
22
22
|
].freeze #: Array[Array[Integer]]
|
|
23
23
|
|
|
24
24
|
INFO_LABEL_WIDTH = 8 #: Integer
|
|
25
25
|
|
|
26
26
|
INDENT = ' ' #: String
|
|
27
27
|
|
|
28
|
-
# @rbs theme: Riffer::Rig::
|
|
28
|
+
# @rbs theme: Riffer::Rig::Terminal::Theme
|
|
29
29
|
# @rbs model: String
|
|
30
30
|
# @rbs cwd: String
|
|
31
|
-
# @rbs context: String
|
|
32
31
|
# @rbs skills: String
|
|
33
32
|
# @rbs version: String
|
|
34
33
|
# @rbs return: String
|
|
35
|
-
def call(theme, model:, cwd:,
|
|
36
|
-
lines(theme, model: model, cwd: cwd,
|
|
34
|
+
def call(theme, model:, cwd:, skills:, version:)
|
|
35
|
+
lines(theme, model: model, cwd: cwd, skills: skills, version: version).join("\n")
|
|
37
36
|
end
|
|
38
37
|
|
|
39
|
-
# @rbs theme: Riffer::Rig::
|
|
38
|
+
# @rbs theme: Riffer::Rig::Terminal::Theme
|
|
40
39
|
# @rbs model: String
|
|
41
40
|
# @rbs cwd: String
|
|
42
|
-
# @rbs context: String
|
|
43
41
|
# @rbs skills: String
|
|
44
42
|
# @rbs version: String
|
|
45
43
|
# @rbs return: Array[String]
|
|
46
|
-
def lines(theme, model:, cwd:,
|
|
47
|
-
[''] + art(theme) + [''] +
|
|
48
|
-
info(theme, model: model, cwd: cwd, context: context, skills: skills, version: version) + ['']
|
|
44
|
+
def lines(theme, model:, cwd:, skills:, version:)
|
|
45
|
+
[''] + art(theme) + [''] + info(theme, model: model, cwd: cwd, skills: skills, version: version) + ['']
|
|
49
46
|
end
|
|
50
47
|
|
|
51
|
-
# @rbs theme: Riffer::Rig::
|
|
48
|
+
# @rbs theme: Riffer::Rig::Terminal::Theme
|
|
52
49
|
# @rbs return: Array[String]
|
|
53
50
|
def art(theme)
|
|
54
51
|
art = WORDMARK.each_index.map { |i| "#{INDENT}#{theme.paint(WORDMARK[i], ROW_COLOURS[i])}" }
|
|
55
52
|
art + ["#{INDENT}#{theme.cyan("♪ let's riff ♪")}"]
|
|
56
53
|
end
|
|
57
54
|
|
|
58
|
-
# @rbs theme: Riffer::Rig::
|
|
55
|
+
# @rbs theme: Riffer::Rig::Terminal::Theme
|
|
59
56
|
# @rbs model: String
|
|
60
57
|
# @rbs cwd: String
|
|
61
|
-
# @rbs context: String
|
|
62
58
|
# @rbs skills: String
|
|
63
59
|
# @rbs version: String
|
|
64
60
|
# @rbs return: Array[String]
|
|
65
|
-
def info(theme, model:, cwd:,
|
|
66
|
-
rows = { model: model, cwd: cwd,
|
|
61
|
+
def info(theme, model:, cwd:, skills:, version:)
|
|
62
|
+
rows = { model: model, cwd: cwd, skills: skills, version: version }.map do |label, value|
|
|
67
63
|
" #{theme.magenta('▸')} #{theme.grey(label.to_s.ljust(INFO_LABEL_WIDTH))}#{value}"
|
|
68
64
|
end
|
|
69
65
|
rows + ['', " #{theme.dim('type /exit to quit')}"]
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
class Riffer::Rig::
|
|
3
|
+
class Riffer::Rig::Terminal::Cursor
|
|
4
4
|
HIDE = "\e[?25l" #: String
|
|
5
5
|
|
|
6
6
|
SHOW = "\e[?25h" #: String
|
|
7
7
|
|
|
8
8
|
# @rbs @io: IO
|
|
9
|
-
# @rbs @theme: Riffer::Rig::
|
|
9
|
+
# @rbs @theme: Riffer::Rig::Terminal::Theme
|
|
10
10
|
|
|
11
11
|
# @rbs io: IO
|
|
12
|
-
# @rbs theme: Riffer::Rig::
|
|
12
|
+
# @rbs theme: Riffer::Rig::Terminal::Theme
|
|
13
13
|
# @rbs return: void
|
|
14
|
-
def initialize(io: $stdout, theme: Riffer::Rig::
|
|
14
|
+
def initialize(io: $stdout, theme: Riffer::Rig::Terminal::Theme.for(io))
|
|
15
15
|
@io = io
|
|
16
16
|
@theme = theme
|
|
17
17
|
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'io/console'
|
|
4
|
+
|
|
5
|
+
class Riffer::Rig::Terminal::Host
|
|
6
|
+
CAPABILITIES = Set[:ask, :confirm, :notify, :progress].freeze #: Set[Symbol]
|
|
7
|
+
|
|
8
|
+
# @rbs @input: IO
|
|
9
|
+
# @rbs @renderer: Riffer::Rig::Terminal::Renderer
|
|
10
|
+
# @rbs @animator: Riffer::Rig::Terminal::Animator
|
|
11
|
+
|
|
12
|
+
# @rbs input: IO
|
|
13
|
+
# @rbs renderer: Riffer::Rig::Terminal::Renderer
|
|
14
|
+
# @rbs animator: Riffer::Rig::Terminal::Animator
|
|
15
|
+
# @rbs return: void
|
|
16
|
+
def initialize(input:, renderer:, animator:)
|
|
17
|
+
@input = input
|
|
18
|
+
@renderer = renderer
|
|
19
|
+
@animator = animator
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @rbs return: Set[Symbol]
|
|
23
|
+
def capabilities
|
|
24
|
+
CAPABILITIES
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @rbs question: String?
|
|
28
|
+
# @rbs options: Array[String]?
|
|
29
|
+
# @rbs secret: bool
|
|
30
|
+
# @rbs return: String?
|
|
31
|
+
def ask(question = nil, options: nil, secret: false)
|
|
32
|
+
@animator.stop
|
|
33
|
+
@renderer.question(question, options, secret)
|
|
34
|
+
answer = secret ? read_secret : @input.gets
|
|
35
|
+
return nil if answer.nil?
|
|
36
|
+
|
|
37
|
+
choice(answer.strip, options)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @rbs question: String?
|
|
41
|
+
# @rbs return: bool
|
|
42
|
+
def confirm(question = nil)
|
|
43
|
+
ask("#{question} [y/N]").to_s.downcase.start_with?('y')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @rbs message: String?
|
|
47
|
+
# @rbs level: Symbol
|
|
48
|
+
# @rbs return: void
|
|
49
|
+
def notify(message = nil, level: :info)
|
|
50
|
+
@animator.stop
|
|
51
|
+
@renderer.notify(message.to_s, level)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @rbs label: String?
|
|
55
|
+
# @rbs &block: ? () -> void
|
|
56
|
+
# @rbs return: void
|
|
57
|
+
def progress(label = nil, &block)
|
|
58
|
+
@renderer.notice(label) if label
|
|
59
|
+
@animator.start
|
|
60
|
+
block&.call
|
|
61
|
+
ensure
|
|
62
|
+
@animator.stop
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
# @rbs return: String?
|
|
68
|
+
def read_secret
|
|
69
|
+
return @input.gets unless @input.tty?
|
|
70
|
+
|
|
71
|
+
answer = @input.noecho(&:gets)
|
|
72
|
+
# noecho swallows the newline the user typed along with the secret.
|
|
73
|
+
@renderer.newline
|
|
74
|
+
answer
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @rbs answer: String
|
|
78
|
+
# @rbs options: Array[String]?
|
|
79
|
+
# @rbs return: String
|
|
80
|
+
def choice(answer, options)
|
|
81
|
+
return answer unless options && answer.match?(/\A[1-9]\d*\z/)
|
|
82
|
+
|
|
83
|
+
options[answer.to_i - 1] || answer
|
|
84
|
+
end
|
|
85
|
+
end
|