Module: AIA::ConfigModules::Base

Defined in:
lib/aia/config/base.rb

Class Method Summary collapse

Class Method Details

.apply_file_config_to_struct(config, file_config) ⇒ Object



115
116
117
# File 'lib/aia/config/base.rb', line 115

def apply_file_config_to_struct(config, file_config)
  FileLoader.apply_file_config_to_struct(config, file_config)
end

.cf_options(file) ⇒ Object



19
20
21
# File 'lib/aia/config/base.rb', line 19

def cf_options(file)
  FileLoader.cf_options(file)
end

.cli_optionsObject

Delegate to other config modules



15
16
17
# File 'lib/aia/config/base.rb', line 15

def cli_options
  CLIParser.cli_options
end

.configure_prompt_manager(config) ⇒ Object



119
120
121
# File 'lib/aia/config/base.rb', line 119

def configure_prompt_manager(config)
  Validator.configure_prompt_manager(config)
end

.create_option_parser(config) ⇒ Object



111
112
113
# File 'lib/aia/config/base.rb', line 111

def create_option_parser(config)
  CLIParser.create_option_parser(config)
end

.dump_config(config, file) ⇒ Object



23
24
25
# File 'lib/aia/config/base.rb', line 23

def dump_config(config, file)
  FileLoader.dump_config(config, file)
end

.eager_load_tools_from_library(library) ⇒ Object

Attempt to eager load tools from a required library Libraries that use Zeitwerk need their tools eager loaded so they appear in ObjectSpace for AIA’s tool discovery



177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/aia/config/base.rb', line 177

def eager_load_tools_from_library(library)
  # Convert library name to module constant (e.g., 'shared_tools' -> 'SharedTools')
  module_name = library.split('/').first.split('_').map(&:capitalize).join

  begin
    mod = Object.const_get(module_name)
    if mod.respond_to?(:load_all_tools)
      mod.load_all_tools
    end
  rescue NameError
    # Module doesn't exist with expected name, skip
  end
end

.envar_options(default, cli_config) ⇒ Object

envar values are always String object so need other config layers to know the prompter type for each key’s value



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/aia/config/base.rb', line 276

def envar_options(default, cli_config)
  config = OpenStruct.merge(default, cli_config)
  envars = ENV.keys.select { |key, _| key.start_with?('AIA_') }
  envars.each do |envar|
    key   = envar.sub(/^AIA_/, '').downcase.to_sym
    value = ENV[envar]

    value = case config[key]
            when TrueClass, FalseClass
              value.downcase == 'true'
            when Integer
              value.to_i
            when Float
              value.to_f
            when Array
              # Special handling for :model to support inline role syntax (ADR-005 v2)
              if key == :model && value.include?('=')
                CLIParser.parse_models_with_roles(value)
              else
                value.split(',').map(&:strip)
              end
            else
              value # defaults to String
            end
    config[key] = value
  end

  config
end

.generate_completion_script(shell) ⇒ Object



27
28
29
# File 'lib/aia/config/base.rb', line 27

def generate_completion_script(shell)
  FileLoader.generate_completion_script(shell)
end

.handle_executable_prompt(config) ⇒ Object



103
104
105
# File 'lib/aia/config/base.rb', line 103

def handle_executable_prompt(config)
  Validator.handle_executable_prompt(config)
end

.handle_fuzzy_search_prompt_id(config) ⇒ Object



107
108
109
# File 'lib/aia/config/base.rb', line 107

def handle_fuzzy_search_prompt_id(config)
  Validator.handle_fuzzy_search_prompt_id(config)
end

.load_libraries(config) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/aia/config/base.rb', line 152

def load_libraries(config)
  return if config.require_libs.empty?

  exit_on_error = false

  config.require_libs.each do |library|
    begin
      require(library)
      # Check if the library provides tools that need eager loading
      # Convert library name to module constant (e.g., 'shared_tools' -> 'SharedTools')
      eager_load_tools_from_library(library)
    rescue => e
      STDERR.puts "Error loading library '#{library}' #{e.message}"
    exit_on_error = true
    end
  end

  exit(1) if exit_on_error

  config
end

.load_mcp_servers(config) ⇒ Object



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/aia/config/base.rb', line 216

def load_mcp_servers(config)
  servers = config.mcp_servers

  return config if servers.nil? || (servers.respond_to?(:empty?) && servers.empty?)

  servers.each do |server|
    name    = server[:name]    || server["name"]
    command = server[:command] || server["command"]
    args    = server[:args]    || server["args"] || []
    env     = server[:env]     || server["env"]  || {}
    timeout = server[:timeout] || server["timeout"] || 8000  # default 8 seconds in ms

    unless name && command
      STDERR.puts "WARNING: MCP server entry missing name or command: #{server.inspect}"
      next
    end

    # Resolve command path if not absolute
    resolved_command = resolve_command_path(command)
    unless resolved_command
      STDERR.puts "WARNING: MCP server '#{name}' command not found: #{command}"
      next
    end

    begin
      RubyLLM::MCP.add_client(
        name:            name,
        transport_type:  :stdio,
        request_timeout: timeout,
        config: {
          command:  resolved_command,
          args:     args,
          env:      env
        }
      )
    rescue => e
      STDERR.puts "ERROR: Failed to load MCP server '#{name}': #{e.message}"
    end
  end

  config
end

.load_tools(config) ⇒ Object



191
192
193
194
195
196
197
# File 'lib/aia/config/base.rb', line 191

def load_tools(config)
  return if config.tool_paths.empty?

  require_all_tools(config)

  config
end

.normalize_boolean_flag(config, flag) ⇒ Object



39
40
41
# File 'lib/aia/config/base.rb', line 39

def normalize_boolean_flag(config, flag)
  Validator.normalize_boolean_flag(config, flag)
end

.normalize_boolean_flags(config) ⇒ Object



99
100
101
# File 'lib/aia/config/base.rb', line 99

def normalize_boolean_flags(config)
  Validator.normalize_boolean_flags(config)
end

.normalize_last_refresh_date(config) ⇒ Object



59
60
61
# File 'lib/aia/config/base.rb', line 59

def normalize_last_refresh_date(config)
  FileLoader.normalize_last_refresh_date(config)
end

.parse_config_content(content, ext) ⇒ Object



55
56
57
# File 'lib/aia/config/base.rb', line 55

def parse_config_content(content, ext)
  FileLoader.parse_config_content(content, ext)
end

.prepare_pipeline(config) ⇒ Object



71
72
73
# File 'lib/aia/config/base.rb', line 71

def prepare_pipeline(config)
  Validator.prepare_pipeline(config)
end

.process_allowed_tools_option(tools_list, config) ⇒ Object



91
92
93
# File 'lib/aia/config/base.rb', line 91

def process_allowed_tools_option(tools_list, config)
  CLIParser.process_allowed_tools_option(tools_list, config)
end

.process_prompt_id_from_args(config, remaining_args) ⇒ Object



63
64
65
# File 'lib/aia/config/base.rb', line 63

def process_prompt_id_from_args(config, remaining_args)
  Validator.process_prompt_id_from_args(config, remaining_args)
end

.process_rejected_tools_option(tools_list, config) ⇒ Object



95
96
97
# File 'lib/aia/config/base.rb', line 95

def process_rejected_tools_option(tools_list, config)
  CLIParser.process_rejected_tools_option(tools_list, config)
end

.process_role_configuration(config) ⇒ Object



67
68
69
# File 'lib/aia/config/base.rb', line 67

def process_role_configuration(config)
  Validator.process_role_configuration(config)
end

.process_stdin_contentObject



87
88
89
# File 'lib/aia/config/base.rb', line 87

def process_stdin_content
  Validator.process_stdin_content
end

.process_tools_option(path_list, config) ⇒ Object



43
44
45
# File 'lib/aia/config/base.rb', line 43

def process_tools_option(path_list, config)
  CLIParser.process_tools_option(path_list, config)
end

.read_and_process_config_file(file) ⇒ Object



83
84
85
# File 'lib/aia/config/base.rb', line 83

def read_and_process_config_file(file)
  FileLoader.read_and_process_config_file(file)
end

.require_all_tools(config) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/aia/config/base.rb', line 199

def require_all_tools(config)
  exit_on_error = false

  config.tool_paths.each do |tool_path|
    begin
      # expands path based on PWD
      absolute_tool_path = File.expand_path(tool_path)
      require(absolute_tool_path)
    rescue => e
      STDERR.puts "Error loading tool '#{tool_path}' #{e.message}"
    exit_on_error = true
    end
  end

  exit(1) if exit_on_error
end

.resolve_command_path(command) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/aia/config/base.rb', line 259

def resolve_command_path(command)
  # If already absolute path, verify it exists
  if command.start_with?('/')
    return File.executable?(command) ? command : nil
  end

  # Search in PATH
  ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir|
    full_path = File.join(dir, command)
    return full_path if File.executable?(full_path)
  end

  nil
end

.setupObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/aia/config/base.rb', line 123

def setup
  default_config  = Defaults::DEFAULT_CONFIG.dup
  cli_config      = cli_options
  envar_config    = envar_options(default_config, cli_config)

  file = envar_config.config_file   unless envar_config.config_file.nil?
  file = cli_config.config_file     unless cli_config.config_file.nil?

  cf_config     = cf_options(file)

  config        = OpenStruct.merge(
                    default_config,
                    cf_config    || {},
                    envar_config || {},
                    cli_config   || {}
                  )

  config = tailor_the_config(config)
  load_libraries(config)
  load_tools(config)
  load_mcp_servers(config)

  if config.dump_file
    dump_config(config, config.dump_file)
  end

  config
end

.setup_ai_parameters(opts, config) ⇒ Object



79
80
81
# File 'lib/aia/config/base.rb', line 79

def setup_ai_parameters(opts, config)
  CLIParser.setup_ai_parameters(opts, config)
end

.setup_mode_options(opts, config) ⇒ Object



51
52
53
# File 'lib/aia/config/base.rb', line 51

def setup_mode_options(opts, config)
  CLIParser.setup_mode_options(opts, config)
end

.setup_model_options(opts, config) ⇒ Object



75
76
77
# File 'lib/aia/config/base.rb', line 75

def setup_model_options(opts, config)
  CLIParser.setup_model_options(opts, config)
end

.tailor_the_config(config) ⇒ Object



31
32
33
# File 'lib/aia/config/base.rb', line 31

def tailor_the_config(config)
  Validator.tailor_the_config(config)
end

.validate_and_set_context_files(config, remaining_args) ⇒ Object



47
48
49
# File 'lib/aia/config/base.rb', line 47

def validate_and_set_context_files(config, remaining_args)
  Validator.validate_and_set_context_files(config, remaining_args)
end

.validate_pipeline_prompts(config) ⇒ Object



35
36
37
# File 'lib/aia/config/base.rb', line 35

def validate_pipeline_prompts(config)
  Validator.validate_pipeline_prompts(config)
end