riffer 0.32.0 → 0.32.1
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/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +7 -0
- data/docs/13_SKILLS.md +6 -2
- data/lib/riffer/agent.rb +4 -4
- data/lib/riffer/skills/activate_tool.rb +1 -0
- data/lib/riffer/skills/context.rb +16 -1
- data/lib/riffer/skills/frontmatter.rb +13 -5
- data/lib/riffer/version.rb +1 -1
- data/sig/generated/riffer/skills/context.rbs +10 -0
- data/sig/generated/riffer/skills/frontmatter.rbs +10 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 06d5acfa86b1320573aeb4dd2135a8540dbda454871a1e49cca8ee7b30e43336
|
|
4
|
+
data.tar.gz: b216a00806b2f08516b197cf03c4bc23766c756174ab43fc3a5e45fb5a4f83d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6408ae651fb7944a4618eced7dcc19658262560748a6fedf96cab6989cd7497acde23beb1962a4117e67e595f23e870e241d511c60329f10dd2aafa4f2cca8ff
|
|
7
|
+
data.tar.gz: ce8962451448533b266b8411172e49311616b6b81a3c54a71c4484a249420f16ad474c5bf06e40d7d4e811ec37edf131db150e25b15e001e7970fa56f0ea711c
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.32.1](https://github.com/janeapp/riffer/compare/riffer/v0.32.0...riffer/v0.32.1) (2026-06-10)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* respect disable-model-invocation in skills ([#303](https://github.com/janeapp/riffer/issues/303)) ([2cf8719](https://github.com/janeapp/riffer/commit/2cf8719ecc36d748fabf7f03c8427e2b3043d30c))
|
|
14
|
+
|
|
8
15
|
## [0.32.0](https://github.com/janeapp/riffer/compare/riffer/v0.31.0...riffer/v0.32.0) (2026-06-08)
|
|
9
16
|
|
|
10
17
|
|
data/docs/13_SKILLS.md
CHANGED
|
@@ -36,7 +36,11 @@ Review the code for:
|
|
|
36
36
|
- `name` — lowercase alphanumeric with hyphens, 1-64 chars (must match directory name)
|
|
37
37
|
- `description` — 1-1024 chars, helps the LLM decide when to activate
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
**Optional frontmatter fields:**
|
|
40
|
+
|
|
41
|
+
- `disable-model-invocation` — when `true`, the skill is hidden from the catalog and the LLM cannot activate it via the `skill_activate` tool. It stays reachable through the programmatic `activate` config (see [Activated Skills](#activated-skills)), so you can inject it under your own logic instead of the model's. Only the literal value `true` disables invocation; any other value (or its absence) leaves the skill model-invocable.
|
|
42
|
+
|
|
43
|
+
Any other frontmatter keys are passed through as metadata.
|
|
40
44
|
|
|
41
45
|
## Configuring an Agent
|
|
42
46
|
|
|
@@ -81,7 +85,7 @@ end
|
|
|
81
85
|
|
|
82
86
|
### Activated Skills
|
|
83
87
|
|
|
84
|
-
Load skill instructions into the system prompt at startup (no tool call needed):
|
|
88
|
+
Load skill instructions into the system prompt at startup (no tool call needed). This is also the only way to surface a skill marked `disable-model-invocation: true`, which the model can never activate on its own:
|
|
85
89
|
|
|
86
90
|
```ruby
|
|
87
91
|
skills do
|
data/lib/riffer/agent.rb
CHANGED
|
@@ -343,9 +343,9 @@ class Riffer::Agent
|
|
|
343
343
|
#--
|
|
344
344
|
#: () -> Riffer::Messages::System?
|
|
345
345
|
def build_skills_message
|
|
346
|
-
|
|
347
|
-
return nil
|
|
348
|
-
Riffer::Messages::System.new(
|
|
346
|
+
content = @context.skills&.system_prompt
|
|
347
|
+
return nil if content.nil? || content.empty?
|
|
348
|
+
Riffer::Messages::System.new(content)
|
|
349
349
|
end
|
|
350
350
|
|
|
351
351
|
#--
|
|
@@ -415,7 +415,7 @@ class Riffer::Agent
|
|
|
415
415
|
|
|
416
416
|
skills_config = @config.skills_config
|
|
417
417
|
|
|
418
|
-
if skills_config
|
|
418
|
+
if skills_config && @context.skills&.activatable?
|
|
419
419
|
skill_activate_tool_class = skills_config.activate_tool || Riffer.config.skills.default_activate_tool
|
|
420
420
|
|
|
421
421
|
if tools.any? { |t| t.name == skill_activate_tool_class.name }
|
|
@@ -19,6 +19,7 @@ class Riffer::Skills::ActivateTool < Riffer::Tool
|
|
|
19
19
|
def call(context:, name:)
|
|
20
20
|
skills_context = context&.skills
|
|
21
21
|
return error("Skills not configured") unless skills_context
|
|
22
|
+
return error("Unknown skill: '#{name}'") unless skills_context.model_invocable?(name)
|
|
22
23
|
|
|
23
24
|
text(skills_context.activate(name))
|
|
24
25
|
rescue Riffer::ArgumentError => e
|
|
@@ -48,6 +48,21 @@ class Riffer::Skills::Context
|
|
|
48
48
|
@activated.key?(name)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
# Returns whether a skill exists and may be activated by the model.
|
|
52
|
+
#--
|
|
53
|
+
#: (String) -> bool
|
|
54
|
+
def model_invocable?(name)
|
|
55
|
+
skill = skills[name]
|
|
56
|
+
!skill.nil? && !skill.disable_model_invocation
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns whether any skill is available for the model to activate.
|
|
60
|
+
#--
|
|
61
|
+
#: () -> bool
|
|
62
|
+
def activatable?
|
|
63
|
+
available_skills.any?
|
|
64
|
+
end
|
|
65
|
+
|
|
51
66
|
# Returns the complete skills section for the system prompt — the catalog plus
|
|
52
67
|
# any pre-activated skill bodies.
|
|
53
68
|
#--
|
|
@@ -65,6 +80,6 @@ class Riffer::Skills::Context
|
|
|
65
80
|
#--
|
|
66
81
|
#: () -> Array[Riffer::Skills::Frontmatter]
|
|
67
82
|
def available_skills
|
|
68
|
-
skills.values.reject { |skill| @activated.key?(skill.name) }
|
|
83
|
+
skills.values.reject { |skill| @activated.key?(skill.name) || skill.disable_model_invocation }
|
|
69
84
|
end
|
|
70
85
|
end
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
require "yaml"
|
|
5
5
|
|
|
6
6
|
# Immutable value object holding parsed SKILL.md YAML frontmatter. Required
|
|
7
|
-
# fields: +name+ and +description+;
|
|
7
|
+
# fields: +name+ and +description+; the optional +disable-model-invocation+
|
|
8
|
+
# flag is recognized, and any other unrecognized top-level keys are merged into
|
|
8
9
|
# +metadata+.
|
|
9
10
|
class Riffer::Skills::Frontmatter
|
|
10
11
|
NAME_PATTERN = /\A[a-z0-9]+(-[a-z0-9]+)*\z/ #: Regexp
|
|
@@ -17,6 +18,11 @@ class Riffer::Skills::Frontmatter
|
|
|
17
18
|
# The skill description (1-1024 chars).
|
|
18
19
|
attr_reader :description #: String
|
|
19
20
|
|
|
21
|
+
# Whether the skill opts out of model-driven activation. Hidden from the
|
|
22
|
+
# catalog and rejected at model activation; still reachable via programmatic
|
|
23
|
+
# activation.
|
|
24
|
+
attr_reader :disable_model_invocation #: bool
|
|
25
|
+
|
|
20
26
|
# Metadata from the spec's +metadata+ field plus any unrecognized top-level
|
|
21
27
|
# keys.
|
|
22
28
|
attr_reader :metadata #: Hash[Symbol, untyped]
|
|
@@ -29,7 +35,7 @@ class Riffer::Skills::Frontmatter
|
|
|
29
35
|
def self.parse(raw)
|
|
30
36
|
yaml, body = split_frontmatter(raw)
|
|
31
37
|
raise Riffer::ArgumentError, "missing YAML frontmatter (expected --- delimiters)" if yaml.empty?
|
|
32
|
-
[new(name: yaml.delete(:name), description: yaml.delete(:description), metadata: yaml), body]
|
|
38
|
+
[new(name: yaml.delete(:name), description: yaml.delete(:description), disable_model_invocation: yaml.delete(:"disable-model-invocation"), metadata: yaml), body]
|
|
33
39
|
end
|
|
34
40
|
|
|
35
41
|
# Parses only the frontmatter from a raw SKILL.md string, ignoring the body.
|
|
@@ -39,7 +45,7 @@ class Riffer::Skills::Frontmatter
|
|
|
39
45
|
def self.parse_frontmatter(raw)
|
|
40
46
|
yaml, _ = split_frontmatter(raw)
|
|
41
47
|
raise Riffer::ArgumentError, "missing YAML frontmatter (expected --- delimiters)" if yaml.empty?
|
|
42
|
-
new(name: yaml.delete(:name), description: yaml.delete(:description), metadata: yaml)
|
|
48
|
+
new(name: yaml.delete(:name), description: yaml.delete(:description), disable_model_invocation: yaml.delete(:"disable-model-invocation"), metadata: yaml)
|
|
43
49
|
end
|
|
44
50
|
|
|
45
51
|
#--
|
|
@@ -58,13 +64,15 @@ class Riffer::Skills::Frontmatter
|
|
|
58
64
|
private_class_method :split_frontmatter
|
|
59
65
|
|
|
60
66
|
# Raises Riffer::ArgumentError if +name+ or +description+ is invalid.
|
|
67
|
+
# +disable_model_invocation+ is treated as set only when literally +true+.
|
|
61
68
|
#--
|
|
62
|
-
#: (name: String, description: String, ?metadata: Hash[Symbol, untyped]) -> void
|
|
63
|
-
def initialize(name:, description:, metadata: {})
|
|
69
|
+
#: (name: String, description: String, ?disable_model_invocation: bool, ?metadata: Hash[Symbol, untyped]) -> void
|
|
70
|
+
def initialize(name:, description:, disable_model_invocation: false, metadata: {})
|
|
64
71
|
validate_name!(name)
|
|
65
72
|
validate_description!(description)
|
|
66
73
|
@name = name.freeze
|
|
67
74
|
@description = description.freeze
|
|
75
|
+
@disable_model_invocation = (disable_model_invocation == true)
|
|
68
76
|
@metadata = metadata.freeze
|
|
69
77
|
end
|
|
70
78
|
|
data/lib/riffer/version.rb
CHANGED
|
@@ -35,6 +35,16 @@ class Riffer::Skills::Context
|
|
|
35
35
|
# : (String) -> bool
|
|
36
36
|
def activated?: (String) -> bool
|
|
37
37
|
|
|
38
|
+
# Returns whether a skill exists and may be activated by the model.
|
|
39
|
+
# --
|
|
40
|
+
# : (String) -> bool
|
|
41
|
+
def model_invocable?: (String) -> bool
|
|
42
|
+
|
|
43
|
+
# Returns whether any skill is available for the model to activate.
|
|
44
|
+
# --
|
|
45
|
+
# : () -> bool
|
|
46
|
+
def activatable?: () -> bool
|
|
47
|
+
|
|
38
48
|
# Returns the complete skills section for the system prompt — the catalog plus
|
|
39
49
|
# any pre-activated skill bodies.
|
|
40
50
|
# --
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Generated from lib/riffer/skills/frontmatter.rb with RBS::Inline
|
|
2
2
|
|
|
3
3
|
# Immutable value object holding parsed SKILL.md YAML frontmatter. Required
|
|
4
|
-
# fields: +name+ and +description+;
|
|
4
|
+
# fields: +name+ and +description+; the optional +disable-model-invocation+
|
|
5
|
+
# flag is recognized, and any other unrecognized top-level keys are merged into
|
|
5
6
|
# +metadata+.
|
|
6
7
|
class Riffer::Skills::Frontmatter
|
|
7
8
|
NAME_PATTERN: Regexp
|
|
@@ -16,6 +17,11 @@ class Riffer::Skills::Frontmatter
|
|
|
16
17
|
# The skill description (1-1024 chars).
|
|
17
18
|
attr_reader description: String
|
|
18
19
|
|
|
20
|
+
# Whether the skill opts out of model-driven activation. Hidden from the
|
|
21
|
+
# catalog and rejected at model activation; still reachable via programmatic
|
|
22
|
+
# activation.
|
|
23
|
+
attr_reader disable_model_invocation: bool
|
|
24
|
+
|
|
19
25
|
# Metadata from the spec's +metadata+ field plus any unrecognized top-level
|
|
20
26
|
# keys.
|
|
21
27
|
attr_reader metadata: Hash[Symbol, untyped]
|
|
@@ -38,9 +44,10 @@ class Riffer::Skills::Frontmatter
|
|
|
38
44
|
def self.split_frontmatter: (String) -> [ Hash[Symbol, untyped], String ]
|
|
39
45
|
|
|
40
46
|
# Raises Riffer::ArgumentError if +name+ or +description+ is invalid.
|
|
47
|
+
# +disable_model_invocation+ is treated as set only when literally +true+.
|
|
41
48
|
# --
|
|
42
|
-
# : (name: String, description: String, ?metadata: Hash[Symbol, untyped]) -> void
|
|
43
|
-
def initialize: (name: String, description: String, ?metadata: Hash[Symbol, untyped]) -> void
|
|
49
|
+
# : (name: String, description: String, ?disable_model_invocation: bool, ?metadata: Hash[Symbol, untyped]) -> void
|
|
50
|
+
def initialize: (name: String, description: String, ?disable_model_invocation: bool, ?metadata: Hash[Symbol, untyped]) -> void
|
|
44
51
|
|
|
45
52
|
private
|
|
46
53
|
|