aia 1.1.0 → 2.0.0.0.pre.alpha

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.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +5 -1
  3. data/.loki +231 -0
  4. data/.quality/flay_baseline.txt +1 -0
  5. data/.quality/flog_baseline.txt +29 -0
  6. data/.quality/reek_baseline.txt +80 -0
  7. data/.rubocop.yml +116 -0
  8. data/.version +1 -1
  9. data/CHANGELOG.md +266 -42
  10. data/IMPLEMENTATION_PLAN.md +506 -0
  11. data/README.md +266 -238
  12. data/Rakefile +118 -5
  13. data/architecture_review.md +314 -0
  14. data/bin/aia +16 -0
  15. data/docs/AGENTS.md +40 -0
  16. data/docs/advanced-prompting.md +67 -3
  17. data/docs/cli-reference.md +312 -56
  18. data/docs/configuration.md +130 -19
  19. data/docs/contributing.md +56 -2
  20. data/docs/directives-reference.md +593 -78
  21. data/docs/faq.md +85 -3
  22. data/docs/guides/available-models.md +1 -1
  23. data/docs/guides/basic-usage.md +6 -6
  24. data/docs/guides/chat.md +40 -16
  25. data/docs/guides/crew.md +239 -0
  26. data/docs/guides/executable-prompts.md +1 -1
  27. data/docs/guides/index.md +1 -0
  28. data/docs/guides/models.md +15 -0
  29. data/docs/index.md +29 -2
  30. data/docs/installation.md +44 -17
  31. data/docs/mcp-integration.md +40 -0
  32. data/docs/prompt_management.md +85 -86
  33. data/docs/security.md +47 -0
  34. data/docs/special_projects_guide.md +386 -0
  35. data/docs/tools-and-mcp-examples.md +23 -0
  36. data/docs/workflows-and-pipelines.md +84 -7
  37. data/examples/.gitignore +1 -0
  38. data/examples/00_setup_aia.sh +27 -44
  39. data/examples/11_multi_model.sh +4 -14
  40. data/examples/12_token_usage.sh +3 -12
  41. data/examples/18_tools.sh +10 -2
  42. data/examples/22_chat_mode.sh +0 -10
  43. data/examples/23_verify.sh +139 -0
  44. data/examples/24_decompose.sh +139 -0
  45. data/examples/25_spawn.sh +139 -0
  46. data/examples/26_debate.sh +97 -0
  47. data/examples/27_mention_routing.sh +157 -0
  48. data/examples/28_model_switching.sh +106 -0
  49. data/examples/29_agent_harness.sh +177 -0
  50. data/examples/README.md +65 -0
  51. data/examples/advanced_multi_robot_capabilities_without_examples.md +106 -0
  52. data/examples/aia_config.yml +1 -1
  53. data/examples/aia_config_orchestrator.yml +45 -0
  54. data/examples/common.sh +19 -0
  55. data/examples/context/tech_stack.md +2 -2
  56. data/examples/prompts_dir/project_summary +2 -2
  57. data/examples/prompts_dir/roles/orchestrator.md +21 -0
  58. data/examples/requirements/sinatra_taskflow_app.md +139 -0
  59. data/examples/rules/01_classify_ruby.rb +16 -0
  60. data/examples/rules/02_prefer_claude_for_code.rb +19 -0
  61. data/examples/rules/03_gate_prompt_length.rb +19 -0
  62. data/examples/rules/04_tool_selection.rb +41 -0
  63. data/examples/rules/README.md +30 -0
  64. data/examples/run_all.sh +48 -15
  65. data/examples/tools/word_count_tool.rb +1 -1
  66. data/lib/AGENTS.md +57 -0
  67. data/lib/aia/chat_loop.rb +306 -159
  68. data/lib/aia/config/cli_parser.rb +174 -111
  69. data/lib/aia/config/defaults.yml +62 -33
  70. data/lib/aia/config/mcp_parser.rb +39 -46
  71. data/lib/aia/config/model_spec.rb +34 -2
  72. data/lib/aia/config/validator.rb +121 -138
  73. data/lib/aia/config.rb +110 -145
  74. data/lib/aia/content_extractor.rb +153 -0
  75. data/lib/aia/cost_calculator.rb +38 -0
  76. data/lib/aia/crew.rb +164 -0
  77. data/lib/aia/debate_handler.rb +166 -0
  78. data/lib/aia/delegate_handler.rb +112 -0
  79. data/lib/aia/directive.rb +33 -18
  80. data/lib/aia/directive_processor.rb +16 -7
  81. data/lib/aia/directives/configuration_directives.rb +160 -20
  82. data/lib/aia/directives/context_directives.rb +38 -26
  83. data/lib/aia/directives/execution_directives.rb +136 -4
  84. data/lib/aia/directives/model_directives.rb +76 -34
  85. data/lib/aia/directives/trakflow_directives.rb +44 -0
  86. data/lib/aia/directives/utility_directives.rb +203 -6
  87. data/lib/aia/directives/web_and_file_directives.rb +96 -60
  88. data/lib/aia/errors.rb +15 -0
  89. data/lib/aia/fact_asserter.rb +27 -0
  90. data/lib/aia/fzf.rb +9 -31
  91. data/lib/aia/handler_context.rb +17 -0
  92. data/lib/aia/handler_protocol.rb +19 -0
  93. data/lib/aia/history_transfer.rb +55 -0
  94. data/lib/aia/input_collector.rb +3 -3
  95. data/lib/aia/layered_orchestrator.rb +448 -0
  96. data/lib/aia/logger.rb +24 -4
  97. data/lib/aia/mcp_config_normalizer.rb +35 -0
  98. data/lib/aia/mcp_connection_manager.rb +305 -0
  99. data/lib/aia/mcp_discovery.rb +44 -0
  100. data/lib/aia/mcp_grouper.rb +33 -0
  101. data/lib/aia/mcp_utility.rb +57 -0
  102. data/lib/aia/mention_router.rb +260 -0
  103. data/lib/aia/model_alias_registry.rb +97 -0
  104. data/lib/aia/model_switch_handler.rb +100 -0
  105. data/lib/aia/network_builder.rb +155 -0
  106. data/lib/aia/network_memory_manager.rb +55 -0
  107. data/lib/aia/patches/ruby_llm_streaming_error.rb +43 -0
  108. data/lib/aia/patches/ruby_llm_tool_error.rb +96 -0
  109. data/lib/aia/pipeline_orchestrator.rb +262 -0
  110. data/lib/aia/plugin_loader.rb +170 -0
  111. data/lib/aia/plugin_monitor.rb +208 -0
  112. data/lib/aia/prompt_decomposer.rb +157 -0
  113. data/lib/aia/prompt_handler.rb +19 -39
  114. data/lib/aia/robot_builder.rb +51 -0
  115. data/lib/aia/robot_factory.rb +334 -0
  116. data/lib/aia/robot_namer.rb +116 -0
  117. data/lib/aia/session.rb +83 -17
  118. data/lib/aia/session_tracker.rb +209 -0
  119. data/lib/aia/similarity_scorer.rb +39 -0
  120. data/lib/aia/skill_utils.rb +105 -1
  121. data/lib/aia/spawn_handler.rb +129 -0
  122. data/lib/aia/spawn_spec_parser.rb +65 -0
  123. data/lib/aia/special_mode_handler.rb +302 -0
  124. data/lib/aia/startup_coordinator.rb +150 -0
  125. data/lib/aia/streaming_runner.rb +169 -0
  126. data/lib/aia/system_prompt_assembler.rb +88 -0
  127. data/lib/aia/task_coordinator.rb +202 -0
  128. data/lib/aia/task_decomposer.rb +57 -0
  129. data/lib/aia/task_executor.rb +51 -0
  130. data/lib/aia/tfidf_math.rb +27 -0
  131. data/lib/aia/tool_filter/tfidf.rb +116 -0
  132. data/lib/aia/tool_filter/wordnet_expander.rb +127 -0
  133. data/lib/aia/tool_filter.rb +82 -0
  134. data/lib/aia/tool_filter_registry.rb +30 -0
  135. data/lib/aia/tool_filter_strategy.rb +143 -0
  136. data/lib/aia/tool_loader.rb +210 -0
  137. data/lib/aia/tool_utility.rb +30 -0
  138. data/lib/aia/tools/delegate_to_foreman_tool.rb +70 -0
  139. data/lib/aia/tools/recruit_robot_tool.rb +60 -0
  140. data/lib/aia/tools/reskill_robot_tool.rb +44 -0
  141. data/lib/aia/tools/task_board_tool.rb +114 -0
  142. data/lib/aia/trakflow_bridge.rb +173 -0
  143. data/lib/aia/turn_state.rb +94 -0
  144. data/lib/aia/ui_presenter.rb +166 -198
  145. data/lib/aia/utility.rb +134 -87
  146. data/lib/aia/{history_manager.rb → variable_input_collector.rb} +8 -9
  147. data/lib/aia/verification_network.rb +58 -0
  148. data/lib/aia.rb +108 -63
  149. data/mkdocs.yml +1 -0
  150. metadata +179 -56
  151. data/justfile +0 -215
  152. data/lib/aia/adapter/chat_execution.rb +0 -242
  153. data/lib/aia/adapter/error_handler.rb +0 -68
  154. data/lib/aia/adapter/gem_activator.rb +0 -57
  155. data/lib/aia/adapter/mcp_connector.rb +0 -274
  156. data/lib/aia/adapter/modality_handlers.rb +0 -167
  157. data/lib/aia/adapter/model_registry.rb +0 -81
  158. data/lib/aia/adapter/multi_model_chat.rb +0 -218
  159. data/lib/aia/adapter/provider_configurator.rb +0 -59
  160. data/lib/aia/adapter/tool_filter.rb +0 -85
  161. data/lib/aia/adapter/tool_loader.rb +0 -90
  162. data/lib/aia/chat_processor_service.rb +0 -164
  163. data/lib/aia/prompt_pipeline.rb +0 -183
  164. data/lib/aia/ruby_llm_adapter.rb +0 -95
  165. data/lib/extensions/openstruct_merge.rb +0 -48
  166. data/lib/extensions/ruby_llm/.irbrc +0 -56
  167. data/lib/extensions/ruby_llm/modalities.rb +0 -36
  168. data/lib/extensions/ruby_llm/provider_fix.rb +0 -79
  169. data/lib/refinements/string.rb +0 -16
  170. data/main.just +0 -76
@@ -11,6 +11,7 @@ require_relative 'model_spec'
11
11
  require_relative '../skill_utils'
12
12
 
13
13
  module AIA
14
+ # rubocop:disable Metrics/ModuleLength
14
15
  module CLIParser
15
16
  class << self
16
17
  # Parse CLI arguments and return a hash of overrides
@@ -23,8 +24,8 @@ module AIA
23
24
  parser = create_option_parser(options)
24
25
  parser.parse!
25
26
  rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
26
- warn "ERROR: #{e.message}"
27
- warn " use --help for usage report"
27
+ $stderr.puts "ERROR: #{e.message}"
28
+ $stderr.puts " use --help for usage report"
28
29
  exit 1
29
30
  end
30
31
 
@@ -51,8 +52,8 @@ module AIA
51
52
  end
52
53
 
53
54
  def setup_banner(opts)
54
- opts.banner = "Usage: aia [options] [PROMPT_ID] [CONTEXT_FILE]*\n" +
55
- " aia [options] --chat [PROMPT_ID] [CONTEXT_FILE]*"
55
+ opts.banner = "Usage: aia [options] [PROMPT_ID] [CONTEXT_FILE]*\n " \
56
+ "aia [options] --chat [PROMPT_ID] [CONTEXT_FILE]*"
56
57
  end
57
58
 
58
59
  def setup_mode_options(opts, options)
@@ -64,14 +65,34 @@ module AIA
64
65
 
65
66
  opts.on("-f", "--fuzzy", "Use fuzzy matching for prompt search") do
66
67
  unless system("which fzf > /dev/null 2>&1")
67
- warn "Error: 'fzf' is not installed. Please install 'fzf' to use the --fuzzy option."
68
+ $stderr.puts "Error: 'fzf' is not installed. Please install 'fzf' to use the --fuzzy option."
68
69
  exit 1
69
70
  end
70
71
  options[:fuzzy] = true
71
72
  end
72
73
 
73
- opts.on("--terse", "[DEPRECATED] No longer supported") do
74
- warn "Warning: --terse is deprecated and has no effect."
74
+ opts.on("--expert-routing", "Route each prompt to a specialist robot based on domain classification") do
75
+ options[:expert_routing] = true
76
+ end
77
+
78
+ opts.on("--track-pipeline", "Track pipeline execution in TrakFlow") do
79
+ options[:track_pipeline] = true
80
+ end
81
+
82
+ opts.on("--concurrent-auto", "Automatically parallelize independent MCP servers") do
83
+ options[:concurrent_auto] = true
84
+ end
85
+
86
+ opts.on("-A", "--[no-]auto-tool-filter", "Use TF-IDF similarity-based tool filtering (default: on)") do |v|
87
+ options[:auto_tool_filter] = v
88
+ end
89
+
90
+ opts.on("--load", "Load persisted tool filter databases from ~/.config/aia/") do
91
+ options[:tool_filter_load] = true
92
+ end
93
+
94
+ opts.on("--save", "Save tool filter databases to ~/.config/aia/") do
95
+ options[:tool_filter_save] = true
75
96
  end
76
97
  end
77
98
 
@@ -82,8 +103,10 @@ module AIA
82
103
  list_available_models(query)
83
104
  end
84
105
 
85
- opts.on("-m MODEL", "--model MODEL", "Set LLM model(s) to use. Format: MODEL[=ROLE][,MODEL[=ROLE]]...") do |model_string|
86
- options[:models] = parse_models_with_roles(model_string)
106
+ opts.on("-m MODEL", "--model MODEL",
107
+ "Set LLM model(s). Format: MODEL[=ROLE][,MODEL[=ROLE]]... " \
108
+ "Multiple models run in parallel; use --consensus to synthesize results") do |model_string|
109
+ options[:models] = (options[:models] || []) + parse_models_with_roles(model_string)
87
110
  end
88
111
 
89
112
  opts.on("--[no-]consensus", "Enable/disable consensus mode for multi-model responses") do |consensus|
@@ -112,13 +135,13 @@ module AIA
112
135
  end
113
136
 
114
137
  opts.on("-o", "--[no-]output [FILE]", "Write response to FILE (default: temp.md; --no-output to disable)") do |file|
115
- if file == false
116
- options[:output] = nil
117
- elsif file.nil?
118
- options[:output] = 'temp.md'
119
- else
120
- options[:output] = File.expand_path(file, Dir.pwd)
121
- end
138
+ options[:output] = if file == false
139
+ nil
140
+ elsif file.nil?
141
+ 'temp.md'
142
+ else
143
+ File.expand_path(file, Dir.pwd)
144
+ end
122
145
  end
123
146
 
124
147
  opts.on("-a", "--[no-]append", "Append to output file instead of overwriting") do |append|
@@ -141,6 +164,10 @@ module AIA
141
164
  options[:prompts_dir] = dir
142
165
  end
143
166
 
167
+ opts.on("--plugins-dir DIR", "Set directory containing AIA plugin files") do |dir|
168
+ options[:plugins_dir] = dir
169
+ end
170
+
144
171
  opts.on("--roles-prefix PREFIX", "Set subdirectory name for role files (default: roles)") do |prefix|
145
172
  options[:roles_prefix] = prefix
146
173
  end
@@ -149,6 +176,19 @@ module AIA
149
176
  options[:role] = role
150
177
  end
151
178
 
179
+ opts.on("--skills-dir DIR", "Set directory containing skill subdirectories") do |dir|
180
+ options[:skills_dir] = dir
181
+ end
182
+
183
+ opts.on("-s", "--skill SKILL_IDS", "Append skill(s) to prompt; comma-separated IDs or paths") do |skill_ids|
184
+ options[:skills] ||= []
185
+ options[:skills] += skill_ids.split(',').map(&:strip).reject(&:empty?)
186
+ end
187
+
188
+ opts.on("--list-skills", "List available skills and exit") do
189
+ options[:list_skills] = true
190
+ end
191
+
152
192
  opts.on("-n", "--next PROMPT_ID", "Set next prompt to process") do |next_prompt|
153
193
  options[:pipeline] ||= []
154
194
  options[:pipeline] << next_prompt
@@ -159,32 +199,9 @@ module AIA
159
199
  options[:pipeline] += pipeline.split(',').map(&:strip)
160
200
  end
161
201
 
162
-
163
202
  opts.on("--system-prompt PROMPT_ID", "Set system prompt for chat sessions") do |prompt_id|
164
203
  options[:system_prompt] = prompt_id
165
204
  end
166
-
167
- opts.on('--regex PATTERN', '[DEPRECATED] Parameter regex (PM v1.0.0 uses ERB parameters)') do |pattern|
168
- warn "Warning: --regex is deprecated. PM v1.0.0 uses ERB parameters (<%= param %>)."
169
- options[:parameter_regex] = pattern
170
- end
171
-
172
- opts.on("--skills-dir DIR", "Set directory containing skill subdirectories") do |dir|
173
- options[:skills_dir] = dir
174
- end
175
-
176
- opts.on("--skills-prefix PREFIX", "Set subdirectory name for skill files (default: skills)") do |prefix|
177
- options[:skills_prefix] = prefix
178
- end
179
-
180
- opts.on("-s", "--skill SKILL_IDS", "Prepend skill(s) to prompt (comma-separated IDs or paths)") do |ids|
181
- options[:skills] ||= []
182
- options[:skills] += ids.split(',').map(&:strip)
183
- end
184
-
185
- opts.on("--list-skills", "List available skills and exit") do
186
- options[:list_skills] = true
187
- end
188
205
  end
189
206
 
190
207
  def setup_ai_parameters(opts, options)
@@ -194,7 +211,7 @@ module AIA
194
211
  options[:temperature] = temp
195
212
  end
196
213
 
197
- opts.on("--max-tokens TOKENS", Integer, "Set maximum tokens for text generation (default: 2048)") do |tokens|
214
+ opts.on("--max-tokens TOKENS", Integer, "Set maximum tokens for text generation (default: 32767)") do |tokens|
198
215
  options[:max_tokens] = tokens
199
216
  end
200
217
 
@@ -218,10 +235,22 @@ module AIA
218
235
  options[:speak] = true
219
236
  end
220
237
 
221
- opts.on("--voice VOICE", "Set voice for speech output (default: alloy)") do |voice|
238
+ opts.on("--voice VOICE", "Set voice for speech output") do |voice|
222
239
  options[:voice] = voice
223
240
  end
224
241
 
242
+ opts.on("--speak-command CMD", "Command used for TTS (default: say)") do |cmd|
243
+ options[:speak_command] = cmd
244
+ end
245
+
246
+ opts.on("--paint", "Generate an image from the prompt instead of a text response") do
247
+ options[:paint] = true
248
+ end
249
+
250
+ opts.on("--im", "--image-model MODEL", "Set image model for generation (default: dall-e-3)") do |model|
251
+ options[:image_model] = model
252
+ end
253
+
225
254
  opts.on("--is", "--image-size SIZE", "Set image size for generation (default: 1024x1024)") do |size|
226
255
  options[:image_size] = size
227
256
  end
@@ -238,6 +267,10 @@ module AIA
238
267
  def setup_tool_options(opts, options)
239
268
  opts.separator "\nTool & Extension Options:"
240
269
 
270
+ opts.on('--allow-ruby-eval', 'Enable /ruby directive (executes arbitrary Ruby code with full process privileges)') do
271
+ options[:allow_ruby_eval] = true
272
+ end
273
+
241
274
  opts.on("--rq LIBS", "--require LIBS", "Require Ruby libraries for Ruby directive") do |libs|
242
275
  options[:require_libs] ||= []
243
276
  options[:require_libs] += libs.split(',')
@@ -264,11 +297,17 @@ module AIA
264
297
 
265
298
  def setup_utility_options(opts, options)
266
299
  opts.separator "\nUtility Options:"
300
+ setup_logging_options(opts, options)
301
+ setup_output_options(opts, options)
302
+ setup_mcp_options(opts, options)
303
+ setup_meta_options(opts, options)
304
+ end
267
305
 
306
+ def setup_logging_options(opts, options)
268
307
  opts.on("--log-level LEVEL", "Set log level (debug|info|warn|error|fatal)") do |level|
269
308
  level = level.downcase
270
309
  unless %w[debug info warn error fatal].include?(level)
271
- warn "ERROR: Invalid log level '#{level}'. Must be one of: debug, info, warn, error, fatal"
310
+ $stderr.puts "ERROR: Invalid log level '#{level}'. Must be one of: debug, info, warn, error, fatal"
272
311
  exit 1
273
312
  end
274
313
  options[:log_level_override] = level
@@ -296,6 +335,21 @@ module AIA
296
335
  opts.on("-v", "--[no-]verbose", "Enable verbose output") do |value|
297
336
  options[:verbose] = value
298
337
  end
338
+ end
339
+
340
+ def setup_output_options(opts, options)
341
+ opts.on("--[no-]thinking", "Show raw thinking/reasoning blocks in output (default: off)") do |v|
342
+ options[:thinking] = v
343
+ end
344
+
345
+ opts.on("--tokens", "Display token usage and elapsed response time after each turn") do
346
+ options[:tokens] = true
347
+ end
348
+
349
+ opts.on("--cost", "Display cost calculations and elapsed time (implies --tokens)") do
350
+ options[:cost] = true
351
+ options[:tokens] = true
352
+ end
299
353
 
300
354
  opts.on("--refresh DAYS", Integer, "Set refresh interval (days) for cached models list (default: 7)") do |days|
301
355
  options[:refresh] = days || 0
@@ -308,16 +362,9 @@ module AIA
308
362
  opts.on("--completion SHELL", "Generate shell completion script (bash|zsh|fish) and exit") do |shell|
309
363
  options[:completion] = shell
310
364
  end
365
+ end
311
366
 
312
- opts.on("--tokens", "Display token usage") do
313
- options[:tokens] = true
314
- end
315
-
316
- opts.on("--cost", "Display cost calculations (implies --tokens)") do
317
- options[:cost] = true
318
- options[:tokens] = true # --cost implies --tokens
319
- end
320
-
367
+ def setup_mcp_options(opts, options)
321
368
  opts.on("--mcp FILE", "Load MCP server(s) from JSON file (repeatable)") do |file|
322
369
  options[:mcp_files] ||= []
323
370
  options[:mcp_files] << file
@@ -340,47 +387,55 @@ module AIA
340
387
  options[:mcp_skip] ||= []
341
388
  options[:mcp_skip] += names.split(',').map(&:strip)
342
389
  end
390
+ end
343
391
 
392
+ def setup_meta_options(opts, _options)
344
393
  opts.on("--version", "Show version and exit") do
345
394
  puts AIA::VERSION
346
395
  exit
347
396
  end
348
397
 
349
398
  opts.on("-h", "--help", "Show this help and exit") do
350
- puts <<~HELP
351
-
352
- AIA - Your AI Assistant (v#{AIA::VERSION})
353
- - Manage AI prompts with embedded directives
354
- - Integrate with shell and Ruby (ERB) processing
355
- - Run batch processes and prompt pipelines
356
- - Engage in interactive chat sessions
357
- - Use custom tools and MCP servers
358
-
359
- HELP
360
-
361
- puts opts
362
-
363
- puts <<~EXTRA
364
-
365
- Explore Further:
366
- - AIA GitHub Repository: https://github.com/MadBomber/aia
367
- - AIA Documentation: https://madbomber.github.io/aia
368
- - AIA Changelog: https://github.com/MadBomber/aia/blob/main/CHANGELOG.md
369
- - AIA Examples: https://github.com/MadBomber/aia/tree/main/examples
370
- - Report an Issue: https://github.com/MadBomber/aia/issues
371
- - RubyLLM Documentation: https://rubyllm.com
372
- - RubyLLM Tool Docs: https://rubyllm.com/guides/tools
373
- - PromptManager Docs: https://madbomber.github.io/prompt_manager
374
- - ERB Documentation: https://docs.ruby-lang.org/en/master/ERB.html
375
- - MCP Specification: https://modelcontextprotocol.io
376
- - MCP Client Docs: https://github.com/patvice/ruby_llm-mcp/blob/main/README.md
377
-
378
- EXTRA
379
-
399
+ print_help(opts)
380
400
  exit
381
401
  end
382
402
  end
383
403
 
404
+ def print_help(opts)
405
+ puts <<~HELP
406
+
407
+ AIA - Your AI Assistant (v#{AIA::VERSION})
408
+ - Manage AI prompts with embedded directives
409
+ - Integrate with shell and Ruby (ERB) processing
410
+ - Run batch processes and prompt pipelines
411
+ - Engage in interactive chat sessions
412
+ - Use custom tools and MCP servers
413
+ - Compare the same prompt across multiple models (--model m1,m2,m3 [--consensus])
414
+ - Measure response time and token usage with --tokens; add cost with --cost
415
+ - Benchmark concurrent vs. serial performance (/decompose reports speedup metrics)
416
+
417
+ HELP
418
+
419
+ puts opts
420
+
421
+ puts <<~EXTRA
422
+
423
+ Explore Further:
424
+ - AIA GitHub Repository: https://github.com/MadBomber/aia
425
+ - AIA Documentation: https://madbomber.github.io/aia
426
+ - AIA Changelog: https://github.com/MadBomber/aia/blob/main/CHANGELOG.md
427
+ - AIA Examples: https://github.com/MadBomber/aia/tree/main/examples
428
+ - Report an Issue: https://github.com/MadBomber/aia/issues
429
+ - RubyLLM Documentation: https://rubyllm.com
430
+ - RubyLLM Tool Docs: https://rubyllm.com/guides/tools
431
+ - PromptManager Docs: https://madbomber.github.io/prompt_manager
432
+ - ERB Documentation: https://docs.ruby-lang.org/en/master/ERB.html
433
+ - MCP Specification: https://modelcontextprotocol.io
434
+ - MCP Client Docs: https://github.com/patvice/ruby_llm-mcp/blob/main/README.md
435
+
436
+ EXTRA
437
+ end
438
+
384
439
  # Parse model string into array of ModelSpec-compatible hashes
385
440
  #
386
441
  # @param model_string [String] comma-separated models with optional roles
@@ -389,6 +444,7 @@ module AIA
389
444
  models = []
390
445
  model_counts = Hash.new(0)
391
446
 
447
+ # rubocop:disable Metrics/BlockLength
392
448
  model_string.split(',').each do |spec|
393
449
  spec.strip!
394
450
 
@@ -424,6 +480,7 @@ module AIA
424
480
  }
425
481
  end
426
482
  end
483
+ # rubocop:enable Metrics/BlockLength
427
484
 
428
485
  models
429
486
  end
@@ -433,10 +490,11 @@ module AIA
433
490
  expanded = File.expand_path(role_id)
434
491
  expanded += '.md' if File.extname(expanded).empty?
435
492
  raise ArgumentError, "Role file not found: #{expanded}" unless File.exist?(expanded)
493
+
436
494
  return
437
495
  end
438
496
 
439
- prompts_dir = ENV.fetch('AIA_PROMPTS__DIR', File.join(ENV['HOME'], '.prompts'))
497
+ prompts_dir = ENV.fetch('AIA_PROMPTS__DIR', File.join(Dir.home, '.prompts'))
440
498
  roles_prefix = ENV.fetch('AIA_PROMPTS__ROLES_PREFIX', 'roles')
441
499
 
442
500
  unless role_id.start_with?(roles_prefix)
@@ -445,26 +503,25 @@ module AIA
445
503
 
446
504
  role_file_path = File.join(prompts_dir, "#{role_id}.md")
447
505
 
448
- unless File.exist?(role_file_path)
449
- available_roles = list_available_role_names(prompts_dir, roles_prefix)
450
-
451
- error_msg = "Role file not found: #{role_file_path}\n\n"
506
+ return if File.exist?(role_file_path)
507
+ available_roles = list_available_role_names(prompts_dir, roles_prefix)
452
508
 
453
- if available_roles.empty?
454
- error_msg += "No roles directory found at #{File.join(prompts_dir, roles_prefix)}\n"
455
- error_msg += "Create the directory and add role files to use this feature."
456
- else
457
- error_msg += "Available roles:\n"
458
- error_msg += available_roles.map { |r| " - #{r}" }.join("\n")
459
- error_msg += "\n\nCreate the role file or use an existing role."
460
- end
509
+ error_msg = "Role file not found: #{role_file_path}\n\n"
461
510
 
462
- raise ArgumentError, error_msg
511
+ if available_roles.empty?
512
+ error_msg += "No roles directory found at #{File.join(prompts_dir, roles_prefix)}\n"
513
+ error_msg += "Create the directory and add role files to use this feature."
514
+ else
515
+ error_msg += "Available roles:\n"
516
+ error_msg += available_roles.map { |r| " - #{r}" }.join("\n")
517
+ error_msg += "\n\nCreate the role file or use an existing role."
463
518
  end
519
+
520
+ raise ArgumentError, error_msg
464
521
  end
465
522
 
466
523
  def list_available_roles
467
- prompts_dir = ENV.fetch('AIA_PROMPTS__DIR', File.join(ENV['HOME'], '.prompts'))
524
+ prompts_dir = ENV.fetch('AIA_PROMPTS__DIR', File.join(Dir.home, '.prompts'))
468
525
  roles_prefix = ENV.fetch('AIA_PROMPTS__ROLES_PREFIX', 'roles')
469
526
  roles_dir = File.join(prompts_dir, roles_prefix)
470
527
 
@@ -484,13 +541,16 @@ module AIA
484
541
 
485
542
  roles.each do |role_id|
486
543
  role_file = File.join(roles_dir, "#{role_id}.md")
487
- fm = AIA::SkillUtils.parse_front_matter(role_file)
544
+ front_matter = AIA::SkillUtils.parse_front_matter(role_file)
488
545
 
489
546
  puts "## #{role_id}"
490
547
  puts
491
548
  puts "| Key | Value |"
492
549
  puts "|-----|-------|"
493
- fm.each { |key, value| puts "| #{key} | #{value} |" }
550
+ front_matter.each do |key, value|
551
+ safe_value = value.to_s.gsub('|', '\\|')
552
+ puts "| #{key} | #{safe_value} |"
553
+ end
494
554
  puts
495
555
  end
496
556
  end
@@ -500,19 +560,21 @@ module AIA
500
560
  return [] unless Dir.exist?(roles_dir)
501
561
 
502
562
  Dir.glob("**/*.md", base: roles_dir)
503
- .map { |f| f.chomp('.md') }
504
- .reject { |f| f.split('/').any? { |part| part.start_with?('_') } }
505
- .sort
563
+ .map { |f| f.chomp('.md') }
564
+ .reject { |f| f.split('/').any? { |part| part.start_with?('_') } }
565
+ .sort
506
566
  end
507
567
 
568
+ # rubocop:disable Metrics/AbcSize
508
569
  def list_available_models(query)
509
570
  require 'ruby_llm'
510
571
 
511
- if query.nil?
512
- query = []
513
- else
514
- query = query.split(',')
515
- end
572
+ query = if query.nil?
573
+ []
574
+ else
575
+ query.split(',')
576
+ end
577
+ # rubocop:enable Metrics/AbcSize
516
578
 
517
579
  header = "\nAvailable LLMs"
518
580
  header += " for #{query.join(' and ')}" if query.any?
@@ -546,28 +608,29 @@ module AIA
546
608
  end
547
609
  end
548
610
 
549
- puts if counter > 0
611
+ puts if counter.positive?
550
612
  puts "#{counter} LLMs matching your query"
551
613
  puts
552
614
 
553
615
  exit
554
616
  end
617
+ # rubocop:enable Metrics/ModuleLength
555
618
 
556
619
  def process_tools_paths(path_list)
557
620
  paths = []
558
621
 
559
622
  if path_list.empty?
560
- warn "No list of paths for --tools option"
623
+ $stderr.puts "No list of paths for --tools option"
561
624
  exit 1
562
625
  end
563
626
 
564
627
  path_list.split(',').map(&:strip).uniq.each do |a_path|
565
628
  if File.exist?(a_path)
566
629
  if File.file?(a_path)
567
- if '.rb' == File.extname(a_path)
630
+ if File.extname(a_path) == '.rb'
568
631
  paths << a_path
569
632
  else
570
- warn "file should have *.rb extension: #{a_path}"
633
+ $stderr.puts "file should have *.rb extension: #{a_path}"
571
634
  exit 1
572
635
  end
573
636
  elsif File.directory?(a_path)
@@ -575,7 +638,7 @@ module AIA
575
638
  paths += rb_files
576
639
  end
577
640
  else
578
- warn "file/dir path is not valid: #{a_path}"
641
+ $stderr.puts "file/dir path is not valid: #{a_path}"
579
642
  exit 1
580
643
  end
581
644
  end
@@ -1,4 +1,4 @@
1
- # AIA Configuration Schema and Defaults
1
+ # AIA v2 Configuration Schema and Defaults
2
2
  #
3
3
  # This file is the SINGLE SOURCE OF TRUTH for AIA configuration.
4
4
  # All attributes must be declared here to be recognized.
@@ -28,8 +28,8 @@ defaults:
28
28
  # ---------------------------------------------------------------------------
29
29
  llm:
30
30
  temperature: 0.7
31
- max_tokens: 2048
32
- top_p: 1.0
31
+ max_tokens: 32767
32
+ top_p: ~ # nil by default — some models reject requests with both temperature AND top_p
33
33
  frequency_penalty: 0.0
34
34
  presence_penalty: 0.0
35
35
 
@@ -65,13 +65,11 @@ defaults:
65
65
  roles_prefix: roles
66
66
  #
67
67
  skills: []
68
- skills_prefix: skills
69
68
  #
70
69
  tool: ~
71
70
  tools_prefix: tools
72
71
  #
73
72
  system_prompt: ~
74
- parameter_regex: ~
75
73
 
76
74
  roles:
77
75
  dir: ~/.prompts/roles
@@ -96,10 +94,10 @@ defaults:
96
94
  # Env: AIA_AUDIO__VOICE, AIA_AUDIO__SPEAK_COMMAND, etc.
97
95
  # ---------------------------------------------------------------------------
98
96
  audio:
99
- voice: alloy
100
- speak_command: afplay
101
- speech_model: tts-1
102
- transcription_model: whisper-1
97
+ voice: ~
98
+ speak_command: say
99
+ speech_model: ~
100
+ transcription_model: ~
103
101
 
104
102
  # ---------------------------------------------------------------------------
105
103
  # Image Configuration
@@ -145,26 +143,31 @@ defaults:
145
143
  tokens: false
146
144
  no_mcp: false
147
145
  speak: false
148
- terse: false # DEPRECATED: no longer used
149
146
  shell: true
150
147
  erb: true
151
148
  clear: false
152
149
  consensus: false
150
+ track_pipeline: false
151
+ expert_routing: false
152
+ auto_tool_filter: true
153
+ tool_filter_load: false
154
+ tool_filter_save: false
155
+ allow_ruby_eval: false
156
+ paint: false
157
+ thinking: false
158
+
159
+ # ---------------------------------------------------------------------------
160
+ # Tool Filter Configuration
161
+ # Access: AIA.config.tool_filter.timeout_s
162
+ # Env: AIA_TOOL_FILTER__TIMEOUT_S
163
+ # ---------------------------------------------------------------------------
164
+ tool_filter:
165
+ timeout_s: 10
153
166
 
154
167
  # ---------------------------------------------------------------------------
155
168
  # Logger Configuration
156
169
  # Access: AIA.config.logger.aia.file, AIA.config.logger.llm.level, etc.
157
170
  # Env: AIA_LOGGER__AIA__FILE, AIA_LOGGER__LLM__LEVEL, etc.
158
- #
159
- # Configures logging for three systems:
160
- # aia: AIA application logging
161
- # llm: RubyLLM gem logging
162
- # mcp: RubyLLM::MCP gem logging
163
- #
164
- # file: STDOUT, STDERR, or a file path (e.g., ~/.aia/aia.log)
165
- # All three loggers can write to the same file safely.
166
- # level: debug, info, warn, error, fatal
167
- # flush: true = immediate write (no buffering), false = buffered writes
168
171
  # ---------------------------------------------------------------------------
169
172
  logger:
170
173
  aia:
@@ -190,7 +193,6 @@ defaults:
190
193
  # Model Registry Configuration
191
194
  # Access: AIA.config.registry.refresh
192
195
  # Env: AIA_REGISTRY__REFRESH
193
- # Note: last_refresh is derived from models.json file modification time
194
196
  # ---------------------------------------------------------------------------
195
197
  registry:
196
198
  refresh: 7 # days between refreshes (0 = disable periodic refresh)
@@ -204,14 +206,6 @@ defaults:
204
206
  # ---------------------------------------------------------------------------
205
207
  # MCP Servers Configuration
206
208
  # Access: AIA.config.mcp_servers (array of server configs)
207
- #
208
- # Example:
209
- # mcp_servers:
210
- # - name: my-server
211
- # command: /path/to/server
212
- # args: []
213
- # env: {}
214
- # timeout: 8000
215
209
  # ---------------------------------------------------------------------------
216
210
  mcp_servers: []
217
211
  mcp_use: ~
@@ -219,15 +213,50 @@ defaults:
219
213
 
220
214
  # ---------------------------------------------------------------------------
221
215
  # Paths Configuration
222
- # Access: AIA.config.paths.aia_dir, AIA.config.paths.config_file
216
+ # Access: AIA.config.paths.aia_dir, AIA.config.paths.config_file, AIA.config.paths.plugins_dir
223
217
  # Env: AIA_PATHS__AIA_DIR, AIA_PATHS__CONFIG_FILE
224
- #
225
- # Note: MywayConfig uses XDG Base Directory Specification by default.
226
- # User config is loaded from ~/.config/aia/aia.yml
218
+ # Special-case env: AIA_PLUGINS_DIR
227
219
  # ---------------------------------------------------------------------------
228
220
  paths:
229
221
  aia_dir: ~/.config/aia
230
222
  config_file: ~/.config/aia/aia.yml
223
+ plugins_dir: ~
224
+
225
+ # ---------------------------------------------------------------------------
226
+ # Model Aliases (user-defined aliases override defaults)
227
+ # Access: AIA.config.model_aliases
228
+ # ---------------------------------------------------------------------------
229
+ model_aliases: {}
230
+
231
+ # ---------------------------------------------------------------------------
232
+ # Model Switch History Mode
233
+ # Controls conversation history transfer when switching models mid-chat.
234
+ # Values: clean (fresh start), replay (replay messages), summarize (summary)
235
+ # Access: AIA.config.model_switch_history
236
+ # ---------------------------------------------------------------------------
237
+ model_switch_history: clean
238
+
239
+ # ---------------------------------------------------------------------------
240
+ # Concurrency Configuration
241
+ # Controls automatic MCP server parallelization.
242
+ # Access: AIA.config.concurrency.auto, etc.
243
+ # ---------------------------------------------------------------------------
244
+ concurrency:
245
+ auto: false
246
+ independent_servers: []
247
+ threshold: 2
248
+ mcp_timeout: 120
249
+
250
+ # ---------------------------------------------------------------------------
251
+ # Rules Configuration (Enhanced Multi-KB in v2)
252
+ # Access: AIA.config.rules.dir, AIA.config.rules.enabled, etc.
253
+ # Env: AIA_RULES__DIR, AIA_RULES__ENABLED
254
+ # ---------------------------------------------------------------------------
255
+ rules:
256
+ dir: ~/.config/aia/rules
257
+ enabled: true
258
+ store: memory
259
+ db_path: ~/.config/aia/rules.db
231
260
 
232
261
  # ---------------------------------------------------------------------------
233
262
  # Context Files (set at runtime)