Commit 0972e52
Changed files (9)
lib
elelem
spec
elelem
lib/elelem/skills/catalog.rb
@@ -45,14 +45,14 @@ module Elelem
dir = File.expand_path(path)
return unless File.directory?(dir)
- Dir["#{dir}/*/SKILL.md"].sort.each { |skill_md| load_into(skill_md, seen) }
+ Dir["#{dir}/*/SKILL.md"].each { |skill_md| load_into(skill_md, seen) }
end
def scan_remote(seen)
dir = File.expand_path(@remote_skills)
return unless File.directory?(dir)
- Dir["#{dir}/*/*/SKILL.md"].sort.each { |skill_md| load_into(skill_md, seen) }
+ Dir["#{dir}/*/*/SKILL.md"].each { |skill_md| load_into(skill_md, seen) }
end
def load_into(skill_md, seen)
@@ -60,7 +60,7 @@ module Elelem
return unless skill
seen[skill.name] = skill
- rescue => e
+ rescue StandardError => e
Elelem.logger.warn("elelem-skills: failed to load #{skill_md}: #{e.message}")
end
lib/elelem/skills/fetcher.rb
@@ -11,7 +11,8 @@ module Elelem
class Fetcher
SCHEMA = "https://schemas.agentskills.io/discovery/0.2.0/schema.json"
- FetchError = Class.new(StandardError)
+ class FetchError < StandardError
+ end
def initialize(install_dir: Catalog::REMOTE_SKILLS, http: Net::HTTP, lockfile: nil)
@install_dir = install_dir
@@ -49,7 +50,8 @@ module Elelem
name, type, url, digest = entry.values_at("name", "type", "url", "digest")
unless type == "skill-md"
- Elelem.logger.warn("elelem-skills: fetch: skipping #{name.inspect}, unsupported type #{type.inspect} (archives are not implemented)")
+ Elelem.logger.warn("elelem-skills: fetch: skipping #{name.inspect}, unsupported type #{type.inspect} " \
+ "(archives are not implemented)")
return nil
end
@@ -86,7 +88,7 @@ module Elelem
response.body
rescue FetchError
raise
- rescue => e
+ rescue StandardError => e
raise FetchError, "GET #{uri} failed: #{e.class}: #{e.message}"
end
end
lib/elelem/skills/setup.rb
@@ -25,10 +25,10 @@ module Elelem
catalog = @catalog
@agent.toolbox.add("skill",
- description: "Load a skill's instructions by name. Available skills:\n#{catalog.index(limit: SUMMARY_LIMIT)}",
- params: { name: { type: "string", description: "the skill name" } },
- required: ["name"]
- ) do |a|
+ description: "Load a skill's instructions by name. Available skills:\n" \
+ "#{catalog.index(limit: SUMMARY_LIMIT)}",
+ params: { name: { type: "string", description: "the skill name" } },
+ required: ["name"]) do |a|
skill = catalog.find(a["name"])
next { error: "unknown skill: #{a["name"]}" } unless skill
@@ -41,14 +41,19 @@ module Elelem
agent = @agent
completions = -> { catalog.all.map(&:name) }
- agent.commands.register("skills", description: "List available skills, or `/skills fetch <origin>` to install from a remote index", completions: completions) do |arg|
+ agent.commands.register("skills",
+ description: "List available skills, or `/skills fetch <origin>` to install " \
+ "from a remote index",
+ completions: completions) do |arg|
if arg&.start_with?("fetch ")
fetch(agent, arg.delete_prefix("fetch ").strip)
elsif arg && !arg.empty?
skill = catalog.find(arg)
skill ? agent.output.say(skill.body) : agent.output.say("Unknown skill: #{arg}")
else
- list = catalog.all.map { |skill| "- **#{skill.name}**: #{truncate(skill.description, SUMMARY_LIMIT)}" }.join("\n")
+ list = catalog.all.map do |skill|
+ "- **#{skill.name}**: #{truncate(skill.description, SUMMARY_LIMIT)}"
+ end.join("\n")
agent.output.say(list.empty? ? "No skills found." : list, as: :markdown)
end
end
@@ -64,7 +69,8 @@ module Elelem
return agent.output.say("No skills installed from #{origin}.") if names.empty?
@catalog.all(reload: true)
- agent.output.say("Installed: #{names.join(", ")}\nAvailable via /skills and the skill tool now. Restart elelem to refresh the skill tool's description with the new catalog.")
+ agent.output.say("Installed: #{names.join(", ")}\nAvailable via /skills and the skill tool now. " \
+ "Restart elelem to refresh the skill tool's description with the new catalog.")
rescue Fetcher::FetchError => e
agent.output.say(e.message, as: :error)
end
lib/elelem/skills/skill_file.rb
@@ -15,7 +15,8 @@ module Elelem
end
attrs = YAML.safe_load(frontmatter)
- name, description = attrs["name"], attrs["description"]
+ name = attrs["name"]
+ description = attrs["description"]
unless name && description
Elelem.logger.warn("elelem-skills: #{path} is missing name or description, skipping")
return nil
spec/elelem/skills/catalog_spec.rb
@@ -86,7 +86,8 @@ RSpec.describe Elelem::Skills::Catalog do
catalog = described_class.new(load_paths: [fixtures], remote_skills: remote_root)
- expect(catalog.find("tdd").description).to eq("Test-driven development. Use when the user wants tests written first.")
+ expect(catalog.find("tdd").description)
+ .to eq("Test-driven development. Use when the user wants tests written first.")
end
end
spec/elelem/skills/cli_spec.rb
@@ -44,7 +44,8 @@ RSpec.describe Elelem::Skills::CLI do
it "lists discovered skills" do
fixtures = File.expand_path("../../fixtures/skills", __dir__)
- catalog = instance_double(Elelem::Skills::Catalog, all: [Elelem::Skills::Skill.new("tdd", "Test-driven development.", fixtures, "body")])
+ catalog = instance_double(Elelem::Skills::Catalog,
+ all: [Elelem::Skills::Skill.new("tdd", "Test-driven development.", fixtures, "body")])
allow(Elelem::Skills::Catalog).to receive(:new).and_return(catalog)
output = capture_stdout { described_class.new(["list"]).run }
spec/elelem/skills/fetcher_spec.rb
@@ -95,7 +95,9 @@ RSpec.describe Elelem::Skills::Fetcher do
.with(URI("https://example.com/.well-known/agent-skills/index.json"))
.and_return(ok(bad))
- expect { fetcher.fetch("example.com") }.to raise_error(Elelem::Skills::Fetcher::FetchError, /unrecognized index schema/)
+ expect do
+ fetcher.fetch("example.com")
+ end.to raise_error(Elelem::Skills::Fetcher::FetchError, /unrecognized index schema/)
end
it "raises when the schema field is absent (treated as incompatible v0.1.0)" do
@@ -104,7 +106,9 @@ RSpec.describe Elelem::Skills::Fetcher do
.with(URI("https://example.com/.well-known/agent-skills/index.json"))
.and_return(ok(no_schema))
- expect { fetcher.fetch("example.com") }.to raise_error(Elelem::Skills::Fetcher::FetchError, /unrecognized index schema/)
+ expect do
+ fetcher.fetch("example.com")
+ end.to raise_error(Elelem::Skills::Fetcher::FetchError, /unrecognized index schema/)
end
it "skips archive entries and warns, without raising" do
spec/elelem/skills/lockfile_spec.rb
@@ -15,7 +15,8 @@ RSpec.describe Elelem::Skills::Lockfile do
describe "#record" do
it "writes a new entry keyed by host/name" do
- lockfile.record(host: "example.com", name: "example", url: "https://example.com/example/SKILL.md", digest: "sha256:abc")
+ lockfile.record(host: "example.com", name: "example", url: "https://example.com/example/SKILL.md",
+ digest: "sha256:abc")
entry = lockfile.read.dig("skills", "example.com/example")
expect(entry["host"]).to eq("example.com")
@@ -26,10 +27,12 @@ RSpec.describe Elelem::Skills::Lockfile do
end
it "keeps the original installedAt but bumps updatedAt on re-install" do
- lockfile.record(host: "example.com", name: "example", url: "https://example.com/example/SKILL.md", digest: "sha256:abc")
+ lockfile.record(host: "example.com", name: "example", url: "https://example.com/example/SKILL.md",
+ digest: "sha256:abc")
first_installed_at = lockfile.read.dig("skills", "example.com/example", "installedAt")
- lockfile.record(host: "example.com", name: "example", url: "https://example.com/example/SKILL.md", digest: "sha256:def")
+ lockfile.record(host: "example.com", name: "example", url: "https://example.com/example/SKILL.md",
+ digest: "sha256:def")
entry = lockfile.read.dig("skills", "example.com/example")
expect(entry["installedAt"]).to eq(first_installed_at)
@@ -37,8 +40,10 @@ RSpec.describe Elelem::Skills::Lockfile do
end
it "does not clobber an entry for the same skill name from a different host" do
- lockfile.record(host: "example.com", name: "example", url: "https://example.com/example/SKILL.md", digest: "sha256:abc")
- lockfile.record(host: "other.example", name: "example", url: "https://other.example/example/SKILL.md", digest: "sha256:def")
+ lockfile.record(host: "example.com", name: "example", url: "https://example.com/example/SKILL.md",
+ digest: "sha256:abc")
+ lockfile.record(host: "other.example", name: "example", url: "https://other.example/example/SKILL.md",
+ digest: "sha256:def")
expect(lockfile.read["skills"].keys).to contain_exactly("example.com/example", "other.example/example")
end
spec/elelem/skills/setup_spec.rb
@@ -48,7 +48,9 @@ RSpec.describe "elelem-skills setup" do
agent = Elelem::Session.new(Elelem::Agent.new(Elelem::StubProvider.new, output: output))
Elelem::Config.default.instance_variable_get(:@registration).setups.fetch("skills").call(agent)
- expect(output).to receive(:say).with("- **tdd**: Test-driven development. Use when the user wants tests written first.", as: :markdown)
+ expect(output).to receive(:say).with(
+ "- **tdd**: Test-driven development. Use when the user wants tests written first.", as: :markdown
+ )
agent.commands.run("skills", nil)
end