rubyn-code 0.1.0 → 0.2.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 (159) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +269 -467
  3. data/db/migrations/009_create_teams.sql +6 -6
  4. data/db/migrations/011_fix_mailbox_messages_columns.rb +35 -0
  5. data/db/migrations/012_expand_mailbox_message_types.rb +37 -0
  6. data/exe/rubyn-code +1 -1
  7. data/lib/rubyn_code/agent/RUBYN.md +17 -0
  8. data/lib/rubyn_code/agent/conversation.rb +68 -19
  9. data/lib/rubyn_code/agent/loop.rb +312 -54
  10. data/lib/rubyn_code/agent/loop_detector.rb +6 -6
  11. data/lib/rubyn_code/auth/RUBYN.md +19 -0
  12. data/lib/rubyn_code/auth/oauth.rb +40 -35
  13. data/lib/rubyn_code/auth/server.rb +16 -12
  14. data/lib/rubyn_code/auth/token_store.rb +22 -22
  15. data/lib/rubyn_code/autonomous/RUBYN.md +14 -0
  16. data/lib/rubyn_code/autonomous/daemon.rb +115 -79
  17. data/lib/rubyn_code/autonomous/idle_poller.rb +4 -8
  18. data/lib/rubyn_code/autonomous/task_claimer.rb +11 -11
  19. data/lib/rubyn_code/background/RUBYN.md +13 -0
  20. data/lib/rubyn_code/background/notifier.rb +0 -2
  21. data/lib/rubyn_code/background/worker.rb +60 -15
  22. data/lib/rubyn_code/cli/RUBYN.md +30 -0
  23. data/lib/rubyn_code/cli/app.rb +85 -9
  24. data/lib/rubyn_code/cli/commands/RUBYN.md +133 -0
  25. data/lib/rubyn_code/cli/commands/base.rb +53 -0
  26. data/lib/rubyn_code/cli/commands/budget.rb +24 -0
  27. data/lib/rubyn_code/cli/commands/clear.rb +16 -0
  28. data/lib/rubyn_code/cli/commands/compact.rb +21 -0
  29. data/lib/rubyn_code/cli/commands/context.rb +44 -0
  30. data/lib/rubyn_code/cli/commands/context_info.rb +56 -0
  31. data/lib/rubyn_code/cli/commands/cost.rb +23 -0
  32. data/lib/rubyn_code/cli/commands/diff.rb +30 -0
  33. data/lib/rubyn_code/cli/commands/doctor.rb +112 -0
  34. data/lib/rubyn_code/cli/commands/help.rb +41 -0
  35. data/lib/rubyn_code/cli/commands/model.rb +37 -0
  36. data/lib/rubyn_code/cli/commands/plan.rb +22 -0
  37. data/lib/rubyn_code/cli/commands/quit.rb +17 -0
  38. data/lib/rubyn_code/cli/commands/registry.rb +64 -0
  39. data/lib/rubyn_code/cli/commands/resume.rb +51 -0
  40. data/lib/rubyn_code/cli/commands/review.rb +26 -0
  41. data/lib/rubyn_code/cli/commands/skill.rb +32 -0
  42. data/lib/rubyn_code/cli/commands/spawn.rb +24 -0
  43. data/lib/rubyn_code/cli/commands/tasks.rb +32 -0
  44. data/lib/rubyn_code/cli/commands/tokens.rb +76 -0
  45. data/lib/rubyn_code/cli/commands/undo.rb +17 -0
  46. data/lib/rubyn_code/cli/commands/version.rb +16 -0
  47. data/lib/rubyn_code/cli/daemon_runner.rb +129 -0
  48. data/lib/rubyn_code/cli/input_handler.rb +20 -23
  49. data/lib/rubyn_code/cli/renderer.rb +25 -27
  50. data/lib/rubyn_code/cli/repl.rb +161 -194
  51. data/lib/rubyn_code/cli/setup.rb +117 -0
  52. data/lib/rubyn_code/cli/spinner.rb +40 -40
  53. data/lib/rubyn_code/cli/stream_formatter.rb +29 -28
  54. data/lib/rubyn_code/cli/version_check.rb +94 -0
  55. data/lib/rubyn_code/config/RUBYN.md +14 -0
  56. data/lib/rubyn_code/config/defaults.rb +28 -19
  57. data/lib/rubyn_code/config/project_config.rb +7 -9
  58. data/lib/rubyn_code/config/settings.rb +3 -3
  59. data/lib/rubyn_code/context/RUBYN.md +20 -0
  60. data/lib/rubyn_code/context/auto_compact.rb +7 -7
  61. data/lib/rubyn_code/context/compactor.rb +2 -2
  62. data/lib/rubyn_code/context/context_collapse.rb +45 -0
  63. data/lib/rubyn_code/context/manager.rb +20 -3
  64. data/lib/rubyn_code/context/manual_compact.rb +7 -7
  65. data/lib/rubyn_code/context/micro_compact.rb +12 -12
  66. data/lib/rubyn_code/db/RUBYN.md +40 -0
  67. data/lib/rubyn_code/db/connection.rb +13 -13
  68. data/lib/rubyn_code/db/migrator.rb +67 -27
  69. data/lib/rubyn_code/db/schema.rb +6 -6
  70. data/lib/rubyn_code/debug.rb +74 -0
  71. data/lib/rubyn_code/hooks/RUBYN.md +17 -0
  72. data/lib/rubyn_code/hooks/built_in.rb +9 -9
  73. data/lib/rubyn_code/hooks/registry.rb +5 -5
  74. data/lib/rubyn_code/hooks/runner.rb +1 -1
  75. data/lib/rubyn_code/hooks/user_hooks.rb +16 -16
  76. data/lib/rubyn_code/learning/RUBYN.md +16 -0
  77. data/lib/rubyn_code/learning/extractor.rb +22 -22
  78. data/lib/rubyn_code/learning/injector.rb +17 -18
  79. data/lib/rubyn_code/learning/instinct.rb +18 -14
  80. data/lib/rubyn_code/llm/RUBYN.md +15 -0
  81. data/lib/rubyn_code/llm/client.rb +121 -55
  82. data/lib/rubyn_code/llm/message_builder.rb +19 -15
  83. data/lib/rubyn_code/llm/streaming.rb +80 -50
  84. data/lib/rubyn_code/mcp/RUBYN.md +21 -0
  85. data/lib/rubyn_code/mcp/client.rb +25 -24
  86. data/lib/rubyn_code/mcp/config.rb +7 -7
  87. data/lib/rubyn_code/mcp/sse_transport.rb +27 -26
  88. data/lib/rubyn_code/mcp/stdio_transport.rb +22 -19
  89. data/lib/rubyn_code/mcp/tool_bridge.rb +32 -32
  90. data/lib/rubyn_code/memory/RUBYN.md +17 -0
  91. data/lib/rubyn_code/memory/models.rb +3 -3
  92. data/lib/rubyn_code/memory/search.rb +17 -17
  93. data/lib/rubyn_code/memory/session_persistence.rb +49 -34
  94. data/lib/rubyn_code/memory/store.rb +17 -17
  95. data/lib/rubyn_code/observability/RUBYN.md +19 -0
  96. data/lib/rubyn_code/observability/budget_enforcer.rb +16 -15
  97. data/lib/rubyn_code/observability/cost_calculator.rb +3 -3
  98. data/lib/rubyn_code/observability/token_counter.rb +1 -1
  99. data/lib/rubyn_code/observability/usage_reporter.rb +35 -35
  100. data/lib/rubyn_code/output/RUBYN.md +11 -0
  101. data/lib/rubyn_code/output/diff_renderer.rb +6 -6
  102. data/lib/rubyn_code/output/formatter.rb +4 -4
  103. data/lib/rubyn_code/permissions/RUBYN.md +17 -0
  104. data/lib/rubyn_code/permissions/prompter.rb +8 -8
  105. data/lib/rubyn_code/protocols/RUBYN.md +14 -0
  106. data/lib/rubyn_code/protocols/interrupt_handler.rb +1 -1
  107. data/lib/rubyn_code/protocols/plan_approval.rb +9 -9
  108. data/lib/rubyn_code/protocols/shutdown_handshake.rb +9 -11
  109. data/lib/rubyn_code/skills/RUBYN.md +19 -0
  110. data/lib/rubyn_code/skills/catalog.rb +7 -7
  111. data/lib/rubyn_code/skills/document.rb +15 -15
  112. data/lib/rubyn_code/skills/loader.rb +6 -8
  113. data/lib/rubyn_code/sub_agents/RUBYN.md +12 -0
  114. data/lib/rubyn_code/sub_agents/runner.rb +15 -15
  115. data/lib/rubyn_code/sub_agents/summarizer.rb +1 -1
  116. data/lib/rubyn_code/tasks/RUBYN.md +13 -0
  117. data/lib/rubyn_code/tasks/dag.rb +12 -16
  118. data/lib/rubyn_code/tasks/manager.rb +24 -24
  119. data/lib/rubyn_code/tasks/models.rb +4 -4
  120. data/lib/rubyn_code/teams/RUBYN.md +14 -0
  121. data/lib/rubyn_code/teams/mailbox.rb +38 -18
  122. data/lib/rubyn_code/teams/manager.rb +19 -19
  123. data/lib/rubyn_code/teams/teammate.rb +3 -4
  124. data/lib/rubyn_code/tools/RUBYN.md +38 -0
  125. data/lib/rubyn_code/tools/background_run.rb +9 -11
  126. data/lib/rubyn_code/tools/base.rb +54 -3
  127. data/lib/rubyn_code/tools/bash.rb +16 -34
  128. data/lib/rubyn_code/tools/bundle_add.rb +10 -12
  129. data/lib/rubyn_code/tools/bundle_install.rb +9 -11
  130. data/lib/rubyn_code/tools/compact.rb +10 -9
  131. data/lib/rubyn_code/tools/db_migrate.rb +17 -15
  132. data/lib/rubyn_code/tools/edit_file.rb +12 -12
  133. data/lib/rubyn_code/tools/executor.rb +9 -4
  134. data/lib/rubyn_code/tools/git_commit.rb +29 -34
  135. data/lib/rubyn_code/tools/git_diff.rb +17 -18
  136. data/lib/rubyn_code/tools/git_log.rb +17 -19
  137. data/lib/rubyn_code/tools/git_status.rb +18 -20
  138. data/lib/rubyn_code/tools/glob.rb +7 -9
  139. data/lib/rubyn_code/tools/grep.rb +11 -9
  140. data/lib/rubyn_code/tools/load_skill.rb +7 -7
  141. data/lib/rubyn_code/tools/memory_search.rb +13 -12
  142. data/lib/rubyn_code/tools/memory_write.rb +14 -12
  143. data/lib/rubyn_code/tools/rails_generate.rb +16 -16
  144. data/lib/rubyn_code/tools/read_file.rb +8 -7
  145. data/lib/rubyn_code/tools/read_inbox.rb +5 -5
  146. data/lib/rubyn_code/tools/registry.rb +2 -2
  147. data/lib/rubyn_code/tools/review_pr.rb +55 -55
  148. data/lib/rubyn_code/tools/run_specs.rb +20 -19
  149. data/lib/rubyn_code/tools/schema.rb +9 -11
  150. data/lib/rubyn_code/tools/send_message.rb +10 -10
  151. data/lib/rubyn_code/tools/spawn_agent.rb +51 -23
  152. data/lib/rubyn_code/tools/spawn_teammate.rb +21 -21
  153. data/lib/rubyn_code/tools/task.rb +28 -28
  154. data/lib/rubyn_code/tools/web_fetch.rb +46 -31
  155. data/lib/rubyn_code/tools/web_search.rb +64 -66
  156. data/lib/rubyn_code/tools/write_file.rb +7 -6
  157. data/lib/rubyn_code/version.rb +1 -1
  158. data/lib/rubyn_code.rb +136 -105
  159. metadata +94 -21
@@ -1,31 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "open3"
4
- require "cgi"
5
- require "json"
6
- require "faraday"
7
- require_relative "base"
8
- require_relative "registry"
3
+ require 'open3'
4
+ require 'cgi'
5
+ require 'json'
6
+ require 'faraday'
7
+ require_relative 'base'
8
+ require_relative 'registry'
9
9
 
10
10
  module RubynCode
11
11
  module Tools
12
12
  class WebSearch < Base
13
- TOOL_NAME = "web_search"
14
- DESCRIPTION = "Search the web for information. Returns search results with titles, URLs, and snippets."
13
+ TOOL_NAME = 'web_search'
14
+ DESCRIPTION = 'Search the web for information. Returns search results with titles, URLs, and snippets.'
15
15
  PARAMETERS = {
16
- query: { type: :string, required: true, description: "The search query string" },
17
- num_results: { type: :integer, required: false, default: 5, description: "Number of results (default: 5)" }
16
+ query: { type: :string, required: true, description: 'The search query string' },
17
+ num_results: { type: :integer, required: false, default: 5, description: 'Number of results (default: 5)' }
18
18
  }.freeze
19
19
  RISK_LEVEL = :external
20
20
  REQUIRES_CONFIRMATION = true
21
21
 
22
22
  # Adapter registry — add new providers here
23
23
  ADAPTERS = {
24
- "duckduckgo" => :search_duckduckgo,
25
- "brave" => :search_brave,
26
- "serpapi" => :search_serpapi,
27
- "tavily" => :search_tavily,
28
- "google" => :search_google
24
+ 'duckduckgo' => :search_duckduckgo,
25
+ 'brave' => :search_brave,
26
+ 'serpapi' => :search_serpapi,
27
+ 'tavily' => :search_tavily,
28
+ 'google' => :search_google
29
29
  }.freeze
30
30
 
31
31
  def execute(query:, num_results: 5)
@@ -47,21 +47,21 @@ module RubynCode
47
47
 
48
48
  # Pick the best available provider based on env vars
49
49
  def detect_provider
50
- return "tavily" if ENV["TAVILY_API_KEY"]
51
- return "brave" if ENV["BRAVE_API_KEY"]
52
- return "serpapi" if ENV["SERPAPI_API_KEY"]
53
- return "google" if ENV["GOOGLE_SEARCH_API_KEY"] && ENV["GOOGLE_SEARCH_CX"]
50
+ return 'tavily' if ENV['TAVILY_API_KEY']
51
+ return 'brave' if ENV['BRAVE_API_KEY']
52
+ return 'serpapi' if ENV['SERPAPI_API_KEY']
53
+ return 'google' if ENV['GOOGLE_SEARCH_API_KEY'] && ENV['GOOGLE_SEARCH_CX']
54
54
 
55
- "duckduckgo"
55
+ 'duckduckgo'
56
56
  end
57
57
 
58
58
  # ─── DuckDuckGo (no API key, free) ───
59
59
 
60
60
  def search_duckduckgo(query, num_results)
61
61
  encoded = CGI.escape(query)
62
- stdout, _, status = Open3.capture3(
63
- "curl", "-sL", "--max-time", "15",
64
- "-H", "User-Agent: Mozilla/5.0 (compatible; RubynCode/1.0)",
62
+ stdout, _, status = safe_capture3(
63
+ 'curl', '-sL', '--max-time', '15',
64
+ '-H', 'User-Agent: Mozilla/5.0 (compatible; RubynCode/1.0)',
65
65
  "https://lite.duckduckgo.com/lite/?q=#{encoded}"
66
66
  )
67
67
  return [] unless status.success?
@@ -71,18 +71,18 @@ module RubynCode
71
71
 
72
72
  def parse_duckduckgo(html, max)
73
73
  results = []
74
- links = html.scan(/<a[^>]+rel="nofollow"[^>]+href="([^"]+)"[^>]*>(.*?)<\/a>/i)
75
- links = html.scan(/<a[^>]+href="(https?:\/\/(?!lite\.duckduckgo)[^"]+)"[^>]*>(.*?)<\/a>/i) if links.empty?
76
- snippets = html.scan(/<td[^>]*class="result-snippet"[^>]*>(.*?)<\/td>/im)
74
+ links = html.scan(%r{<a[^>]+rel="nofollow"[^>]+href="([^"]+)"[^>]*>(.*?)</a>}i)
75
+ links = html.scan(%r{<a[^>]+href="(https?://(?!lite\.duckduckgo)[^"]+)"[^>]*>(.*?)</a>}i) if links.empty?
76
+ snippets = html.scan(%r{<td[^>]*class="result-snippet"[^>]*>(.*?)</td>}im)
77
77
 
78
78
  links.each_with_index do |match, idx|
79
79
  break if results.length >= max
80
80
 
81
81
  url = match[0].strip
82
82
  title = strip_html(match[1]).strip
83
- next if url.empty? || title.empty? || url.include?("duckduckgo.com")
83
+ next if url.empty? || title.empty? || url.include?('duckduckgo.com')
84
84
 
85
- snippet = snippets[idx] ? strip_html(snippets[idx][0]).strip : ""
85
+ snippet = snippets[idx] ? strip_html(snippets[idx][0]).strip : ''
86
86
  results << { title: title, url: url, snippet: snippet }
87
87
  end
88
88
 
@@ -92,44 +92,42 @@ module RubynCode
92
92
  # ─── Brave Search (free tier: 2000 queries/mo) ───
93
93
 
94
94
  def search_brave(query, num_results)
95
- resp = Faraday.get("https://api.search.brave.com/res/v1/web/search") do |req|
96
- req.params["q"] = query
97
- req.params["count"] = num_results
98
- req.headers["Accept"] = "application/json"
99
- req.headers["Accept-Encoding"] = "gzip"
100
- req.headers["X-Subscription-Token"] = ENV["BRAVE_API_KEY"]
95
+ resp = Faraday.get('https://api.search.brave.com/res/v1/web/search') do |req|
96
+ req.params['q'] = query
97
+ req.params['count'] = num_results
98
+ req.headers['Accept'] = 'application/json'
99
+ req.headers['Accept-Encoding'] = 'gzip'
100
+ req.headers['X-Subscription-Token'] = ENV.fetch('BRAVE_API_KEY', nil)
101
101
  req.options.timeout = 15
102
102
  end
103
103
 
104
104
  data = JSON.parse(resp.body)
105
- (data.dig("web", "results") || []).map do |r|
106
- { title: r["title"], url: r["url"], snippet: r["description"] || "" }
105
+ (data.dig('web', 'results') || []).map do |r|
106
+ { title: r['title'], url: r['url'], snippet: r['description'] || '' }
107
107
  end
108
108
  end
109
109
 
110
110
  # ─── Tavily (built for AI agents, free tier: 1000 queries/mo) ───
111
111
 
112
112
  def search_tavily(query, num_results)
113
- resp = Faraday.post("https://api.tavily.com/search") do |req|
114
- req.headers["Content-Type"] = "application/json"
113
+ resp = Faraday.post('https://api.tavily.com/search') do |req|
114
+ req.headers['Content-Type'] = 'application/json'
115
115
  req.body = JSON.generate({
116
- api_key: ENV["TAVILY_API_KEY"],
117
- query: query,
118
- max_results: num_results,
119
- include_answer: true
120
- })
116
+ api_key: ENV.fetch('TAVILY_API_KEY', nil),
117
+ query: query,
118
+ max_results: num_results,
119
+ include_answer: true
120
+ })
121
121
  req.options.timeout = 15
122
122
  end
123
123
 
124
124
  data = JSON.parse(resp.body)
125
- results = (data["results"] || []).map do |r|
126
- { title: r["title"], url: r["url"], snippet: r["content"] || "" }
125
+ results = (data['results'] || []).map do |r|
126
+ { title: r['title'], url: r['url'], snippet: r['content'] || '' }
127
127
  end
128
128
 
129
129
  # Tavily provides a direct answer — prepend it
130
- if data["answer"]
131
- results.unshift({ title: "AI Answer", url: "", snippet: data["answer"] })
132
- end
130
+ results.unshift({ title: 'AI Answer', url: '', snippet: data['answer'] }) if data['answer']
133
131
 
134
132
  results
135
133
  end
@@ -137,44 +135,44 @@ module RubynCode
137
135
  # ─── SerpAPI (free tier: 100 queries/mo) ───
138
136
 
139
137
  def search_serpapi(query, num_results)
140
- resp = Faraday.get("https://serpapi.com/search.json") do |req|
141
- req.params["q"] = query
142
- req.params["num"] = num_results
143
- req.params["api_key"] = ENV["SERPAPI_API_KEY"]
138
+ resp = Faraday.get('https://serpapi.com/search.json') do |req|
139
+ req.params['q'] = query
140
+ req.params['num'] = num_results
141
+ req.params['api_key'] = ENV.fetch('SERPAPI_API_KEY', nil)
144
142
  req.options.timeout = 15
145
143
  end
146
144
 
147
145
  data = JSON.parse(resp.body)
148
- (data["organic_results"] || []).map do |r|
149
- { title: r["title"], url: r["link"], snippet: r["snippet"] || "" }
146
+ (data['organic_results'] || []).map do |r|
147
+ { title: r['title'], url: r['link'], snippet: r['snippet'] || '' }
150
148
  end
151
149
  end
152
150
 
153
151
  # ─── Google Custom Search (free tier: 100 queries/day) ───
154
152
 
155
153
  def search_google(query, num_results)
156
- resp = Faraday.get("https://www.googleapis.com/customsearch/v1") do |req|
157
- req.params["q"] = query
158
- req.params["num"] = [num_results, 10].min
159
- req.params["key"] = ENV["GOOGLE_SEARCH_API_KEY"]
160
- req.params["cx"] = ENV["GOOGLE_SEARCH_CX"]
154
+ resp = Faraday.get('https://www.googleapis.com/customsearch/v1') do |req|
155
+ req.params['q'] = query
156
+ req.params['num'] = [num_results, 10].min
157
+ req.params['key'] = ENV.fetch('GOOGLE_SEARCH_API_KEY', nil)
158
+ req.params['cx'] = ENV.fetch('GOOGLE_SEARCH_CX', nil)
161
159
  req.options.timeout = 15
162
160
  end
163
161
 
164
162
  data = JSON.parse(resp.body)
165
- (data["items"] || []).map do |r|
166
- { title: r["title"], url: r["link"], snippet: r["snippet"] || "" }
163
+ (data['items'] || []).map do |r|
164
+ { title: r['title'], url: r['link'], snippet: r['snippet'] || '' }
167
165
  end
168
166
  end
169
167
 
170
168
  # ─── Shared ───
171
169
 
172
170
  def strip_html(text)
173
- return "" if text.nil?
171
+ return '' if text.nil?
174
172
 
175
- text.gsub(/<[^>]*>/, "").gsub(/&amp;/, "&").gsub(/&lt;/, "<")
176
- .gsub(/&gt;/, ">").gsub(/&quot;/, '"').gsub(/&#39;/, "'")
177
- .gsub(/&nbsp;/, " ").gsub(/\s+/, " ").strip
173
+ text.gsub(/<[^>]*>/, '').gsub('&amp;', '&').gsub('&lt;', '<')
174
+ .gsub('&gt;', '>').gsub('&quot;', '"').gsub('&#39;', "'")
175
+ .gsub('&nbsp;', ' ').gsub(/\s+/, ' ').strip
178
176
  end
179
177
 
180
178
  def format_results(query, results, provider)
@@ -184,7 +182,7 @@ module RubynCode
184
182
  lines << "#{idx + 1}. #{result[:title]}"
185
183
  lines << " URL: #{result[:url]}" unless result[:url].empty?
186
184
  lines << " #{result[:snippet]}" unless result[:snippet].empty?
187
- lines << ""
185
+ lines << ''
188
186
  end
189
187
 
190
188
  truncate(lines.join("\n"), max: 30_000)
@@ -1,16 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base"
4
- require_relative "registry"
3
+ require_relative 'base'
4
+ require_relative 'registry'
5
5
 
6
6
  module RubynCode
7
7
  module Tools
8
8
  class WriteFile < Base
9
- TOOL_NAME = "write_file"
10
- DESCRIPTION = "Writes content to a file. Creates parent directories if needed."
9
+ TOOL_NAME = 'write_file'
10
+ DESCRIPTION = 'Writes content to a file. Creates parent directories if needed.'
11
11
  PARAMETERS = {
12
- path: { type: :string, required: true, description: "Path to the file to write (relative to project root or absolute)" },
13
- content: { type: :string, required: true, description: "Content to write to the file" }
12
+ path: { type: :string, required: true,
13
+ description: 'Path to the file to write (relative to project root or absolute)' },
14
+ content: { type: :string, required: true, description: 'Content to write to the file' }
14
15
  }.freeze
15
16
  RISK_LEVEL = :write
16
17
  REQUIRES_CONFIRMATION = false
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubynCode
4
- VERSION = "0.1.0"
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/rubyn_code.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "rubyn_code/version"
3
+ require_relative 'rubyn_code/version'
4
+ require_relative 'rubyn_code/debug'
4
5
 
5
6
  module RubynCode
6
7
  class Error < StandardError; end
@@ -12,192 +13,222 @@ module RubynCode
12
13
  class ConfigError < Error; end
13
14
 
14
15
  # Infrastructure
15
- autoload :Config, "rubyn_code/config/settings"
16
+ autoload :Config, 'rubyn_code/config/settings'
16
17
 
17
18
  # Database
18
19
  module DB
19
- autoload :Connection, "rubyn_code/db/connection"
20
- autoload :Migrator, "rubyn_code/db/migrator"
21
- autoload :Schema, "rubyn_code/db/schema"
20
+ autoload :Connection, 'rubyn_code/db/connection'
21
+ autoload :Migrator, 'rubyn_code/db/migrator'
22
+ autoload :Schema, 'rubyn_code/db/schema'
22
23
  end
23
24
 
24
25
  # Auth
25
26
  module Auth
26
- autoload :OAuth, "rubyn_code/auth/oauth"
27
- autoload :TokenStore, "rubyn_code/auth/token_store"
28
- autoload :Server, "rubyn_code/auth/server"
27
+ autoload :OAuth, 'rubyn_code/auth/oauth'
28
+ autoload :TokenStore, 'rubyn_code/auth/token_store'
29
+ autoload :Server, 'rubyn_code/auth/server'
29
30
  end
30
31
 
31
32
  # LLM
32
33
  module LLM
33
- autoload :Client, "rubyn_code/llm/client"
34
- autoload :Streaming, "rubyn_code/llm/streaming"
35
- autoload :MessageBuilder, "rubyn_code/llm/message_builder"
34
+ autoload :Client, 'rubyn_code/llm/client'
35
+ autoload :Streaming, 'rubyn_code/llm/streaming'
36
+ autoload :MessageBuilder, 'rubyn_code/llm/message_builder'
36
37
  end
37
38
 
38
39
  # Layer 1: Agent Loop
39
40
  module Agent
40
- autoload :Loop, "rubyn_code/agent/loop"
41
- autoload :LoopDetector, "rubyn_code/agent/loop_detector"
42
- autoload :Conversation, "rubyn_code/agent/conversation"
41
+ autoload :Loop, 'rubyn_code/agent/loop'
42
+ autoload :LoopDetector, 'rubyn_code/agent/loop_detector'
43
+ autoload :Conversation, 'rubyn_code/agent/conversation'
43
44
  end
44
45
 
45
46
  # Layer 2: Tool System
46
47
  module Tools
47
- autoload :Base, "rubyn_code/tools/base"
48
- autoload :Registry, "rubyn_code/tools/registry"
49
- autoload :Schema, "rubyn_code/tools/schema"
50
- autoload :Executor, "rubyn_code/tools/executor"
51
- autoload :ReadFile, "rubyn_code/tools/read_file"
52
- autoload :WriteFile, "rubyn_code/tools/write_file"
53
- autoload :EditFile, "rubyn_code/tools/edit_file"
54
- autoload :Glob, "rubyn_code/tools/glob"
55
- autoload :Grep, "rubyn_code/tools/grep"
56
- autoload :Bash, "rubyn_code/tools/bash"
57
- autoload :RailsGenerate, "rubyn_code/tools/rails_generate"
58
- autoload :DbMigrate, "rubyn_code/tools/db_migrate"
59
- autoload :RunSpecs, "rubyn_code/tools/run_specs"
60
- autoload :BundleInstall, "rubyn_code/tools/bundle_install"
61
- autoload :BundleAdd, "rubyn_code/tools/bundle_add"
62
- autoload :Compact, "rubyn_code/tools/compact"
63
- autoload :LoadSkill, "rubyn_code/tools/load_skill"
64
- autoload :Task, "rubyn_code/tools/task"
65
- autoload :MemorySearch, "rubyn_code/tools/memory_search"
66
- autoload :MemoryWrite, "rubyn_code/tools/memory_write"
67
- autoload :SendMessage, "rubyn_code/tools/send_message"
68
- autoload :ReadInbox, "rubyn_code/tools/read_inbox"
69
- autoload :ReviewPr, "rubyn_code/tools/review_pr"
70
- autoload :SpawnAgent, "rubyn_code/tools/spawn_agent"
71
- autoload :BackgroundRun, "rubyn_code/tools/background_run"
72
- autoload :WebSearch, "rubyn_code/tools/web_search"
73
- autoload :WebFetch, "rubyn_code/tools/web_fetch"
74
- autoload :GitCommit, "rubyn_code/tools/git_commit"
75
- autoload :GitDiff, "rubyn_code/tools/git_diff"
76
- autoload :GitLog, "rubyn_code/tools/git_log"
77
- autoload :GitStatus, "rubyn_code/tools/git_status"
78
- autoload :SpawnTeammate, "rubyn_code/tools/spawn_teammate"
48
+ autoload :Base, 'rubyn_code/tools/base'
49
+ autoload :Registry, 'rubyn_code/tools/registry'
50
+ autoload :Schema, 'rubyn_code/tools/schema'
51
+ autoload :Executor, 'rubyn_code/tools/executor'
52
+ autoload :ReadFile, 'rubyn_code/tools/read_file'
53
+ autoload :WriteFile, 'rubyn_code/tools/write_file'
54
+ autoload :EditFile, 'rubyn_code/tools/edit_file'
55
+ autoload :Glob, 'rubyn_code/tools/glob'
56
+ autoload :Grep, 'rubyn_code/tools/grep'
57
+ autoload :Bash, 'rubyn_code/tools/bash'
58
+ autoload :RailsGenerate, 'rubyn_code/tools/rails_generate'
59
+ autoload :DbMigrate, 'rubyn_code/tools/db_migrate'
60
+ autoload :RunSpecs, 'rubyn_code/tools/run_specs'
61
+ autoload :BundleInstall, 'rubyn_code/tools/bundle_install'
62
+ autoload :BundleAdd, 'rubyn_code/tools/bundle_add'
63
+ autoload :Compact, 'rubyn_code/tools/compact'
64
+ autoload :LoadSkill, 'rubyn_code/tools/load_skill'
65
+ autoload :Task, 'rubyn_code/tools/task'
66
+ autoload :MemorySearch, 'rubyn_code/tools/memory_search'
67
+ autoload :MemoryWrite, 'rubyn_code/tools/memory_write'
68
+ autoload :SendMessage, 'rubyn_code/tools/send_message'
69
+ autoload :ReadInbox, 'rubyn_code/tools/read_inbox'
70
+ autoload :ReviewPr, 'rubyn_code/tools/review_pr'
71
+ autoload :SpawnAgent, 'rubyn_code/tools/spawn_agent'
72
+ autoload :BackgroundRun, 'rubyn_code/tools/background_run'
73
+ autoload :WebSearch, 'rubyn_code/tools/web_search'
74
+ autoload :WebFetch, 'rubyn_code/tools/web_fetch'
75
+ autoload :GitCommit, 'rubyn_code/tools/git_commit'
76
+ autoload :GitDiff, 'rubyn_code/tools/git_diff'
77
+ autoload :GitLog, 'rubyn_code/tools/git_log'
78
+ autoload :GitStatus, 'rubyn_code/tools/git_status'
79
+ autoload :SpawnTeammate, 'rubyn_code/tools/spawn_teammate'
79
80
  end
80
81
 
81
82
  # Layer 3: Permissions
82
83
  module Permissions
83
- autoload :Tier, "rubyn_code/permissions/tier"
84
- autoload :Policy, "rubyn_code/permissions/policy"
85
- autoload :DenyList, "rubyn_code/permissions/deny_list"
86
- autoload :Prompter, "rubyn_code/permissions/prompter"
84
+ autoload :Tier, 'rubyn_code/permissions/tier'
85
+ autoload :Policy, 'rubyn_code/permissions/policy'
86
+ autoload :DenyList, 'rubyn_code/permissions/deny_list'
87
+ autoload :Prompter, 'rubyn_code/permissions/prompter'
87
88
  end
88
89
 
89
90
  # Layer 4: Context Management
90
91
  module Context
91
- autoload :Manager, "rubyn_code/context/manager"
92
- autoload :Compactor, "rubyn_code/context/compactor"
93
- autoload :MicroCompact, "rubyn_code/context/micro_compact"
94
- autoload :AutoCompact, "rubyn_code/context/auto_compact"
95
- autoload :ManualCompact, "rubyn_code/context/manual_compact"
92
+ autoload :Manager, 'rubyn_code/context/manager'
93
+ autoload :Compactor, 'rubyn_code/context/compactor'
94
+ autoload :MicroCompact, 'rubyn_code/context/micro_compact'
95
+ autoload :AutoCompact, 'rubyn_code/context/auto_compact'
96
+ autoload :ManualCompact, 'rubyn_code/context/manual_compact'
97
+ autoload :ContextCollapse, 'rubyn_code/context/context_collapse'
96
98
  end
97
99
 
98
100
  # Layer 5: Skills
99
101
  module Skills
100
- autoload :Loader, "rubyn_code/skills/loader"
101
- autoload :Catalog, "rubyn_code/skills/catalog"
102
- autoload :Document, "rubyn_code/skills/document"
102
+ autoload :Loader, 'rubyn_code/skills/loader'
103
+ autoload :Catalog, 'rubyn_code/skills/catalog'
104
+ autoload :Document, 'rubyn_code/skills/document'
103
105
  end
104
106
 
105
107
  # Layer 6: Sub-Agents
106
108
  module SubAgents
107
- autoload :Runner, "rubyn_code/sub_agents/runner"
108
- autoload :Summarizer, "rubyn_code/sub_agents/summarizer"
109
+ autoload :Runner, 'rubyn_code/sub_agents/runner'
110
+ autoload :Summarizer, 'rubyn_code/sub_agents/summarizer'
109
111
  end
110
112
 
111
113
  # Layer 7: Tasks
112
114
  module Tasks
113
- autoload :Manager, "rubyn_code/tasks/manager"
114
- autoload :DAG, "rubyn_code/tasks/dag"
115
- autoload :Models, "rubyn_code/tasks/models"
115
+ autoload :Manager, 'rubyn_code/tasks/manager'
116
+ autoload :DAG, 'rubyn_code/tasks/dag'
117
+ autoload :Models, 'rubyn_code/tasks/models'
116
118
  end
117
119
 
118
120
  # Layer 8: Background
119
121
  module Background
120
- autoload :Worker, "rubyn_code/background/worker"
121
- autoload :Job, "rubyn_code/background/job"
122
- autoload :Notifier, "rubyn_code/background/notifier"
122
+ autoload :Worker, 'rubyn_code/background/worker'
123
+ autoload :Job, 'rubyn_code/background/job'
124
+ autoload :Notifier, 'rubyn_code/background/notifier'
123
125
  end
124
126
 
125
127
  # Layer 9: Teams
126
128
  module Teams
127
- autoload :Manager, "rubyn_code/teams/manager"
128
- autoload :Mailbox, "rubyn_code/teams/mailbox"
129
- autoload :Teammate, "rubyn_code/teams/teammate"
129
+ autoload :Manager, 'rubyn_code/teams/manager'
130
+ autoload :Mailbox, 'rubyn_code/teams/mailbox'
131
+ autoload :Teammate, 'rubyn_code/teams/teammate'
130
132
  end
131
133
 
132
134
  # Layer 10: Protocols
133
135
  module Protocols
134
- autoload :ShutdownHandshake, "rubyn_code/protocols/shutdown_handshake"
135
- autoload :PlanApproval, "rubyn_code/protocols/plan_approval"
136
- autoload :InterruptHandler, "rubyn_code/protocols/interrupt_handler"
136
+ autoload :ShutdownHandshake, 'rubyn_code/protocols/shutdown_handshake'
137
+ autoload :PlanApproval, 'rubyn_code/protocols/plan_approval'
138
+ autoload :InterruptHandler, 'rubyn_code/protocols/interrupt_handler'
137
139
  end
138
140
 
139
141
  # Layer 11: Autonomous
140
142
  module Autonomous
141
- autoload :Daemon, "rubyn_code/autonomous/daemon"
142
- autoload :IdlePoller, "rubyn_code/autonomous/idle_poller"
143
- autoload :TaskClaimer, "rubyn_code/autonomous/task_claimer"
143
+ autoload :Daemon, 'rubyn_code/autonomous/daemon'
144
+ autoload :IdlePoller, 'rubyn_code/autonomous/idle_poller'
145
+ autoload :TaskClaimer, 'rubyn_code/autonomous/task_claimer'
144
146
  end
145
147
 
146
148
  # Layer 12: Memory
147
149
  module Memory
148
- autoload :Store, "rubyn_code/memory/store"
149
- autoload :Search, "rubyn_code/memory/search"
150
- autoload :SessionPersistence, "rubyn_code/memory/session_persistence"
151
- autoload :Models, "rubyn_code/memory/models"
150
+ autoload :Store, 'rubyn_code/memory/store'
151
+ autoload :Search, 'rubyn_code/memory/search'
152
+ autoload :SessionPersistence, 'rubyn_code/memory/session_persistence'
153
+ autoload :Models, 'rubyn_code/memory/models'
152
154
  end
153
155
 
154
156
  # Layer 13: Observability
155
157
  module Observability
156
- autoload :TokenCounter, "rubyn_code/observability/token_counter"
157
- autoload :CostCalculator, "rubyn_code/observability/cost_calculator"
158
- autoload :BudgetEnforcer, "rubyn_code/observability/budget_enforcer"
159
- autoload :UsageReporter, "rubyn_code/observability/usage_reporter"
160
- autoload :Models, "rubyn_code/observability/models"
158
+ autoload :TokenCounter, 'rubyn_code/observability/token_counter'
159
+ autoload :CostCalculator, 'rubyn_code/observability/cost_calculator'
160
+ autoload :BudgetEnforcer, 'rubyn_code/observability/budget_enforcer'
161
+ autoload :UsageReporter, 'rubyn_code/observability/usage_reporter'
162
+ autoload :Models, 'rubyn_code/observability/models'
161
163
  end
162
164
 
163
165
  # Layer 14: Hooks
164
166
  module Hooks
165
- autoload :Registry, "rubyn_code/hooks/registry"
166
- autoload :Runner, "rubyn_code/hooks/runner"
167
- autoload :BuiltIn, "rubyn_code/hooks/built_in"
168
- autoload :UserHooks, "rubyn_code/hooks/user_hooks"
167
+ autoload :Registry, 'rubyn_code/hooks/registry'
168
+ autoload :Runner, 'rubyn_code/hooks/runner'
169
+ autoload :BuiltIn, 'rubyn_code/hooks/built_in'
170
+ autoload :UserHooks, 'rubyn_code/hooks/user_hooks'
169
171
  end
170
172
 
171
173
  # Layer 15: MCP
172
174
  module MCP
173
- autoload :Client, "rubyn_code/mcp/client"
174
- autoload :StdioTransport, "rubyn_code/mcp/stdio_transport"
175
- autoload :SSETransport, "rubyn_code/mcp/sse_transport"
176
- autoload :ToolBridge, "rubyn_code/mcp/tool_bridge"
177
- autoload :Config, "rubyn_code/mcp/config"
175
+ autoload :Client, 'rubyn_code/mcp/client'
176
+ autoload :StdioTransport, 'rubyn_code/mcp/stdio_transport'
177
+ autoload :SSETransport, 'rubyn_code/mcp/sse_transport'
178
+ autoload :ToolBridge, 'rubyn_code/mcp/tool_bridge'
179
+ autoload :Config, 'rubyn_code/mcp/config'
178
180
  end
179
181
 
180
182
  # Layer 16: Learning
181
183
  module Learning
182
- autoload :Extractor, "rubyn_code/learning/extractor"
183
- autoload :Instinct, "rubyn_code/learning/instinct"
184
- autoload :InstinctMethods, "rubyn_code/learning/instinct"
185
- autoload :Injector, "rubyn_code/learning/injector"
184
+ autoload :Extractor, 'rubyn_code/learning/extractor'
185
+ autoload :Instinct, 'rubyn_code/learning/instinct'
186
+ autoload :InstinctMethods, 'rubyn_code/learning/instinct'
187
+ autoload :Injector, 'rubyn_code/learning/injector'
186
188
  end
187
189
 
188
190
  # CLI
189
191
  module CLI
190
- autoload :App, "rubyn_code/cli/app"
191
- autoload :REPL, "rubyn_code/cli/repl"
192
- autoload :InputHandler, "rubyn_code/cli/input_handler"
193
- autoload :Renderer, "rubyn_code/cli/renderer"
194
- autoload :Spinner, "rubyn_code/cli/spinner"
195
- autoload :StreamFormatter, "rubyn_code/cli/stream_formatter"
192
+ autoload :App, 'rubyn_code/cli/app'
193
+ autoload :REPL, 'rubyn_code/cli/repl'
194
+ autoload :InputHandler, 'rubyn_code/cli/input_handler'
195
+ autoload :Renderer, 'rubyn_code/cli/renderer'
196
+ autoload :Spinner, 'rubyn_code/cli/spinner'
197
+ autoload :StreamFormatter, 'rubyn_code/cli/stream_formatter'
198
+ autoload :Setup, 'rubyn_code/cli/setup'
199
+ autoload :DaemonRunner, 'rubyn_code/cli/daemon_runner'
200
+ autoload :VersionCheck, 'rubyn_code/cli/version_check'
201
+
202
+ # Slash Command System
203
+ module Commands
204
+ autoload :Base, 'rubyn_code/cli/commands/base'
205
+ autoload :Context, 'rubyn_code/cli/commands/context'
206
+ autoload :Registry, 'rubyn_code/cli/commands/registry'
207
+ autoload :Help, 'rubyn_code/cli/commands/help'
208
+ autoload :Quit, 'rubyn_code/cli/commands/quit'
209
+ autoload :Compact, 'rubyn_code/cli/commands/compact'
210
+ autoload :Cost, 'rubyn_code/cli/commands/cost'
211
+ autoload :Clear, 'rubyn_code/cli/commands/clear'
212
+ autoload :Undo, 'rubyn_code/cli/commands/undo'
213
+ autoload :Tasks, 'rubyn_code/cli/commands/tasks'
214
+ autoload :Budget, 'rubyn_code/cli/commands/budget'
215
+ autoload :Skill, 'rubyn_code/cli/commands/skill'
216
+ autoload :Version, 'rubyn_code/cli/commands/version'
217
+ autoload :Review, 'rubyn_code/cli/commands/review'
218
+ autoload :Resume, 'rubyn_code/cli/commands/resume'
219
+ autoload :Spawn, 'rubyn_code/cli/commands/spawn'
220
+ autoload :Doctor, 'rubyn_code/cli/commands/doctor'
221
+ autoload :Tokens, 'rubyn_code/cli/commands/tokens'
222
+ autoload :Plan, 'rubyn_code/cli/commands/plan'
223
+ autoload :ContextInfo, 'rubyn_code/cli/commands/context_info'
224
+ autoload :Diff, 'rubyn_code/cli/commands/diff'
225
+ autoload :Model, 'rubyn_code/cli/commands/model'
226
+ end
196
227
  end
197
228
 
198
229
  # Output
199
230
  module Output
200
- autoload :Formatter, "rubyn_code/output/formatter"
201
- autoload :DiffRenderer, "rubyn_code/output/diff_renderer"
231
+ autoload :Formatter, 'rubyn_code/output/formatter'
232
+ autoload :DiffRenderer, 'rubyn_code/output/diff_renderer'
202
233
  end
203
234
  end