gem-skill 0.1.3 → 0.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 +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +55 -14
- data/docs/cache.md +82 -5
- data/docs/commands/bundle-skill.md +10 -3
- data/docs/commands/gem-skill.md +83 -6
- data/docs/configuration.md +37 -0
- data/docs/how-it-works.md +58 -9
- data/docs/index.md +15 -11
- data/docs/skill-files.md +73 -10
- data/lib/gem/skill/cache.rb +24 -0
- data/lib/gem/skill/cli/bundle_command.rb +46 -18
- data/lib/gem/skill/cli/gem_command.rb +158 -8
- data/lib/gem/skill/fetcher.rb +52 -0
- data/lib/gem/skill/frontmatter.rb +64 -0
- data/lib/gem/skill/generator.rb +1 -0
- data/lib/gem/skill/linker.rb +17 -3
- data/lib/gem/skill/runner.rb +69 -9
- data/lib/gem/skill/verifier.rb +136 -0
- data/lib/gem/skill/version.rb +1 -1
- data/lib/gem/skill.rb +12 -0
- data/ruby-gem-skills/SKILL.md +53 -0
- metadata +4 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gem::Skill
|
|
4
|
+
# Builds the YAML frontmatter that makes a SKILL.md discoverable as an Agent
|
|
5
|
+
# Skill. Both Claude Code and OpenAI Codex require `name` + `description`
|
|
6
|
+
# frontmatter; without it the file is never registered/triggered as a skill.
|
|
7
|
+
#
|
|
8
|
+
# Constraints satisfied here (intersection of both assistants):
|
|
9
|
+
# - name: lowercase letters, digits, hyphens only; no leading/trailing or
|
|
10
|
+
# doubled hyphens; <= 40 chars (Claude Code's rule, also valid for Codex)
|
|
11
|
+
# - description: single line, no angle brackets (Claude Code rejects < and >),
|
|
12
|
+
# length-capped (Codex shortens long descriptions)
|
|
13
|
+
#
|
|
14
|
+
# Generation is deterministic (no LLM): the name is derived from the gem name
|
|
15
|
+
# and the description from the skill's Overview section, so the frontmatter is
|
|
16
|
+
# always valid regardless of what the model emitted.
|
|
17
|
+
module Frontmatter
|
|
18
|
+
MAX_NAME_LENGTH = 40
|
|
19
|
+
MAX_DESCRIPTION_LENGTH = 500
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# Return content with a freshly-built, valid frontmatter block. Any existing
|
|
24
|
+
# leading frontmatter is stripped and replaced, so this is idempotent.
|
|
25
|
+
def build(gem_name, version, content)
|
|
26
|
+
body = strip(content)
|
|
27
|
+
fm = "---\nname: #{slug(gem_name)}\ndescription: #{yaml_quote(description_for(gem_name, version, body))}\n---\n"
|
|
28
|
+
"#{fm}\n#{body}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# True when content already begins with a YAML frontmatter block.
|
|
32
|
+
def present?(content)
|
|
33
|
+
content.to_s.lstrip.start_with?("---")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Remove a leading frontmatter block (if any) and return the body.
|
|
37
|
+
def strip(content)
|
|
38
|
+
content.to_s.sub(/\A\s*---\s*\n.*?\n---\s*\n+/m, "").lstrip
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Gem name -> valid skill name. "ruby_llm" -> "ruby-llm", "TTY-Spinner" ->
|
|
42
|
+
# "tty-spinner". Falls back to "skill" if nothing usable remains.
|
|
43
|
+
def slug(gem_name)
|
|
44
|
+
s = gem_name.to_s.downcase.gsub(/[^a-z0-9]+/, "-").gsub(/\A-+|-+\z/, "")
|
|
45
|
+
s = "skill" if s.empty?
|
|
46
|
+
s[0, MAX_NAME_LENGTH].sub(/-+\z/, "")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Derive a trigger-oriented description from the body's Overview section,
|
|
50
|
+
# appending the version for context. Sanitized for both assistants.
|
|
51
|
+
def description_for(gem_name, version, body)
|
|
52
|
+
overview = body[/^##\s+Overview\s*\n+(.+?)(?=\n\s*\n|\n##\s|\z)/m, 1]
|
|
53
|
+
text = overview || "Ruby gem #{gem_name}. Use when working with #{gem_name} in Ruby code."
|
|
54
|
+
text = text.gsub(/\s+/, " ").delete("<>").strip
|
|
55
|
+
text = "#{text} (#{gem_name} v#{version})" unless text.include?(version.to_s)
|
|
56
|
+
text[0, MAX_DESCRIPTION_LENGTH].strip
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Quote a string as a YAML double-quoted scalar, escaping \ and ".
|
|
60
|
+
def yaml_quote(str)
|
|
61
|
+
%("#{str.gsub(/[\\"]/) { |c| "\\#{c}" }}")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/gem/skill/generator.rb
CHANGED
|
@@ -73,6 +73,7 @@ module Gem::Skill
|
|
|
73
73
|
raise Error, "No documentation found for #{gem_name} #{version}" if sources.empty?
|
|
74
74
|
|
|
75
75
|
skill_content = block ? call_llm_streaming(sources, &block) : call_llm(sources)
|
|
76
|
+
skill_content = Frontmatter.build(gem_name, version, skill_content)
|
|
76
77
|
Cache.store(gem_name, version, skill_content, { sources: sources.keys.map(&:to_s), model: model })
|
|
77
78
|
skill_content
|
|
78
79
|
rescue RubyLLM::Error => e
|
data/lib/gem/skill/linker.rb
CHANGED
|
@@ -3,12 +3,26 @@
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
|
|
5
5
|
module Gem::Skill
|
|
6
|
-
# Manages
|
|
6
|
+
# Manages per-project skill symlinks pointing into the ~/.gem/skills cache.
|
|
7
7
|
# Each symlink is a directory link: <gem_name> -> ~/.gem/skills/<gem>/<version>/
|
|
8
|
-
#
|
|
8
|
+
# The assistant discovers skills by reading SKILL.md inside each linked directory.
|
|
9
|
+
#
|
|
10
|
+
# The project-relative directory is configurable via GEMSKILL_PROJECT_DIR
|
|
11
|
+
# (default ".claude/skills" for Claude Code). Codex users might set it to
|
|
12
|
+
# ".agents" or ".codex"; see the configuration docs.
|
|
9
13
|
module Linker
|
|
14
|
+
DEFAULT_PROJECT_DIR = ".claude/skills"
|
|
15
|
+
|
|
16
|
+
# Project-relative directory where skill symlinks are written. Read from the
|
|
17
|
+
# environment each call so a changed GEMSKILL_PROJECT_DIR takes effect without
|
|
18
|
+
# reloading.
|
|
19
|
+
def self.project_dir
|
|
20
|
+
value = ENV.fetch("GEMSKILL_PROJECT_DIR", DEFAULT_PROJECT_DIR).to_s.strip
|
|
21
|
+
value.empty? ? DEFAULT_PROJECT_DIR : value
|
|
22
|
+
end
|
|
23
|
+
|
|
10
24
|
def self.skills_dir(project_root = Dir.pwd)
|
|
11
|
-
File.join(project_root,
|
|
25
|
+
File.join(project_root, project_dir)
|
|
12
26
|
end
|
|
13
27
|
|
|
14
28
|
def self.link(gem_name, version, project_root = Dir.pwd)
|
data/lib/gem/skill/runner.rb
CHANGED
|
@@ -1,24 +1,84 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "time"
|
|
4
|
+
|
|
3
5
|
module Gem::Skill
|
|
4
6
|
# Core install logic shared by gem_command and bundle_command.
|
|
5
7
|
# Callers are responsible for spinner.auto_spin and title setup before calling.
|
|
6
8
|
module Runner
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
|
|
9
|
+
# error: nil on success, message string on failure
|
|
10
|
+
# verify_fixed: true when --verify ran and corrected the skill
|
|
11
|
+
Result = Data.define(:error, :verify_fixed) do
|
|
12
|
+
def ok? = error.nil?
|
|
13
|
+
|
|
14
|
+
def self.failure(message) = new(error: message, verify_fixed: false)
|
|
15
|
+
def self.success(verify_fixed: false) = new(error: nil, verify_fixed: verify_fixed)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Generate + cache + link one skill, optionally verifying it against source.
|
|
19
|
+
# Returns a Runner::Result.
|
|
20
|
+
def self.install_skill(gem_name, version, spinner, force:, model:, verify: false)
|
|
10
21
|
if Cache.cached?(gem_name, version) && !force
|
|
11
22
|
Linker.link(gem_name, version)
|
|
12
|
-
|
|
13
|
-
return
|
|
23
|
+
content = Cache.read(gem_name, version)
|
|
24
|
+
return finalize(gem_name, version, content, spinner, model: model, verify: verify, status: "already cached")
|
|
14
25
|
end
|
|
15
|
-
|
|
26
|
+
|
|
27
|
+
content = Generator.new(gem_name, version, model: model).generate(force: force)
|
|
16
28
|
Linker.link(gem_name, version)
|
|
17
|
-
spinner
|
|
18
|
-
nil
|
|
29
|
+
finalize(gem_name, version, content, spinner, model: model, verify: verify, status: "done")
|
|
19
30
|
rescue => e
|
|
20
31
|
spinner.error("failed")
|
|
21
|
-
e.message
|
|
32
|
+
Result.failure(e.message)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Run the optional verify pass and settle the spinner + metadata.
|
|
36
|
+
def self.finalize(gem_name, version, content, spinner, model:, verify:, status:)
|
|
37
|
+
unless verify
|
|
38
|
+
spinner.success(status)
|
|
39
|
+
return Result.success
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
result = Verifier.new(gem_name, version, model: model).verify(content)
|
|
43
|
+
|
|
44
|
+
unless result.verifiable
|
|
45
|
+
Cache.merge_metadata(gem_name, version, verification: {
|
|
46
|
+
verified: false,
|
|
47
|
+
verified_at: Time.now.iso8601,
|
|
48
|
+
model: model,
|
|
49
|
+
skipped_reason: "no installed source available"
|
|
50
|
+
})
|
|
51
|
+
spinner.success("#{status} (no source to verify)")
|
|
52
|
+
return Result.success
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Cache.write_skill(gem_name, version, result.content) if result.changed?
|
|
56
|
+
Cache.merge_metadata(gem_name, version, verification: verification_metadata(result))
|
|
57
|
+
|
|
58
|
+
if result.changed?
|
|
59
|
+
spinner.success("verified — fixed")
|
|
60
|
+
Result.success(verify_fixed: true)
|
|
61
|
+
else
|
|
62
|
+
spinner.success("verified — ok")
|
|
63
|
+
Result.success
|
|
64
|
+
end
|
|
65
|
+
rescue => e
|
|
66
|
+
spinner.error("verify failed")
|
|
67
|
+
Result.failure(e.message)
|
|
68
|
+
end
|
|
69
|
+
private_class_method :finalize
|
|
70
|
+
|
|
71
|
+
# Records that the skill was verified against real source and whether that
|
|
72
|
+
# verification changed anything. Intentionally minimal — the itemized list of
|
|
73
|
+
# what changed is not retained.
|
|
74
|
+
def self.verification_metadata(result)
|
|
75
|
+
{
|
|
76
|
+
verified: true,
|
|
77
|
+
verified_at: Time.now.iso8601,
|
|
78
|
+
model: result.model,
|
|
79
|
+
fixed: result.changed?
|
|
80
|
+
}
|
|
22
81
|
end
|
|
82
|
+
private_class_method :verification_metadata
|
|
23
83
|
end
|
|
24
84
|
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ruby_llm"
|
|
4
|
+
|
|
5
|
+
module Gem::Skill
|
|
6
|
+
# Second-pass quality gate for a generated SKILL.md.
|
|
7
|
+
#
|
|
8
|
+
# Generation synthesizes prose sources (README, changelog, examples) which are
|
|
9
|
+
# frequently wrong or stale about exact signatures. The verifier re-checks the
|
|
10
|
+
# generated skill against the gem's ACTUAL source code — the only source of
|
|
11
|
+
# truth — and corrects mismatched method signatures, default argument values,
|
|
12
|
+
# visibility, return values, and behavioral claims.
|
|
13
|
+
#
|
|
14
|
+
# Whether the skill actually changed is decided by a deterministic diff of the
|
|
15
|
+
# content before and after, not by trusting the model's self-report, so callers
|
|
16
|
+
# can rely on #changed? for an exit code and a "fixed" flag.
|
|
17
|
+
class Verifier
|
|
18
|
+
BEGIN_MARK = "===BEGIN SKILL==="
|
|
19
|
+
END_MARK = "===END SKILL==="
|
|
20
|
+
|
|
21
|
+
SYSTEM_INSTRUCTIONS = <<~SYSTEM
|
|
22
|
+
You verify a generated Claude Code SKILL.md for a Ruby gem against the gem's
|
|
23
|
+
ACTUAL SOURCE CODE. The source code is the only source of truth. READMEs,
|
|
24
|
+
changelogs, and docstrings are frequently stale or wrong; when the SKILL.md
|
|
25
|
+
disagrees with the source, the source always wins.
|
|
26
|
+
|
|
27
|
+
Check every concrete claim against the source: method signatures, default
|
|
28
|
+
argument values, keyword vs positional arguments, public/private/protected
|
|
29
|
+
visibility, return values, constant and class/module names, default option
|
|
30
|
+
values, and described runtime behavior (including what arguments a yielded
|
|
31
|
+
block actually receives). Correct anything the source contradicts.
|
|
32
|
+
|
|
33
|
+
Rules:
|
|
34
|
+
- Do NOT invent APIs, methods, or options that are absent from the source.
|
|
35
|
+
- Do NOT restructure, re-style, or "improve" content that is already correct.
|
|
36
|
+
Preserve correct text verbatim so the diff stays minimal.
|
|
37
|
+
- Only change what the source proves is wrong.
|
|
38
|
+
SYSTEM
|
|
39
|
+
|
|
40
|
+
PROMPT = <<~PROMPT
|
|
41
|
+
Verify the SKILL.md below for "%<gem_name>s" v%<version>s against the gem's
|
|
42
|
+
source code. Correct every claim the source contradicts.
|
|
43
|
+
|
|
44
|
+
Output ONLY the full corrected SKILL.md in raw Markdown (even if you change
|
|
45
|
+
nothing), wrapped exactly between these marker lines and with no other text:
|
|
46
|
+
%<begin_mark>s
|
|
47
|
+
<corrected SKILL.md here>
|
|
48
|
+
%<end_mark>s
|
|
49
|
+
|
|
50
|
+
============================================================
|
|
51
|
+
CURRENT SKILL.md
|
|
52
|
+
============================================================
|
|
53
|
+
|
|
54
|
+
%<skill>s
|
|
55
|
+
|
|
56
|
+
============================================================
|
|
57
|
+
GEM SOURCE CODE (ground truth)
|
|
58
|
+
============================================================
|
|
59
|
+
|
|
60
|
+
%<source>s
|
|
61
|
+
PROMPT
|
|
62
|
+
|
|
63
|
+
# content: the (possibly corrected) skill markdown
|
|
64
|
+
# changed: true iff content differs from the original (diff-based)
|
|
65
|
+
# verifiable: false when no source was available to check against
|
|
66
|
+
# model: the model used for verification
|
|
67
|
+
Result = Data.define(:content, :changed, :verifiable, :model) do
|
|
68
|
+
def changed? = changed
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
attr_reader :gem_name, :version, :model
|
|
72
|
+
|
|
73
|
+
def initialize(gem_name, version, model: Generator::DEFAULT_MODEL)
|
|
74
|
+
@gem_name = gem_name
|
|
75
|
+
@version = version
|
|
76
|
+
@model = model
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Verify skill_content against the gem source. Returns a Result.
|
|
80
|
+
def verify(skill_content)
|
|
81
|
+
fetcher = Fetcher.new(gem_name, version)
|
|
82
|
+
source = fetcher.source_code
|
|
83
|
+
if source.nil? || source.strip.empty?
|
|
84
|
+
return Result.new(content: skill_content, changed: false, verifiable: false, model: model)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
raw = build_chat.ask(format_prompt(skill_content, source)).content.to_s
|
|
88
|
+
# Re-apply frontmatter to both sides so the diff compares like-for-like and
|
|
89
|
+
# the stored skill always keeps valid frontmatter, even if the model dropped it.
|
|
90
|
+
original = Frontmatter.build(gem_name, version, skill_content)
|
|
91
|
+
corrected = Frontmatter.build(gem_name, version, extract_skill(raw, skill_content))
|
|
92
|
+
changed = normalize(corrected) != normalize(original)
|
|
93
|
+
|
|
94
|
+
Result.new(content: (changed ? corrected : skill_content), changed: changed,
|
|
95
|
+
verifiable: true, model: model)
|
|
96
|
+
rescue RubyLLM::Error => e
|
|
97
|
+
raise Error, e.message
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def build_chat
|
|
103
|
+
RubyLLM.chat(model: model).with_instructions(SYSTEM_INSTRUCTIONS)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def format_prompt(skill_content, source)
|
|
107
|
+
format(
|
|
108
|
+
PROMPT,
|
|
109
|
+
gem_name: gem_name,
|
|
110
|
+
version: version,
|
|
111
|
+
begin_mark: BEGIN_MARK,
|
|
112
|
+
end_mark: END_MARK,
|
|
113
|
+
skill: skill_content,
|
|
114
|
+
source: source
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Pull the corrected skill out from between the markers. If the model didn't
|
|
119
|
+
# honor the protocol, fall back to the original so we never corrupt the cache.
|
|
120
|
+
def extract_skill(raw, fallback)
|
|
121
|
+
start = raw.index(BEGIN_MARK)
|
|
122
|
+
return fallback unless start
|
|
123
|
+
|
|
124
|
+
body = raw[(start + BEGIN_MARK.length)..]
|
|
125
|
+
stop = body.index(END_MARK)
|
|
126
|
+
body = body[0...stop] if stop
|
|
127
|
+
|
|
128
|
+
body = body.to_s.strip
|
|
129
|
+
body.empty? ? fallback : body
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def normalize(text)
|
|
133
|
+
text.to_s.gsub(/[ \t]+$/, "").strip
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
data/lib/gem/skill/version.rb
CHANGED
data/lib/gem/skill.rb
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
require_relative "skill/version"
|
|
4
4
|
require_relative "skill/cache"
|
|
5
5
|
require_relative "skill/fetcher"
|
|
6
|
+
require_relative "skill/frontmatter"
|
|
6
7
|
require_relative "skill/generator"
|
|
8
|
+
require_relative "skill/verifier"
|
|
7
9
|
require_relative "skill/linker"
|
|
8
10
|
require_relative "skill/lockfile"
|
|
9
11
|
require_relative "skill/runner"
|
|
@@ -11,6 +13,16 @@ require_relative "skill/runner"
|
|
|
11
13
|
module Gem::Skill
|
|
12
14
|
class Error < StandardError; end
|
|
13
15
|
|
|
16
|
+
# Exit status used when --verify found and corrected mistakes in a generated
|
|
17
|
+
# skill (grep-style: 0 = clean, 1 = error, 2 = verify applied fixes).
|
|
18
|
+
EXIT_VERIFY_FIXED = 2
|
|
19
|
+
|
|
20
|
+
# The bundled "router" skill that teaches assistants how to find cached gem
|
|
21
|
+
# skills in ~/.gem/skills. `gem skill setup` copies it into the assistants'
|
|
22
|
+
# default skill roots. Lives at the repo/gem root, beside README/CHANGELOG.
|
|
23
|
+
ROUTER_SKILL_NAME = "ruby-gem-skills"
|
|
24
|
+
ROUTER_SKILL_DIR = File.expand_path("../../#{ROUTER_SKILL_NAME}", __dir__)
|
|
25
|
+
|
|
14
26
|
ENV_KEY_MAP = {
|
|
15
27
|
anthropic_api_key: "ANTHROPIC_API_KEY",
|
|
16
28
|
openai_api_key: "OPENAI_API_KEY",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ruby-gem-skills
|
|
3
|
+
description: "Locate and load gem-skill's cached, version-specific SKILL.md for a Ruby gem. Use whenever working with, debugging, or writing Ruby code that uses a third-party gem (Bundler/Gemfile projects or installed gems) to get accurate, version-pinned API knowledge before relying on memory."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ruby-gem-skills
|
|
7
|
+
|
|
8
|
+
Detailed, version-specific knowledge for installed Ruby gems is generated by the
|
|
9
|
+
`gem-skill` tool and cached on this machine at:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
~/.gem/skills/<gem_name>/<version>/SKILL.md
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
(Use `$GEMSKILL_DIR` instead of `~/.gem/skills` if that variable is set.)
|
|
16
|
+
|
|
17
|
+
Most assistants do not scan that directory by default, so follow this procedure
|
|
18
|
+
to find and load a gem's skill before answering from memory.
|
|
19
|
+
|
|
20
|
+
## When to use
|
|
21
|
+
|
|
22
|
+
- Writing or debugging Ruby code that calls a third-party gem's API.
|
|
23
|
+
- Verifying exact method signatures, options, defaults, or behavior for a
|
|
24
|
+
specific gem version.
|
|
25
|
+
|
|
26
|
+
## How to load a gem's skill
|
|
27
|
+
|
|
28
|
+
1. Determine the gem version in use:
|
|
29
|
+
- In a project with a `Gemfile.lock`, read the locked version for the gem.
|
|
30
|
+
- Otherwise run `gem list <gem_name>` (or `bundle show <gem_name>`) to find
|
|
31
|
+
the installed version.
|
|
32
|
+
2. Read the cached skill for that exact version:
|
|
33
|
+
```
|
|
34
|
+
~/.gem/skills/<gem_name>/<version>/SKILL.md
|
|
35
|
+
```
|
|
36
|
+
3. If it exists, treat its contents as the authoritative, version-pinned
|
|
37
|
+
reference for that gem and prefer it over training memory. Skills are
|
|
38
|
+
version-specific — always match the version actually in use.
|
|
39
|
+
|
|
40
|
+
## If the skill is missing
|
|
41
|
+
|
|
42
|
+
Tell the user how to generate it:
|
|
43
|
+
|
|
44
|
+
- One gem: `gem skill install <gem_name>`
|
|
45
|
+
- Every gem in a project (from the project root): `bundle skill install`
|
|
46
|
+
- Verify an existing skill against the gem's real source: `gem skill verify <gem_name>`
|
|
47
|
+
|
|
48
|
+
## Notes
|
|
49
|
+
|
|
50
|
+
- A `metadata.json` sits beside each `SKILL.md` with provenance (model, sources,
|
|
51
|
+
and verification status). It is for tooling and humans, not required reading.
|
|
52
|
+
- A green checkmark next to a version in `gem skill list` means that skill was
|
|
53
|
+
verified against the gem's actual source code.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gem-skill
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dewayne VanHoozer
|
|
@@ -82,14 +82,17 @@ files:
|
|
|
82
82
|
- lib/gem/skill/cli/bundle_command.rb
|
|
83
83
|
- lib/gem/skill/cli/gem_command.rb
|
|
84
84
|
- lib/gem/skill/fetcher.rb
|
|
85
|
+
- lib/gem/skill/frontmatter.rb
|
|
85
86
|
- lib/gem/skill/generator.rb
|
|
86
87
|
- lib/gem/skill/linker.rb
|
|
87
88
|
- lib/gem/skill/lockfile.rb
|
|
88
89
|
- lib/gem/skill/runner.rb
|
|
90
|
+
- lib/gem/skill/verifier.rb
|
|
89
91
|
- lib/gem/skill/version.rb
|
|
90
92
|
- lib/rubygems_plugin.rb
|
|
91
93
|
- mkdocs.yml
|
|
92
94
|
- plugins.rb
|
|
95
|
+
- ruby-gem-skills/SKILL.md
|
|
93
96
|
- scripts/e2e_test
|
|
94
97
|
- sig/gem/skill.rbs
|
|
95
98
|
homepage: https://github.com/madbomber/gem-skill
|