aia 1.1.1 → 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 (169) 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 +259 -50
  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 +18 -6
  55. data/examples/context/tech_stack.md +2 -2
  56. data/examples/prompts_dir/roles/orchestrator.md +21 -0
  57. data/examples/requirements/sinatra_taskflow_app.md +139 -0
  58. data/examples/rules/01_classify_ruby.rb +16 -0
  59. data/examples/rules/02_prefer_claude_for_code.rb +19 -0
  60. data/examples/rules/03_gate_prompt_length.rb +19 -0
  61. data/examples/rules/04_tool_selection.rb +41 -0
  62. data/examples/rules/README.md +30 -0
  63. data/examples/run_all.sh +48 -15
  64. data/examples/tools/word_count_tool.rb +1 -1
  65. data/lib/AGENTS.md +57 -0
  66. data/lib/aia/chat_loop.rb +304 -164
  67. data/lib/aia/config/cli_parser.rb +174 -111
  68. data/lib/aia/config/defaults.yml +62 -33
  69. data/lib/aia/config/mcp_parser.rb +39 -46
  70. data/lib/aia/config/model_spec.rb +34 -2
  71. data/lib/aia/config/validator.rb +108 -142
  72. data/lib/aia/config.rb +110 -145
  73. data/lib/aia/content_extractor.rb +153 -0
  74. data/lib/aia/cost_calculator.rb +38 -0
  75. data/lib/aia/crew.rb +164 -0
  76. data/lib/aia/debate_handler.rb +166 -0
  77. data/lib/aia/delegate_handler.rb +112 -0
  78. data/lib/aia/directive.rb +33 -18
  79. data/lib/aia/directive_processor.rb +16 -7
  80. data/lib/aia/directives/configuration_directives.rb +160 -20
  81. data/lib/aia/directives/context_directives.rb +38 -26
  82. data/lib/aia/directives/execution_directives.rb +136 -4
  83. data/lib/aia/directives/model_directives.rb +76 -34
  84. data/lib/aia/directives/trakflow_directives.rb +44 -0
  85. data/lib/aia/directives/utility_directives.rb +203 -6
  86. data/lib/aia/directives/web_and_file_directives.rb +96 -60
  87. data/lib/aia/errors.rb +15 -0
  88. data/lib/aia/fact_asserter.rb +27 -0
  89. data/lib/aia/fzf.rb +9 -31
  90. data/lib/aia/handler_context.rb +17 -0
  91. data/lib/aia/handler_protocol.rb +19 -0
  92. data/lib/aia/history_transfer.rb +55 -0
  93. data/lib/aia/input_collector.rb +3 -3
  94. data/lib/aia/layered_orchestrator.rb +448 -0
  95. data/lib/aia/logger.rb +24 -4
  96. data/lib/aia/mcp_config_normalizer.rb +35 -0
  97. data/lib/aia/mcp_connection_manager.rb +305 -0
  98. data/lib/aia/mcp_discovery.rb +44 -0
  99. data/lib/aia/mcp_grouper.rb +33 -0
  100. data/lib/aia/mcp_utility.rb +57 -0
  101. data/lib/aia/mention_router.rb +260 -0
  102. data/lib/aia/model_alias_registry.rb +97 -0
  103. data/lib/aia/model_switch_handler.rb +100 -0
  104. data/lib/aia/network_builder.rb +155 -0
  105. data/lib/aia/network_memory_manager.rb +55 -0
  106. data/lib/aia/patches/ruby_llm_streaming_error.rb +43 -0
  107. data/lib/aia/patches/ruby_llm_tool_error.rb +96 -0
  108. data/lib/aia/pipeline_orchestrator.rb +262 -0
  109. data/lib/aia/plugin_loader.rb +170 -0
  110. data/lib/aia/plugin_monitor.rb +208 -0
  111. data/lib/aia/prompt_decomposer.rb +157 -0
  112. data/lib/aia/prompt_handler.rb +19 -39
  113. data/lib/aia/robot_builder.rb +51 -0
  114. data/lib/aia/robot_factory.rb +334 -0
  115. data/lib/aia/robot_namer.rb +116 -0
  116. data/lib/aia/session.rb +83 -17
  117. data/lib/aia/session_tracker.rb +209 -0
  118. data/lib/aia/similarity_scorer.rb +39 -0
  119. data/lib/aia/skill_utils.rb +105 -1
  120. data/lib/aia/spawn_handler.rb +129 -0
  121. data/lib/aia/spawn_spec_parser.rb +65 -0
  122. data/lib/aia/special_mode_handler.rb +302 -0
  123. data/lib/aia/startup_coordinator.rb +150 -0
  124. data/lib/aia/streaming_runner.rb +169 -0
  125. data/lib/aia/system_prompt_assembler.rb +88 -0
  126. data/lib/aia/task_coordinator.rb +202 -0
  127. data/lib/aia/task_decomposer.rb +57 -0
  128. data/lib/aia/task_executor.rb +51 -0
  129. data/lib/aia/tfidf_math.rb +27 -0
  130. data/lib/aia/tool_filter/tfidf.rb +116 -0
  131. data/lib/aia/tool_filter/wordnet_expander.rb +127 -0
  132. data/lib/aia/tool_filter.rb +82 -0
  133. data/lib/aia/tool_filter_registry.rb +30 -0
  134. data/lib/aia/tool_filter_strategy.rb +143 -0
  135. data/lib/aia/tool_loader.rb +210 -0
  136. data/lib/aia/tool_utility.rb +30 -0
  137. data/lib/aia/tools/delegate_to_foreman_tool.rb +70 -0
  138. data/lib/aia/tools/recruit_robot_tool.rb +60 -0
  139. data/lib/aia/tools/reskill_robot_tool.rb +44 -0
  140. data/lib/aia/tools/task_board_tool.rb +114 -0
  141. data/lib/aia/trakflow_bridge.rb +173 -0
  142. data/lib/aia/turn_state.rb +94 -0
  143. data/lib/aia/ui_presenter.rb +166 -198
  144. data/lib/aia/utility.rb +134 -87
  145. data/lib/aia/{history_manager.rb → variable_input_collector.rb} +8 -9
  146. data/lib/aia/verification_network.rb +58 -0
  147. data/lib/aia.rb +108 -63
  148. data/mkdocs.yml +1 -0
  149. metadata +179 -56
  150. data/justfile +0 -215
  151. data/lib/aia/adapter/chat_execution.rb +0 -242
  152. data/lib/aia/adapter/error_handler.rb +0 -68
  153. data/lib/aia/adapter/gem_activator.rb +0 -57
  154. data/lib/aia/adapter/mcp_connector.rb +0 -274
  155. data/lib/aia/adapter/modality_handlers.rb +0 -167
  156. data/lib/aia/adapter/model_registry.rb +0 -81
  157. data/lib/aia/adapter/multi_model_chat.rb +0 -218
  158. data/lib/aia/adapter/provider_configurator.rb +0 -59
  159. data/lib/aia/adapter/tool_filter.rb +0 -85
  160. data/lib/aia/adapter/tool_loader.rb +0 -90
  161. data/lib/aia/chat_processor_service.rb +0 -178
  162. data/lib/aia/prompt_pipeline.rb +0 -183
  163. data/lib/aia/ruby_llm_adapter.rb +0 -95
  164. data/lib/extensions/openstruct_merge.rb +0 -48
  165. data/lib/extensions/ruby_llm/.irbrc +0 -56
  166. data/lib/extensions/ruby_llm/modalities.rb +0 -36
  167. data/lib/extensions/ruby_llm/provider_fix.rb +0 -79
  168. data/lib/refinements/string.rb +0 -16
  169. data/main.just +0 -76
data/lib/aia/utility.rb CHANGED
@@ -1,118 +1,165 @@
1
1
  # lib/aia/utility.rb
2
2
 
3
- require 'word_wrapper' # Pure ruby word wrapping
3
+ require 'word_wrapper'
4
+ require 'simple_flow'
5
+ require 'trak_flow'
6
+ require 'tty-table'
7
+ begin
8
+ require 'ruby_llm/mcp'
9
+ rescue LoadError, StandardError
10
+ # ruby_llm-mcp may not be installed
11
+ end
12
+
13
+ require_relative 'mcp_utility'
14
+ require_relative 'tool_utility'
4
15
 
5
16
  module AIA
6
- class Utility
17
+ module Utility
7
18
  class << self
8
- def tools?
9
- AIA.config&.tool_names && !AIA.config.tool_names.empty?
19
+ include MCPUtility
20
+ include ToolUtility
21
+
22
+ def models_last_refresh
23
+ aia_dir = AIA.config&.paths&.aia_dir
24
+ return nil if aia_dir.nil?
25
+ models_file = File.join(File.expand_path(aia_dir), 'models.json')
26
+ File.mtime(models_file).strftime('%Y-%m-%d %H:%M')
27
+ rescue Errno::ENOENT
28
+ nil
10
29
  end
11
30
 
12
- def user_tools?
13
- AIA.config&.tools&.paths && !AIA.config.tools.paths.empty?
31
+ def build_crew_line
32
+ names = robot_names
33
+ return '' if names.empty?
34
+ "Today's crew: #{format_crew_mentions(names)}"
14
35
  end
15
36
 
16
- def mcp_servers?
17
- AIA.config&.mcp_servers && !AIA.config.mcp_servers.empty?
37
+ def robot
38
+ art = [
39
+ ' , ,',
40
+ ' (\____/)',
41
+ ' (_oo_)',
42
+ ' (O)',
43
+ ' __|||__ \)',
44
+ ' [/ Tobor \] /',
45
+ ' / \_______/ \/',
46
+ ' / /___\\',
47
+ ' (\ /_____\\',
48
+ ' ::: :::',
49
+ ' ::: :::'
50
+ ].join("\n")
51
+
52
+ art_width = art.lines.map(&:length).max
53
+ info_width = [TTY::Screen.width - art_width - 4, 30].max
54
+ label_width = 7 # "Models:" is the widest label
55
+ value_width = [info_width - label_width - 1, 20].max
56
+
57
+ header = "AIA v#{AIA::VERSION} is Online"
58
+
59
+ inner = TTY::Table.new(banner_detail_rows)
60
+ details = inner.render(:basic, multiline: true,
61
+ column_widths: [label_width, value_width],
62
+ alignments: %i[right left])
63
+
64
+ outer = TTY::Table.new([[art, "#{header}\n\n#{details}"]])
65
+ puts "\n#{outer.render(:basic, multiline: true, column_widths: [art_width, info_width], padding: [0, 1, 0, 0])}"
18
66
  end
19
67
 
20
- # Returns only successfully connected MCP server names
21
- def mcp_server_names
22
- # Use connected_mcp_servers if available (populated during MCP setup)
23
- connected = AIA.config&.connected_mcp_servers
24
- return connected if connected && !connected.empty?
68
+ private
25
69
 
26
- # Fallback to configured servers if connection status not yet known
27
- return [] unless mcp_servers?
28
- AIA.config.mcp_servers.map { |s| s[:name] || s["name"] }.compact
70
+ def robot_names
71
+ client = AIA.client
72
+ return [] unless client
73
+ client.crew.map(&:name)
29
74
  end
30
75
 
31
- # Returns true if there are any connected MCP servers
32
- def connected_mcp_servers?
33
- connected = AIA.config&.connected_mcp_servers
34
- connected && !connected.empty?
76
+ def format_crew_mentions(names)
77
+ names.map { |n| "@#{n.downcase}" }.join(', ')
35
78
  end
36
79
 
37
- # Returns list of failed MCP servers with their errors
38
- def failed_mcp_servers
39
- AIA.config&.failed_mcp_servers || []
80
+ def banner_detail_rows
81
+ [
82
+ ['Models:', banner_models],
83
+ ['DB:', banner_db],
84
+ ['Libs:', banner_libs],
85
+ ['Tools:', banner_tools],
86
+ ['MCP:', banner_mcp],
87
+ ['Crew:', banner_crew]
88
+ ]
40
89
  end
41
90
 
42
- def supports_tools?
43
- AIA.client&.model&.supports_functions? || false
91
+ def banner_models
92
+ models = AIA.config&.models
93
+ return 'unknown-model' if models.nil? || models.empty?
94
+ models.map do |spec|
95
+ case spec
96
+ when AIA::ModelSpec then spec.name
97
+ when Hash then spec[:name] || spec['name'] || spec.to_s
98
+ else spec.to_s
99
+ end
100
+ end.join(', ')
44
101
  end
45
102
 
46
- # Returns the last refresh date from models.json modification time
47
- def models_last_refresh
48
- aia_dir = AIA.config&.paths&.aia_dir
49
- return nil if aia_dir.nil?
50
-
51
- models_file = File.join(File.expand_path(aia_dir), 'models.json')
52
- return nil unless File.exist?(models_file)
53
-
54
- File.mtime(models_file).strftime('%Y-%m-%d %H:%M')
103
+ def banner_libs
104
+ parts = ["ruby_llm v#{RubyLLM::VERSION}"]
105
+ parts << "ruby_llm-mcp v#{RubyLLM::MCP::VERSION}" if defined?(RubyLLM::MCP::VERSION)
106
+ parts << "robot_lab v#{RobotLab::VERSION}" if defined?(RobotLab::VERSION)
107
+ parts << "simple_flow v#{SimpleFlow::VERSION}" if defined?(SimpleFlow::VERSION)
108
+ parts << "trak_flow v#{TrakFlow::VERSION}" if defined?(TrakFlow::VERSION)
109
+ parts << "typed_bus v#{TypedBus::VERSION}" if defined?(TypedBus::VERSION)
110
+ parts.join(', ')
55
111
  end
56
112
 
57
- # Displays the AIA robot ASCII art
58
- # Yes, its slightly frivolous but it does contain some
59
- # useful configuration information.
60
- def robot
61
- indent = 18
62
- spaces = " "*indent
63
- width = TTY::Screen.width - indent - 2
64
-
65
- mcp_version = defined?(RubyLLM::MCP::VERSION) ? " MCP v" + RubyLLM::MCP::VERSION : ''
66
-
67
- # Extract model names from config (handles ModelSpec objects or Hashes)
68
- model_display = if AIA.config&.models && !AIA.config.models.empty?
69
- models = AIA.config.models
70
- models.map do |spec|
71
- if spec.is_a?(AIA::ModelSpec)
72
- spec.name
73
- elsif spec.is_a?(Hash)
74
- spec[:name] || spec['name'] || spec.to_s
75
- else
76
- spec.to_s
77
- end
78
- end.join(', ')
113
+ def banner_tools
114
+ count = total_tool_count
115
+ if count.positive?
116
+ "#{count} #{count == 1 ? 'tool' : 'tools'} loaded"
79
117
  else
80
- 'unknown-model'
118
+ 'none loaded'
81
119
  end
120
+ end
82
121
 
83
- # Build MCP line based on connection status
84
- mcp_line = if !mcp_servers?
85
- '' # No MCP servers configured
86
- elsif connected_mcp_servers?
87
- "MCP: #{mcp_server_names.join(', ')}"
88
- else
89
- "MCP: (none connected)"
90
- end
122
+ def banner_mcp
123
+ connected = mcp_client_labels
124
+ failed = failed_mcp_servers.map { |f| server_name(f) }
125
+ return '(none configured)' if connected.empty? && failed.empty?
126
+ parts = []
127
+ parts << connected.join(', ') unless connected.empty?
128
+ parts << "FAILED: #{failed.join(', ')}" unless failed.empty?
129
+ parts.join(' | ')
130
+ end
91
131
 
92
- puts <<-ROBOT
93
-
94
- , ,
95
- (\\____/) AI Assistant (v#{AIA::VERSION}) is Online
96
- (_oo_) #{model_display}#{supports_tools? ? ' (supports tools)' : ''}
97
- (O) using ruby_llm (v#{RubyLLM::VERSION}#{mcp_version})
98
- __||__ \\) model db was last refreshed on
99
- [/______\\] / #{models_last_refresh || 'unknown'}
100
- / \\__AI__/ \\/ #{user_tools? ? 'I will also use your tools' : (tools? ? 'You can share my tools' : 'I did not bring any tools')}
101
- / /__\\ #{mcp_line}
102
- (\\ /____\\ #{user_tools? && tools? ? 'My Toolbox contains:' : ''}
103
- ROBOT
104
- if user_tools? && tools?
105
- tool_names = AIA.config.tool_names
106
- if tool_names && !tool_names.to_s.empty?
107
- puts WordWrapper::MinimumRaggedness.new(
108
- width,
109
- tool_names.to_s # String of tool names, comma separated
110
- ).wrap
111
- .split("\n")
112
- .map{|s| spaces+s+"\n"}
113
- .join
132
+ # Post-connection: connected_mcp_servers is the authoritative name list;
133
+ # mcp_server_tool_counts provides per-server tool counts.
134
+ # Pre-connection fallback: read live from RubyLLM::MCP.clients (--require clients).
135
+ def mcp_client_labels
136
+ return [] if AIA.config&.flags&.no_mcp
137
+
138
+ connected = AIA.config&.connected_mcp_servers
139
+ unless connected.nil?
140
+ counts = AIA.config&.mcp_server_tool_counts || {}
141
+ return connected.map do |name|
142
+ count = counts[name]
143
+ count ? "#{name}(#{count})" : name
114
144
  end
115
145
  end
146
+
147
+ return [] unless defined?(RubyLLM::MCP)
148
+ RubyLLM::MCP.clients.filter_map do |name, client|
149
+ count = client.tools.count rescue nil
150
+ "#{name}(#{count})" if count
151
+ end
152
+ end
153
+
154
+ def banner_db
155
+ refresh = models_last_refresh
156
+ refresh ? "refreshed #{refresh.gsub(' ', ' at ')}" : 'not yet refreshed'
157
+ end
158
+
159
+ def banner_crew
160
+ names = robot_names
161
+ return '' if names.empty?
162
+ format_crew_mentions(names)
116
163
  end
117
164
  end
118
165
  end
@@ -1,15 +1,14 @@
1
- # lib/aia/history_manager.rb
1
+ # lib/aia/variable_input_collector.rb
2
2
 
3
3
  require 'json'
4
4
  require 'fileutils'
5
5
 
6
6
  module AIA
7
- class HistoryManager
7
+ class VariableInputCollector
8
8
  def initialize
9
9
  # No prompt dependency — just handles user input for parameter collection
10
10
  end
11
11
 
12
-
13
12
  def request_variable_value(variable_name:, default_value: nil)
14
13
  Reline::HISTORY.clear
15
14
 
@@ -28,24 +27,24 @@ module AIA
28
27
 
29
28
  if input.nil? # Ctrl+D
30
29
  return default_value if default_value
31
- puts "\nParameter '#{variable_name}' is required."
32
- exit(1)
30
+ raise AIA::Error, "Parameter '#{variable_name}' is required."
33
31
  end
34
32
 
35
33
  value = input.strip
36
34
  if value.empty?
37
35
  return default_value if default_value
38
- puts "Parameter '#{variable_name}' is required."
39
- exit(1)
36
+ raise AIA::Error, "Parameter '#{variable_name}' is required."
40
37
  end
41
38
 
42
39
  value
43
40
  rescue Interrupt
44
- puts "\nVariable input interrupted."
45
- exit(1)
41
+ raise AIA::Error, "Variable input interrupted."
46
42
  ensure
47
43
  Reline.line_editor.prompt_proc = original_prompt_proc
48
44
  end
49
45
  end
50
46
  end
47
+
48
+ # Backward-compatibility alias — prefer VariableInputCollector in new code.
49
+ HistoryManager = VariableInputCollector
51
50
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # lib/aia/verification_network.rb
4
+ #
5
+ # Self-review via consensus: two robots independently answer,
6
+ # then a reconciler compares and produces a verified answer.
7
+
8
+ module AIA
9
+ class VerificationNetwork
10
+ # Build a verification network for the given config.
11
+ #
12
+ # @param config the AIA configuration
13
+ # @return [RobotLab::Network] a consensus network with verifiers + reconciler
14
+ def self.build(config)
15
+ model = config.models.first
16
+ run_config = RobotFactory.build_run_config(config)
17
+ tools = ToolLoader.filtered_tools(config)
18
+ mcp = RobotFactory.mcp_server_configs(config)
19
+ model_id = model.internal_id
20
+
21
+ # rubocop:disable Metrics/BlockLength
22
+ RobotLab.create_network(name: "aia-verification") do
23
+ worker_a = RobotLab.build(
24
+ name: "verifier-a",
25
+ model: model_id,
26
+ system_prompt: "Answer the following question. Be thorough and precise.",
27
+ local_tools: tools,
28
+ mcp_servers: mcp,
29
+ config: run_config
30
+ )
31
+ task :verify_a, worker_a, depends_on: :none
32
+
33
+ worker_b = RobotLab.build(
34
+ name: "verifier-b",
35
+ model: model_id,
36
+ system_prompt: "Answer the following question independently. " \
37
+ "Focus on correctness and completeness.",
38
+ local_tools: tools,
39
+ mcp_servers: mcp,
40
+ config: run_config
41
+ )
42
+ task :verify_b, worker_b, depends_on: :none
43
+
44
+ reconciler = RobotLab.build(
45
+ name: "reconciler",
46
+ model: model_id,
47
+ system_prompt: "Compare two independent answers to the same question. " \
48
+ "Identify agreements and disagreements. " \
49
+ "Produce a final, reconciled answer that is most accurate. " \
50
+ "Note any areas of uncertainty.",
51
+ config: run_config
52
+ )
53
+ task :reconcile, reconciler, depends_on: %i[verify_a verify_b]
54
+ end
55
+ # rubocop:enable Metrics/BlockLength
56
+ end
57
+ end
58
+ end
data/lib/aia.rb CHANGED
@@ -1,65 +1,96 @@
1
1
  # lib/aia.rb
2
2
  #
3
- # This is the main entry point for the AIA application.
4
- # The AIA module serves as the namespace for the AIA application, which
5
- # provides an interface for interacting with AI models and managing prompts.
6
-
7
- require 'ruby_llm'
8
- require 'ruby_llm/mcp'
9
-
10
- # NOTE: Complex parameters are now supported natively in ruby_llm-mcp >= 0.8.0
11
- # The support_complex_parameters! method has been removed.
3
+ # Main entry point for the AIA application (v2).
4
+ # AIA is a thin CLI shell orchestrating robot_lab robots.
12
5
 
6
+ gem 'bigdecimal', '>= 4.0'
7
+ require 'bigdecimal'
8
+ require 'robot_lab'
13
9
  require 'pm'
14
10
 
15
-
16
- require 'debug_me'
17
- include DebugMe
18
- $DEBUG_ME = false
19
- DebugMeDefaultOptions[:skip1] = true
20
-
21
- require_relative 'extensions/openstruct_merge' # adds self.merge self.get_value
22
- require_relative 'extensions/ruby_llm/modalities' # adds model.modalities.text_to_text? etc.
23
-
24
- require_relative 'refinements/string' # adds #include_any? #include_all?
11
+ require_relative 'aia/patches/ruby_llm_tool_error'
12
+ require_relative 'aia/patches/ruby_llm_streaming_error'
25
13
 
26
14
  require_relative 'aia/errors'
15
+ require_relative 'aia/turn_state'
16
+ require_relative 'aia/content_extractor'
27
17
  require_relative 'aia/utility'
28
18
  require_relative 'aia/skill_utils'
29
19
  require_relative 'aia/version'
30
20
  require_relative 'aia/config'
31
21
  require_relative 'aia/logger'
32
22
 
33
- # Top-level logger method available anywhere in the application
34
- def logger
35
- AIA::LoggerManager.aia_logger
36
- end
37
-
38
23
  require_relative 'aia/config/cli_parser'
39
24
  require_relative 'aia/config/validator'
40
25
  require_relative 'aia/prompt_handler'
41
- require_relative 'aia/ruby_llm_adapter'
26
+ require_relative 'aia/tool_loader'
27
+ require_relative 'aia/plugin_loader'
28
+ require_relative 'aia/plugin_monitor'
29
+ require_relative 'aia/spawn_spec_parser'
30
+ require_relative 'aia/crew'
31
+ require_relative 'aia/tools/recruit_robot_tool'
32
+ require_relative 'aia/tools/reskill_robot_tool'
33
+ require_relative 'aia/system_prompt_assembler'
34
+ require_relative 'aia/mcp_config_normalizer'
35
+ require_relative 'aia/network_memory_manager'
36
+ require_relative 'aia/robot_factory'
37
+ require_relative 'aia/robot_builder'
42
38
  require_relative 'aia/directive_processor'
43
- require_relative 'aia/history_manager'
39
+ require_relative 'aia/variable_input_collector'
44
40
  require_relative 'aia/ui_presenter'
45
- require_relative 'aia/chat_processor_service'
46
41
  require_relative 'aia/input_collector'
47
- require_relative 'aia/prompt_pipeline'
42
+ require_relative 'aia/handler_context'
43
+ require_relative 'aia/handler_protocol'
44
+ require_relative 'aia/model_alias_registry'
45
+ require_relative 'aia/model_switch_handler'
46
+ require_relative 'aia/mcp_discovery'
47
+ require_relative 'aia/mcp_grouper'
48
+ require_relative 'aia/cost_calculator'
49
+ require_relative 'aia/session_tracker'
50
+ require_relative 'aia/similarity_scorer'
51
+ require_relative 'aia/fact_asserter'
52
+ require_relative 'aia/tool_filter'
53
+ require_relative 'aia/tool_filter_registry'
54
+ require_relative 'aia/tool_filter_strategy'
55
+ require_relative 'aia/streaming_runner'
56
+ require_relative 'aia/mention_router'
57
+ require_relative 'aia/special_mode_handler'
58
+ require_relative 'aia/mcp_connection_manager'
59
+ require_relative 'aia/startup_coordinator'
60
+ require_relative 'aia/pipeline_orchestrator'
48
61
  require_relative 'aia/chat_loop'
49
62
  require_relative 'aia/session'
50
63
 
51
64
  # The AIA module serves as the namespace for the AIA application, which
52
65
  # provides an interface for interacting with AI models and managing prompts.
53
66
  module AIA
67
+ require 'debug_me'
68
+ include DebugMe
69
+
70
+ $DEBUG_ME = false
71
+ DebugMeDefaultOptions[:skip1] = true
72
+
54
73
  at_exit do
55
- warn 'Exiting AIA application...'
74
+ $stderr.puts 'Exiting AIA application...'
56
75
  end
57
76
 
58
77
  @config = nil
59
78
  @client = nil
79
+ @session_tracker = nil
80
+ @turn_state = TurnState.new
60
81
 
61
82
  class << self
62
- attr_accessor :config, :client
83
+ attr_accessor :config, :client, :session_tracker, :turn_state, :task_coordinator
84
+
85
+ def logger
86
+ AIA::LoggerManager.aia_logger
87
+ end
88
+
89
+ def reset!
90
+ @config = @client = @session_tracker = @turn_state =
91
+ @task_coordinator = nil
92
+ ToolLoader.reset_instance!
93
+ end
63
94
 
64
95
  def good_file?(filename)
65
96
  File.exist?(filename) &&
@@ -67,78 +98,92 @@ module AIA
67
98
  !File.directory?(filename)
68
99
  end
69
100
 
70
-
71
101
  def bad_file?(filename)
72
102
  !good_file?(filename)
73
103
  end
74
104
 
75
-
76
105
  # Convenience flag accessors (delegate to config.flags section)
77
106
  def chat?
78
107
  @config&.flags&.chat == true
79
108
  end
80
109
 
81
-
82
110
  def debug?
83
111
  @config&.flags&.debug == true
84
112
  end
85
113
 
114
+ # Emit a warning always; if --debug is active and an exception is given,
115
+ # also print the first 5 backtrace frames so errors are diagnosable.
116
+ #
117
+ # @param msg [String]
118
+ # @param exc [Exception, nil]
119
+ def debug_warn(msg, exc: nil)
120
+ $stderr.puts msg
121
+ return unless exc && exc.backtrace && config&.flags&.debug
122
+ $stderr.puts exc.backtrace.first(5).join("\n")
123
+ end
86
124
 
87
125
  def verbose?
88
126
  @config&.flags&.verbose == true
89
127
  end
90
128
 
91
-
92
129
  def fuzzy?
93
130
  @config&.flags&.fuzzy == true
94
131
  end
95
132
 
96
-
97
133
  def speak?
98
134
  @config&.flags&.speak == true
99
135
  end
100
136
 
137
+ def paint?
138
+ @config&.flags&.paint == true
139
+ end
101
140
 
102
141
  def append?
103
142
  @config&.output&.append == true
104
143
  end
105
144
 
106
-
107
145
  def run
108
- # Parse CLI arguments
109
146
  cli_overrides = CLIParser.parse
110
-
111
- # Create config with CLI overrides
112
147
  @config = Config.setup(cli_overrides)
148
+ return if ConfigValidator.tailor(@config) == :early_exit
149
+
150
+ PluginLoader.load!(@config)
151
+ start_plugin_monitor
152
+ RobotFactory.setup(@config)
153
+ load_fzf_integration
154
+ start_session
155
+ rescue AIA::ConfigurationError => e
156
+ $stderr.puts e.message
157
+ exit 1
158
+ rescue AIA::Error => e
159
+ $stderr.puts e.message
160
+ exit 1
161
+ end
113
162
 
114
- # Validate and tailor configuration (handles --dump early exit)
115
- ConfigValidator.tailor(@config)
116
-
117
- # Load Fzf if fuzzy search is enabled and fzf is installed
118
- if @config.flags.fuzzy
119
- begin
120
- if system('which fzf >/dev/null 2>&1')
121
- require_relative 'aia/fzf'
122
- else
123
- warn 'Warning: Fuzzy search enabled but fzf not found. Install fzf for enhanced search capabilities.'
124
- end
125
- rescue StandardError => e
126
- warn "Warning: Failed to load fzf: #{e.message}"
127
- end
128
- end
163
+ private
129
164
 
130
- prompt_handler = PromptHandler.new
165
+ def start_plugin_monitor
166
+ return unless @config.flags.chat
131
167
 
132
- @client = RubyLLMAdapter.new
168
+ @plugin_monitor = PluginMonitor.new(@config).start
169
+ at_exit { @plugin_monitor&.stop }
170
+ end
133
171
 
134
- # There are two kinds of sessions: batch and chat
135
- # A chat session is started when the --chat CLI option is used
136
- # BUT its also possible to start a chat session with an initial prompt AND
137
- # within that initial prompt there can be a workflow (aka pipeline)
138
- # defined. If that is the case, then the chat session will not start
139
- # until the initial prompt has completed its workflow.
172
+ def load_fzf_integration
173
+ return unless @config.flags.fuzzy
174
+
175
+ if system('which fzf >/dev/null 2>&1')
176
+ require_relative 'aia/fzf'
177
+ else
178
+ $stderr.puts 'Warning: Fuzzy search enabled but fzf not found. Install fzf for enhanced search capabilities.'
179
+ end
180
+ rescue StandardError => e
181
+ $stderr.puts "Warning: Failed to load fzf: #{e.message}"
182
+ end
140
183
 
141
- session = Session.new(prompt_handler)
184
+ def start_session
185
+ session = Session.new(PromptHandler.new)
186
+ at_exit { session.cleanup }
142
187
  session.start
143
188
  end
144
189
  end
data/mkdocs.yml CHANGED
@@ -151,6 +151,7 @@ nav:
151
151
  - Getting Started: guides/getting-started.md
152
152
  - Chat Mode: guides/chat.md
153
153
  - Working with Models: guides/models.md
154
+ - Crews: guides/crew.md
154
155
  - Local Models: guides/local-models.md
155
156
  - Available Models: guides/available-models.md
156
157
  - Image Generation: guides/image-generation.md