agent_skills_configurations 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.
@@ -0,0 +1,278 @@
1
+ # Class: AgentSkillsConfigurations::Registry
2
+ **Inherits:** Object
3
+
4
+
5
+ Loads agent configurations, resolves paths, and exposes query helpers.
6
+
7
+ The Registry is the internal implementation class that handles:
8
+
9
+ * Loading and parsing the YAML configuration file
10
+ * Resolving environment variables to absolute paths
11
+ * Handling fallback paths for missing directories
12
+ * Detecting which agents are installed on the system
13
+ * Caching results for performance
14
+
15
+ This class is typically used through the public API methods in the
16
+ {AgentSkillsConfigurations} module, but can also be used directly for advanced
17
+ use cases.
18
+
19
+ ## Path Resolution
20
+
21
+ The Registry uses a sophisticated path resolution system that respects
22
+ environment variables and provides fallback locations. The resolution process
23
+ works as follows:
24
+
25
+ 1. Check if the configured environment variable is set and non-empty
26
+ 2. If set, use that value as the base path
27
+ 3. If not set, use the configured fallback path (often relative to home)
28
+ 4. Expand relative paths using the user's home directory
29
+
30
+ Example resolution flow for XDG_CONFIG_HOME:
31
+
32
+ # Configuration in YAML:
33
+ base_paths:
34
+ xdg_config:
35
+ env_var: XDG_CONFIG_HOME
36
+ fallback: ".config"
37
+
38
+ # With environment variable set:
39
+ ENV["XDG_CONFIG_HOME"] = "/custom/xdg"
40
+ # => resolves to "/custom/xdg"
41
+
42
+ # Without environment variable:
43
+ ENV["XDG_CONFIG_HOME"] = nil
44
+ # => resolves to "/Users/username/.config"
45
+
46
+ # Empty environment variable treated as unset:
47
+ ENV["XDG_CONFIG_HOME"] = ""
48
+ # => resolves to "/Users/username/.config"
49
+
50
+ ## Global Skills Path Resolution
51
+
52
+ Global skills paths are resolved relative to the agent's base path and support
53
+ multiple fallbacks. The Registry checks each candidate path in order and
54
+ returns the first one that exists:
55
+
56
+ # Configuration in YAML:
57
+ agents:
58
+ - name: moltbot
59
+ base_path: home
60
+ global_skills_path: ".moltbot/skills"
61
+ global_skills_path_fallbacks:
62
+ - ".clawdbot/skills"
63
+ - ".moltbot/skills"
64
+
65
+ # Resolution order:
66
+ # 1. Check ~/.moltbot/skills
67
+ # 2. Check ~/.clawdbot/skills
68
+ # 3. Check ~/.moltbot/skills (fallback)
69
+ # 4. Return first existing path, or primary path if none exist
70
+
71
+ ## Installation Detection
72
+
73
+ The Registry determines whether an agent is installed by checking the paths
74
+ configured in the agent's `detect_paths` array. Each path spec can be one of
75
+ several types:
76
+
77
+ * **String**: Check if the path exists relative to the user's home directory
78
+ * *Hash with `cwd`*: Check if the path exists relative to the current
79
+ working directory
80
+ * *Hash with `base` and `path`*: Check if the path exists relative to a
81
+ configured base path
82
+ * *Hash with `absolute`*: Check if the absolute path exists
83
+
84
+ An agent is considered installed if **any** of its detect paths exists.
85
+
86
+ Examples of detect paths:
87
+
88
+ agents:
89
+ - name: cursor
90
+ detect_paths:
91
+ - ".cursor" # Check ~/.cursor exists
92
+
93
+ - name: antigravity
94
+ detect_paths:
95
+ - { cwd: ".agent" } # Check .agent exists in current dir
96
+ - { base: home, path: ".gemini/antigravity" } # Check ~/.gemini/antigravity exists
97
+
98
+ - name: codex
99
+ detect_paths:
100
+ - "" # Always considered installed (empty string matches)
101
+ - { absolute: "/etc/codex" } # Check /etc/codex exists
102
+
103
+ ## Caching
104
+
105
+ The Registry caches the results of {all} and {installed} to avoid repeatedly
106
+ parsing the YAML file and checking file system paths. The cache can be cleared
107
+ using {reset}.
108
+
109
+ Use {reset} when:
110
+
111
+ * Environment variables that affect path resolution have changed
112
+ * Agents have been installed or uninstalled
113
+ * The YAML configuration file has been modified
114
+
115
+ **@see** [] Public API module that uses this class
116
+
117
+ **@see** [] Path to the configuration file
118
+
119
+ **@since** [] 0.1.0
120
+
121
+
122
+
123
+ # Instance Methods
124
+ ## all() [](#method-i-all)
125
+ Return all configured agents.
126
+
127
+ Returns a frozen array of all {Agent} objects defined in the configuration.
128
+ The result is cached on first call for performance. Path resolution happens
129
+ once during caching and the results are reused on subsequent calls.
130
+
131
+ Use {reset} to clear the cache and force re-resolution when needed.
132
+
133
+ **@raise** [Psych::SyntaxError] when the YAML is invalid (only on first call)
134
+
135
+ **@return** [Array<Agent>] frozen array of all agents with resolved paths
136
+
137
+ **@see** [] Clear the cache
138
+
139
+ **@see** [] Get only installed agents
140
+
141
+ **@since** [] 0.1.0
142
+
143
+
144
+ **@example**
145
+ ```ruby
146
+ registry = AgentSkillsConfigurations::Registry.new
147
+ all = registry.all
148
+ all.map(&:name)
149
+ # => ["amp", "claude-code", "cursor", "codex", ...]
150
+ ```
151
+ **@example**
152
+ ```ruby
153
+ registry = AgentSkillsConfigurations::Registry.new
154
+ first_call = registry.all
155
+ second_call = registry.all
156
+ first_call.equal?(second_call) # => true (same object)
157
+ first_call.frozen? # => true
158
+ ```## find(name) [](#method-i-find)
159
+ Find an agent by name.
160
+
161
+ Looks up an agent configuration by its canonical name and returns an {Agent}
162
+ value object with resolved paths. This method performs path resolution each
163
+ time it's called, so it reflects the current environment variables and file
164
+ system state.
165
+
166
+ **@param** [String] the canonical agent name from agents.yml
167
+
168
+ **@raise** [Error] when the agent name is unknown
169
+
170
+ **@return** [Agent] the resolved agent configuration with absolute paths
171
+
172
+ **@since** [] 0.1.0
173
+
174
+
175
+ **@example**
176
+ ```ruby
177
+ registry = AgentSkillsConfigurations::Registry.new
178
+ agent = registry.find("cursor")
179
+ agent.name # => "cursor"
180
+ agent.global_skills_dir # => "/Users/username/.cursor/skills"
181
+ ```
182
+ **@example**
183
+ ```ruby
184
+ registry.find("unknown-agent")
185
+ # => raises AgentSkillsConfigurations::Error: Unknown agent: unknown-agent
186
+ ```## initialize() [](#method-i-initialize)
187
+ Create a registry from the YAML configuration.
188
+
189
+ Loads the agents.yml file and parses it into a data structure that can be
190
+ queried for agent information. The YAML is loaded safely with permitted
191
+ classes for security.
192
+
193
+ **@raise** [Psych::SyntaxError] when the YAML is invalid or malformed
194
+
195
+ **@return** [Registry] a new registry instance with loaded configuration
196
+
197
+ **@since** [] 0.1.0
198
+
199
+
200
+ **@example**
201
+ ```ruby
202
+ registry = AgentSkillsConfigurations::Registry.new
203
+ registry.find("cursor").name # => "cursor"
204
+ ```## installed() [](#method-i-installed)
205
+ Return agents detected as installed on this machine.
206
+
207
+ Filters the list of all agents to those that are detected as installed.
208
+ Installation detection uses the paths configured in each agent's
209
+ `detect_paths` configuration. An agent is considered installed if **any** of
210
+ its detect paths exists.
211
+
212
+ Detection strategies:
213
+
214
+ * String: Check if path exists relative to user's home directory
215
+ * Hash with `cwd`: Check relative to current working directory
216
+ * Hash with `base` and `path`: Check relative to configured base path
217
+ * Hash with `absolute`: Check absolute path directly
218
+
219
+ The result is cached on first call. Use {reset} to clear the cache.
220
+
221
+ **@raise** [Psych::SyntaxError] when the YAML is invalid (only on first call)
222
+
223
+ **@return** [Array<Agent>] frozen array of installed agents with resolved paths
224
+
225
+ **@see** [] Get all configured agents
226
+
227
+ **@see** [] Clear the cache
228
+
229
+ **@since** [] 0.1.0
230
+
231
+
232
+ **@example**
233
+ ```ruby
234
+ registry = AgentSkillsConfigurations::Registry.new
235
+ installed = registry.installed
236
+ installed.map(&:name)
237
+ # => ["cursor", "claude-code"]
238
+ ```
239
+ **@example**
240
+ ```ruby
241
+ registry = AgentSkillsConfigurations::Registry.new
242
+ installed_names = registry.installed.map(&:name)
243
+ installed_names.include?("cursor") # => true (if installed)
244
+ installed_names.include?("unknown") # => false
245
+ ```## reset() [](#method-i-reset)
246
+ Clear cached agent lists.
247
+
248
+ Clears the internal caches for {all} and {installed} results. This forces path
249
+ resolution to be re-executed on the next call, which is useful when:
250
+
251
+ * Environment variables affecting path resolution have changed
252
+ * Agents have been installed or uninstalled
253
+ * The YAML configuration file has been modified
254
+
255
+ **@return** [void]
256
+
257
+ **@see** [] Get all agents
258
+
259
+ **@see** [] Get installed agents
260
+
261
+ **@since** [] 0.1.0
262
+
263
+
264
+ **@example**
265
+ ```ruby
266
+ registry = AgentSkillsConfigurations::Registry.new
267
+ ENV["XDG_CONFIG_HOME"] = "/new/path"
268
+ registry.reset
269
+ agent = registry.find("amp")
270
+ agent.global_skills_dir # => "/new/path/agents/skills"
271
+ ```
272
+ **@example**
273
+ ```ruby
274
+ registry = AgentSkillsConfigurations::Registry.new
275
+ # Install an agent...
276
+ registry.reset
277
+ registry.installed # => includes newly installed agent
278
+ ```
@@ -0,0 +1,297 @@
1
+ # Module: AgentSkillsConfigurations
2
+
3
+
4
+ AgentSkillsConfigurations provides a unified interface for discovering and
5
+ accessing skill configuration paths for various AI coding agents (Cursor,
6
+ Claude Code, Codex, etc.).
7
+
8
+ This library loads agent configurations from a YAML file and resolves
9
+ platform-specific paths for skill directories, taking into account environment
10
+ variables, user home directories, and fallback locations. It supports
11
+ detection of which agents are currently installed on the system and provides
12
+ convenient query methods for accessing agent information.
13
+
14
+ ## Overview
15
+
16
+ Each AI coding agent has two types of skill directories:
17
+
18
+ 1. *Project-level skills* (skills_dir): A relative path within a project
19
+ where project-specific skills are stored (e.g., `.cursor/skills`,
20
+ `.claude/skills`)
21
+ 2. *Global skills* (global_skills_dir): An absolute path to the user's global
22
+ skill repository shared across all projects (e.g., `~/.cursor/skills`)
23
+
24
+ The library abstracts away the differences between agents, providing a
25
+ consistent API for working with any supported agent type.
26
+
27
+ ## Configuration
28
+
29
+ Agent configurations are defined in `agents.yml`, which contains:
30
+
31
+ * Base path definitions with environment variable references and fallbacks
32
+ * Agent entries with names, display names, skill paths, and detection rules
33
+
34
+ Example YAML structure:
35
+
36
+ base_paths:
37
+ home:
38
+ env_var: ""
39
+ fallback: ""
40
+ xdg_config:
41
+ env_var: XDG_CONFIG_HOME
42
+ fallback: ".config"
43
+
44
+ agents:
45
+ - name: cursor
46
+ display_name: Cursor
47
+ skills_dir: ".cursor/skills"
48
+ base_path: home
49
+ global_skills_path: ".cursor/skills"
50
+ detect_paths:
51
+ - ".cursor"
52
+
53
+ ## Finding Agents
54
+
55
+ To get a specific agent configuration by name:
56
+
57
+ agent = AgentSkillsConfigurations.find("cursor")
58
+ agent.name # => "cursor"
59
+ agent.display_name # => "Cursor"
60
+ agent.skills_dir # => ".cursor/skills"
61
+ agent.global_skills_dir # => "/Users/username/.cursor/skills"
62
+
63
+ Finding an unknown agent raises an error:
64
+
65
+ AgentSkillsConfigurations.find("unknown-agent")
66
+ # => raises AgentSkillsConfigurations::Error: Unknown agent: unknown-agent
67
+
68
+ ## Listing All Agents
69
+
70
+ To get all configured agents:
71
+
72
+ all_agents = AgentSkillsConfigurations.all
73
+ all_agents.map(&:name)
74
+ # => ["amp", "claude-code", "cursor", "codex", "windsurf", ...]
75
+
76
+ The result is cached for performance. Use {reset!} to clear the cache:
77
+
78
+ AgentSkillsConfigurations.reset!
79
+
80
+ ## Detecting Installed Agents
81
+
82
+ To find which agents are installed on the current machine:
83
+
84
+ installed = AgentSkillsConfigurations.installed
85
+ installed.map(&:name)
86
+ # => ["cursor", "claude-code"]
87
+
88
+ Installation detection works by checking configured paths:
89
+
90
+ * String paths: Checks if the path exists relative to the user's home
91
+ directory
92
+ * Hash paths with `cwd`: Checks relative to the current working directory
93
+ * Hash paths with `base`: Resolves using the configured base path
94
+ * Hash paths with `absolute`: Checks the absolute path directly
95
+
96
+ Examples from the configuration:
97
+
98
+ detect_paths:
99
+ - ".cursor" # Check ~/.cursor exists
100
+ - { cwd: ".agent" } # Check .agent exists in current dir
101
+ - { base: home, path: ".codex" } # Check ~/.codex exists
102
+ - { absolute: "/etc/codex" } # Check /etc/codex exists
103
+
104
+ ## Environment Variables
105
+
106
+ Global skill paths are resolved using environment variables when available,
107
+ with automatic fallbacks to default locations:
108
+
109
+ * `XDG_CONFIG_HOME`: Used by Amp, Goose, and other XDG-compliant agents
110
+ * `CLAUDE_CONFIG_DIR`: Used by Claude Code and OpenCode
111
+ * `CODEX_HOME`: Used by Codex
112
+
113
+ Example with XDG_CONFIG_HOME:
114
+
115
+ ENV["XDG_CONFIG_HOME"] = "/custom/xdg"
116
+ agent = AgentSkillsConfigurations.find("amp")
117
+ agent.global_skills_dir # => "/custom/xdg/agents/skills"
118
+
119
+ Without the environment variable, falls back to default:
120
+
121
+ ENV["XDG_CONFIG_HOME"] = nil
122
+ agent = AgentSkillsConfigurations.find("amp")
123
+ agent.global_skills_dir # => "/Users/username/.config/agents/skills"
124
+
125
+ ## Path Resolution with Fallbacks
126
+
127
+ Some agents support multiple fallback paths for global skills. The first
128
+ existing path is used:
129
+
130
+ agents:
131
+ - name: moltbot
132
+ global_skills_path: ".moltbot/skills"
133
+ global_skills_path_fallbacks:
134
+ - ".clawdbot/skills"
135
+ - ".moltbot/skills"
136
+
137
+ The library checks each candidate path in order and returns the first one that
138
+ exists.
139
+
140
+ ## Error Handling
141
+
142
+ The library raises {AgentSkillsConfigurations::Error} for configuration errors
143
+ (unknown agents) and {Psych::SyntaxError} for invalid YAML syntax.
144
+
145
+ **@author** [] Lucian Ghinda
146
+
147
+ **@since** [] 0.1.0
148
+
149
+
150
+ # Class Methods
151
+ ## all() [](#method-c-all)
152
+ Return all configured agents.
153
+
154
+ Returns a frozen array of all {Agent} objects defined in the configuration.
155
+ The result is cached for performance. Use {reset!} to clear the cache when you
156
+ need fresh results (e.g., after changing environment variables).
157
+ **@raise** [Psych::SyntaxError] when the YAML configuration is invalid
158
+
159
+ **@return** [Array<Agent>] all agents defined in `agents.yml`
160
+
161
+ **@see** [] Clear cached agent lists
162
+
163
+ **@see** [] Get only installed agents
164
+
165
+ **@since** [] 0.1.0
166
+
167
+
168
+ **@example**
169
+ ```ruby
170
+ all_agents = AgentSkillsConfigurations.all
171
+ all_agents.map(&:name)
172
+ # => ["amp", "claude-code", "cursor", "codex", "windsurf", ...]
173
+ ```
174
+ **@example**
175
+ ```ruby
176
+ AgentSkillsConfigurations.all.each do |agent|
177
+ puts "#{agent.display_name}: #{agent.skills_dir}"
178
+ end
179
+ ```
180
+ **@example**
181
+ ```ruby
182
+ all = AgentSkillsConfigurations.all
183
+ cursor = all.find { |a| a.name == "cursor" }
184
+ cursor.global_skills_dir # => "/Users/username/.cursor/skills"
185
+ ```## find(name ) [](#method-c-find)
186
+ Find a configured agent by name.
187
+
188
+ Returns an {Agent} value object containing the agent's name, display name, and
189
+ resolved skill directory paths. This is the primary method for accessing agent
190
+ configuration.
191
+ **@param** [String] agent name from `agents.yml`
192
+
193
+ **@raise** [Error] when the agent name is unknown
194
+
195
+ **@raise** [Psych::SyntaxError] when the YAML configuration is invalid
196
+
197
+ **@return** [Agent] resolved agent configuration
198
+
199
+ **@since** [] 0.1.0
200
+
201
+
202
+ **@example**
203
+ ```ruby
204
+ agent = AgentSkillsConfigurations.find("cursor")
205
+ agent.name # => "cursor"
206
+ agent.display_name # => "Cursor"
207
+ agent.skills_dir # => ".cursor/skills"
208
+ agent.global_skills_dir # => "/Users/username/.cursor/skills"
209
+ ```
210
+ **@example**
211
+ ```ruby
212
+ ENV["CLAUDE_CONFIG_DIR"] = "/custom/claude"
213
+ agent = AgentSkillsConfigurations.find("claude-code")
214
+ agent.global_skills_dir # => "/custom/claude/skills"
215
+ ```
216
+ **@example**
217
+ ```ruby
218
+ AgentSkillsConfigurations.find("unknown-agent")
219
+ # => raises AgentSkillsConfigurations::Error: Unknown agent: unknown-agent
220
+ ```## installed() [](#method-c-installed)
221
+ Return agents that appear to be installed on this machine.
222
+
223
+ Installation is detected by checking the paths configured in each agent's
224
+ `detect_paths` configuration. Different detection strategies are supported:
225
+
226
+ * String paths: Check if the path exists relative to user's home directory
227
+ * Hash with `cwd`: Check relative to current working directory
228
+ * Hash with `base`: Resolve using a configured base path
229
+ * Hash with `absolute`: Check an absolute path directly
230
+
231
+ The result is cached for performance. Use {reset!} to clear the cache.
232
+ **@raise** [Psych::SyntaxError] when the YAML configuration is invalid
233
+
234
+ **@return** [Array<Agent>] agents matching their detect paths
235
+
236
+ **@see** [] Get all configured agents regardless of installation status
237
+
238
+ **@see** [] Clear cached agent lists
239
+
240
+ **@since** [] 0.1.0
241
+
242
+
243
+ **@example**
244
+ ```ruby
245
+ installed = AgentSkillsConfigurations.installed
246
+ installed.map(&:name)
247
+ # => ["cursor", "claude-code"]
248
+ ```
249
+ **@example**
250
+ ```ruby
251
+ installed_names = AgentSkillsConfigurations.installed.map(&:name)
252
+ installed_names.include?("cursor") # => true
253
+ installed_names.include?("unknown") # => false
254
+ ```
255
+ **@example**
256
+ ```ruby
257
+ AgentSkillsConfigurations.installed.each do |agent|
258
+ puts "#{agent.display_name} is installed"
259
+ end
260
+ ```## reset!() [](#method-c-reset!)
261
+ Clear cached agent lists.
262
+
263
+ This method clears the internal caches for {all} and {installed} results. Use
264
+ this when you need fresh data, such as:
265
+
266
+ * After changing environment variables that affect path resolution
267
+ * After installing or uninstalling agents
268
+ * After modifying the YAML configuration file
269
+ **@raise** [Psych::SyntaxError] when the YAML configuration is invalid
270
+
271
+ **@return** [void]
272
+
273
+ **@see** [] Returns cached all agents
274
+
275
+ **@see** [] Returns cached installed agents
276
+
277
+ **@since** [] 0.1.0
278
+
279
+
280
+ **@example**
281
+ ```ruby
282
+ ENV["XDG_CONFIG_HOME"] = "/new/path"
283
+ AgentSkillsConfigurations.reset!
284
+ agent = AgentSkillsConfigurations.find("amp")
285
+ agent.global_skills_dir # => "/new/path/agents/skills"
286
+ ```
287
+ **@example**
288
+ ```ruby
289
+ AgentSkillsConfigurations.reset!
290
+ installed = AgentSkillsConfigurations.installed
291
+ ```
292
+
293
+ # Documentation
294
+
295
+ - [AgentSkillsConfigurations/Agent.md](AgentSkillsConfigurations/Agent.md)
296
+ - [AgentSkillsConfigurations/Error.md](AgentSkillsConfigurations/Error.md)
297
+ - [AgentSkillsConfigurations/Registry.md](AgentSkillsConfigurations/Registry.md)
data/doc/index.csv ADDED
@@ -0,0 +1,18 @@
1
+ name,type,path
2
+ AgentSkillsConfigurations,Module,AgentSkillsConfigurations.md
3
+ AgentSkillsConfigurations.all,Method,AgentSkillsConfigurations.md#method-c-all
4
+ AgentSkillsConfigurations.find,Method,AgentSkillsConfigurations.md#method-c-find
5
+ AgentSkillsConfigurations.installed,Method,AgentSkillsConfigurations.md#method-c-installed
6
+ AgentSkillsConfigurations.reset!,Method,AgentSkillsConfigurations.md#method-c-reset!
7
+ AgentSkillsConfigurations::Error,Class,AgentSkillsConfigurations/Error.md
8
+ AgentSkillsConfigurations::Agent,Class,AgentSkillsConfigurations/Agent.md
9
+ display_name,Attribute,AgentSkillsConfigurations/Agent.md#attribute-i-display_name
10
+ global_skills_dir,Attribute,AgentSkillsConfigurations/Agent.md#attribute-i-global_skills_dir
11
+ name,Attribute,AgentSkillsConfigurations/Agent.md#attribute-i-name
12
+ skills_dir,Attribute,AgentSkillsConfigurations/Agent.md#attribute-i-skills_dir
13
+ AgentSkillsConfigurations::Registry,Class,AgentSkillsConfigurations/Registry.md
14
+ AgentSkillsConfigurations::Registry.all,Method,AgentSkillsConfigurations/Registry.md#method-i-all
15
+ AgentSkillsConfigurations::Registry.find,Method,AgentSkillsConfigurations/Registry.md#method-i-find
16
+ AgentSkillsConfigurations::Registry.initialize,Method,AgentSkillsConfigurations/Registry.md#method-i-initialize
17
+ AgentSkillsConfigurations::Registry.installed,Method,AgentSkillsConfigurations/Registry.md#method-i-installed
18
+ AgentSkillsConfigurations::Registry.reset,Method,AgentSkillsConfigurations/Registry.md#method-i-reset
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AgentSkillsConfigurations
4
+ # Value object describing a configured agent and its skill locations.
5
+ #
6
+ # An Agent represents a single AI coding agent configuration with information about
7
+ # where to find project-level skills and global skills for that agent. This is a
8
+ # read-only data structure returned by {AgentSkillsConfigurations.find} and other
9
+ # query methods.
10
+ #
11
+ # == Agent Attributes
12
+ #
13
+ # Each agent has four key attributes:
14
+ #
15
+ # * +name+ - The canonical identifier used in code (e.g., "cursor", "claude-code")
16
+ # * +display_name+ - A human-friendly name for UI/display purposes (e.g., "Cursor", "Claude Code")
17
+ # * +skills_dir+ - A relative path for project-specific skills (e.g., ".cursor/skills")
18
+ # * +global_skills_dir+ - An absolute path to the user's global skill repository
19
+ #
20
+ # == Understanding Skill Directories
21
+ #
22
+ # AI coding agents typically support two types of skills:
23
+ #
24
+ # 1. *Project Skills* ({skills_dir}): These are specific to a single project and
25
+ # live alongside the project code. For example, a project might have a
26
+ # <tt>.cursor/skills</tt> directory containing skills tailored to that project.
27
+ #
28
+ # 2. *Global Skills* ({global_skills_dir}): These are shared across all projects
29
+ # and typically live in the user's home directory. For example,
30
+ # <tt>~/.cursor/skills</tt> contains reusable skills that work with any project.
31
+ #
32
+ # When working with skills, you typically:
33
+ #
34
+ # * Check the project's +skills_dir+ for project-specific skills
35
+ # * Fall back to +global_skills_dir+ for general-purpose skills
36
+ # * Combine both sources to give the agent full access to available skills
37
+ #
38
+ # == Accessing Agent Information
39
+ #
40
+ # Since Agent is a Data object, all attributes are accessible via reader methods:
41
+ #
42
+ # agent = AgentSkillsConfigurations.find("cursor")
43
+ # agent.name # => "cursor"
44
+ # agent.display_name # => "Cursor"
45
+ # agent.skills_dir # => ".cursor/skills"
46
+ # agent.global_skills_dir # => "/Users/username/.cursor/skills"
47
+ #
48
+ # You can also convert to a Hash:
49
+ #
50
+ # agent.to_h
51
+ # # => { name: "cursor",
52
+ # # display_name: "Cursor",
53
+ # # skills_dir: ".cursor/skills",
54
+ # # global_skills_dir: "/Users/username/.cursor/skills" }
55
+ #
56
+ # @attr_reader [String] name Canonical agent name from `agents.yml`. This is the
57
+ # identifier used when finding agents via {AgentSkillsConfigurations.find}.
58
+ #
59
+ # @attr_reader [String] display_name Human-friendly label for UI/display purposes.
60
+ # This is the name shown to users, e.g., in menus or configuration interfaces.
61
+ #
62
+ # @attr_reader [String] skills_dir Relative directory where project-specific
63
+ # skills live. This path is relative to the project root and should not start
64
+ # with a slash (e.g., ".cursor/skills", not "/.cursor/skills").
65
+ #
66
+ # @attr_reader [String] global_skills_dir Absolute resolved path to global skills.
67
+ # This path is resolved from the YAML configuration, taking into account
68
+ # environment variables and fallbacks. It always begins with a slash.
69
+ #
70
+ # @see AgentSkillsConfigurations.find Find an agent by name
71
+ # @see AgentSkillsConfigurations.all Get all agents
72
+ # @see AgentSkillsConfigurations.detected Get detected agents
73
+ Agent = Data.define(:name, :display_name, :skills_dir, :global_skills_dir)
74
+ end