rubyn-code 0.1.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 (235) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +620 -0
  4. data/db/migrations/000_create_schema_migrations.sql +4 -0
  5. data/db/migrations/001_create_sessions.sql +16 -0
  6. data/db/migrations/002_create_messages.sql +16 -0
  7. data/db/migrations/003_create_tasks.sql +17 -0
  8. data/db/migrations/004_create_task_dependencies.sql +8 -0
  9. data/db/migrations/005_create_memories.sql +44 -0
  10. data/db/migrations/006_create_cost_records.sql +16 -0
  11. data/db/migrations/007_create_hooks.sql +12 -0
  12. data/db/migrations/008_create_skills_cache.sql +8 -0
  13. data/db/migrations/009_create_teams.sql +27 -0
  14. data/db/migrations/010_create_instincts.sql +15 -0
  15. data/exe/rubyn-code +6 -0
  16. data/lib/rubyn_code/agent/conversation.rb +193 -0
  17. data/lib/rubyn_code/agent/loop.rb +517 -0
  18. data/lib/rubyn_code/agent/loop_detector.rb +78 -0
  19. data/lib/rubyn_code/auth/oauth.rb +174 -0
  20. data/lib/rubyn_code/auth/server.rb +126 -0
  21. data/lib/rubyn_code/auth/token_store.rb +153 -0
  22. data/lib/rubyn_code/autonomous/daemon.rb +233 -0
  23. data/lib/rubyn_code/autonomous/idle_poller.rb +111 -0
  24. data/lib/rubyn_code/autonomous/task_claimer.rb +100 -0
  25. data/lib/rubyn_code/background/job.rb +19 -0
  26. data/lib/rubyn_code/background/notifier.rb +44 -0
  27. data/lib/rubyn_code/background/worker.rb +146 -0
  28. data/lib/rubyn_code/cli/app.rb +118 -0
  29. data/lib/rubyn_code/cli/input_handler.rb +79 -0
  30. data/lib/rubyn_code/cli/renderer.rb +205 -0
  31. data/lib/rubyn_code/cli/repl.rb +519 -0
  32. data/lib/rubyn_code/cli/spinner.rb +100 -0
  33. data/lib/rubyn_code/cli/stream_formatter.rb +149 -0
  34. data/lib/rubyn_code/config/defaults.rb +43 -0
  35. data/lib/rubyn_code/config/project_config.rb +120 -0
  36. data/lib/rubyn_code/config/settings.rb +127 -0
  37. data/lib/rubyn_code/context/auto_compact.rb +81 -0
  38. data/lib/rubyn_code/context/compactor.rb +89 -0
  39. data/lib/rubyn_code/context/manager.rb +91 -0
  40. data/lib/rubyn_code/context/manual_compact.rb +87 -0
  41. data/lib/rubyn_code/context/micro_compact.rb +135 -0
  42. data/lib/rubyn_code/db/connection.rb +176 -0
  43. data/lib/rubyn_code/db/migrator.rb +146 -0
  44. data/lib/rubyn_code/db/schema.rb +106 -0
  45. data/lib/rubyn_code/hooks/built_in.rb +124 -0
  46. data/lib/rubyn_code/hooks/registry.rb +99 -0
  47. data/lib/rubyn_code/hooks/runner.rb +88 -0
  48. data/lib/rubyn_code/hooks/user_hooks.rb +90 -0
  49. data/lib/rubyn_code/learning/extractor.rb +191 -0
  50. data/lib/rubyn_code/learning/injector.rb +138 -0
  51. data/lib/rubyn_code/learning/instinct.rb +172 -0
  52. data/lib/rubyn_code/llm/client.rb +218 -0
  53. data/lib/rubyn_code/llm/message_builder.rb +116 -0
  54. data/lib/rubyn_code/llm/streaming.rb +203 -0
  55. data/lib/rubyn_code/mcp/client.rb +139 -0
  56. data/lib/rubyn_code/mcp/config.rb +83 -0
  57. data/lib/rubyn_code/mcp/sse_transport.rb +225 -0
  58. data/lib/rubyn_code/mcp/stdio_transport.rb +196 -0
  59. data/lib/rubyn_code/mcp/tool_bridge.rb +164 -0
  60. data/lib/rubyn_code/memory/models.rb +62 -0
  61. data/lib/rubyn_code/memory/search.rb +181 -0
  62. data/lib/rubyn_code/memory/session_persistence.rb +194 -0
  63. data/lib/rubyn_code/memory/store.rb +199 -0
  64. data/lib/rubyn_code/observability/budget_enforcer.rb +159 -0
  65. data/lib/rubyn_code/observability/cost_calculator.rb +61 -0
  66. data/lib/rubyn_code/observability/models.rb +29 -0
  67. data/lib/rubyn_code/observability/token_counter.rb +42 -0
  68. data/lib/rubyn_code/observability/usage_reporter.rb +140 -0
  69. data/lib/rubyn_code/output/diff_renderer.rb +212 -0
  70. data/lib/rubyn_code/output/formatter.rb +120 -0
  71. data/lib/rubyn_code/permissions/deny_list.rb +49 -0
  72. data/lib/rubyn_code/permissions/policy.rb +59 -0
  73. data/lib/rubyn_code/permissions/prompter.rb +80 -0
  74. data/lib/rubyn_code/permissions/tier.rb +22 -0
  75. data/lib/rubyn_code/protocols/interrupt_handler.rb +95 -0
  76. data/lib/rubyn_code/protocols/plan_approval.rb +67 -0
  77. data/lib/rubyn_code/protocols/shutdown_handshake.rb +109 -0
  78. data/lib/rubyn_code/skills/catalog.rb +70 -0
  79. data/lib/rubyn_code/skills/document.rb +80 -0
  80. data/lib/rubyn_code/skills/loader.rb +57 -0
  81. data/lib/rubyn_code/sub_agents/runner.rb +168 -0
  82. data/lib/rubyn_code/sub_agents/summarizer.rb +57 -0
  83. data/lib/rubyn_code/tasks/dag.rb +208 -0
  84. data/lib/rubyn_code/tasks/manager.rb +212 -0
  85. data/lib/rubyn_code/tasks/models.rb +31 -0
  86. data/lib/rubyn_code/teams/mailbox.rb +128 -0
  87. data/lib/rubyn_code/teams/manager.rb +175 -0
  88. data/lib/rubyn_code/teams/teammate.rb +38 -0
  89. data/lib/rubyn_code/tools/background_run.rb +41 -0
  90. data/lib/rubyn_code/tools/base.rb +84 -0
  91. data/lib/rubyn_code/tools/bash.rb +81 -0
  92. data/lib/rubyn_code/tools/bundle_add.rb +53 -0
  93. data/lib/rubyn_code/tools/bundle_install.rb +41 -0
  94. data/lib/rubyn_code/tools/compact.rb +57 -0
  95. data/lib/rubyn_code/tools/db_migrate.rb +52 -0
  96. data/lib/rubyn_code/tools/edit_file.rb +49 -0
  97. data/lib/rubyn_code/tools/executor.rb +62 -0
  98. data/lib/rubyn_code/tools/git_commit.rb +97 -0
  99. data/lib/rubyn_code/tools/git_diff.rb +61 -0
  100. data/lib/rubyn_code/tools/git_log.rb +59 -0
  101. data/lib/rubyn_code/tools/git_status.rb +59 -0
  102. data/lib/rubyn_code/tools/glob.rb +44 -0
  103. data/lib/rubyn_code/tools/grep.rb +81 -0
  104. data/lib/rubyn_code/tools/load_skill.rb +41 -0
  105. data/lib/rubyn_code/tools/memory_search.rb +77 -0
  106. data/lib/rubyn_code/tools/memory_write.rb +52 -0
  107. data/lib/rubyn_code/tools/rails_generate.rb +54 -0
  108. data/lib/rubyn_code/tools/read_file.rb +38 -0
  109. data/lib/rubyn_code/tools/read_inbox.rb +64 -0
  110. data/lib/rubyn_code/tools/registry.rb +48 -0
  111. data/lib/rubyn_code/tools/review_pr.rb +145 -0
  112. data/lib/rubyn_code/tools/run_specs.rb +75 -0
  113. data/lib/rubyn_code/tools/schema.rb +59 -0
  114. data/lib/rubyn_code/tools/send_message.rb +53 -0
  115. data/lib/rubyn_code/tools/spawn_agent.rb +154 -0
  116. data/lib/rubyn_code/tools/spawn_teammate.rb +168 -0
  117. data/lib/rubyn_code/tools/task.rb +148 -0
  118. data/lib/rubyn_code/tools/web_fetch.rb +108 -0
  119. data/lib/rubyn_code/tools/web_search.rb +196 -0
  120. data/lib/rubyn_code/tools/write_file.rb +30 -0
  121. data/lib/rubyn_code/version.rb +5 -0
  122. data/lib/rubyn_code.rb +203 -0
  123. data/skills/code_quality/fits_in_your_head.md +189 -0
  124. data/skills/code_quality/naming_conventions.md +213 -0
  125. data/skills/code_quality/null_object.md +205 -0
  126. data/skills/code_quality/technical_debt.md +135 -0
  127. data/skills/code_quality/value_objects.md +216 -0
  128. data/skills/code_quality/yagni.md +176 -0
  129. data/skills/design_patterns/adapter.md +191 -0
  130. data/skills/design_patterns/bridge_memento_visitor.md +254 -0
  131. data/skills/design_patterns/builder.md +158 -0
  132. data/skills/design_patterns/command.md +126 -0
  133. data/skills/design_patterns/composite.md +147 -0
  134. data/skills/design_patterns/decorator.md +204 -0
  135. data/skills/design_patterns/facade.md +133 -0
  136. data/skills/design_patterns/factory_method.md +169 -0
  137. data/skills/design_patterns/iterator.md +116 -0
  138. data/skills/design_patterns/mediator.md +133 -0
  139. data/skills/design_patterns/observer.md +177 -0
  140. data/skills/design_patterns/proxy.md +140 -0
  141. data/skills/design_patterns/singleton.md +124 -0
  142. data/skills/design_patterns/state.md +207 -0
  143. data/skills/design_patterns/strategy.md +127 -0
  144. data/skills/design_patterns/template_method.md +173 -0
  145. data/skills/gems/devise.md +365 -0
  146. data/skills/gems/dry_rb.md +186 -0
  147. data/skills/gems/factory_bot.md +268 -0
  148. data/skills/gems/faraday.md +263 -0
  149. data/skills/gems/graphql_ruby.md +514 -0
  150. data/skills/gems/pundit.md +446 -0
  151. data/skills/gems/redis.md +219 -0
  152. data/skills/gems/rubocop.md +257 -0
  153. data/skills/gems/sidekiq.md +360 -0
  154. data/skills/gems/stripe.md +224 -0
  155. data/skills/minitest/assertions.md +185 -0
  156. data/skills/minitest/fixtures.md +238 -0
  157. data/skills/minitest/integration_tests.md +210 -0
  158. data/skills/minitest/mailers_and_jobs.md +218 -0
  159. data/skills/minitest/mocking_stubbing.md +202 -0
  160. data/skills/minitest/service_tests_and_performance.md +246 -0
  161. data/skills/minitest/structure_and_conventions.md +169 -0
  162. data/skills/minitest/system_tests.md +237 -0
  163. data/skills/rails/action_cable.md +160 -0
  164. data/skills/rails/active_record_basics.md +174 -0
  165. data/skills/rails/active_storage.md +242 -0
  166. data/skills/rails/api_design.md +212 -0
  167. data/skills/rails/associations.md +182 -0
  168. data/skills/rails/background_jobs.md +212 -0
  169. data/skills/rails/caching.md +158 -0
  170. data/skills/rails/callbacks.md +135 -0
  171. data/skills/rails/concerns_controllers.md +218 -0
  172. data/skills/rails/concerns_models.md +280 -0
  173. data/skills/rails/controllers.md +190 -0
  174. data/skills/rails/engines.md +201 -0
  175. data/skills/rails/form_objects.md +168 -0
  176. data/skills/rails/hotwire.md +229 -0
  177. data/skills/rails/internationalization.md +192 -0
  178. data/skills/rails/logging.md +198 -0
  179. data/skills/rails/mailers.md +180 -0
  180. data/skills/rails/migrations.md +200 -0
  181. data/skills/rails/multitenancy.md +207 -0
  182. data/skills/rails/n_plus_one.md +151 -0
  183. data/skills/rails/presenters.md +244 -0
  184. data/skills/rails/query_objects.md +177 -0
  185. data/skills/rails/routing.md +194 -0
  186. data/skills/rails/scopes.md +187 -0
  187. data/skills/rails/security.md +233 -0
  188. data/skills/rails/serializers.md +243 -0
  189. data/skills/rails/service_objects.md +184 -0
  190. data/skills/rails/testing_strategy.md +258 -0
  191. data/skills/rails/validations.md +206 -0
  192. data/skills/refactoring/code_smells.md +251 -0
  193. data/skills/refactoring/command_query_separation.md +166 -0
  194. data/skills/refactoring/encapsulate_collection.md +125 -0
  195. data/skills/refactoring/extract_class.md +138 -0
  196. data/skills/refactoring/extract_method.md +185 -0
  197. data/skills/refactoring/replace_conditional.md +211 -0
  198. data/skills/refactoring/value_objects.md +246 -0
  199. data/skills/rspec/build_stubbed.md +199 -0
  200. data/skills/rspec/factory_design.md +206 -0
  201. data/skills/rspec/let_vs_let_bang.md +161 -0
  202. data/skills/rspec/mocking_stubbing.md +209 -0
  203. data/skills/rspec/request_specs.md +212 -0
  204. data/skills/rspec/service_specs.md +262 -0
  205. data/skills/rspec/shared_examples.md +244 -0
  206. data/skills/rspec/system_specs.md +286 -0
  207. data/skills/rspec/test_performance.md +215 -0
  208. data/skills/ruby/blocks_procs_lambdas.md +204 -0
  209. data/skills/ruby/classes.md +155 -0
  210. data/skills/ruby/concurrency.md +194 -0
  211. data/skills/ruby/data_struct_openstruct.md +158 -0
  212. data/skills/ruby/debugging_profiling.md +204 -0
  213. data/skills/ruby/enumerable_patterns.md +168 -0
  214. data/skills/ruby/exception_handling.md +199 -0
  215. data/skills/ruby/file_io.md +217 -0
  216. data/skills/ruby/hashes.md +195 -0
  217. data/skills/ruby/metaprogramming.md +170 -0
  218. data/skills/ruby/modules.md +210 -0
  219. data/skills/ruby/pattern_matching.md +177 -0
  220. data/skills/ruby/regular_expressions.md +166 -0
  221. data/skills/ruby/result_objects.md +200 -0
  222. data/skills/ruby/strings.md +177 -0
  223. data/skills/ruby_project/bundler_dependencies.md +181 -0
  224. data/skills/ruby_project/cli_tools.md +224 -0
  225. data/skills/ruby_project/rake_tasks.md +146 -0
  226. data/skills/ruby_project/structure.md +261 -0
  227. data/skills/sinatra/application_structure.md +241 -0
  228. data/skills/sinatra/middleware_and_deployment.md +221 -0
  229. data/skills/sinatra/testing.md +233 -0
  230. data/skills/solid/dependency_inversion.md +195 -0
  231. data/skills/solid/interface_segregation.md +237 -0
  232. data/skills/solid/liskov_substitution.md +263 -0
  233. data/skills/solid/open_closed.md +212 -0
  234. data/skills/solid/single_responsibility.md +183 -0
  235. metadata +397 -0
data/lib/rubyn_code.rb ADDED
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rubyn_code/version"
4
+
5
+ module RubynCode
6
+ class Error < StandardError; end
7
+ class AuthenticationError < Error; end
8
+ class BudgetExceededError < Error; end
9
+ class PermissionDeniedError < Error; end
10
+ class StallDetectedError < Error; end
11
+ class ToolNotFoundError < Error; end
12
+ class ConfigError < Error; end
13
+
14
+ # Infrastructure
15
+ autoload :Config, "rubyn_code/config/settings"
16
+
17
+ # Database
18
+ module DB
19
+ autoload :Connection, "rubyn_code/db/connection"
20
+ autoload :Migrator, "rubyn_code/db/migrator"
21
+ autoload :Schema, "rubyn_code/db/schema"
22
+ end
23
+
24
+ # Auth
25
+ module Auth
26
+ autoload :OAuth, "rubyn_code/auth/oauth"
27
+ autoload :TokenStore, "rubyn_code/auth/token_store"
28
+ autoload :Server, "rubyn_code/auth/server"
29
+ end
30
+
31
+ # LLM
32
+ module LLM
33
+ autoload :Client, "rubyn_code/llm/client"
34
+ autoload :Streaming, "rubyn_code/llm/streaming"
35
+ autoload :MessageBuilder, "rubyn_code/llm/message_builder"
36
+ end
37
+
38
+ # Layer 1: Agent Loop
39
+ module Agent
40
+ autoload :Loop, "rubyn_code/agent/loop"
41
+ autoload :LoopDetector, "rubyn_code/agent/loop_detector"
42
+ autoload :Conversation, "rubyn_code/agent/conversation"
43
+ end
44
+
45
+ # Layer 2: Tool System
46
+ 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"
79
+ end
80
+
81
+ # Layer 3: Permissions
82
+ 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"
87
+ end
88
+
89
+ # Layer 4: Context Management
90
+ 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"
96
+ end
97
+
98
+ # Layer 5: Skills
99
+ module Skills
100
+ autoload :Loader, "rubyn_code/skills/loader"
101
+ autoload :Catalog, "rubyn_code/skills/catalog"
102
+ autoload :Document, "rubyn_code/skills/document"
103
+ end
104
+
105
+ # Layer 6: Sub-Agents
106
+ module SubAgents
107
+ autoload :Runner, "rubyn_code/sub_agents/runner"
108
+ autoload :Summarizer, "rubyn_code/sub_agents/summarizer"
109
+ end
110
+
111
+ # Layer 7: Tasks
112
+ module Tasks
113
+ autoload :Manager, "rubyn_code/tasks/manager"
114
+ autoload :DAG, "rubyn_code/tasks/dag"
115
+ autoload :Models, "rubyn_code/tasks/models"
116
+ end
117
+
118
+ # Layer 8: Background
119
+ module Background
120
+ autoload :Worker, "rubyn_code/background/worker"
121
+ autoload :Job, "rubyn_code/background/job"
122
+ autoload :Notifier, "rubyn_code/background/notifier"
123
+ end
124
+
125
+ # Layer 9: Teams
126
+ module Teams
127
+ autoload :Manager, "rubyn_code/teams/manager"
128
+ autoload :Mailbox, "rubyn_code/teams/mailbox"
129
+ autoload :Teammate, "rubyn_code/teams/teammate"
130
+ end
131
+
132
+ # Layer 10: Protocols
133
+ 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"
137
+ end
138
+
139
+ # Layer 11: Autonomous
140
+ 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"
144
+ end
145
+
146
+ # Layer 12: Memory
147
+ 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"
152
+ end
153
+
154
+ # Layer 13: Observability
155
+ 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"
161
+ end
162
+
163
+ # Layer 14: Hooks
164
+ 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"
169
+ end
170
+
171
+ # Layer 15: MCP
172
+ 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"
178
+ end
179
+
180
+ # Layer 16: Learning
181
+ 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"
186
+ end
187
+
188
+ # CLI
189
+ 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"
196
+ end
197
+
198
+ # Output
199
+ module Output
200
+ autoload :Formatter, "rubyn_code/output/formatter"
201
+ autoload :DiffRenderer, "rubyn_code/output/diff_renderer"
202
+ end
203
+ end
@@ -0,0 +1,189 @@
1
+ # Code Quality: Code That Fits in Your Head
2
+
3
+ ## Core Principle
4
+
5
+ The human brain can hold approximately 7 items in working memory at once. Code should be structured so that understanding any single unit (method, class, module) requires holding fewer than 7 concepts simultaneously. When code exceeds working memory limits, bugs creep in because developers can't track all the moving parts.
6
+
7
+ ## The 7±2 Rule Applied to Code
8
+
9
+ ### Methods: Max 7 Lines of Logic
10
+
11
+ A method should fit in your mental "scratchpad." If you can't hold the entire method in your head at once, it's too complex.
12
+
13
+ ```ruby
14
+ # GOOD: 5 concepts — easily fits in working memory
15
+ # 1. Load order 2. Check editable 3. Update 4. Success path 5. Failure path
16
+ def update
17
+ order = current_user.orders.find(params[:id])
18
+ return head :forbidden unless order.editable?
19
+
20
+ if order.update(order_params)
21
+ redirect_to order, notice: "Updated."
22
+ else
23
+ render :edit, status: :unprocessable_entity
24
+ end
25
+ end
26
+
27
+ # BAD: 12+ concepts — exceeds working memory
28
+ def update
29
+ order = current_user.orders.find(params[:id])
30
+ return head :forbidden unless order.editable?
31
+
32
+ old_total = order.total
33
+ order.assign_attributes(order_params)
34
+
35
+ order.line_items.each do |item|
36
+ product = Product.find(item.product_id)
37
+ if product.stock < item.quantity
38
+ order.errors.add(:base, "#{product.name} insufficient stock")
39
+ end
40
+ item.unit_price = product.current_price # Price may have changed
41
+ end
42
+
43
+ order.subtotal = order.line_items.sum { |li| li.quantity * li.unit_price }
44
+ order.tax = TaxService.calculate(order.subtotal, order.shipping_address)
45
+ order.total = order.subtotal + order.tax
46
+
47
+ if order.total != old_total && order.paid?
48
+ difference = order.total - old_total
49
+ if difference > 0
50
+ Payments::ChargeService.call(order, difference)
51
+ else
52
+ Payments::RefundService.call(order, difference.abs)
53
+ end
54
+ end
55
+
56
+ if order.errors.empty? && order.save
57
+ OrderMailer.updated(order).deliver_later if order.total != old_total
58
+ redirect_to order, notice: "Updated."
59
+ else
60
+ render :edit, status: :unprocessable_entity
61
+ end
62
+ end
63
+ ```
64
+
65
+ The second method requires tracking: order, old_total, line_items, products, stock levels, pricing changes, subtotal, tax, total, paid status, payment difference, charge vs refund, email condition, save result. That's 13+ concepts — far beyond working memory.
66
+
67
+ ### Classes: Max 7 Public Methods
68
+
69
+ A class with 3-7 public methods is graspable. You can understand its entire interface at a glance. Beyond 7, you start forgetting methods while reading others.
70
+
71
+ ```ruby
72
+ # GOOD: 5 public methods — clear, focused interface
73
+ class Order < ApplicationRecord
74
+ # Queries
75
+ scope :recent, -> { where(created_at: 30.days.ago..) }
76
+ scope :pending, -> { where(status: :pending) }
77
+
78
+ # Commands
79
+ def confirm!
80
+ current_state.confirm(self)
81
+ end
82
+
83
+ def cancel!(reason:)
84
+ current_state.cancel(self, reason: reason)
85
+ end
86
+
87
+ # Queries
88
+ def total
89
+ line_items.sum { |li| li.quantity * li.unit_price }
90
+ end
91
+
92
+ def editable?
93
+ pending? || confirmed?
94
+ end
95
+ end
96
+ ```
97
+
98
+ ### Parameters: Max 3-4
99
+
100
+ After 4 parameters, callers start confusing argument order, even with keyword arguments. Extract a parameter object or rethink the method's responsibility.
101
+
102
+ ```ruby
103
+ # GOOD: 2 parameters — trivially understandable
104
+ def send_notification(user, message)
105
+
106
+ # OK: 4 keyword parameters — manageable with names
107
+ def create_order(user:, items:, address:, payment_method:)
108
+
109
+ # BAD: 7 parameters — nobody can remember these
110
+ def create_order(user:, items:, address:, payment_method:, discount_code:, currency:, notify:)
111
+
112
+ # FIX: Extract a parameter object
113
+ OrderRequest = Data.define(:user, :items, :address, :payment_method, :discount_code, :currency, :notify)
114
+ def create_order(request)
115
+ ```
116
+
117
+ ### Nesting: Max 2 Levels
118
+
119
+ Each level of nesting adds a context to track. At 3+ levels, you're juggling too many conditions.
120
+
121
+ ```ruby
122
+ # GOOD: 1 level of nesting
123
+ def process(order)
124
+ return error("Empty") if order.line_items.empty?
125
+ return error("Invalid address") unless order.address_valid?
126
+
127
+ charge(order)
128
+ end
129
+
130
+ # BAD: 4 levels
131
+ def process(order)
132
+ if order.line_items.any?
133
+ if order.address_valid?
134
+ if order.user.payment_method.present?
135
+ if order.user.payment_method.valid?
136
+ charge(order)
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ ```
143
+
144
+ ## The Transformation Priority Principle
145
+
146
+ When your code is complex, simplify in this order:
147
+
148
+ 1. **Extract till you drop.** Break long methods into smaller ones until each method is 5-7 lines.
149
+ 2. **Flatten conditionals.** Use guard clauses to eliminate nesting.
150
+ 3. **Replace primitives with objects.** Turn strings and hashes into value objects with named methods.
151
+ 4. **Replace branching with polymorphism.** Case statements on type become separate classes.
152
+ 5. **Compose small objects.** Large classes become coordinators of small, focused collaborators.
153
+
154
+ ## Practical Heuristics
155
+
156
+ ### The Squint Test
157
+ Squint at your code. If the indentation forms a deep "V" shape (deep nesting), it needs flattening. If you see large blocks of similar-looking code, it has duplication.
158
+
159
+ ### The Headline Test
160
+ Can you describe what a method does in a short headline? "Calculates order total" — good. "Validates, calculates, charges, and notifies" — too many verbs, extract.
161
+
162
+ ### The Scroll Test
163
+ If a method or class requires scrolling to read in your editor, it's too long. A method should fit on screen. A class should fit in a few screens.
164
+
165
+ ### The Rename Test
166
+ If you can't think of a good name for a method, it probably does too many things. A method that does one thing always has a clear name. "Process" and "handle" are signs of unfocused methods.
167
+
168
+ ## When To Apply
169
+
170
+ - **Every code review.** Check: does each method fit in working memory? Can you understand it without scrolling back?
171
+ - **When you re-read code and feel confused.** If you wrote it last week and can't quickly understand it, future-you (and every teammate) will have the same problem.
172
+ - **When modifying existing code.** Before adding a feature to a 50-line method, extract until the method is small, then add the feature to the appropriate extracted method.
173
+
174
+ ## When NOT To Apply
175
+
176
+ - **Don't optimize for line count.** A 12-line method that reads clearly is better than 4 methods of 3 lines each where you lose the overall flow.
177
+ - **Don't extract if names don't improve clarity.** `do_step_1`, `do_step_2` are worse than inline code. Extract when the method name adds meaning.
178
+ - **Configuration and setup code is naturally longer.** A Rails initializer or a factory definition doesn't need to be 7 lines. The heuristics apply to logic, not configuration.
179
+
180
+ ## Connection to Other Principles
181
+
182
+ This principle underpins everything else:
183
+ - **SRP** keeps classes small enough to fit in your head
184
+ - **Extract Method** keeps methods small enough to fit in your head
185
+ - **Guard Clauses** reduce nesting to fit in your head
186
+ - **Value Objects** replace primitives so you think in domain terms, not data types
187
+ - **Service Objects** keep controllers small enough to fit in your head
188
+
189
+ The goal is always the same: any developer should be able to read a unit of code and fully understand it without external aids, notes, or extensive scrolling.
@@ -0,0 +1,213 @@
1
+ # Code Quality: Naming Conventions
2
+
3
+ ## Pattern
4
+
5
+ Good names are the cheapest documentation. A well-named method, class, or variable eliminates the need for comments and makes code read like prose. Ruby and Rails have strong naming conventions — follow them.
6
+
7
+ ### Ruby Naming Rules
8
+
9
+ ```ruby
10
+ # Classes and modules: CamelCase (PascalCase)
11
+ class OrderProcessor; end
12
+ module Authenticatable; end
13
+ class Api::V1::OrdersController; end
14
+
15
+ # Methods and variables: snake_case
16
+ def calculate_total; end
17
+ total_cents = 19_99
18
+ current_user = User.find(session[:user_id])
19
+
20
+ # Constants: SCREAMING_SNAKE_CASE
21
+ MAX_RETRIES = 3
22
+ DEFAULT_CURRENCY = "USD"
23
+ API_BASE_URL = "https://api.rubyn.ai"
24
+
25
+ # Predicates (boolean methods): end with ?
26
+ def active?; end
27
+ def can_cancel?; end
28
+ def shipped?; end
29
+ # Returns true/false. Never name it `is_active` or `check_active`.
30
+
31
+ # Dangerous methods: end with !
32
+ def save!; end # Raises on failure (vs save which returns false)
33
+ def destroy!; end # Raises on failure
34
+ def normalize!; end # Mutates in place (vs normalize which returns new value)
35
+ # ! means "this version does something surprising" — usually raises or mutates.
36
+
37
+ # Setters: end with =
38
+ def name=(value); end
39
+ attr_writer :name # Generates name= method
40
+
41
+ # Private/internal: prefix with _ (convention, not enforced)
42
+ def _build_cache_key; end
43
+ _temp_value = compute_intermediate_step
44
+ ```
45
+
46
+ ### Method Naming
47
+
48
+ ```ruby
49
+ # GOOD: Verb for actions, noun for queries
50
+ def create_order(params); end # Action: does something
51
+ def send_confirmation(order); end # Action: does something
52
+ def total; end # Query: returns a value
53
+ def line_items; end # Query: returns a collection
54
+ def pending?; end # Predicate: returns boolean
55
+
56
+ # GOOD: Specific verbs that communicate intent
57
+ def charge_payment(order); end # Specific: charges
58
+ def validate_inventory(items); end # Specific: validates
59
+ def generate_reference; end # Specific: generates
60
+
61
+ # BAD: Vague verbs that could mean anything
62
+ def process(order); end # Process how? Does it charge? Ship? Validate?
63
+ def handle(data); end # Handle what exactly?
64
+ def do_stuff; end # Meaningless
65
+ def run; end # What does it run?
66
+ def execute; end # Same problem
67
+
68
+ # GOOD: call for service objects (convention)
69
+ class Orders::CreateService
70
+ def self.call(params, user); end # .call is the Rails service object convention
71
+ end
72
+ # This works because the CLASS NAME describes the action (CreateService)
73
+ # so .call doesn't need to be more specific
74
+
75
+ # GOOD: Named constructors for clarity
76
+ Order.from_cart(cart, user: current_user)
77
+ Money.from_dollars(19.99)
78
+ DateRange.parse("2026-01-01..2026-03-20")
79
+ ```
80
+
81
+ ### Class Naming
82
+
83
+ ```ruby
84
+ # GOOD: Noun describing what it IS
85
+ class Order; end
86
+ class LineItem; end
87
+ class User; end
88
+
89
+ # GOOD: Adjective or role for modules (describes capability)
90
+ module Searchable; end
91
+ module Authenticatable; end
92
+ module Sluggable; end
93
+
94
+ # GOOD: Noun + purpose for service objects
95
+ class Orders::CreateService; end
96
+ class Credits::DeductionService; end
97
+ class Embeddings::CodebaseIndexer; end
98
+
99
+ # GOOD: Noun + type for specific patterns
100
+ class OrderPresenter; end # Presenter
101
+ class RegistrationForm; end # Form object
102
+ class OrdersSearchQuery; end # Query object
103
+ class StripeAdapter; end # Adapter
104
+ class EmailNotifier; end # Notifier
105
+
106
+ # BAD: Manager, Handler, Processor, Helper (vague — WHAT does it manage?)
107
+ class OrderManager; end # What aspect of orders?
108
+ class DataHandler; end # What data? What handling?
109
+ class OrderProcessor; end # Processes how?
110
+ class OrderHelper; end # Helps with what?
111
+
112
+ # FIX: Name the specific responsibility
113
+ class OrderManager → Orders::CreateService + Orders::CancelService
114
+ class DataHandler → CsvImporter + JsonParser
115
+ class OrderProcessor → Orders::FulfillmentService
116
+ ```
117
+
118
+ ### Variable Naming
119
+
120
+ ```ruby
121
+ # GOOD: Descriptive, matches the domain
122
+ active_users = User.where(active: true)
123
+ pending_orders = Order.pending
124
+ credit_balance = user.credit_ledger_entries.sum(:amount)
125
+ shipping_address = order.addresses.find_by(type: "shipping")
126
+
127
+ # GOOD: Plural for collections, singular for individuals
128
+ orders = Order.recent # Collection
129
+ order = orders.first # Single item
130
+ line_items = order.line_items # Collection
131
+ line_item = line_items.first # Single item
132
+
133
+ # BAD: Single-letter variables (except in tiny blocks)
134
+ u = User.find(params[:id]) # What's u?
135
+ o = u.orders.last # What's o?
136
+ x = o.total * 0.08 # What's x?
137
+
138
+ # OK: Single-letter in small, obvious blocks
139
+ users.map { |u| u.email } # OK — the block is 1 line, u is clearly a user
140
+ users.map(&:email) # BETTER — no variable needed
141
+
142
+ # BAD: Misleading names
143
+ user_count = User.pluck(:email) # It's emails, not a count
144
+ is_valid = order.save # It's a save result, not a validation check
145
+ temp = Order.where(status: :pending) # "temp" tells you nothing
146
+
147
+ # BAD: Hungarian notation or type prefixes
148
+ str_name = "Alice" # Ruby doesn't need type prefixes
149
+ arr_items = [1, 2, 3]
150
+ int_count = 5
151
+ hash_config = { timeout: 30 }
152
+ ```
153
+
154
+ ### Rails-Specific Conventions
155
+
156
+ ```ruby
157
+ # Controllers: plural noun + Controller
158
+ class OrdersController; end # Not OrderController
159
+ class Api::V1::UsersController; end
160
+
161
+ # Models: singular noun
162
+ class Order; end # Not Orders
163
+ class LineItem; end # Not LineItems
164
+
165
+ # Tables: plural snake_case (matches model pluralized)
166
+ # orders, line_items, users, credit_ledger_entries
167
+
168
+ # Foreign keys: singular_model_id
169
+ # order_id, user_id, product_id
170
+
171
+ # Join tables: alphabetical, both pluralized
172
+ # orders_products, categories_products
173
+
174
+ # Migrations: verb + noun
175
+ class AddStatusToOrders; end
176
+ class CreateProjectMemberships; end
177
+ class RemoveDeletedAtFromOrders; end
178
+
179
+ # Jobs: noun + Job
180
+ class OrderConfirmationJob; end
181
+ class CodebaseIndexJob; end
182
+
183
+ # Mailers: noun + Mailer
184
+ class OrderMailer; end
185
+ class UserMailer; end
186
+
187
+ # Serializers: singular model + Serializer
188
+ class OrderSerializer; end
189
+
190
+ # Factories: plural snake_case (matching table)
191
+ # spec/factories/orders.rb
192
+ # spec/factories/line_items.rb
193
+ ```
194
+
195
+ ## Why This Is Good
196
+
197
+ - **Convention eliminates decisions.** When every controller is `PluralNounController` and every service is `Noun::VerbService`, developers don't waste time choosing names. The convention chooses for them.
198
+ - **Names replace comments.** `calculate_shipping_cost` doesn't need a comment explaining what it does. `process` does.
199
+ - **Predictable file locations.** `Orders::CreateService` lives at `app/services/orders/create_service.rb`. The name maps to the path. You can find any class without grep.
200
+ - **`?` and `!` suffixes communicate behavior.** `save` returns false on failure. `save!` raises. The developer knows what to expect from the suffix alone.
201
+
202
+ ## The Rename Test
203
+
204
+ If you can't think of a good name for a method or class, it probably does too many things. A method that does one thing always has a clear name.
205
+
206
+ - ❌ `process_order` → What processing? Creating? Shipping? Billing? All three?
207
+ - ✅ `Orders::CreateService`, `Orders::ShipService`, `Orders::BillService`
208
+
209
+ - ❌ `handle_response` → Handle how?
210
+ - ✅ `parse_json_response`, `validate_response_status`, `extract_order_from_response`
211
+
212
+ - ❌ `UserManager` → Manages what about users?
213
+ - ✅ `Users::RegistrationService`, `Users::ProfileUpdater`, `Users::DeactivationService`