ruby-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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7280b4bcc88be0ead80c3553f82357e294dff35ea86d6c9ac6d8887d3191bd5c
4
+ data.tar.gz: e69af730461bdeadf3f11778f5dfd208b5d2288c70ec2edcee3db226f561b449
5
+ SHA512:
6
+ metadata.gz: 51aab53ad414ea98a590a0cfc458d6a34b525501c70fa27eefb17e09d51e4a168b7dc5a6926910c366ebf031635d2e79235f7ad3ec6011353965b120ce891a56
7
+ data.tar.gz: 9c4d8b9776bda7309d9500e2c4223e09792338d221eae62d89e789b98b61845cc2b0a0254d5c0cf687658d0e9e65990f99be2d8788e6ad297fad1cd0260252b4
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,46 @@
1
+ plugins:
2
+ - rubocop-performance
3
+ - rubocop-rspec
4
+
5
+ AllCops:
6
+ NewCops: enable
7
+ TargetRubyVersion: 3.1
8
+ SuggestExtensions: false
9
+ Exclude:
10
+ - "bin/*"
11
+ - "tmp/**/*"
12
+ - "vendor/**/*"
13
+ - "doc/**/*"
14
+
15
+ Layout/LineLength:
16
+ Max: 100
17
+
18
+ Metrics/AbcSize:
19
+ Enabled: false
20
+
21
+ Metrics/MethodLength:
22
+ Enabled: false
23
+
24
+ Style/StringLiterals:
25
+ EnforcedStyle: double_quotes
26
+
27
+ Style/StringLiteralsInInterpolation:
28
+ EnforcedStyle: double_quotes
29
+
30
+ Style/BlockDelimiters:
31
+ EnforcedStyle: semantic
32
+
33
+ Style/OptionalBooleanParameter:
34
+ Enabled: false
35
+
36
+ Gemspec/DevelopmentDependencies:
37
+ Enabled: false
38
+
39
+ RSpec/ExampleLength:
40
+ Max: 40
41
+
42
+ RSpec/MultipleExpectations:
43
+ Max: 8
44
+
45
+ RSpec/MultipleDescribes:
46
+ Enabled: false
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --title "Ruby Skills"
2
+ --markup markdown
3
+ --no-private
4
+ --output-dir doc
5
+ lib/**/*.rb
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,110 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ruby-skills (0.1.0)
5
+ thor (~> 1.3)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.3)
11
+ diff-lcs (1.6.2)
12
+ json (2.21.2)
13
+ language_server-protocol (3.17.0.6)
14
+ lint_roller (1.1.0)
15
+ parallel (2.1.0)
16
+ parser (3.3.12.0)
17
+ ast (~> 2.4.1)
18
+ racc
19
+ prism (1.9.0)
20
+ racc (1.8.1)
21
+ rainbow (3.1.1)
22
+ rake (13.4.2)
23
+ regexp_parser (2.12.0)
24
+ rspec (3.13.2)
25
+ rspec-core (~> 3.13.0)
26
+ rspec-expectations (~> 3.13.0)
27
+ rspec-mocks (~> 3.13.0)
28
+ rspec-core (3.13.6)
29
+ rspec-support (~> 3.13.0)
30
+ rspec-expectations (3.13.5)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.13.0)
33
+ rspec-mocks (3.13.8)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.13.0)
36
+ rspec-support (3.13.7)
37
+ rubocop (1.89.0)
38
+ json (~> 2.3)
39
+ language_server-protocol (~> 3.17.0.2)
40
+ lint_roller (~> 1.1.0)
41
+ parallel (>= 1.10)
42
+ parser (>= 3.3.0.2)
43
+ rainbow (>= 2.2.2, < 4.0)
44
+ regexp_parser (>= 2.9.3, < 3.0)
45
+ rubocop-ast (>= 1.49.0, < 2.0)
46
+ ruby-progressbar (~> 1.7)
47
+ unicode-display_width (>= 2.4.0, < 4.0)
48
+ rubocop-ast (1.50.0)
49
+ parser (>= 3.3.7.2)
50
+ prism (~> 1.7)
51
+ rubocop-performance (1.27.0)
52
+ lint_roller (~> 1.1)
53
+ rubocop (>= 1.89.0, < 2.0)
54
+ rubocop-ast (>= 1.47.1, < 2.0)
55
+ rubocop-rspec (3.10.2)
56
+ lint_roller (~> 1.1)
57
+ regexp_parser (>= 2.0)
58
+ rubocop (~> 1.86, >= 1.86.2)
59
+ ruby-progressbar (1.13.0)
60
+ thor (1.5.0)
61
+ unicode-display_width (3.2.0)
62
+ unicode-emoji (~> 4.1)
63
+ unicode-emoji (4.2.0)
64
+ yard (0.9.45)
65
+
66
+ PLATFORMS
67
+ ruby
68
+ x86_64-linux
69
+
70
+ DEPENDENCIES
71
+ rake (~> 13.0)
72
+ rspec (~> 3.12)
73
+ rubocop (~> 1.58)
74
+ rubocop-performance (~> 1.20)
75
+ rubocop-rspec (~> 3.0)
76
+ ruby-skills!
77
+ yard (~> 0.9)
78
+
79
+ CHECKSUMS
80
+ ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
81
+ bundler (4.0.17) sha256=214e21431b5665dd2f99df8a5511c6b151d7a72e8015c8b38f8b775b61cbb6c1
82
+ diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
83
+ json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
84
+ language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
85
+ lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
86
+ parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
87
+ parser (3.3.12.0) sha256=21a6d7f755d5a24dfbdc6e6b772e4e879a52e7631a88bc5a3a134606052c9828
88
+ prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
89
+ racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
90
+ rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
91
+ rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
92
+ regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
93
+ rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
94
+ rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
95
+ rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
96
+ rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
97
+ rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
98
+ rubocop (1.89.0) sha256=4dee8e3ee9c45e474834efd9e8d6fd031e8331c8dacdff0de4ad65ae0a6faae7
99
+ rubocop-ast (1.50.0) sha256=b9ca88300da0803ee222ad20cdb30494c0a784eed06fdc35d254b06d662788db
100
+ rubocop-performance (1.27.0) sha256=eeeb1374d062a368ee1c787b70eb0b0cc4b184cb1f8565f424760946146d61ce
101
+ rubocop-rspec (3.10.2) sha256=0b3e2ecc592cd10ecbf0095bb58d1e357905276e069643523cc19eb7495f65e2
102
+ ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
103
+ ruby-skills (0.1.0)
104
+ thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
105
+ unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
106
+ unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
107
+ yard (0.9.45) sha256=52e211493f7cb8a3ebf7e104a25a1e73937a3103092545d34cb88fafebb3dc51
108
+
109
+ BUNDLED WITH
110
+ 4.0.17
data/LICENSE ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # ruby-skills
2
+
3
+ Package manager for installing, updating, removing, and sharing AI development skills in Ruby and Rails projects.
4
+
5
+ Skills are declared in a `Skillfile`, copied into `.ruby-skills/`, recorded in `Skills.lock`, and exposed to editors through adapters (Claude, Codex, Cursor, VS Code).
6
+
7
+ ## Requirements
8
+
9
+ - Ruby 3.2 or later
10
+ - [Thor](https://github.com/rails/thor) `~> 1.3` (pulled in by the gem)
11
+
12
+ ## Install
13
+
14
+ From a checkout:
15
+
16
+ ```bash
17
+ bundle install
18
+ ruby -Ilib bin/ruby-skills version
19
+ ```
20
+
21
+ Once published:
22
+
23
+ ```bash
24
+ gem install ruby-skills
25
+ ```
26
+
27
+ ## Quick start
28
+
29
+ ```bash
30
+ ruby-skills init
31
+ ```
32
+
33
+ That creates a `Skillfile` and a `.ruby-skills/` directory. Declare skills in the Skillfile:
34
+
35
+ ```ruby
36
+ # Skillfile
37
+
38
+ skill "rails-performance",
39
+ github: "username/rails-performance"
40
+
41
+ skill "local-review",
42
+ path: "../skills/local-review",
43
+ version: "0.1.0"
44
+ ```
45
+
46
+ Each `skill` needs exactly one source: `github:` (`owner/repo`) or `path:`. `version:` is optional.
47
+
48
+ Then install:
49
+
50
+ ```bash
51
+ ruby-skills install
52
+ ruby-skills install rails-performance
53
+ ```
54
+
55
+ ## Commands
56
+
57
+ | Command | Description |
58
+ | --- | --- |
59
+ | `ruby-skills init` | Create `Skillfile` and `.ruby-skills/` |
60
+ | `ruby-skills install [SKILL]` | Install one skill, or every skill in the Skillfile |
61
+ | `ruby-skills list` | Print installed skills from `Skills.lock` |
62
+ | `ruby-skills list --json` | Same data as JSON (`-j`) |
63
+ | `ruby-skills update [SKILL]` | Update one skill, or every installed skill |
64
+ | `ruby-skills remove SKILL` | Remove an installed skill |
65
+ | `ruby-skills version` | Print the gem version |
66
+ | `ruby-skills help [COMMAND]` | Command help |
67
+
68
+ Human-readable list:
69
+
70
+ ```
71
+ Installed skills:
72
+
73
+ rails-performance 0.1.0
74
+ rails-security 0.2.0
75
+ ```
76
+
77
+ Machine-readable list (`ruby-skills list --json`):
78
+
79
+ ```json
80
+ {
81
+ "skills": [
82
+ {
83
+ "name": "rails-performance",
84
+ "version": "0.1.0",
85
+ "source": "github:username/rails-performance"
86
+ }
87
+ ]
88
+ }
89
+ ```
90
+
91
+ An empty lockfile prints `{"skills":[]}`. Editors should consume `--json` instead of parsing the table.
92
+
93
+ ## Project files
94
+
95
+ | Path | Role |
96
+ | --- | --- |
97
+ | `Skillfile` | Declared skills (Ruby DSL) |
98
+ | `Skills.lock` | Installed name, version, and source |
99
+ | `.ruby-skills/` | Local copies of installed skills |
100
+
101
+ `Skillfile` and `Skills.lock` are gitignored in this repo because they are per-project workspace files created by `init` / `install`.
102
+
103
+ ## Editor plugins
104
+
105
+ Install still notifies Claude, Codex, Cursor, and VS Code adapters so the skill is visible in those tools.
106
+
107
+ The VS Code extension lives in [`rubyskills-plugins/rubyskills-vscode`](../rubyskills-plugins/rubyskills-vscode). It wraps this CLI and loads the sidebar from `ruby-skills list --json`.
108
+
109
+ ## Development
110
+
111
+ ```bash
112
+ bundle install
113
+ ruby -Ilib bin/ruby-skills help
114
+ ```
115
+
116
+ Library code is under `lib/ruby_skills/`. The executable is `bin/ruby-skills`.
117
+
118
+ ## License
119
+
120
+ MIT
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "rubocop/rake_task"
6
+ require "yard"
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+ RuboCop::RakeTask.new
10
+
11
+ YARD::Rake::YardocTask.new do |t|
12
+ t.files = ["lib/**/*.rb"]
13
+ t.stats_options = ["--list-undoc"]
14
+ end
15
+
16
+ task default: :spec
17
+ task docs: :yard
data/bin/ruby-skills ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "ruby_skills"
5
+
6
+ RubySkills::CLI.start(ARGV)
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubySkills
4
+ module Adapters
5
+ # Base implementation for skill adapters.
6
+ #
7
+ # Subclasses must define an ADAPTER_INFO constant containing:
8
+ #
9
+ # - :adapter — tool directory under the project root
10
+ # - :directory — directory containing skills
11
+ #
12
+ # @abstract
13
+ # @since 0.1.0
14
+ class Base
15
+ # @param root [String, Pathname] project root
16
+ def initialize(root: Dir.pwd)
17
+ @root = Pathname.new(root)
18
+ end
19
+
20
+ # Install a skill into the adapter destination.
21
+ #
22
+ # @param name [String] skill identifier
23
+ # @param source [String, Pathname] source skill path
24
+ # @return [void]
25
+ def install(name, source)
26
+ link(source, destination_for(name))
27
+ end
28
+
29
+ # Remove an installed skill.
30
+ #
31
+ # @param name [String] skill identifier
32
+ # @return [void]
33
+ def remove(name)
34
+ FileUtils.rm_rf(destination_for(name))
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :root
40
+
41
+ # @return [Hash{Symbol => String}]
42
+ def adapter_info
43
+ self.class::ADAPTER_INFO
44
+ end
45
+
46
+ # @param name [String]
47
+ # @return [Pathname]
48
+ def destination_for(name)
49
+ root.join(
50
+ adapter_info.fetch(:adapter),
51
+ adapter_info.fetch(:directory),
52
+ name
53
+ )
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubySkills
4
+ module Adapters
5
+ # Exposes skills to Claude Code under +.claude/skills+.
6
+ #
7
+ # @see RubySkills::Adapters::Base
8
+ # @since 0.1.0
9
+ class Claude < Base
10
+ # Path fragments that locate Claude skills.
11
+ #
12
+ # @return [Hash{Symbol => String}]
13
+ ADAPTER_INFO = {
14
+ adapter: ".claude",
15
+ directory: "skills"
16
+ }.freeze
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubySkills
4
+ module Adapters
5
+ # Exposes skills to Codex under +.codex/skills+.
6
+ #
7
+ # @see RubySkills::Adapters::Base
8
+ # @since 0.1.0
9
+ class Codex < Base
10
+ # Path fragments that locate Codex skills.
11
+ #
12
+ # @return [Hash{Symbol => String}]
13
+ ADAPTER_INFO = {
14
+ adapter: ".codex",
15
+ directory: "skills"
16
+ }.freeze
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubySkills
4
+ module Adapters
5
+ # Exposes skills to Cursor under +.cursor/skills+.
6
+ #
7
+ # @see RubySkills::Adapters::Base
8
+ # @since 0.1.0
9
+ class Cursor < Base
10
+ # Path fragments that locate Cursor skills.
11
+ #
12
+ # @return [Hash{Symbol => String}]
13
+ ADAPTER_INFO = {
14
+ adapter: ".cursor",
15
+ directory: "skills"
16
+ }.freeze
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubySkills
4
+ module Adapters
5
+ # Exposes skills to VS Code under +.vscode/skills+.
6
+ #
7
+ # @see RubySkills::Adapters::Base
8
+ # @since 0.1.0
9
+ class Vscode < Base
10
+ # Path fragments that locate VS Code skills.
11
+ #
12
+ # @return [Hash{Symbol => String}]
13
+ ADAPTER_INFO = {
14
+ adapter: ".vscode",
15
+ directory: "skills"
16
+ }.freeze
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "thor"
5
+
6
+ module RubySkills
7
+ # Thor-based command-line interface for Ruby Skills.
8
+ #
9
+ # The default command is {#help}. +-h+ and +--help+ are aliases for it.
10
+ #
11
+ # @example Initialize a project
12
+ # RubySkills::CLI.start(["init"])
13
+ #
14
+ # @since 0.1.0
15
+ class CLI < Thor
16
+ # Display name used in Thor help banners.
17
+ package_name "ruby-skills"
18
+
19
+ # Show {#help} when no command is given.
20
+ default_command :help
21
+
22
+ # Accept +-h+ and +--help+ as aliases for {#help}.
23
+ map %w[-h --help] => :help
24
+
25
+ desc "help [COMMAND]", "List all commands or show help for one command"
26
+ # List every available command, or describe a single command.
27
+ #
28
+ # @param command [String, nil] command to describe, or +nil+ to list all
29
+ # @param subcommand [Boolean] whether +command+ is a subcommand
30
+ # @return [void]
31
+ def help(command = nil, subcommand = false)
32
+ super
33
+ end
34
+
35
+ desc "init", "Initialize Ruby Skills in the current project"
36
+ # Create a Skillfile and +.ruby-skills+ directory in the current project.
37
+ #
38
+ # @return [void]
39
+ # @raise [SystemExit] when initialization fails
40
+ def init
41
+ config = Config.new
42
+
43
+ config.initialize_project!
44
+
45
+ say "Ruby Skills initialized"
46
+ say " Skillfile created"
47
+ say " .ruby-skills directory created"
48
+ rescue RubySkills::Error => e
49
+ say "Error: #{e.message}", :red
50
+ exit 1
51
+ end
52
+
53
+ desc "install [Skill]", "Install one skill or all skills from Skillfile"
54
+ # Install one skill or every skill declared in the Skillfile.
55
+ #
56
+ # @param skill [String, nil] skill name to install, or +nil+ to install all
57
+ # @return [void]
58
+ # @raise [SystemExit] when installation fails
59
+ def install(skill = nil)
60
+ Installer.new.install(skill)
61
+ rescue RubySkills::Error => e
62
+ say "Error: #{e.message}", :red
63
+ exit 1
64
+ end
65
+
66
+ desc "list", "List installed skills"
67
+ option :json, type: :boolean, default: false, aliases: "-j",
68
+ desc: "Output installed skills as JSON"
69
+ # Print installed skills from the lockfile.
70
+ #
71
+ # With +--json+, emit a machine-readable payload instead of a table.
72
+ #
73
+ # @return [void]
74
+ def list
75
+ skills = Lockfile.new.skills
76
+
77
+ if options[:json]
78
+ puts JSON.generate(list_payload(skills))
79
+ return
80
+ end
81
+
82
+ if skills.empty?
83
+ say "No skills installed."
84
+ return
85
+ end
86
+
87
+ say "Installed skills:"
88
+ say ""
89
+
90
+ skills.each do |name, data|
91
+ say "#{name.ljust(35)} #{data["version"]}"
92
+ end
93
+ end
94
+
95
+ desc "remove SKILL", "Remove an installed skill"
96
+ # Remove an installed skill from the project.
97
+ #
98
+ # @param skill [String] name of the skill to remove
99
+ # @return [void]
100
+ # @raise [SystemExit] when removal fails
101
+ def remove(skill)
102
+ Remover.new.remove(skill)
103
+ rescue RubySkills::Error => e
104
+ say "Error: #{e.message}", :red
105
+ exit 1
106
+ end
107
+
108
+ desc "update [SKILL]", "Update one skill or all installed skills"
109
+ # Update one skill or every installed skill.
110
+ #
111
+ # @param skill [String, nil] skill name to update, or +nil+ to update all
112
+ # @return [void]
113
+ # @raise [SystemExit] when the update fails
114
+ def update(skill = nil)
115
+ Updater.new.update(skill)
116
+ rescue RubySkills::Error => e
117
+ say "Error: #{e.message}", :red
118
+ exit 1
119
+ end
120
+
121
+ desc "version", "Display Ruby Skills version"
122
+ # Print the installed gem version.
123
+ #
124
+ # @return [void]
125
+ def version
126
+ say "ruby-skills #{RubySkills::VERSION}"
127
+ end
128
+
129
+ no_commands do
130
+ # @api private
131
+ # @param skills [Hash{String => Hash}] lockfile skill map
132
+ # @return [Hash] JSON-serializable list payload
133
+ def list_payload(skills)
134
+ {
135
+ "skills" => skills.map do |name, data|
136
+ {
137
+ "name" => name,
138
+ "version" => data["version"],
139
+ "source" => data["source"]
140
+ }
141
+ end
142
+ }
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubySkills
4
+ # Resolves project paths and bootstraps a Ruby Skills workspace.
5
+ #
6
+ # Paths for +Skillfile+, +Skills.lock+ and the local skills directory
7
+ # are derived from a project root.
8
+ #
9
+ # @example Initialize the current directory
10
+ # RubySkills::Config.new.initialize_project!
11
+ #
12
+ # @since 0.1.0
13
+ class Config
14
+ # Filename of the project skill manifest.
15
+ SKILLFILE = "Skillfile"
16
+
17
+ # Filename of the installed-skills lockfile.
18
+ LOCKFILE = "Skills.lock"
19
+
20
+ # Directory where installed skills are stored.
21
+ SKILLS_DIRECTORY = ".ruby-skills"
22
+
23
+ # @param root [String, Pathname] project root used to resolve skill paths
24
+ def initialize(root: Dir.pwd)
25
+ @root = Pathname.new(root)
26
+ end
27
+
28
+ # Create the skills directory and a starter Skillfile when missing.
29
+ #
30
+ # @return [void]
31
+ def initialize_project!
32
+ create_skills_directory
33
+ create_skillfile
34
+ end
35
+
36
+ # @return [Pathname] absolute path to the project Skillfile
37
+ def skillfile_path
38
+ @root.join(SKILLFILE)
39
+ end
40
+
41
+ # @return [Pathname] absolute path to the project lockfile
42
+ def lockfile_path
43
+ @root.join(LOCKFILE)
44
+ end
45
+
46
+ # @return [Pathname] absolute path to the local skills directory
47
+ def skills_path
48
+ @root.join(SKILLS_DIRECTORY)
49
+ end
50
+
51
+ private
52
+
53
+ # @api private
54
+ # @return [void]
55
+ def create_skills_directory
56
+ FileUtils.mkdir_p(skills_path)
57
+ end
58
+
59
+ # @api private
60
+ # @return [void]
61
+ def create_skillfile
62
+ return if skillfile_path.exist?
63
+
64
+ skillfile_path.write(<<~RUBY)
65
+ # Ruby Skills
66
+
67
+ # skill "rails-performance",
68
+ # github: "username/rails-performance"
69
+ RUBY
70
+ end
71
+ end
72
+ end