agent_skills 0.1.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 +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +27 -0
- data/LICENSE +21 -0
- data/README.md +140 -0
- data/RELEASING.md +95 -0
- data/Rakefile +21 -0
- data/agent_skills.gemspec +41 -0
- data/exe/agent-skills +7 -0
- data/lib/agent_skills/cli.rb +146 -0
- data/lib/agent_skills/errors.rb +11 -0
- data/lib/agent_skills/generator.rb +85 -0
- data/lib/agent_skills/loader.rb +69 -0
- data/lib/agent_skills/packager.rb +79 -0
- data/lib/agent_skills/skill.rb +108 -0
- data/lib/agent_skills/validator.rb +91 -0
- data/lib/agent_skills/version.rb +5 -0
- data/lib/agent_skills.rb +12 -0
- metadata +97 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 57798b894d8e2eff5fba30de74130a813e7ae2bdc6599021e3f3ff610a9a9908
|
|
4
|
+
data.tar.gz: 38f5cda0c7ee6714794ae177bd575c066f27f0d3f1738408b39d9b09dc17ef75
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 3668a7b89dc969da83ae0dbf6eddf7355ae1df69ba4d784fa9946605e9bd2d189f9269b12da21e990b28705c27b2247807a6e82b5f63b9a700f86535183c5d01
|
|
7
|
+
data.tar.gz: a3e154ddb1665f9aff7cf90d921414c9b0fa2a1541d90b35eade04ae82c744e9cb7398d29a65208f643adb038f96bb45b8dd19e2c4de68e1a0e49155dc3ea0dc
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2025-01-29
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `Skill` class for parsing SKILL.md files with YAML frontmatter
|
|
15
|
+
- `Validator` class for validating skills against agentskills.io specification
|
|
16
|
+
- `Generator` class for scaffolding new skills
|
|
17
|
+
- `Loader` class for discovering skills from filesystem paths
|
|
18
|
+
- `Packager` class for creating and extracting .skill bundles
|
|
19
|
+
- CLI tool with commands: `new`, `validate`, `list`, `info`, `pack`, `unpack`, `version`
|
|
20
|
+
- Support for `scripts/`, `references/`, and `assets/` directories
|
|
21
|
+
- `to_prompt_xml` method for LLM prompt injection
|
|
22
|
+
- GitHub Actions CI workflow
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
[Unreleased]: https://github.com/rubyonai/agent_skills/compare/v0.1.0...HEAD
|
|
27
|
+
[0.1.0]: https://github.com/rubyonai/agent_skills/releases/tag/v0.1.0
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nagendra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Agent Skills
|
|
2
|
+
|
|
3
|
+
[](https://github.com/rubyonai/agent_skills/actions/workflows/ci.yml)
|
|
4
|
+
[](https://badge.fury.io/rb/agent_skills)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Ruby implementation of the [Agent Skills](https://agentskills.io) open standard — a simple format for giving AI agents new capabilities.
|
|
8
|
+
|
|
9
|
+
## What are Agent Skills?
|
|
10
|
+
|
|
11
|
+
Agent Skills are portable folders of instructions that AI agents can load to perform specialized tasks. The format is supported by **Claude, GitHub Copilot, Cursor, VS Code**, and [26+ other tools](https://agentskills.io).
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
my-skill/
|
|
15
|
+
├── SKILL.md # Instructions for the agent (required)
|
|
16
|
+
├── scripts/ # Executable code (optional)
|
|
17
|
+
├── references/ # Supporting docs (optional)
|
|
18
|
+
└── assets/ # Templates, data files (optional)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
Add to your Gemfile:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
gem 'agent_skills'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or install directly:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
gem install agent_skills
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Create a skill
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
agent-skills new my-skill -d "Description of what this skill does"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Validate a skill
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
agent-skills validate ./my-skill
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Package for distribution
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
agent-skills pack ./my-skill
|
|
53
|
+
# Creates: my-skill.skill
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## CLI Commands
|
|
57
|
+
|
|
58
|
+
| Command | Description |
|
|
59
|
+
|---------|-------------|
|
|
60
|
+
| `agent-skills new NAME -d DESC` | Create a new skill |
|
|
61
|
+
| `agent-skills validate PATH` | Validate skill against spec |
|
|
62
|
+
| `agent-skills list` | List discovered skills |
|
|
63
|
+
| `agent-skills info PATH` | Show skill details |
|
|
64
|
+
| `agent-skills pack PATH` | Package into .skill file |
|
|
65
|
+
| `agent-skills unpack FILE` | Extract a .skill file |
|
|
66
|
+
|
|
67
|
+
## Ruby API
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
require 'agent_skills'
|
|
71
|
+
|
|
72
|
+
# Load and parse a skill
|
|
73
|
+
skill = AgentSkills::Skill.load('./my-skill')
|
|
74
|
+
skill.name # => "my-skill"
|
|
75
|
+
skill.description # => "What it does..."
|
|
76
|
+
|
|
77
|
+
# Validate against spec
|
|
78
|
+
validator = AgentSkills::Validator.new(skill)
|
|
79
|
+
validator.valid? # => true
|
|
80
|
+
validator.errors # => []
|
|
81
|
+
|
|
82
|
+
# Discover skills from paths
|
|
83
|
+
loader = AgentSkills::Loader.new(paths: ['./skills'])
|
|
84
|
+
loader.discover
|
|
85
|
+
loader['my-skill'] # => #<AgentSkills::Skill>
|
|
86
|
+
|
|
87
|
+
# Generate prompt XML for LLM injection
|
|
88
|
+
skill.to_prompt_xml
|
|
89
|
+
# => "<skill name=\"my-skill\"><description>...</description>...</skill>"
|
|
90
|
+
|
|
91
|
+
# Create a new skill programmatically
|
|
92
|
+
AgentSkills::Generator.create(
|
|
93
|
+
path: './skills',
|
|
94
|
+
name: 'my-skill',
|
|
95
|
+
description: 'What this skill does'
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Package and distribute
|
|
99
|
+
AgentSkills::Packager.pack('./my-skill') # => "my-skill.skill"
|
|
100
|
+
AgentSkills::Packager.unpack('my-skill.skill', output: './extracted')
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## SKILL.md Format
|
|
104
|
+
|
|
105
|
+
```markdown
|
|
106
|
+
---
|
|
107
|
+
name: my-skill
|
|
108
|
+
description: What this skill does and when to use it.
|
|
109
|
+
license: MIT # optional
|
|
110
|
+
compatibility: Requires Python 3.x # optional
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
# My Skill
|
|
114
|
+
|
|
115
|
+
Instructions for the agent go here...
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
See the [full specification](https://agentskills.io/specification) for details.
|
|
119
|
+
|
|
120
|
+
## Development
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
git clone https://github.com/rubyonai/agent_skills.git
|
|
124
|
+
cd agent_skills
|
|
125
|
+
bundle install
|
|
126
|
+
bundle exec rspec
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Contributing
|
|
130
|
+
|
|
131
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/rubyonai/agent_skills).
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
136
|
+
|
|
137
|
+
## Resources
|
|
138
|
+
|
|
139
|
+
- [Agent Skills Specification](https://agentskills.io)
|
|
140
|
+
- [Example Skills](https://github.com/anthropics/skills)
|
data/RELEASING.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
This document describes how to release a new version of the `agent_skills` gem.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
1. Push access to the GitHub repository
|
|
8
|
+
2. RubyGems account with ownership of `agent_skills` gem
|
|
9
|
+
3. `RUBYGEMS_API_KEY` secret configured in GitHub repository settings
|
|
10
|
+
|
|
11
|
+
## Setup (One-time)
|
|
12
|
+
|
|
13
|
+
### Configure RubyGems API Key
|
|
14
|
+
|
|
15
|
+
1. Get your API key from https://rubygems.org/profile/api_keys
|
|
16
|
+
2. Add it to GitHub: Repository → Settings → Secrets → Actions → New secret
|
|
17
|
+
- Name: `RUBYGEMS_API_KEY`
|
|
18
|
+
- Value: Your API key
|
|
19
|
+
|
|
20
|
+
## Release Process
|
|
21
|
+
|
|
22
|
+
### 1. Update version
|
|
23
|
+
|
|
24
|
+
Edit `lib/agent_skills/version.rb`:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
module AgentSkills
|
|
28
|
+
VERSION = "0.2.0" # Update this
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2. Update CHANGELOG
|
|
33
|
+
|
|
34
|
+
Add release notes to `CHANGELOG.md`:
|
|
35
|
+
|
|
36
|
+
```markdown
|
|
37
|
+
## [0.2.0] - YYYY-MM-DD
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- New feature X
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
- Bug fix Y
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 3. Commit and tag
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git add lib/agent_skills/version.rb CHANGELOG.md
|
|
50
|
+
git commit -m "Release v0.2.0"
|
|
51
|
+
git tag v0.2.0
|
|
52
|
+
git push origin main --tags
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 4. Automated release
|
|
56
|
+
|
|
57
|
+
Pushing the tag triggers the release workflow which:
|
|
58
|
+
- Runs tests
|
|
59
|
+
- Builds the gem
|
|
60
|
+
- Creates a GitHub Release
|
|
61
|
+
- Publishes to RubyGems
|
|
62
|
+
|
|
63
|
+
## Manual Release (if needed)
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Build
|
|
67
|
+
gem build agent_skills.gemspec
|
|
68
|
+
|
|
69
|
+
# Test locally
|
|
70
|
+
gem install ./agent_skills-0.2.0.gem
|
|
71
|
+
|
|
72
|
+
# Publish
|
|
73
|
+
gem push agent_skills-0.2.0.gem
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Rake Tasks
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Build gem
|
|
80
|
+
bundle exec rake build
|
|
81
|
+
|
|
82
|
+
# Install locally
|
|
83
|
+
bundle exec rake install
|
|
84
|
+
|
|
85
|
+
# Release (build, tag, push gem)
|
|
86
|
+
bundle exec rake release
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Version Guidelines
|
|
90
|
+
|
|
91
|
+
Follow [Semantic Versioning](https://semver.org/):
|
|
92
|
+
|
|
93
|
+
- **MAJOR** (1.0.0): Breaking API changes
|
|
94
|
+
- **MINOR** (0.2.0): New features, backward compatible
|
|
95
|
+
- **PATCH** (0.1.1): Bug fixes, backward compatible
|
data/Rakefile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
|
+
require "rubocop/rake_task"
|
|
6
|
+
|
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
8
|
+
RuboCop::RakeTask.new
|
|
9
|
+
|
|
10
|
+
task default: %i[spec rubocop]
|
|
11
|
+
|
|
12
|
+
desc "Run all tests and checks"
|
|
13
|
+
task ci: %i[spec rubocop]
|
|
14
|
+
|
|
15
|
+
desc "Open an IRB session with the gem loaded"
|
|
16
|
+
task :console do
|
|
17
|
+
require "irb"
|
|
18
|
+
require "agent_skills"
|
|
19
|
+
ARGV.clear
|
|
20
|
+
IRB.start(__FILE__)
|
|
21
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/agent_skills/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "agent_skills"
|
|
7
|
+
spec.version = AgentSkills::VERSION
|
|
8
|
+
spec.authors = ["rubyonai"]
|
|
9
|
+
spec.email = ["your-email@example.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Ruby implementation of the Agent Skills open standard"
|
|
12
|
+
spec.description = <<~DESC
|
|
13
|
+
Parse, validate, create, package, and load Agent Skills in Ruby.
|
|
14
|
+
Agent Skills is an open format (by Anthropic) for giving AI agents
|
|
15
|
+
new capabilities through structured instructions, scripts, and resources.
|
|
16
|
+
DESC
|
|
17
|
+
spec.homepage = "https://github.com/rubyonai/agent_skills"
|
|
18
|
+
spec.license = "MIT"
|
|
19
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
20
|
+
|
|
21
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
22
|
+
spec.metadata["source_code_uri"] = "https://github.com/rubyonai/agent_skills"
|
|
23
|
+
spec.metadata["changelog_uri"] = "https://github.com/rubyonai/agent_skills/blob/main/CHANGELOG.md"
|
|
24
|
+
spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/agent_skills"
|
|
25
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
26
|
+
|
|
27
|
+
# Specify which files should be added to the gem
|
|
28
|
+
spec.files = Dir.chdir(__dir__) do
|
|
29
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
30
|
+
(File.expand_path(f) == __FILE__) ||
|
|
31
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .github .circleci appveyor Gemfile])
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
spec.bindir = "exe"
|
|
35
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
36
|
+
spec.require_paths = ["lib"]
|
|
37
|
+
|
|
38
|
+
# Runtime dependencies
|
|
39
|
+
spec.add_dependency "rubyzip", "~> 2.3"
|
|
40
|
+
spec.add_dependency "thor", "~> 1.3"
|
|
41
|
+
end
|
data/exe/agent-skills
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
|
|
5
|
+
module AgentSkills
|
|
6
|
+
class CLI < Thor
|
|
7
|
+
def self.exit_on_failure?
|
|
8
|
+
true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
desc "new NAME", "Create a new skill"
|
|
12
|
+
option :description, aliases: "-d", required: true, desc: "Skill description"
|
|
13
|
+
option :path, aliases: "-p", default: ".", desc: "Output directory"
|
|
14
|
+
option :scripts, type: :boolean, default: false, desc: "Include scripts directory"
|
|
15
|
+
option :references, type: :boolean, default: false, desc: "Include references directory"
|
|
16
|
+
option :assets, type: :boolean, default: false, desc: "Include assets directory"
|
|
17
|
+
def new(name)
|
|
18
|
+
path = Generator.create(
|
|
19
|
+
path: options[:path],
|
|
20
|
+
name: name,
|
|
21
|
+
description: options[:description],
|
|
22
|
+
with_scripts: options[:scripts],
|
|
23
|
+
with_references: options[:references],
|
|
24
|
+
with_assets: options[:assets]
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
say "Created skill at #{path}/", :green
|
|
28
|
+
say " SKILL.md"
|
|
29
|
+
say " scripts/" if options[:scripts]
|
|
30
|
+
say " references/" if options[:references]
|
|
31
|
+
say " assets/" if options[:assets]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
desc "validate PATH", "Validate a skill against the spec"
|
|
35
|
+
def validate(path)
|
|
36
|
+
skill = Skill.load(path)
|
|
37
|
+
validator = Validator.new(skill)
|
|
38
|
+
|
|
39
|
+
if validator.valid?
|
|
40
|
+
say "#{skill.name} is valid", :green
|
|
41
|
+
else
|
|
42
|
+
say "#{skill.name} has errors:", :red
|
|
43
|
+
validator.errors.each { |e| say " - #{e}", :red }
|
|
44
|
+
exit 1
|
|
45
|
+
end
|
|
46
|
+
rescue NotFoundError, ParseError => e
|
|
47
|
+
say "Error: #{e.message}", :red
|
|
48
|
+
exit 1
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
desc "list", "List discovered skills"
|
|
52
|
+
option :path, aliases: "-p", type: :array, desc: "Paths to search"
|
|
53
|
+
def list
|
|
54
|
+
paths = options[:path] || Loader::DEFAULT_PATHS
|
|
55
|
+
loader = Loader.new(paths: paths)
|
|
56
|
+
skills = loader.discover
|
|
57
|
+
|
|
58
|
+
if skills.empty?
|
|
59
|
+
say "No skills found in: #{paths.join(', ')}", :yellow
|
|
60
|
+
return
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
say "Found #{skills.size} skill(s):\n\n"
|
|
64
|
+
|
|
65
|
+
skills.each do |name, skill|
|
|
66
|
+
say name, :green
|
|
67
|
+
say " #{truncate(skill.description, 60)}"
|
|
68
|
+
say " Path: #{skill.path}" if skill.path
|
|
69
|
+
say ""
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
desc "info PATH", "Show detailed skill information"
|
|
74
|
+
def info(path)
|
|
75
|
+
skill = Skill.load(path)
|
|
76
|
+
|
|
77
|
+
say "Name: ", :green, false
|
|
78
|
+
say skill.name
|
|
79
|
+
say "Description: ", :green, false
|
|
80
|
+
say skill.description
|
|
81
|
+
say "Path: ", :green, false
|
|
82
|
+
say skill.path || "(none)"
|
|
83
|
+
|
|
84
|
+
if skill.license
|
|
85
|
+
say "License: ", :green, false
|
|
86
|
+
say skill.license
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if skill.compatibility
|
|
90
|
+
say "Compat: ", :green, false
|
|
91
|
+
say skill.compatibility
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
unless skill.scripts.empty?
|
|
95
|
+
say "Scripts: ", :green, false
|
|
96
|
+
say skill.scripts.map { |s| File.basename(s) }.join(", ")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
unless skill.references.empty?
|
|
100
|
+
say "References: ", :green, false
|
|
101
|
+
say skill.references.map { |r| File.basename(r) }.join(", ")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Validation status
|
|
105
|
+
validator = Validator.new(skill)
|
|
106
|
+
say "Valid: ", :green, false
|
|
107
|
+
say validator.valid? ? "Yes" : "No (#{validator.errors.join(', ')})"
|
|
108
|
+
rescue NotFoundError, ParseError => e
|
|
109
|
+
say "Error: #{e.message}", :red
|
|
110
|
+
exit 1
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
desc "pack PATH", "Package a skill into a .skill file"
|
|
114
|
+
option :output, aliases: "-o", desc: "Output file path"
|
|
115
|
+
def pack(path)
|
|
116
|
+
output = Packager.pack(path, output: options[:output])
|
|
117
|
+
say "Created #{output}", :green
|
|
118
|
+
rescue NotFoundError, ParseError, ValidationError => e
|
|
119
|
+
say "Error: #{e.message}", :red
|
|
120
|
+
exit 1
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
desc "unpack FILE", "Extract a .skill file"
|
|
124
|
+
option :output, aliases: "-o", default: ".", desc: "Output directory"
|
|
125
|
+
def unpack(file)
|
|
126
|
+
extracted = Packager.unpack(file, output: options[:output])
|
|
127
|
+
say "Extracted to #{extracted}", :green
|
|
128
|
+
rescue NotFoundError => e
|
|
129
|
+
say "Error: #{e.message}", :red
|
|
130
|
+
exit 1
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
desc "version", "Show version"
|
|
134
|
+
def version
|
|
135
|
+
say "agent_skills #{VERSION}"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
def truncate(text, length)
|
|
141
|
+
return text if text.length <= length
|
|
142
|
+
|
|
143
|
+
"#{text[0, length - 3]}..."
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module AgentSkills
|
|
6
|
+
class Generator
|
|
7
|
+
SKILL_TEMPLATE = <<~SKILL
|
|
8
|
+
---
|
|
9
|
+
name: %<name>s
|
|
10
|
+
description: %<description>s
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# %<title>s
|
|
14
|
+
|
|
15
|
+
## Instructions
|
|
16
|
+
|
|
17
|
+
[Add step-by-step instructions here]
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Input
|
|
22
|
+
[Example input]
|
|
23
|
+
|
|
24
|
+
### Output
|
|
25
|
+
[Expected output]
|
|
26
|
+
|
|
27
|
+
## Guidelines
|
|
28
|
+
|
|
29
|
+
- [Add guidelines here]
|
|
30
|
+
SKILL
|
|
31
|
+
|
|
32
|
+
attr_reader :path, :name, :description, :options
|
|
33
|
+
|
|
34
|
+
def initialize(path:, name:, description:, **options)
|
|
35
|
+
@path = path
|
|
36
|
+
@name = name
|
|
37
|
+
@description = description
|
|
38
|
+
@options = options
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.create(path:, name:, description:, **options)
|
|
42
|
+
new(path: path, name: name, description: description, **options).create
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def create
|
|
46
|
+
validate_inputs!
|
|
47
|
+
create_directories
|
|
48
|
+
create_skill_md
|
|
49
|
+
skill_path
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def validate_inputs!
|
|
55
|
+
raise ArgumentError, "name is required" if @name.nil? || @name.empty?
|
|
56
|
+
raise ArgumentError, "description is required" if @description.nil? || @description.empty?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def skill_path
|
|
60
|
+
File.join(@path, @name)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def create_directories
|
|
64
|
+
FileUtils.mkdir_p(skill_path)
|
|
65
|
+
FileUtils.mkdir_p(File.join(skill_path, "scripts")) if @options[:with_scripts]
|
|
66
|
+
FileUtils.mkdir_p(File.join(skill_path, "references")) if @options[:with_references]
|
|
67
|
+
FileUtils.mkdir_p(File.join(skill_path, "assets")) if @options[:with_assets]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def create_skill_md
|
|
71
|
+
content = format(
|
|
72
|
+
SKILL_TEMPLATE,
|
|
73
|
+
name: @name,
|
|
74
|
+
description: @description,
|
|
75
|
+
title: titleize(@name)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
File.write(File.join(skill_path, "SKILL.md"), content)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def titleize(name)
|
|
82
|
+
name.split("-").map(&:capitalize).join(" ")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AgentSkills
|
|
4
|
+
class Loader
|
|
5
|
+
DEFAULT_PATHS = [
|
|
6
|
+
File.expand_path("~/.config/claude/skills"),
|
|
7
|
+
".claude/skills",
|
|
8
|
+
"skills"
|
|
9
|
+
].freeze
|
|
10
|
+
|
|
11
|
+
attr_reader :paths, :skills
|
|
12
|
+
|
|
13
|
+
def initialize(paths: DEFAULT_PATHS)
|
|
14
|
+
@paths = Array(paths)
|
|
15
|
+
@skills = {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def discover
|
|
19
|
+
@skills = {}
|
|
20
|
+
|
|
21
|
+
@paths.each do |base_path|
|
|
22
|
+
expanded = File.expand_path(base_path)
|
|
23
|
+
next unless File.directory?(expanded)
|
|
24
|
+
|
|
25
|
+
discover_in_path(expanded)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@skills
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def [](name)
|
|
32
|
+
@skills[name]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def count
|
|
36
|
+
@skills.size
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def each(&block)
|
|
40
|
+
@skills.each(&block)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def find_relevant(query)
|
|
44
|
+
return [] if query.nil? || query.empty?
|
|
45
|
+
|
|
46
|
+
keywords = query.downcase.split(/\s+/)
|
|
47
|
+
|
|
48
|
+
@skills.values.select do |skill|
|
|
49
|
+
text = "#{skill.name} #{skill.description}".downcase
|
|
50
|
+
keywords.any? { |keyword| text.include?(keyword) }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def discover_in_path(base_path)
|
|
57
|
+
Dir.glob(File.join(base_path, "*", "SKILL.md")).each do |skill_md|
|
|
58
|
+
skill_dir = File.dirname(skill_md)
|
|
59
|
+
|
|
60
|
+
begin
|
|
61
|
+
skill = Skill.load(skill_dir)
|
|
62
|
+
@skills[skill.name] = skill
|
|
63
|
+
rescue Error => e
|
|
64
|
+
warn "Warning: Failed to load skill at #{skill_dir}: #{e.message}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zip"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module AgentSkills
|
|
7
|
+
class Packager
|
|
8
|
+
SKILL_EXTENSION = ".skill"
|
|
9
|
+
|
|
10
|
+
def self.pack(skill_path, output: nil)
|
|
11
|
+
new(skill_path).pack(output: output)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.unpack(skill_file, output:)
|
|
15
|
+
new(nil).unpack(skill_file, output: output)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(skill_path)
|
|
19
|
+
@skill_path = skill_path
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def pack(output: nil)
|
|
23
|
+
validate_skill!
|
|
24
|
+
|
|
25
|
+
skill = Skill.load(@skill_path)
|
|
26
|
+
Validator.validate!(skill)
|
|
27
|
+
|
|
28
|
+
output_file = output || "#{skill.name}#{SKILL_EXTENSION}"
|
|
29
|
+
|
|
30
|
+
create_zip(output_file)
|
|
31
|
+
output_file
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def unpack(skill_file, output:)
|
|
35
|
+
raise NotFoundError, "Skill file not found: #{skill_file}" unless File.exist?(skill_file)
|
|
36
|
+
|
|
37
|
+
FileUtils.mkdir_p(output)
|
|
38
|
+
|
|
39
|
+
Zip::File.open(skill_file) do |zip|
|
|
40
|
+
zip.each do |entry|
|
|
41
|
+
dest = File.join(output, entry.name)
|
|
42
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
|
43
|
+
entry.extract(dest) { true } # overwrite existing
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Return the extracted skill directory
|
|
48
|
+
skill_dirs = Dir.glob(File.join(output, "*", "SKILL.md")).map { |f| File.dirname(f) }
|
|
49
|
+
skill_dirs.first || output
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def validate_skill!
|
|
55
|
+
raise NotFoundError, "Skill path not found: #{@skill_path}" unless File.directory?(@skill_path)
|
|
56
|
+
|
|
57
|
+
skill_md = File.join(@skill_path, "SKILL.md")
|
|
58
|
+
raise NotFoundError, "SKILL.md not found in #{@skill_path}" unless File.exist?(skill_md)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def create_zip(output_file)
|
|
62
|
+
File.delete(output_file) if File.exist?(output_file)
|
|
63
|
+
|
|
64
|
+
Zip::File.open(output_file, Zip::File::CREATE) do |zip|
|
|
65
|
+
add_directory_to_zip(zip, @skill_path, File.basename(@skill_path))
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def add_directory_to_zip(zip, dir_path, base_name)
|
|
70
|
+
Dir.glob(File.join(dir_path, "**", "*")).each do |file|
|
|
71
|
+
next if File.directory?(file)
|
|
72
|
+
|
|
73
|
+
relative_path = file.sub("#{dir_path}/", "")
|
|
74
|
+
zip_path = File.join(base_name, relative_path)
|
|
75
|
+
zip.add(zip_path, file)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module AgentSkills
|
|
6
|
+
class Skill
|
|
7
|
+
FRONTMATTER_REGEX = /\A---\s*\n(.+?)\n---\s*\n(.*)/m
|
|
8
|
+
|
|
9
|
+
attr_reader :path, :name, :description, :license, :compatibility,
|
|
10
|
+
:metadata, :allowed_tools, :body
|
|
11
|
+
|
|
12
|
+
def initialize(path:, name:, description:, body:, license: nil,
|
|
13
|
+
compatibility: nil, metadata: {}, allowed_tools: [])
|
|
14
|
+
@path = path
|
|
15
|
+
@name = name
|
|
16
|
+
@description = description
|
|
17
|
+
@body = body
|
|
18
|
+
@license = license
|
|
19
|
+
@compatibility = compatibility
|
|
20
|
+
@metadata = metadata
|
|
21
|
+
@allowed_tools = allowed_tools
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.load(skill_path)
|
|
25
|
+
skill_md_path = File.join(skill_path, "SKILL.md")
|
|
26
|
+
|
|
27
|
+
unless File.exist?(skill_md_path)
|
|
28
|
+
raise NotFoundError, "SKILL.md not found in #{skill_path}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
content = File.read(skill_md_path, encoding: "UTF-8")
|
|
32
|
+
parse(content, skill_path)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.parse(content, path = nil)
|
|
36
|
+
match = content.match(FRONTMATTER_REGEX)
|
|
37
|
+
|
|
38
|
+
unless match
|
|
39
|
+
raise ParseError, "Invalid SKILL.md format: missing YAML frontmatter"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
frontmatter = YAML.safe_load(match[1], symbolize_names: true)
|
|
43
|
+
body = match[2].strip
|
|
44
|
+
|
|
45
|
+
new(
|
|
46
|
+
path: path,
|
|
47
|
+
name: frontmatter[:name],
|
|
48
|
+
description: frontmatter[:description],
|
|
49
|
+
license: frontmatter[:license],
|
|
50
|
+
compatibility: frontmatter[:compatibility],
|
|
51
|
+
metadata: frontmatter[:metadata] || {},
|
|
52
|
+
allowed_tools: parse_allowed_tools(frontmatter[:"allowed-tools"]),
|
|
53
|
+
body: body
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def scripts
|
|
58
|
+
return [] unless @path
|
|
59
|
+
|
|
60
|
+
Dir.glob(File.join(@path, "scripts", "*")).select { |f| File.file?(f) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def references
|
|
64
|
+
return [] unless @path
|
|
65
|
+
|
|
66
|
+
Dir.glob(File.join(@path, "references", "*.md"))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def assets
|
|
70
|
+
return [] unless @path
|
|
71
|
+
|
|
72
|
+
Dir.glob(File.join(@path, "assets", "*")).select { |f| File.file?(f) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def to_prompt_xml
|
|
76
|
+
<<~XML.strip
|
|
77
|
+
<skill name="#{@name}">
|
|
78
|
+
<description>#{@description}</description>
|
|
79
|
+
<instructions>
|
|
80
|
+
#{@body}
|
|
81
|
+
</instructions>
|
|
82
|
+
</skill>
|
|
83
|
+
XML
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def to_h
|
|
87
|
+
{
|
|
88
|
+
name: @name,
|
|
89
|
+
description: @description,
|
|
90
|
+
license: @license,
|
|
91
|
+
compatibility: @compatibility,
|
|
92
|
+
metadata: @metadata,
|
|
93
|
+
allowed_tools: @allowed_tools,
|
|
94
|
+
body: @body
|
|
95
|
+
}.compact
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def self.parse_allowed_tools(value)
|
|
101
|
+
return [] if value.nil?
|
|
102
|
+
|
|
103
|
+
value.to_s.split(/\s+/)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private_class_method :parse_allowed_tools
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AgentSkills
|
|
4
|
+
class Validator
|
|
5
|
+
NAME_REGEX = /\A[a-z0-9]+(-[a-z0-9]+)*\z/
|
|
6
|
+
MAX_NAME_LENGTH = 64
|
|
7
|
+
MAX_DESCRIPTION_LENGTH = 1024
|
|
8
|
+
MAX_COMPATIBILITY_LENGTH = 500
|
|
9
|
+
|
|
10
|
+
attr_reader :skill, :errors
|
|
11
|
+
|
|
12
|
+
def initialize(skill)
|
|
13
|
+
@skill = skill
|
|
14
|
+
@errors = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def valid?
|
|
18
|
+
@errors = []
|
|
19
|
+
validate_name
|
|
20
|
+
validate_description
|
|
21
|
+
validate_compatibility
|
|
22
|
+
validate_directory_match
|
|
23
|
+
@errors.empty?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.validate!(skill)
|
|
27
|
+
validator = new(skill)
|
|
28
|
+
return skill if validator.valid?
|
|
29
|
+
|
|
30
|
+
raise ValidationError, validator.errors.join(", ")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def validate_name
|
|
36
|
+
name = @skill.name.to_s
|
|
37
|
+
|
|
38
|
+
if name.empty?
|
|
39
|
+
@errors << "name is required"
|
|
40
|
+
return
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if name.length > MAX_NAME_LENGTH
|
|
44
|
+
@errors << "name must be #{MAX_NAME_LENGTH} characters or less"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
unless name.match?(NAME_REGEX)
|
|
48
|
+
@errors << "name must contain only lowercase letters, numbers, and hyphens"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if name.start_with?("-") || name.end_with?("-")
|
|
52
|
+
@errors << "name cannot start or end with a hyphen"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if name.include?("--")
|
|
56
|
+
@errors << "name cannot contain consecutive hyphens"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def validate_description
|
|
61
|
+
description = @skill.description.to_s
|
|
62
|
+
|
|
63
|
+
if description.empty?
|
|
64
|
+
@errors << "description is required"
|
|
65
|
+
return
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
if description.length > MAX_DESCRIPTION_LENGTH
|
|
69
|
+
@errors << "description must be #{MAX_DESCRIPTION_LENGTH} characters or less"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def validate_compatibility
|
|
74
|
+
compatibility = @skill.compatibility.to_s
|
|
75
|
+
return if compatibility.empty?
|
|
76
|
+
|
|
77
|
+
if compatibility.length > MAX_COMPATIBILITY_LENGTH
|
|
78
|
+
@errors << "compatibility must be #{MAX_COMPATIBILITY_LENGTH} characters or less"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def validate_directory_match
|
|
83
|
+
return unless @skill.path
|
|
84
|
+
|
|
85
|
+
dir_name = File.basename(@skill.path)
|
|
86
|
+
return if dir_name == @skill.name
|
|
87
|
+
|
|
88
|
+
@errors << "name '#{@skill.name}' must match directory name '#{dir_name}'"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/agent_skills.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "agent_skills/version"
|
|
4
|
+
require_relative "agent_skills/errors"
|
|
5
|
+
require_relative "agent_skills/skill"
|
|
6
|
+
require_relative "agent_skills/validator"
|
|
7
|
+
require_relative "agent_skills/generator"
|
|
8
|
+
require_relative "agent_skills/loader"
|
|
9
|
+
require_relative "agent_skills/packager"
|
|
10
|
+
|
|
11
|
+
module AgentSkills
|
|
12
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: agent_skills
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- rubyonai
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-01-29 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rubyzip
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.3'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.3'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: thor
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.3'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.3'
|
|
41
|
+
description: |
|
|
42
|
+
Parse, validate, create, package, and load Agent Skills in Ruby.
|
|
43
|
+
Agent Skills is an open format (by Anthropic) for giving AI agents
|
|
44
|
+
new capabilities through structured instructions, scripts, and resources.
|
|
45
|
+
email:
|
|
46
|
+
- your-email@example.com
|
|
47
|
+
executables:
|
|
48
|
+
- agent-skills
|
|
49
|
+
extensions: []
|
|
50
|
+
extra_rdoc_files: []
|
|
51
|
+
files:
|
|
52
|
+
- ".rspec"
|
|
53
|
+
- CHANGELOG.md
|
|
54
|
+
- LICENSE
|
|
55
|
+
- README.md
|
|
56
|
+
- RELEASING.md
|
|
57
|
+
- Rakefile
|
|
58
|
+
- agent_skills.gemspec
|
|
59
|
+
- exe/agent-skills
|
|
60
|
+
- lib/agent_skills.rb
|
|
61
|
+
- lib/agent_skills/cli.rb
|
|
62
|
+
- lib/agent_skills/errors.rb
|
|
63
|
+
- lib/agent_skills/generator.rb
|
|
64
|
+
- lib/agent_skills/loader.rb
|
|
65
|
+
- lib/agent_skills/packager.rb
|
|
66
|
+
- lib/agent_skills/skill.rb
|
|
67
|
+
- lib/agent_skills/validator.rb
|
|
68
|
+
- lib/agent_skills/version.rb
|
|
69
|
+
homepage: https://github.com/rubyonai/agent_skills
|
|
70
|
+
licenses:
|
|
71
|
+
- MIT
|
|
72
|
+
metadata:
|
|
73
|
+
homepage_uri: https://github.com/rubyonai/agent_skills
|
|
74
|
+
source_code_uri: https://github.com/rubyonai/agent_skills
|
|
75
|
+
changelog_uri: https://github.com/rubyonai/agent_skills/blob/main/CHANGELOG.md
|
|
76
|
+
documentation_uri: https://rubydoc.info/gems/agent_skills
|
|
77
|
+
rubygems_mfa_required: 'true'
|
|
78
|
+
post_install_message:
|
|
79
|
+
rdoc_options: []
|
|
80
|
+
require_paths:
|
|
81
|
+
- lib
|
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
83
|
+
requirements:
|
|
84
|
+
- - ">="
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: 3.0.0
|
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
|
+
requirements:
|
|
89
|
+
- - ">="
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: '0'
|
|
92
|
+
requirements: []
|
|
93
|
+
rubygems_version: 3.5.22
|
|
94
|
+
signing_key:
|
|
95
|
+
specification_version: 4
|
|
96
|
+
summary: Ruby implementation of the Agent Skills open standard
|
|
97
|
+
test_files: []
|