rubyn-code 0.5.1 → 0.7.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.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +120 -3
  3. data/db/migrations/014_multi_agent_upgrade.rb +79 -0
  4. data/lib/rubyn_code/agent/conversation.rb +89 -3
  5. data/lib/rubyn_code/agent/llm_caller.rb +2 -2
  6. data/lib/rubyn_code/agent/loop.rb +49 -9
  7. data/lib/rubyn_code/agent/system_prompt_builder.rb +37 -2
  8. data/lib/rubyn_code/agent/tool_processor.rb +3 -1
  9. data/lib/rubyn_code/auth/oauth.rb +1 -1
  10. data/lib/rubyn_code/auth/token_store.rb +49 -4
  11. data/lib/rubyn_code/checkpoint/hook.rb +26 -0
  12. data/lib/rubyn_code/checkpoint/manager.rb +109 -0
  13. data/lib/rubyn_code/chisel/debt.rb +65 -0
  14. data/lib/rubyn_code/chisel/inspection.rb +93 -0
  15. data/lib/rubyn_code/chisel.rb +127 -0
  16. data/lib/rubyn_code/cli/commands/agents.rb +31 -0
  17. data/lib/rubyn_code/cli/commands/chisel.rb +52 -0
  18. data/lib/rubyn_code/cli/commands/chisel_audit.rb +19 -0
  19. data/lib/rubyn_code/cli/commands/chisel_debt.rb +28 -0
  20. data/lib/rubyn_code/cli/commands/chisel_gain.rb +30 -0
  21. data/lib/rubyn_code/cli/commands/chisel_review.rb +19 -0
  22. data/lib/rubyn_code/cli/commands/command_template.rb +50 -0
  23. data/lib/rubyn_code/cli/commands/context.rb +3 -1
  24. data/lib/rubyn_code/cli/commands/custom_command.rb +42 -0
  25. data/lib/rubyn_code/cli/commands/custom_loader.rb +69 -0
  26. data/lib/rubyn_code/cli/commands/goal.rb +87 -0
  27. data/lib/rubyn_code/cli/commands/learning.rb +62 -0
  28. data/lib/rubyn_code/cli/commands/loop.rb +58 -0
  29. data/lib/rubyn_code/cli/commands/mcp.rb +18 -5
  30. data/lib/rubyn_code/cli/commands/megaplan.rb +1 -1
  31. data/lib/rubyn_code/cli/commands/registry.rb +14 -9
  32. data/lib/rubyn_code/cli/commands/rewind.rb +65 -0
  33. data/lib/rubyn_code/cli/first_run.rb +1 -1
  34. data/lib/rubyn_code/cli/loop_runner.rb +98 -0
  35. data/lib/rubyn_code/cli/mention_expander.rb +92 -0
  36. data/lib/rubyn_code/cli/renderer.rb +3 -2
  37. data/lib/rubyn_code/cli/repl.rb +37 -14
  38. data/lib/rubyn_code/cli/repl_commands.rb +76 -2
  39. data/lib/rubyn_code/cli/repl_setup.rb +9 -1
  40. data/lib/rubyn_code/cli/stream_formatter.rb +3 -2
  41. data/lib/rubyn_code/cli/version_check.rb +10 -3
  42. data/lib/rubyn_code/config/defaults.rb +13 -1
  43. data/lib/rubyn_code/config/schema.json +4 -0
  44. data/lib/rubyn_code/config/settings.rb +17 -2
  45. data/lib/rubyn_code/context/manager.rb +29 -12
  46. data/lib/rubyn_code/debug.rb +11 -5
  47. data/lib/rubyn_code/goal/evaluator.rb +95 -0
  48. data/lib/rubyn_code/hooks/event_map.rb +56 -0
  49. data/lib/rubyn_code/hooks/external_dispatcher.rb +199 -0
  50. data/lib/rubyn_code/hooks/goal_hook.rb +88 -0
  51. data/lib/rubyn_code/hooks/response.rb +83 -0
  52. data/lib/rubyn_code/hooks/runner.rb +61 -3
  53. data/lib/rubyn_code/hooks/settings_json_loader.rb +109 -0
  54. data/lib/rubyn_code/hooks/subprocess_executor.rb +116 -0
  55. data/lib/rubyn_code/ide/handlers/plan_interview_answer_handler.rb +13 -13
  56. data/lib/rubyn_code/ide/handlers/plan_interview_cancel_handler.rb +1 -1
  57. data/lib/rubyn_code/ide/handlers/plan_interview_start_handler.rb +10 -10
  58. data/lib/rubyn_code/ide/handlers/plan_propose_handler.rb +1 -1
  59. data/lib/rubyn_code/ide/handlers/prompt_handler.rb +9 -1
  60. data/lib/rubyn_code/ide/handlers/recover_ci_handler.rb +27 -16
  61. data/lib/rubyn_code/ide/handlers/session_resume_handler.rb +1 -1
  62. data/lib/rubyn_code/index/codebase_index.rb +39 -1
  63. data/lib/rubyn_code/learning/porter.rb +129 -0
  64. data/lib/rubyn_code/llm/adapters/anthropic.rb +65 -16
  65. data/lib/rubyn_code/llm/adapters/openai.rb +1 -1
  66. data/lib/rubyn_code/llm/adapters/prompt_caching.rb +5 -1
  67. data/lib/rubyn_code/llm/adapters/token_caching.rb +54 -0
  68. data/lib/rubyn_code/llm/model_router.rb +2 -2
  69. data/lib/rubyn_code/mcp/client.rb +59 -0
  70. data/lib/rubyn_code/mcp/server_extras_bridge.rb +110 -0
  71. data/lib/rubyn_code/mcp/sse_transport.rb +2 -1
  72. data/lib/rubyn_code/mcp/tool_bridge.rb +16 -14
  73. data/lib/rubyn_code/megaplan/ci_recovery.rb +3 -3
  74. data/lib/rubyn_code/megaplan/interview_session.rb +8 -3
  75. data/lib/rubyn_code/megaplan/plan_proposer.rb +3 -3
  76. data/lib/rubyn_code/memory/search.rb +9 -5
  77. data/lib/rubyn_code/memory/session_persistence.rb +159 -21
  78. data/lib/rubyn_code/observability/cost_calculator.rb +3 -1
  79. data/lib/rubyn_code/output/diff_renderer.rb +62 -7
  80. data/lib/rubyn_code/skills/auto_suggest.rb +70 -2
  81. data/lib/rubyn_code/skills/registry_client.rb +4 -3
  82. data/lib/rubyn_code/sub_agents/agent_type.rb +17 -0
  83. data/lib/rubyn_code/sub_agents/catalog.rb +124 -0
  84. data/lib/rubyn_code/teams/agent_registry.rb +120 -0
  85. data/lib/rubyn_code/teams/mailbox.rb +99 -10
  86. data/lib/rubyn_code/teams/manager.rb +83 -5
  87. data/lib/rubyn_code/teams/teammate.rb +5 -1
  88. data/lib/rubyn_code/tools/ask_user.rb +15 -1
  89. data/lib/rubyn_code/tools/executor.rb +5 -3
  90. data/lib/rubyn_code/tools/spawn_agent.rb +47 -62
  91. data/lib/rubyn_code/tools/spawn_teammate.rb +7 -2
  92. data/lib/rubyn_code/tools/web_fetch.rb +1 -1
  93. data/lib/rubyn_code/tools/web_search.rb +4 -1
  94. data/lib/rubyn_code/version.rb +1 -1
  95. data/lib/rubyn_code.rb +45 -2
  96. data/skills/rubyn_self_test.md +322 -14
  97. data/skills/self_test/chisel_smoke.rb +84 -0
  98. data/skills/self_test/fixtures/chisel_sample.rb +64 -0
  99. metadata +37 -1
@@ -9,8 +9,9 @@ module RubynCode
9
9
  TOOL_NAME = 'spawn_agent'
10
10
  DESCRIPTION = 'Spawn an isolated sub-agent to handle a task. The sub-agent gets its own ' \
11
11
  "fresh context, works independently, and returns only a summary. Use 'explore' " \
12
- "type for research/reading, 'worker' type for writing code/files. The sub-agent " \
13
- 'shares the filesystem but not your conversation.'
12
+ "type for research/reading, 'worker' type for writing code/files, or the name " \
13
+ 'of any custom agent defined in .rubyn-code/agents/. The sub-agent shares the ' \
14
+ 'filesystem but not your conversation.'
14
15
  PARAMETERS = {
15
16
  prompt: {
16
17
  type: :string,
@@ -19,76 +20,73 @@ module RubynCode
19
20
  },
20
21
  agent_type: {
21
22
  type: :string,
22
- description: "Type of agent: 'explore' (read-only) or 'worker' (full write access). Default: explore",
23
- required: false,
24
- enum: %w[explore worker]
23
+ description: "Agent type: 'explore' (read-only), 'worker' (full write access), or a " \
24
+ 'custom agent name from .rubyn-code/agents/. Default: explore',
25
+ required: false
25
26
  }
26
27
  }.freeze
27
28
  RISK_LEVEL = :execute
28
29
 
30
+ READ_TOOLS = %w[read_file glob grep bash load_skill memory_search].freeze
31
+ BLOCKED_TOOLS = %w[spawn_agent send_message read_inbox compact memory_write].freeze
32
+
29
33
  # These get injected by the executor or the REPL
30
34
  attr_writer :llm_client, :on_status
31
35
 
32
36
  def execute(prompt:, agent_type: 'explore')
33
- type = agent_type.to_sym
37
+ agent = resolve_agent(agent_type)
34
38
  callback = @on_status || method(:default_status)
35
39
  @tool_count = 0
36
40
 
37
- callback.call(:started, "Spawning #{type} agent...")
41
+ callback.call(:started, "Spawning #{agent.name} agent...")
38
42
 
39
- tools = tools_for_type(type)
40
- result, hit_limit = run_sub_agent(
41
- prompt: prompt, tools: tools, type: type, callback: callback
42
- )
43
+ tools = tools_for(agent)
44
+ result, hit_limit = run_sub_agent(prompt: prompt, tools: tools, agent: agent, callback: callback)
43
45
 
44
46
  callback.call(:done, "Agent finished (#{@tool_count} tool calls).")
45
47
 
46
48
  summary = RubynCode::SubAgents::Summarizer.call(result, max_length: 3000)
47
- format_agent_result(type, summary, hit_limit)
49
+ format_agent_result(agent.name, summary, hit_limit)
48
50
  end
49
51
 
50
52
  private
51
53
 
52
- def format_agent_result(type, summary, hit_limit)
54
+ # Resolve the requested type via the catalog, falling back to explore
55
+ # for an unknown name so a typo degrades gracefully instead of erroring.
56
+ def resolve_agent(agent_type)
57
+ catalog = RubynCode::SubAgents::Catalog.new(project_root: project_root)
58
+ catalog.get(agent_type) || catalog.get('explore')
59
+ end
60
+
61
+ def format_agent_result(name, summary, hit_limit)
53
62
  if hit_limit
54
- "## Sub-Agent Result (#{type}) — INCOMPLETE (reached #{@tool_count} tool calls)\n\n" \
63
+ "## Sub-Agent Result (#{name}) — INCOMPLETE (reached #{@tool_count} tool calls)\n\n" \
55
64
  'The sub-agent ran out of turns before finishing. Here is what it accomplished so far:' \
56
65
  "\n\n#{summary}"
57
66
  else
58
- "## Sub-Agent Result (#{type})\n\n#{summary}"
59
- end
60
- end
61
-
62
- def max_iterations_for(type)
63
- if type == :explore
64
- Config::Defaults::MAX_EXPLORE_AGENT_ITERATIONS
65
- else
66
- Config::Defaults::MAX_SUB_AGENT_ITERATIONS
67
+ "## Sub-Agent Result (#{name})\n\n#{summary}"
67
68
  end
68
69
  end
69
70
 
70
71
  # Returns [result_text, hit_limit] tuple
71
- def run_sub_agent(prompt:, tools:, type:, callback:)
72
+ def run_sub_agent(prompt:, tools:, agent:, callback:)
72
73
  conversation = RubynCode::Agent::Conversation.new
73
74
  conversation.add_user_message(prompt)
74
75
 
75
- max_iterations = max_iterations_for(type)
76
76
  iteration = 0
77
77
  last_text = nil
78
78
 
79
79
  loop do
80
- return finish_at_limit(conversation, type, last_text) if iteration >= max_iterations
80
+ return finish_at_limit(conversation, agent, last_text) if iteration >= agent.max_iterations
81
81
 
82
- last_text, done = process_iteration(
83
- conversation, tools, type, callback, last_text
84
- )
82
+ last_text, done = process_iteration(conversation, tools, agent, callback, last_text)
85
83
  return [last_text || '', false] if done
86
84
 
87
85
  iteration += 1
88
86
  end
89
87
  end
90
88
 
91
- def finish_at_limit(conversation, type, last_text)
89
+ def finish_at_limit(conversation, agent, last_text)
92
90
  conversation.add_user_message(
93
91
  'You have reached your turn limit. Summarize everything you found or ' \
94
92
  'accomplished so far. Be thorough — this is your last chance to report back.'
@@ -96,17 +94,17 @@ module RubynCode
96
94
  response = @llm_client.chat(
97
95
  messages: conversation.to_api_format,
98
96
  tools: [],
99
- system: sub_agent_system_prompt(type)
97
+ system: agent.system_prompt
100
98
  )
101
99
  summary = extract_text(response)
102
100
  [summary.empty? ? (last_text || '') : summary, true]
103
101
  end
104
102
 
105
- def process_iteration(conversation, tools, type, callback, last_text)
103
+ def process_iteration(conversation, tools, agent, callback, last_text)
106
104
  response = @llm_client.chat(
107
105
  messages: conversation.to_api_format,
108
106
  tools: tools,
109
- system: sub_agent_system_prompt(type)
107
+ system: agent.system_prompt
110
108
  )
111
109
 
112
110
  content = response_content(response)
@@ -117,22 +115,22 @@ module RubynCode
117
115
  conversation.add_assistant_message(content)
118
116
  return [last_text, true] if tool_calls.empty?
119
117
 
120
- execute_sub_agent_tools(tool_calls, conversation, type, callback)
118
+ execute_sub_agent_tools(tool_calls, conversation, agent, callback)
121
119
  [last_text, false]
122
120
  end
123
121
 
124
- def execute_sub_agent_tools(tool_calls, conversation, type, callback)
122
+ def execute_sub_agent_tools(tool_calls, conversation, agent, callback)
125
123
  tool_calls.each do |tc|
126
124
  name, input, id = extract_tool_call(tc)
127
125
  @tool_count += 1
128
126
  callback.call(:tool, name.to_s)
129
127
 
130
- run_single_tool(name, input, id, conversation, type)
128
+ run_single_tool(name, input, id, conversation, agent)
131
129
  end
132
130
  end
133
131
 
134
- def run_single_tool(name, input, id, conversation, type)
135
- if %w[spawn_agent].include?(name)
132
+ def run_single_tool(name, input, id, conversation, agent)
133
+ if name == 'spawn_agent'
136
134
  conversation.add_tool_result(
137
135
  id, name, 'Error: Sub-agents cannot spawn other agents.', is_error: true
138
136
  )
@@ -140,9 +138,9 @@ module RubynCode
140
138
  end
141
139
 
142
140
  tool_class = RubynCode::Tools::Registry.get(name)
143
- if type == :explore && tool_class.risk_level != :read
141
+ if agent.read_only? && tool_class.risk_level != :read
144
142
  conversation.add_tool_result(
145
- id, name, 'Error: Explore agents can only use read-only tools.', is_error: true
143
+ id, name, "Error: #{agent.name} agents can only use read-only tools.", is_error: true
146
144
  )
147
145
  return
148
146
  end
@@ -154,31 +152,18 @@ module RubynCode
154
152
  conversation.add_tool_result(id, name, "Error: #{e.message}", is_error: true)
155
153
  end
156
154
 
157
- def tools_for_type(type)
155
+ # The tool allowlist: explicit list from a custom agent, else the
156
+ # access-based default (read-only set or everything-minus-blocked).
157
+ def tools_for(agent)
158
158
  all_tools = RubynCode::Tools::Registry.tool_definitions
159
- blocked = %w[spawn_agent send_message read_inbox compact memory_write]
160
-
161
- if type == :explore
162
- read_tools = %w[read_file glob grep bash load_skill memory_search]
163
- all_tools.select { |t| read_tools.include?(t[:name]) }
164
- else
165
- all_tools.reject { |t| blocked.include?(t[:name]) }
166
- end
167
- end
168
159
 
169
- def sub_agent_system_prompt(type)
170
- base = 'You are a Rubyn sub-agent. Complete your task efficiently and ' \
171
- 'return a clear summary of what you found or did.'
172
-
173
- case type
174
- when :explore
175
- "#{base}\nYou have read-only access. Search, read files, and analyze. " \
176
- 'Do NOT attempt to write or modify anything.'
177
- when :worker
178
- "#{base}\nYou have full read/write access. Make the changes needed, " \
179
- 'run tests if appropriate, and report what you did.'
160
+ if agent.tool_names && !agent.tool_names.empty?
161
+ allowed = agent.tool_names - BLOCKED_TOOLS
162
+ all_tools.select { |t| allowed.include?(t[:name]) }
163
+ elsif agent.read_only?
164
+ all_tools.select { |t| READ_TOOLS.include?(t[:name]) }
180
165
  else
181
- base
166
+ all_tools.reject { |t| BLOCKED_TOOLS.include?(t[:name]) }
182
167
  end
183
168
  end
184
169
 
@@ -25,13 +25,18 @@ module RubynCode
25
25
  type: :string,
26
26
  description: 'Initial task or instruction for the teammate',
27
27
  required: true
28
+ },
29
+ parent_agent_id: {
30
+ type: :string,
31
+ description: 'ID of the parent agent spawning this teammate (for lineage tracking)',
32
+ required: false
28
33
  }
29
34
  }.freeze
30
35
  RISK_LEVEL = :execute
31
36
 
32
37
  attr_writer :llm_client, :on_status, :db
33
38
 
34
- def execute(name:, role:, prompt:)
39
+ def execute(name:, role:, prompt:, parent_agent_id: nil)
35
40
  callback = @on_status || method(:default_status)
36
41
 
37
42
  raise Error, 'LLM client not available' unless @llm_client
@@ -40,7 +45,7 @@ module RubynCode
40
45
  mailbox = Teams::Mailbox.new(@db)
41
46
  manager = Teams::Manager.new(@db, mailbox: mailbox)
42
47
 
43
- teammate = manager.spawn(name: name, role: role)
48
+ teammate = manager.spawn(name: name, role: role, parent_agent_id: parent_agent_id)
44
49
  callback.call(:started, "Spawning teammate '#{name}' as #{role}...")
45
50
 
46
51
  Thread.new do
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'faraday'
4
3
  require_relative 'base'
5
4
  require_relative 'registry'
6
5
 
@@ -76,6 +75,7 @@ module RubynCode
76
75
  end
77
76
 
78
77
  def build_connection
78
+ require 'faraday'
79
79
  Faraday.new do |f|
80
80
  f.options.timeout = 30
81
81
  f.options.open_timeout = 10
@@ -3,7 +3,6 @@
3
3
  require 'open3'
4
4
  require 'cgi'
5
5
  require 'json'
6
- require 'faraday'
7
6
  require_relative 'base'
8
7
  require_relative 'registry'
9
8
 
@@ -112,6 +111,7 @@ module RubynCode
112
111
  end
113
112
 
114
113
  def brave_request(query, num_results)
114
+ require 'faraday'
115
115
  Faraday.get('https://api.search.brave.com/res/v1/web/search') do |req|
116
116
  req.params['q'] = query
117
117
  req.params['count'] = num_results
@@ -135,6 +135,7 @@ module RubynCode
135
135
  end
136
136
 
137
137
  def tavily_request(query, num_results)
138
+ require 'faraday'
138
139
  Faraday.post('https://api.tavily.com/search') do |req|
139
140
  req.headers['Content-Type'] = 'application/json'
140
141
  req.body = JSON.generate(
@@ -156,6 +157,7 @@ module RubynCode
156
157
  end
157
158
 
158
159
  def serpapi_request(query, num_results)
160
+ require 'faraday'
159
161
  Faraday.get('https://serpapi.com/search.json') do |req|
160
162
  req.params['q'] = query
161
163
  req.params['num'] = num_results
@@ -175,6 +177,7 @@ module RubynCode
175
177
  end
176
178
 
177
179
  def google_request(query, num_results)
180
+ require 'faraday'
178
181
  Faraday.get('https://www.googleapis.com/customsearch/v1') do |req|
179
182
  req.params['q'] = query
180
183
  req.params['num'] = [num_results, 10].min
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubynCode
4
- VERSION = '0.5.1'
4
+ VERSION = '0.7.0'
5
5
  end
data/lib/rubyn_code.rb CHANGED
@@ -16,11 +16,16 @@ module RubynCode
16
16
  # rather than a successful tool call returning a string like "denied".
17
17
  class UserDeniedError < Error; end
18
18
 
19
- # Infrastructure
20
- autoload :Config, 'rubyn_code/config/settings'
19
+ # Chisel — opt-in "write the minimum that works" enforcement (off by default)
20
+ autoload :Chisel, 'rubyn_code/chisel'
21
21
 
22
+ # Infrastructure
22
23
  module Config
24
+ autoload :Settings, 'rubyn_code/config/settings'
25
+ autoload :Defaults, 'rubyn_code/config/defaults'
26
+ autoload :ProjectConfig, 'rubyn_code/config/project_config'
23
27
  autoload :ProjectProfile, 'rubyn_code/config/project_profile'
28
+ autoload :Validator, 'rubyn_code/config/validator'
24
29
  end
25
30
 
26
31
  # Database
@@ -49,6 +54,7 @@ module RubynCode
49
54
  autoload :Base, 'rubyn_code/llm/adapters/base'
50
55
  autoload :JsonParsing, 'rubyn_code/llm/adapters/json_parsing'
51
56
  autoload :PromptCaching, 'rubyn_code/llm/adapters/prompt_caching'
57
+ autoload :TokenCaching, 'rubyn_code/llm/adapters/token_caching'
52
58
  autoload :Anthropic, 'rubyn_code/llm/adapters/anthropic'
53
59
  autoload :AnthropicCompatible, 'rubyn_code/llm/adapters/anthropic_compatible'
54
60
  autoload :AnthropicStreaming, 'rubyn_code/llm/adapters/anthropic_streaming'
@@ -153,6 +159,14 @@ module RubynCode
153
159
  module SubAgents
154
160
  autoload :Runner, 'rubyn_code/sub_agents/runner'
155
161
  autoload :Summarizer, 'rubyn_code/sub_agents/summarizer'
162
+ autoload :AgentType, 'rubyn_code/sub_agents/agent_type'
163
+ autoload :Catalog, 'rubyn_code/sub_agents/catalog'
164
+ end
165
+
166
+ # Checkpoints (/rewind)
167
+ module Checkpoint
168
+ autoload :Manager, 'rubyn_code/checkpoint/manager'
169
+ autoload :Hook, 'rubyn_code/checkpoint/hook'
156
170
  end
157
171
 
158
172
  # Layer 7: Tasks
@@ -181,6 +195,7 @@ module RubynCode
181
195
  autoload :Manager, 'rubyn_code/teams/manager'
182
196
  autoload :Mailbox, 'rubyn_code/teams/mailbox'
183
197
  autoload :Teammate, 'rubyn_code/teams/teammate'
198
+ autoload :AgentRegistry, 'rubyn_code/teams/agent_registry'
184
199
  end
185
200
 
186
201
  # Layer 10: Protocols
@@ -222,6 +237,17 @@ module RubynCode
222
237
  autoload :Runner, 'rubyn_code/hooks/runner'
223
238
  autoload :BuiltIn, 'rubyn_code/hooks/built_in'
224
239
  autoload :UserHooks, 'rubyn_code/hooks/user_hooks'
240
+ autoload :GoalHook, 'rubyn_code/hooks/goal_hook'
241
+ autoload :EventMap, 'rubyn_code/hooks/event_map'
242
+ autoload :Response, 'rubyn_code/hooks/response'
243
+ autoload :SettingsJsonLoader, 'rubyn_code/hooks/settings_json_loader'
244
+ autoload :SubprocessExecutor, 'rubyn_code/hooks/subprocess_executor'
245
+ autoload :ExternalDispatcher, 'rubyn_code/hooks/external_dispatcher'
246
+ end
247
+
248
+ # Session goals (/goal)
249
+ module Goal
250
+ autoload :Evaluator, 'rubyn_code/goal/evaluator'
225
251
  end
226
252
 
227
253
  # Layer 15: MCP
@@ -230,6 +256,7 @@ module RubynCode
230
256
  autoload :StdioTransport, 'rubyn_code/mcp/stdio_transport'
231
257
  autoload :SSETransport, 'rubyn_code/mcp/sse_transport'
232
258
  autoload :ToolBridge, 'rubyn_code/mcp/tool_bridge'
259
+ autoload :ServerExtrasBridge, 'rubyn_code/mcp/server_extras_bridge'
233
260
  autoload :Config, 'rubyn_code/mcp/config'
234
261
  end
235
262
 
@@ -240,6 +267,7 @@ module RubynCode
240
267
  autoload :InstinctMethods, 'rubyn_code/learning/instinct'
241
268
  autoload :Injector, 'rubyn_code/learning/injector'
242
269
  autoload :Shortcut, 'rubyn_code/learning/shortcut'
270
+ autoload :Porter, 'rubyn_code/learning/porter'
243
271
  end
244
272
 
245
273
  # IDE (VS Code extension server)
@@ -271,6 +299,8 @@ module RubynCode
271
299
  autoload :Setup, 'rubyn_code/cli/setup'
272
300
  autoload :FirstRun, 'rubyn_code/cli/first_run'
273
301
  autoload :DaemonRunner, 'rubyn_code/cli/daemon_runner'
302
+ autoload :LoopRunner, 'rubyn_code/cli/loop_runner'
303
+ autoload :MentionExpander, 'rubyn_code/cli/mention_expander'
274
304
  autoload :VersionCheck, 'rubyn_code/cli/version_check'
275
305
 
276
306
  # Slash Command System
@@ -304,6 +334,19 @@ module RubynCode
304
334
  autoload :RemoveSkills, 'rubyn_code/cli/commands/remove_skills'
305
335
  autoload :Skills, 'rubyn_code/cli/commands/skills'
306
336
  autoload :Megaplan, 'rubyn_code/cli/commands/megaplan'
337
+ autoload :Goal, 'rubyn_code/cli/commands/goal'
338
+ autoload :Loop, 'rubyn_code/cli/commands/loop'
339
+ autoload :Agents, 'rubyn_code/cli/commands/agents'
340
+ autoload :CommandTemplate, 'rubyn_code/cli/commands/command_template'
341
+ autoload :CustomCommand, 'rubyn_code/cli/commands/custom_command'
342
+ autoload :CustomLoader, 'rubyn_code/cli/commands/custom_loader'
343
+ autoload :Learning, 'rubyn_code/cli/commands/learning'
344
+ autoload :Rewind, 'rubyn_code/cli/commands/rewind'
345
+ autoload :Chisel, 'rubyn_code/cli/commands/chisel'
346
+ autoload :ChiselReview, 'rubyn_code/cli/commands/chisel_review'
347
+ autoload :ChiselAudit, 'rubyn_code/cli/commands/chisel_audit'
348
+ autoload :ChiselDebt, 'rubyn_code/cli/commands/chisel_debt'
349
+ autoload :ChiselGain, 'rubyn_code/cli/commands/chisel_gain'
307
350
  end
308
351
  end
309
352