agent_settings 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,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+ Zeitwerk::Loader.for_gem.setup
5
+
6
+ # Discover config locations for Claude Code, OpenCode, and Codex.
7
+ #
8
+ # This gem provides a clean interface to find where agent configuration files
9
+ # are located, whether they exist, and which one is currently effective based
10
+ # on precedence rules and environment variable overrides.
11
+ #
12
+ # ## Trusted vs Untrusted Projects
13
+ #
14
+ # The `trusted` parameter controls whether project-level config is included
15
+ # in resolution. This is a security feature for Codex:
16
+ #
17
+ # - **Trusted projects** (`trusted: true`): Project config is included in resolution.
18
+ # Use this for projects you own or have reviewed.
19
+ # - **Untrusted projects** (`trusted: false`): Project config is ignored for Codex.
20
+ # This prevents untrusted code repositories from injecting malicious configuration.
21
+ # A warning is added if a `.codex/config.toml` file exists but is ignored.
22
+ #
23
+ # Claude Code and OpenCode do not use the trust model - their project configs
24
+ # are always included regardless of the `trusted` parameter.
25
+ #
26
+ # @example Get global config location
27
+ # location = AgentSettings.global(:claude)
28
+ # location.path #=> "/Users/me/.claude/settings.json"
29
+ # location.exists? #=> true
30
+ #
31
+ # @example Resolve effective config with all details
32
+ # result = AgentSettings.resolve(:opencode, dir: "/work/my_app")
33
+ # result.effective.path #=> the active config path
34
+ # result.custom_config? #=> true if env override is active
35
+ #
36
+ # @example Resolve all agents at once
37
+ # results = AgentSettings.all(dir: Dir.pwd)
38
+ # results.each do |agent, config_path|
39
+ # puts "#{agent}: #{config_path.effective.path}"
40
+ # end
41
+ #
42
+ # @example Untrusted project (Codex ignores project config)
43
+ # result = AgentSettings.resolve(:codex, dir: "/untrusted/repo", trusted: false)
44
+ # result.project #=> nil (ignored for security)
45
+ # result.warnings #=> ["Project config ignored for untrusted project"]
46
+ module AgentSettings
47
+ # Base error class for all gem errors.
48
+ class Error < StandardError; end
49
+
50
+ # Raised when an unknown agent symbol is provided.
51
+ # @example
52
+ # AgentSettings.global(:unknown) #=> raises UnknownAgentError
53
+ class UnknownAgentError < Error; end
54
+
55
+ class << self
56
+ # Get the global config location for an agent.
57
+ #
58
+ # The global config is typically stored in the user's home directory
59
+ # and applies across all projects.
60
+ #
61
+ # @param agent [Symbol] the agent identifier (:claude, :opencode, :codex)
62
+ # @param env [Hash] environment variables hash (default: ENV)
63
+ # @param dir [String] project directory path (default: Dir.pwd)
64
+ # @param trusted [Boolean] whether the project is trusted (default: true)
65
+ # @return [Location, nil] the global config location, or nil if not found
66
+ #
67
+ # @example
68
+ # location = AgentSettings.global(:claude)
69
+ # location.path #=> "/Users/me/.claude/settings.json"
70
+ # location.exists? #=> true
71
+ def global(agent, env: ENV, dir: Dir.pwd, trusted: true)
72
+ LocationDiscovery.global(agent, env: env, dir: dir, trusted: trusted)
73
+ end
74
+
75
+ # Get the project-level config location for an agent.
76
+ #
77
+ # Project configs are stored within the project directory and apply
78
+ # only to that specific project.
79
+ #
80
+ # Note: For Codex, project config is ignored when `trusted: false`.
81
+ # This is a security measure to prevent untrusted repositories from
82
+ # injecting malicious configuration. In this case, returns nil and
83
+ # a warning is added to the resolve result.
84
+ #
85
+ # @param agent [Symbol] the agent identifier (:claude, :opencode, :codex)
86
+ # @param dir [String] project directory path (required)
87
+ # @param env [Hash] environment variables hash (default: ENV)
88
+ # @param trusted [Boolean] whether the project is trusted (default: true)
89
+ # @return [Location, nil] the project config location, or nil if not applicable
90
+ #
91
+ # @example
92
+ # location = AgentSettings.project(:codex, dir: "/work/my_app")
93
+ # location.path #=> "/work/my_app/.codex/config.toml"
94
+ # location.exists? #=> false
95
+ def project(agent, dir:, env: ENV, trusted: true)
96
+ LocationDiscovery.project(agent, dir: dir, env: env, trusted: trusted)
97
+ end
98
+
99
+ # Resolve the complete config path information for an agent.
100
+ #
101
+ # This is the primary method for discovering all config-related information
102
+ # for an agent. It returns an AgentConfigPath object containing the effective
103
+ # config location, global and project locations, all layers by precedence,
104
+ # environment variable overrides, and any warnings.
105
+ #
106
+ # The effective location is determined by:
107
+ # 1. Active environment variable overrides (highest precedence)
108
+ # 2. First existing layer in precedence order
109
+ # 3. First layer if none exist
110
+ #
111
+ # @param agent [Symbol] the agent identifier (:claude, :opencode, :codex)
112
+ # @param dir [String] project directory path (required)
113
+ # @param env [Hash] environment variables hash (default: ENV)
114
+ # @param trusted [Boolean] whether the project is trusted (default: true)
115
+ # @return [AgentConfigPath] complete config path information
116
+ #
117
+ # @example Basic usage
118
+ # result = AgentSettings.resolve(:claude, dir: Dir.pwd)
119
+ # result.effective.path #=> the active config path
120
+ # result.effective.exists? #=> whether the config file exists
121
+ # result.custom_config? #=> true if env override is active
122
+ #
123
+ # @example With environment overrides
124
+ # env = { "OPENCODE_CONFIG" => "/custom/config.json" }
125
+ # result = AgentSettings.resolve(:opencode, dir: Dir.pwd, env: env)
126
+ # result.env_overrides.first.name #=> "OPENCODE_CONFIG"
127
+ # result.env_overrides.first.active? #=> true (if file exists)
128
+ def resolve(agent, dir:, env: ENV, trusted: true)
129
+ LocationDiscovery.resolve(agent, dir: dir, env: env, trusted: trusted)
130
+ end
131
+
132
+ # Resolve config paths for all supported agents.
133
+ #
134
+ # Returns a hash mapping each agent symbol to its AgentConfigPath result.
135
+ # Useful for getting an overview of all agent configurations at once.
136
+ #
137
+ # @param dir [String] project directory path (required)
138
+ # @param env [Hash] environment variables hash (default: ENV)
139
+ # @param trusted [Boolean] whether the project is trusted (default: true)
140
+ # @return [Hash{Symbol => AgentConfigPath}] map of agent to config path
141
+ #
142
+ # @example
143
+ # results = AgentSettings.all(dir: "/work/my_app")
144
+ # results.each do |agent, config_path|
145
+ # puts "#{agent}: #{config_path.effective.path}"
146
+ # puts " exists: #{config_path.effective.exists?}"
147
+ # end
148
+ def all(dir:, env: ENV, trusted: true)
149
+ LocationDiscovery.all(dir: dir, env: env, trusted: trusted)
150
+ end
151
+ end
152
+ end
data/llm.md ADDED
@@ -0,0 +1,165 @@
1
+ # Module AgentSettings <a id="module-AgentSettings"></a>
2
+
3
+ Discover config locations for Claude Code, OpenCode, and Codex.
4
+
5
+ This gem provides a clean interface to find where agent configuration files
6
+ are located, whether they exist, and which one is currently effective based on
7
+ precedence rules and environment variable overrides.
8
+
9
+ ## Trusted vs Untrusted Projects
10
+
11
+ The `trusted` parameter controls whether project-level config is included in
12
+ resolution. This is a security feature for Codex:
13
+
14
+ * **Trusted projects** (`trusted: true`): Project config is included in
15
+ resolution. Use this for projects you own or have reviewed.
16
+ * **Untrusted projects** (`trusted: false`): Project config is ignored for
17
+ Codex. This prevents untrusted code repositories from injecting malicious
18
+ configuration. A warning is added if a <code>.codex/config.toml</code>
19
+ file exists but is ignored.
20
+
21
+ Claude Code and OpenCode do not use the trust model - their project configs
22
+ are always included regardless of the `trusted` parameter.
23
+
24
+ **@example Get global config location**
25
+ ```ruby
26
+ location = AgentSettings.global(:claude)
27
+ location.path #=> "/Users/me/.claude/settings.json"
28
+ location.exists? #=> true
29
+ ```
30
+
31
+ **@example Resolve effective config with all details**
32
+ ```ruby
33
+ result = AgentSettings.resolve(:opencode, dir: "/work/my_app")
34
+ result.effective.path #=> the active config path
35
+ result.custom_config? #=> true if env override is active
36
+ ```
37
+
38
+ **@example Resolve all agents at once**
39
+ ```ruby
40
+ results = AgentSettings.all(dir: Dir.pwd)
41
+ results.each do |agent, config_path|
42
+ puts "#{agent}: #{config_path.effective.path}"
43
+ end
44
+ ```
45
+
46
+ **@example Untrusted project (Codex ignores project config)**
47
+ ```ruby
48
+ result = AgentSettings.resolve(:codex, dir: "/untrusted/repo", trusted: false)
49
+ result.project #=> nil (ignored for security)
50
+ result.warnings #=> ["Project config ignored for untrusted project"]
51
+ ```
52
+
53
+ ## Constants
54
+ ### `VERSION` <a id="constant-VERSION"></a> <a id="VERSION-constant"></a>
55
+ Current gem version.
56
+
57
+ ## Public Class Methods
58
+ ### `all(dir:, env: = ENV, trusted: = true)` <a id="method-c-all"></a> <a id="all-class_method"></a>
59
+ Resolve config paths for all supported agents.
60
+
61
+ Returns a hash mapping each agent symbol to its AgentConfigPath result. Useful
62
+ for getting an overview of all agent configurations at once.
63
+ - **@param** `dir` [String] project directory path (required)
64
+ - **@param** `env` [Hash] environment variables hash (default: ENV)
65
+ - **@param** `trusted` [Boolean] whether the project is trusted (default: true)
66
+ - **@return** [Hash{Symbol => AgentConfigPath}] map of agent to config path
67
+
68
+ **@example**
69
+ ```ruby
70
+ results = AgentSettings.all(dir: "/work/my_app")
71
+ results.each do |agent, config_path|
72
+ puts "#{agent}: #{config_path.effective.path}"
73
+ puts " exists: #{config_path.effective.exists?}"
74
+ end
75
+ ```
76
+
77
+ ### `global(agent, env: = ENV, dir: = Dir.pwd, trusted: = true)` <a id="method-c-global"></a> <a id="global-class_method"></a>
78
+ Get the global config location for an agent.
79
+
80
+ The global config is typically stored in the user's home directory and applies
81
+ across all projects.
82
+ - **@param** `agent` [Symbol] the agent identifier (:claude, :opencode, :codex)
83
+ - **@param** `env` [Hash] environment variables hash (default: ENV)
84
+ - **@param** `dir` [String] project directory path (default: Dir.pwd)
85
+ - **@param** `trusted` [Boolean] whether the project is trusted (default: true)
86
+ - **@return** [Location, nil] the global config location, or nil if not found
87
+
88
+ **@example**
89
+ ```ruby
90
+ location = AgentSettings.global(:claude)
91
+ location.path #=> "/Users/me/.claude/settings.json"
92
+ location.exists? #=> true
93
+ ```
94
+
95
+ ### `project(agent, dir:, env: = ENV, trusted: = true)` <a id="method-c-project"></a> <a id="project-class_method"></a>
96
+ Get the project-level config location for an agent.
97
+
98
+ Project configs are stored within the project directory and apply only to that
99
+ specific project.
100
+
101
+ Note: For Codex, project config is ignored when `trusted: false`. This is a
102
+ security measure to prevent untrusted repositories from injecting malicious
103
+ configuration. In this case, returns nil and a warning is added to the resolve
104
+ result.
105
+ - **@param** `agent` [Symbol] the agent identifier (:claude, :opencode, :codex)
106
+ - **@param** `dir` [String] project directory path (required)
107
+ - **@param** `env` [Hash] environment variables hash (default: ENV)
108
+ - **@param** `trusted` [Boolean] whether the project is trusted (default: true)
109
+ - **@return** [Location, nil] the project config location, or nil if not applicable
110
+
111
+ **@example**
112
+ ```ruby
113
+ location = AgentSettings.project(:codex, dir: "/work/my_app")
114
+ location.path #=> "/work/my_app/.codex/config.toml"
115
+ location.exists? #=> false
116
+ ```
117
+
118
+ ### `resolve(agent, dir:, env: = ENV, trusted: = true)` <a id="method-c-resolve"></a> <a id="resolve-class_method"></a>
119
+ Resolve the complete config path information for an agent.
120
+
121
+ This is the primary method for discovering all config-related information for
122
+ an agent. It returns an AgentConfigPath object containing the effective config
123
+ location, global and project locations, all layers by precedence, environment
124
+ variable overrides, and any warnings.
125
+
126
+ The effective location is determined by:
127
+ 1. Active environment variable overrides (highest precedence)
128
+ 2. First existing layer in precedence order
129
+ 3. First layer if none exist
130
+ - **@param** `agent` [Symbol] the agent identifier (:claude, :opencode, :codex)
131
+ - **@param** `dir` [String] project directory path (required)
132
+ - **@param** `env` [Hash] environment variables hash (default: ENV)
133
+ - **@param** `trusted` [Boolean] whether the project is trusted (default: true)
134
+ - **@return** [AgentConfigPath] complete config path information
135
+
136
+ **@example Basic usage**
137
+ ```ruby
138
+ result = AgentSettings.resolve(:claude, dir: Dir.pwd)
139
+ result.effective.path #=> the active config path
140
+ result.effective.exists? #=> whether the config file exists
141
+ result.custom_config? #=> true if env override is active
142
+ ```
143
+
144
+ **@example With environment overrides**
145
+ ```ruby
146
+ env = { "OPENCODE_CONFIG" => "/custom/config.json" }
147
+ result = AgentSettings.resolve(:opencode, dir: Dir.pwd, env: env)
148
+ result.env_overrides.first.name #=> "OPENCODE_CONFIG"
149
+ result.env_overrides.first.active? #=> true (if file exists)
150
+ ```
151
+
152
+ # Documentation
153
+
154
+ - [doc/AgentSettings/Adapters/Claude.md](doc/AgentSettings/Adapters/Claude.md)
155
+ - [doc/AgentSettings/Adapters/Codex.md](doc/AgentSettings/Adapters/Codex.md)
156
+ - [doc/AgentSettings/Adapters/Opencode.md](doc/AgentSettings/Adapters/Opencode.md)
157
+ - [doc/AgentSettings/Adapters.md](doc/AgentSettings/Adapters.md)
158
+ - [doc/AgentSettings/AgentConfigPath.md](doc/AgentSettings/AgentConfigPath.md)
159
+ - [doc/AgentSettings/EnvOverride.md](doc/AgentSettings/EnvOverride.md)
160
+ - [doc/AgentSettings/Error.md](doc/AgentSettings/Error.md)
161
+ - [doc/AgentSettings/Location.md](doc/AgentSettings/Location.md)
162
+ - [doc/AgentSettings/LocationDiscovery.md](doc/AgentSettings/LocationDiscovery.md)
163
+ - [doc/AgentSettings/Registry.md](doc/AgentSettings/Registry.md)
164
+ - [doc/AgentSettings/UnknownAgentError.md](doc/AgentSettings/UnknownAgentError.md)
165
+ - [ruby.md](ruby.md)
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agent_settings
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Lucian Ghinda
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: zeitwerk
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ description: Clean interface to discover configuration file locations forAI coding
27
+ assistants (Claude Code, OpenCode, Codex). Shows global and project config paths,
28
+ handles environment variable overrides, and determines which config is effective
29
+ based on precedence rules.
30
+ email:
31
+ - lucian@ghinda.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - AGENTS.md
37
+ - CHANGELOG.md
38
+ - CLAUDE.md
39
+ - README.md
40
+ - lib/agent_settings.rb
41
+ - lib/agent_settings/adapters/claude.rb
42
+ - lib/agent_settings/adapters/codex.rb
43
+ - lib/agent_settings/adapters/opencode.rb
44
+ - lib/agent_settings/agent_config_path.rb
45
+ - lib/agent_settings/env_override.rb
46
+ - lib/agent_settings/location.rb
47
+ - lib/agent_settings/location_discovery.rb
48
+ - lib/agent_settings/registry.rb
49
+ - lib/agent_settings/version.rb
50
+ - llm.md
51
+ homepage: https://github.com/ghinda/agent_settings
52
+ licenses:
53
+ - Apache 2.0 License
54
+ metadata:
55
+ allowed_push_host: https://rubygems.org
56
+ homepage_uri: https://github.com/ghinda/agent_settings
57
+ source_code_uri: https://github.com/ghinda/agent_settings
58
+ changelog_uri: https://github.com/ghinda/agent_settings/blob/main/CHANGELOG.md
59
+ rubygems_mfa_required: 'true'
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '3.4'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubygems_version: 4.0.6
75
+ specification_version: 4
76
+ summary: Discover config locations for Claude Code, OpenCode, and Codex
77
+ test_files: []