anima-core 0.2.1 → 1.0.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 (280) hide show
  1. checksums.yaml +4 -4
  2. data/.reek.yml +27 -1
  3. data/CHANGELOG.md +19 -0
  4. data/README.md +213 -43
  5. data/agents/codebase-analyzer.md +88 -0
  6. data/agents/codebase-pattern-finder.md +83 -0
  7. data/agents/documentation-researcher.md +59 -0
  8. data/agents/thoughts-analyzer.md +102 -0
  9. data/agents/web-search-researcher.md +71 -0
  10. data/anima-core.gemspec +3 -0
  11. data/app/channels/session_channel.rb +195 -45
  12. data/app/decorators/user_message_decorator.rb +16 -5
  13. data/app/jobs/agent_request_job.rb +55 -2
  14. data/app/jobs/analytical_brain_job.rb +33 -0
  15. data/app/jobs/count_event_tokens_job.rb +15 -4
  16. data/app/models/concerns/event/broadcasting.rb +81 -0
  17. data/app/models/event.rb +20 -1
  18. data/app/models/goal.rb +91 -0
  19. data/app/models/session.rb +366 -21
  20. data/config/application.rb +2 -0
  21. data/config/initializers/event_subscribers.rb +0 -1
  22. data/config/routes.rb +0 -6
  23. data/db/migrate/20260313010000_add_status_to_events.rb +8 -0
  24. data/db/migrate/20260313020000_add_processing_to_sessions.rb +7 -0
  25. data/db/migrate/20260314075248_add_subagent_support_to_sessions.rb +6 -0
  26. data/db/migrate/20260314112417_add_granted_tools_to_sessions.rb +5 -0
  27. data/db/migrate/20260314140000_add_name_to_sessions.rb +7 -0
  28. data/db/migrate/20260314150000_add_viewport_event_ids_to_sessions.rb +7 -0
  29. data/db/migrate/20260315100000_add_active_skills_to_sessions.rb +7 -0
  30. data/db/migrate/20260315140843_create_goals.rb +16 -0
  31. data/db/migrate/20260315144837_add_completed_at_to_goals.rb +5 -0
  32. data/db/migrate/20260315191105_add_active_workflow_to_sessions.rb +5 -0
  33. data/lib/agent_loop.rb +65 -6
  34. data/lib/agents/definition.rb +116 -0
  35. data/lib/agents/registry.rb +106 -0
  36. data/lib/analytical_brain/runner.rb +276 -0
  37. data/lib/analytical_brain/tools/activate_skill.rb +52 -0
  38. data/lib/analytical_brain/tools/deactivate_skill.rb +43 -0
  39. data/lib/analytical_brain/tools/deactivate_workflow.rb +34 -0
  40. data/lib/analytical_brain/tools/everything_is_ready.rb +28 -0
  41. data/lib/analytical_brain/tools/finish_goal.rb +62 -0
  42. data/lib/analytical_brain/tools/read_workflow.rb +58 -0
  43. data/lib/analytical_brain/tools/rename_session.rb +63 -0
  44. data/lib/analytical_brain/tools/set_goal.rb +60 -0
  45. data/lib/analytical_brain/tools/update_goal.rb +60 -0
  46. data/lib/analytical_brain.rb +23 -0
  47. data/lib/anima/cli/mcp/secrets.rb +76 -0
  48. data/lib/anima/cli/mcp.rb +197 -0
  49. data/lib/anima/cli.rb +5 -40
  50. data/lib/anima/installer.rb +168 -0
  51. data/lib/anima/settings.rb +226 -0
  52. data/lib/anima/version.rb +1 -1
  53. data/lib/anima.rb +9 -0
  54. data/lib/credential_store.rb +103 -0
  55. data/lib/environment_probe.rb +232 -0
  56. data/lib/events/subscribers/persister.rb +1 -0
  57. data/lib/events/user_message.rb +17 -0
  58. data/lib/llm/client.rb +29 -10
  59. data/lib/mcp/client_manager.rb +86 -0
  60. data/lib/mcp/config.rb +213 -0
  61. data/lib/mcp/health_check.rb +77 -0
  62. data/lib/mcp/secrets.rb +73 -0
  63. data/lib/mcp/stdio_transport.rb +206 -0
  64. data/lib/providers/anthropic.rb +11 -20
  65. data/lib/shell_session.rb +11 -10
  66. data/lib/skills/definition.rb +97 -0
  67. data/lib/skills/registry.rb +105 -0
  68. data/lib/tools/edit.rb +226 -0
  69. data/lib/tools/mcp_tool.rb +114 -0
  70. data/lib/tools/read.rb +151 -0
  71. data/lib/tools/registry.rb +14 -12
  72. data/lib/tools/request_feature.rb +121 -0
  73. data/lib/tools/return_result.rb +81 -0
  74. data/lib/tools/spawn_specialist.rb +109 -0
  75. data/lib/tools/spawn_subagent.rb +111 -0
  76. data/lib/tools/subagent_prompts.rb +12 -0
  77. data/lib/tools/web_get.rb +8 -9
  78. data/lib/tools/write.rb +86 -0
  79. data/lib/tui/app.rb +985 -26
  80. data/lib/tui/cable_client.rb +69 -31
  81. data/lib/tui/message_store.rb +103 -8
  82. data/lib/tui/screens/chat.rb +293 -45
  83. data/lib/workflows/definition.rb +97 -0
  84. data/lib/workflows/registry.rb +89 -0
  85. data/skills/activerecord/SKILL.md +255 -0
  86. data/skills/activerecord/examples/associations/association_extensions.rb +298 -0
  87. data/skills/activerecord/examples/associations/basic_associations.rb +118 -0
  88. data/skills/activerecord/examples/associations/counter_caches.rb +215 -0
  89. data/skills/activerecord/examples/associations/polymorphic_associations.rb +217 -0
  90. data/skills/activerecord/examples/associations/self_referential.rb +302 -0
  91. data/skills/activerecord/examples/associations/through_associations.rb +203 -0
  92. data/skills/activerecord/examples/basics/crud_operations.rb +209 -0
  93. data/skills/activerecord/examples/basics/dirty_tracking.rb +218 -0
  94. data/skills/activerecord/examples/basics/inheritance.rb +377 -0
  95. data/skills/activerecord/examples/basics/type_casting.rb +317 -0
  96. data/skills/activerecord/examples/callbacks/alternatives_to_callbacks.rb +447 -0
  97. data/skills/activerecord/examples/callbacks/conditional_callbacks.rb +353 -0
  98. data/skills/activerecord/examples/callbacks/lifecycle_callbacks.rb +280 -0
  99. data/skills/activerecord/examples/callbacks/transaction_callbacks.rb +340 -0
  100. data/skills/activerecord/examples/migrations/indexes_and_constraints.rb +337 -0
  101. data/skills/activerecord/examples/migrations/reversible_patterns.rb +403 -0
  102. data/skills/activerecord/examples/migrations/safe_patterns.rb +420 -0
  103. data/skills/activerecord/examples/migrations/schema_changes.rb +277 -0
  104. data/skills/activerecord/examples/querying/batch_processing.rb +226 -0
  105. data/skills/activerecord/examples/querying/eager_loading.rb +259 -0
  106. data/skills/activerecord/examples/querying/finder_methods.rb +170 -0
  107. data/skills/activerecord/examples/querying/optimization.rb +275 -0
  108. data/skills/activerecord/examples/querying/scopes.rb +260 -0
  109. data/skills/activerecord/examples/validations/built_in_validators.rb +277 -0
  110. data/skills/activerecord/examples/validations/conditional_validations.rb +288 -0
  111. data/skills/activerecord/examples/validations/custom_validators.rb +381 -0
  112. data/skills/activerecord/examples/validations/database_constraints.rb +432 -0
  113. data/skills/activerecord/examples/validations/validation_contexts.rb +367 -0
  114. data/skills/activerecord/references/associations.md +709 -0
  115. data/skills/activerecord/references/basics.md +622 -0
  116. data/skills/activerecord/references/callbacks.md +738 -0
  117. data/skills/activerecord/references/migrations.md +657 -0
  118. data/skills/activerecord/references/querying.md +655 -0
  119. data/skills/activerecord/references/validations.md +596 -0
  120. data/skills/dragonruby/SKILL.md +250 -0
  121. data/skills/dragonruby/examples/audio/audio_events.rb +55 -0
  122. data/skills/dragonruby/examples/audio/background_music.rb +29 -0
  123. data/skills/dragonruby/examples/audio/crossfade.rb +51 -0
  124. data/skills/dragonruby/examples/audio/music_controls.rb +51 -0
  125. data/skills/dragonruby/examples/audio/sound_effects.rb +30 -0
  126. data/skills/dragonruby/examples/core/coordinate_system.rb +27 -0
  127. data/skills/dragonruby/examples/core/hello_world.rb +24 -0
  128. data/skills/dragonruby/examples/core/labels.rb +22 -0
  129. data/skills/dragonruby/examples/core/sprites.rb +35 -0
  130. data/skills/dragonruby/examples/core/state_management.rb +29 -0
  131. data/skills/dragonruby/examples/distribution/background_pause.rb +42 -0
  132. data/skills/dragonruby/examples/distribution/build_workflow.sh +26 -0
  133. data/skills/dragonruby/examples/distribution/cvars_production.txt +16 -0
  134. data/skills/dragonruby/examples/distribution/game_metadata_hd.txt +23 -0
  135. data/skills/dragonruby/examples/distribution/game_metadata_minimal.txt +9 -0
  136. data/skills/dragonruby/examples/distribution/game_metadata_mobile.txt +31 -0
  137. data/skills/dragonruby/examples/distribution/platform_detection.rb +36 -0
  138. data/skills/dragonruby/examples/distribution/steam_metadata.txt +19 -0
  139. data/skills/dragonruby/examples/entities/collision_detection.rb +43 -0
  140. data/skills/dragonruby/examples/entities/entity_lifecycle.rb +68 -0
  141. data/skills/dragonruby/examples/entities/entity_storage.rb +38 -0
  142. data/skills/dragonruby/examples/entities/factory_methods.rb +45 -0
  143. data/skills/dragonruby/examples/entities/random_spawning.rb +50 -0
  144. data/skills/dragonruby/examples/game-logic/reset_patterns.rb +98 -0
  145. data/skills/dragonruby/examples/game-logic/save_load.rb +101 -0
  146. data/skills/dragonruby/examples/game-logic/scoring.rb +104 -0
  147. data/skills/dragonruby/examples/game-logic/state_transitions.rb +103 -0
  148. data/skills/dragonruby/examples/game-logic/timers.rb +87 -0
  149. data/skills/dragonruby/examples/input/action_triggers.rb +36 -0
  150. data/skills/dragonruby/examples/input/analog_movement.rb +28 -0
  151. data/skills/dragonruby/examples/input/controller_input.rb +28 -0
  152. data/skills/dragonruby/examples/input/directional_input.rb +24 -0
  153. data/skills/dragonruby/examples/input/keyboard_input.rb +28 -0
  154. data/skills/dragonruby/examples/input/mouse_click.rb +26 -0
  155. data/skills/dragonruby/examples/input/movement_with_bounds.rb +22 -0
  156. data/skills/dragonruby/examples/input/normalized_movement.rb +32 -0
  157. data/skills/dragonruby/examples/rendering/frame_animation.rb +32 -0
  158. data/skills/dragonruby/examples/rendering/labels.rb +32 -0
  159. data/skills/dragonruby/examples/rendering/layering.rb +51 -0
  160. data/skills/dragonruby/examples/rendering/solids.rb +61 -0
  161. data/skills/dragonruby/examples/rendering/sprites.rb +33 -0
  162. data/skills/dragonruby/examples/rendering/spritesheet_animation.rb +39 -0
  163. data/skills/dragonruby/examples/scenes/case_dispatch.rb +60 -0
  164. data/skills/dragonruby/examples/scenes/class_based.rb +150 -0
  165. data/skills/dragonruby/examples/scenes/pause_overlay.rb +100 -0
  166. data/skills/dragonruby/examples/scenes/safe_transitions.rb +68 -0
  167. data/skills/dragonruby/examples/scenes/scene_transitions.rb +98 -0
  168. data/skills/dragonruby/examples/scenes/send_dispatch.rb +88 -0
  169. data/skills/dragonruby/references/audio.md +396 -0
  170. data/skills/dragonruby/references/core.md +385 -0
  171. data/skills/dragonruby/references/distribution.md +434 -0
  172. data/skills/dragonruby/references/entities.md +516 -0
  173. data/skills/dragonruby/references/game-logic/persistence.md +386 -0
  174. data/skills/dragonruby/references/game-logic/state.md +389 -0
  175. data/skills/dragonruby/references/input.md +414 -0
  176. data/skills/dragonruby/references/rendering/animation.md +467 -0
  177. data/skills/dragonruby/references/rendering/primitives.md +403 -0
  178. data/skills/dragonruby/references/scenes.md +443 -0
  179. data/skills/draper-decorators/SKILL.md +344 -0
  180. data/skills/draper-decorators/examples/application_decorator.rb +61 -0
  181. data/skills/draper-decorators/examples/decorator_spec.rb +253 -0
  182. data/skills/draper-decorators/examples/model_decorator.rb +152 -0
  183. data/skills/draper-decorators/references/anti-patterns.md +640 -0
  184. data/skills/draper-decorators/references/patterns.md +507 -0
  185. data/skills/draper-decorators/references/testing.md +559 -0
  186. data/skills/gh-issue.md +182 -0
  187. data/skills/mcp-server/SKILL.md +177 -0
  188. data/skills/mcp-server/examples/dynamic_tools.rb +36 -0
  189. data/skills/mcp-server/examples/file_manager_tool.rb +85 -0
  190. data/skills/mcp-server/examples/http_client.rb +48 -0
  191. data/skills/mcp-server/examples/http_server.rb +97 -0
  192. data/skills/mcp-server/examples/rails_integration.rb +88 -0
  193. data/skills/mcp-server/examples/stdio_server.rb +108 -0
  194. data/skills/mcp-server/examples/streaming_client.rb +95 -0
  195. data/skills/mcp-server/references/gotchas.md +183 -0
  196. data/skills/mcp-server/references/prompts.md +98 -0
  197. data/skills/mcp-server/references/resources.md +53 -0
  198. data/skills/mcp-server/references/server.md +140 -0
  199. data/skills/mcp-server/references/tools.md +146 -0
  200. data/skills/mcp-server/references/transport.md +104 -0
  201. data/skills/ratatui-ruby/SKILL.md +315 -0
  202. data/skills/ratatui-ruby/references/core-concepts.md +340 -0
  203. data/skills/ratatui-ruby/references/events.md +387 -0
  204. data/skills/ratatui-ruby/references/frameworks.md +522 -0
  205. data/skills/ratatui-ruby/references/layout.md +423 -0
  206. data/skills/ratatui-ruby/references/styling.md +268 -0
  207. data/skills/ratatui-ruby/references/testing.md +433 -0
  208. data/skills/ratatui-ruby/references/widgets.md +532 -0
  209. data/skills/rspec/SKILL.md +340 -0
  210. data/skills/rspec/examples/core/basic_structure.rb +69 -0
  211. data/skills/rspec/examples/core/configuration.rb +126 -0
  212. data/skills/rspec/examples/core/hooks.rb +126 -0
  213. data/skills/rspec/examples/core/memoized_helpers.rb +139 -0
  214. data/skills/rspec/examples/core/metadata_filtering.rb +144 -0
  215. data/skills/rspec/examples/core/shared_examples.rb +145 -0
  216. data/skills/rspec/examples/factory_bot/associations.rb +314 -0
  217. data/skills/rspec/examples/factory_bot/build_strategies.rb +272 -0
  218. data/skills/rspec/examples/factory_bot/callbacks.rb +320 -0
  219. data/skills/rspec/examples/factory_bot/custom_construction.rb +328 -0
  220. data/skills/rspec/examples/factory_bot/factory_definition.rb +191 -0
  221. data/skills/rspec/examples/factory_bot/inheritance.rb +314 -0
  222. data/skills/rspec/examples/factory_bot/traits.rb +293 -0
  223. data/skills/rspec/examples/factory_bot/transients.rb +229 -0
  224. data/skills/rspec/examples/matchers/change.rb +115 -0
  225. data/skills/rspec/examples/matchers/collections.rb +154 -0
  226. data/skills/rspec/examples/matchers/comparisons.rb +79 -0
  227. data/skills/rspec/examples/matchers/composing.rb +155 -0
  228. data/skills/rspec/examples/matchers/custom_matchers.rb +197 -0
  229. data/skills/rspec/examples/matchers/equality.rb +58 -0
  230. data/skills/rspec/examples/matchers/errors.rb +136 -0
  231. data/skills/rspec/examples/matchers/output.rb +103 -0
  232. data/skills/rspec/examples/matchers/predicates.rb +87 -0
  233. data/skills/rspec/examples/matchers/truthiness.rb +101 -0
  234. data/skills/rspec/examples/matchers/types.rb +82 -0
  235. data/skills/rspec/examples/matchers/yield.rb +147 -0
  236. data/skills/rspec/examples/mocks/any_instance.rb +172 -0
  237. data/skills/rspec/examples/mocks/argument_matchers.rb +206 -0
  238. data/skills/rspec/examples/mocks/constants.rb +177 -0
  239. data/skills/rspec/examples/mocks/doubles.rb +139 -0
  240. data/skills/rspec/examples/mocks/expectations.rb +137 -0
  241. data/skills/rspec/examples/mocks/message_chains.rb +173 -0
  242. data/skills/rspec/examples/mocks/ordering.rb +144 -0
  243. data/skills/rspec/examples/mocks/receive_counts.rb +181 -0
  244. data/skills/rspec/examples/mocks/responses.rb +223 -0
  245. data/skills/rspec/examples/mocks/spies.rb +149 -0
  246. data/skills/rspec/examples/mocks/stubbing.rb +133 -0
  247. data/skills/rspec/examples/rails/channels.rb +250 -0
  248. data/skills/rspec/examples/rails/controller_specs.rb +302 -0
  249. data/skills/rspec/examples/rails/helper_specs.rb +245 -0
  250. data/skills/rspec/examples/rails/job_specs.rb +256 -0
  251. data/skills/rspec/examples/rails/mailer_specs.rb +228 -0
  252. data/skills/rspec/examples/rails/matchers.rb +374 -0
  253. data/skills/rspec/examples/rails/model_specs.rb +193 -0
  254. data/skills/rspec/examples/rails/request_specs.rb +275 -0
  255. data/skills/rspec/examples/rails/routing_specs.rb +276 -0
  256. data/skills/rspec/examples/rails/system_specs.rb +294 -0
  257. data/skills/rspec/examples/rails/transactions.rb +254 -0
  258. data/skills/rspec/examples/rails/view_specs.rb +252 -0
  259. data/skills/rspec/references/core.md +816 -0
  260. data/skills/rspec/references/factory_bot.md +641 -0
  261. data/skills/rspec/references/matchers.md +516 -0
  262. data/skills/rspec/references/mocks.md +381 -0
  263. data/skills/rspec/references/rails.md +528 -0
  264. data/templates/soul.md +40 -0
  265. data/workflows/commit.md +45 -0
  266. data/workflows/create_handoff.md +98 -0
  267. data/workflows/create_note.md +82 -0
  268. data/workflows/create_plan.md +457 -0
  269. data/workflows/decompose_ticket.md +109 -0
  270. data/workflows/feature.md +91 -0
  271. data/workflows/implement_plan.md +87 -0
  272. data/workflows/iterate_plan.md +247 -0
  273. data/workflows/research_codebase.md +210 -0
  274. data/workflows/resume_handoff.md +217 -0
  275. data/workflows/review_pr.md +320 -0
  276. data/workflows/thoughts_init.md +71 -0
  277. data/workflows/validate_plan.md +166 -0
  278. metadata +290 -3
  279. data/app/controllers/api/sessions_controller.rb +0 -25
  280. data/lib/events/subscribers/action_cable_bridge.rb +0 -59
@@ -0,0 +1,140 @@
1
+ # MCP Server Reference
2
+
3
+ ## Server Initialization
4
+
5
+ ```ruby
6
+ MCP::Server.new(
7
+ # Required
8
+ name: "server_name",
9
+
10
+ # Metadata
11
+ version: "1.0.0", # Default: "0.1.0"
12
+ description: "Server description", # Protocol 2025-11-25+
13
+ title: "Human-Readable Name", # Protocol 2025-06-18+
14
+ website_url: "https://example.com", # Protocol 2025-06-18+
15
+ instructions: "Usage instructions", # Protocol 2025-03-26+
16
+
17
+ # Components
18
+ tools: [ToolClass1, ToolClass2],
19
+ prompts: [PromptClass1],
20
+ resources: [resource_instance],
21
+ resource_templates: [template_instance],
22
+
23
+ # Runtime
24
+ server_context: { key: "value" },
25
+ transport: transport_instance,
26
+
27
+ # Configuration
28
+ configuration: MCP::Configuration.new(...),
29
+ capabilities: { # Override auto-detected
30
+ tools: { listChanged: true },
31
+ prompts: { listChanged: true },
32
+ resources: { listChanged: true }
33
+ }
34
+ )
35
+ ```
36
+
37
+ ## Configuration
38
+
39
+ ```ruby
40
+ MCP::Configuration.new(
41
+ protocol_version: "2025-11-25", # Or: "2025-06-18", "2025-03-26", "2024-11-05"
42
+ exception_reporter: ->(exception, server_context) { ... },
43
+ instrumentation_callback: ->(data) { ... },
44
+ validate_tool_call_arguments: true # Default: true
45
+ )
46
+
47
+ # Global configuration
48
+ MCP.configure do |config|
49
+ config.protocol_version = "2025-11-25"
50
+ config.exception_reporter = ->(e, ctx) { ErrorTracker.report(e, ctx) }
51
+ config.instrumentation_callback = ->(data) { StatsD.increment(data[:method]) }
52
+ end
53
+ ```
54
+
55
+ ### Instrumentation Data Keys
56
+
57
+ ```ruby
58
+ {
59
+ method: "tools/call", # JSON-RPC method
60
+ tool_name: "my_tool", # For tools/call
61
+ prompt_name: "my_prompt", # For prompts/get
62
+ resource_uri: "file://x", # For resources/read
63
+ error: :tool_not_found, # Error symbol if failed
64
+ duration: 0.123 # Seconds (auto-added)
65
+ }
66
+ ```
67
+
68
+ ## Handler Overrides
69
+
70
+ ```ruby
71
+ # Replace default handlers
72
+ server.tools_list_handler do |params|
73
+ [{ name: "custom", description: "Custom tool" }]
74
+ end
75
+
76
+ server.tools_call_handler do |params|
77
+ MCP::Tool::Response.new([{ type: "text", text: "OK" }]).to_h
78
+ end
79
+
80
+ server.prompts_list_handler do |params|
81
+ [{ name: "custom", description: "Custom prompt" }]
82
+ end
83
+
84
+ server.prompts_get_handler do |params|
85
+ MCP::Prompt::Result.new(messages: [...]).to_h
86
+ end
87
+
88
+ server.resources_list_handler do |params|
89
+ [{ uri: "file:///x", name: "x" }]
90
+ end
91
+
92
+ server.resources_read_handler do |params|
93
+ { uri: params[:uri], mimeType: "text/plain", text: "Content" }
94
+ end
95
+
96
+ server.resources_templates_list_handler do |params|
97
+ [{ uriTemplate: "file:///{id}", name: "template" }]
98
+ end
99
+ ```
100
+
101
+ ## Custom Methods
102
+
103
+ ```ruby
104
+ # Define custom JSON-RPC method
105
+ server.define_custom_method(method_name: "custom/method") do |params|
106
+ { result: params[:value] * 2 } # Return value becomes result
107
+ end
108
+
109
+ # Notification method (return nil)
110
+ server.define_custom_method(method_name: "custom/notify") do |params|
111
+ process_notification(params)
112
+ nil # No response sent
113
+ end
114
+ ```
115
+
116
+ ## Notifications
117
+
118
+ ```ruby
119
+ # Requires: server.transport = transport
120
+ server.notify_tools_list_changed
121
+ server.notify_prompts_list_changed
122
+ server.notify_resources_list_changed
123
+ ```
124
+
125
+ ---
126
+
127
+ ## See Also
128
+
129
+ ### Related References
130
+ - **[Tools](tools.md)** - Tool definition DSL and response types
131
+ - **[Prompts](prompts.md)** - Prompt definition and content types
132
+ - **[Resources](resources.md)** - Resource and template definitions
133
+ - **[Transport](transport.md)** - STDIO and HTTP transport configuration
134
+ - **[Gotchas](gotchas.md)** - Configuration merging, notification quirks
135
+
136
+ ### Related Examples
137
+ - **[`../examples/stdio_server.rb`](../examples/stdio_server.rb)** - Complete server with all components
138
+ - **[`../examples/http_server.rb`](../examples/http_server.rb)** - HTTP server with Rack
139
+ - **[`../examples/rails_integration.rb`](../examples/rails_integration.rb)** - Rails controller and routes
140
+ - **[`../examples/dynamic_tools.rb`](../examples/dynamic_tools.rb)** - Runtime registration with notifications
@@ -0,0 +1,146 @@
1
+ # MCP Tools Reference
2
+
3
+ ## Tool Definition
4
+
5
+ Three patterns for defining tools:
6
+
7
+ ### Class-Based (recommended)
8
+
9
+ ```ruby
10
+ class MyTool < MCP::Tool
11
+ # Name (optional - defaults to snake_case of class name)
12
+ tool_name "custom_name"
13
+
14
+ # Metadata
15
+ title "Human Title"
16
+ description "What this tool does"
17
+
18
+ # Input validation (JSON Schema)
19
+ input_schema(
20
+ type: "object", # Default if omitted
21
+ properties: {
22
+ message: { type: "string", minLength: 1 },
23
+ count: { type: "integer", minimum: 0, maximum: 100 },
24
+ options: {
25
+ type: "object",
26
+ properties: { verbose: { type: "boolean" } }
27
+ },
28
+ tags: {
29
+ type: "array",
30
+ items: { type: "string" }
31
+ }
32
+ },
33
+ required: ["message"],
34
+ additionalProperties: false # Strict mode
35
+ )
36
+
37
+ # Output documentation (optional)
38
+ output_schema(
39
+ properties: {
40
+ result: { type: "string" },
41
+ success: { type: "boolean" }
42
+ },
43
+ required: ["result", "success"]
44
+ )
45
+
46
+ # Behavior hints (protocol 2025-03-26+)
47
+ annotations(
48
+ read_only_hint: false, # Default: false
49
+ destructive_hint: true, # Default: true
50
+ idempotent_hint: false, # Default: false
51
+ open_world_hint: true, # Default: true
52
+ title: "Tool Title"
53
+ )
54
+
55
+ # Custom metadata
56
+ meta(version: "1.0", author: "Team")
57
+
58
+ # Implementation
59
+ class << self
60
+ def call(message:, count: 1, options: {}, server_context: nil)
61
+ MCP::Tool::Response.new([
62
+ { type: "text", text: "Result" }
63
+ ])
64
+ end
65
+ end
66
+ end
67
+ ```
68
+
69
+ ### Block-Based (Tool.define)
70
+
71
+ ```ruby
72
+ tool = MCP::Tool.define(
73
+ name: "tool_name",
74
+ title: "Tool Title",
75
+ description: "Description",
76
+ input_schema: { properties: { x: { type: "string" } } },
77
+ output_schema: { properties: { y: { type: "string" } } },
78
+ annotations: { read_only_hint: true },
79
+ meta: { version: "1.0" }
80
+ ) do |x:, server_context: nil|
81
+ MCP::Tool::Response.new([{ type: "text", text: x }])
82
+ end
83
+ ```
84
+
85
+ ### Dynamic (server.define_tool)
86
+
87
+ ```ruby
88
+ server.define_tool(
89
+ name: "dynamic_tool",
90
+ description: "Added at runtime",
91
+ input_schema: { properties: { arg: { type: "string" } } }
92
+ ) do |arg:, server_context:|
93
+ MCP::Tool::Response.new([{ type: "text", text: arg }])
94
+ end
95
+ ```
96
+
97
+ ## Tool Response
98
+
99
+ ```ruby
100
+ MCP::Tool::Response.new(
101
+ content, # Array of content items
102
+ error: false, # Mark as error without exception
103
+ structured_content: nil # Machine-readable data
104
+ )
105
+
106
+ # Content item types
107
+ { type: "text", text: "..." }
108
+ { type: "image", data: "base64...", mimeType: "image/png" }
109
+ { type: "resource", resource: { uri: "...", ... } }
110
+ ```
111
+
112
+ ## Schema Objects
113
+
114
+ ```ruby
115
+ # As Hash (auto-converted)
116
+ input_schema({ properties: { x: { type: "string" } } })
117
+
118
+ # As InputSchema object
119
+ input_schema MCP::Tool::InputSchema.new(
120
+ properties: { x: { type: "string" } },
121
+ required: ["x"]
122
+ )
123
+
124
+ # Schema methods
125
+ schema.to_h
126
+ schema.missing_required_arguments({ x: "value" }) # => []
127
+ schema.validate_arguments({ x: 123 }) # Raises ValidationError
128
+ ```
129
+
130
+ **Constraints:**
131
+ - `$ref` is NOT allowed (raises ArgumentError)
132
+ - Type defaults to "object" if not specified
133
+ - Required fields auto-convert symbols to strings
134
+
135
+ ---
136
+
137
+ ## See Also
138
+
139
+ ### Related References
140
+ - **[Server](server.md)** - Dynamic tool registration with `server.define_tool`
141
+ - **[Gotchas](gotchas.md)** - Schema quirks, tool name derivation, validation flow
142
+
143
+ ### Related Examples
144
+ - **[`../examples/stdio_server.rb`](../examples/stdio_server.rb)** - Class-based and dynamic tool definitions
145
+ - **[`../examples/file_manager_tool.rb`](../examples/file_manager_tool.rb)** - Complex tool with security patterns
146
+ - **[`../examples/dynamic_tools.rb`](../examples/dynamic_tools.rb)** - Runtime tool registration
@@ -0,0 +1,104 @@
1
+ # MCP Transport Reference
2
+
3
+ ## Transport Configuration
4
+
5
+ ### StdioTransport
6
+
7
+ ```ruby
8
+ transport = MCP::Server::Transports::StdioTransport.new(server)
9
+ transport.open # Blocking loop
10
+ transport.close # Signal stop
11
+ transport.send_response({ jsonrpc: "2.0", result: {} })
12
+ transport.send_notification("method", { key: "value" })
13
+ ```
14
+
15
+ ### StreamableHTTPTransport
16
+
17
+ ```ruby
18
+ transport = MCP::Server::Transports::StreamableHTTPTransport.new(
19
+ server,
20
+ stateless: false # Default: false (session-based)
21
+ )
22
+
23
+ # Link to server for notifications
24
+ server.transport = transport
25
+
26
+ # Handle Rack requests
27
+ response = transport.handle_request(rack_request)
28
+ # Returns: [status, headers, body]
29
+
30
+ # Send notifications
31
+ transport.send_notification("method", params, session_id: id) # Specific session
32
+ transport.send_notification("method", params) # Broadcast to all
33
+ ```
34
+
35
+ ## Request Handling
36
+
37
+ ```ruby
38
+ # Handle Hash request
39
+ response = server.handle({
40
+ jsonrpc: "2.0",
41
+ method: "tools/call",
42
+ params: { name: "my_tool", arguments: { x: 1 } },
43
+ id: 1
44
+ })
45
+
46
+ # Handle JSON string
47
+ json_response = server.handle_json('{"jsonrpc":"2.0",...}')
48
+ ```
49
+
50
+ ## Protocol Methods
51
+
52
+ ```ruby
53
+ # Core methods
54
+ "initialize"
55
+ "ping"
56
+ "tools/list"
57
+ "tools/call"
58
+ "prompts/list"
59
+ "prompts/get"
60
+ "resources/list"
61
+ "resources/read"
62
+ "resources/templates/list"
63
+
64
+ # Notifications (no response)
65
+ "notifications/initialized"
66
+ "notifications/tools/list_changed"
67
+ "notifications/prompts/list_changed"
68
+ "notifications/resources/list_changed"
69
+ ```
70
+
71
+ ## Error Classes
72
+
73
+ ```ruby
74
+ MCP::Server::RequestHandlerError # General request errors
75
+ MCP::Server::MethodAlreadyDefinedError # Duplicate custom method
76
+ MCP::Server::ToolNotUnique # Duplicate tool names
77
+ MCP::Tool::InputSchema::ValidationError # Schema validation failed
78
+ MCP::Tool::OutputSchema::ValidationError # Output validation failed
79
+ MCP::Methods::MissingRequiredCapabilityError # Capability not supported
80
+ ```
81
+
82
+ ## Protocol Versions
83
+
84
+ | Version | Features Added |
85
+ |---------|----------------|
86
+ | 2024-11-05 | Base protocol |
87
+ | 2025-03-26 | `instructions`, `annotations`, `title` (server) |
88
+ | 2025-06-18 | `title`, `website_url` |
89
+ | 2025-11-25 | `description` (latest stable) |
90
+
91
+ ---
92
+
93
+ ## See Also
94
+
95
+ ### Related References
96
+ - **[Server](server.md)** - Server initialization and `server.transport` linking
97
+ - **[Gotchas](gotchas.md)** - StreamableHTTP quirks, notification behavior, error handling
98
+
99
+ ### Related Examples
100
+ - **[`../examples/stdio_server.rb`](../examples/stdio_server.rb)** - STDIO transport usage
101
+ - **[`../examples/http_server.rb`](../examples/http_server.rb)** - Streamable HTTP with Rack
102
+ - **[`../examples/rails_integration.rb`](../examples/rails_integration.rb)** - HTTP transport in Rails
103
+ - **[`../examples/http_client.rb`](../examples/http_client.rb)** - Client connecting to HTTP server
104
+ - **[`../examples/streaming_client.rb`](../examples/streaming_client.rb)** - SSE streaming client
@@ -0,0 +1,315 @@
1
+ ---
2
+ name: ratatui-ruby
3
+ description: "RatatuiRuby terminal UI development — widgets, layouts, events, Tea MVU. Activate when building TUI apps, working with RatatuiRuby, terminal rendering, or editing TUI application files."
4
+ ---
5
+
6
+ # RatatuiRuby
7
+
8
+ This skill provides guidance for building terminal user interfaces with RatatuiRuby, a Ruby gem wrapping Rust's Ratatui library. Use for TUI development, terminal widgets, layout systems, event handling, and testing terminal applications.
9
+
10
+ ## Quick Reference
11
+
12
+ ### Minimal Application
13
+
14
+ ```ruby
15
+ require "ratatui_ruby"
16
+
17
+ RatatuiRuby.run do |tui|
18
+ loop do
19
+ tui.draw do |frame|
20
+ widget = tui.paragraph(text: "Hello, TUI!", block: tui.block(title: "App"))
21
+ frame.render_widget(widget, frame.area)
22
+ end
23
+
24
+ case tui.poll_event
25
+ in {type: :key, code: "q"}
26
+ break
27
+ in {type: :key, code: "c", modifiers: ["ctrl"]}
28
+ break
29
+ else
30
+ # Continue
31
+ end
32
+ end
33
+ end
34
+ ```
35
+
36
+ ### Key Concepts
37
+
38
+ | Concept | Purpose |
39
+ |---------|---------|
40
+ | `RatatuiRuby.run` | Managed loop handling terminal setup/teardown |
41
+ | `tui.draw` | Render widgets each frame |
42
+ | `tui.poll_event` | Capture keyboard/mouse input |
43
+ | `frame.area` | Available rendering area (Rect) |
44
+ | `frame.render_widget` | Render stateless widgets |
45
+ | `frame.render_stateful_widget` | Render widgets with state (List, Table) |
46
+
47
+ ### Two Operating Modes
48
+
49
+ | Mode | Use Case | Setup |
50
+ |------|----------|-------|
51
+ | **Full-Screen** | Complete TUI applications | `RatatuiRuby.run { }` (default) |
52
+ | **Inline Viewport** | Rich CLI moments (spinners, progress) | `RatatuiRuby.run(viewport: :inline, height: 5) { }` |
53
+
54
+ **Full-Screen**: Takes over terminal, alternate screen, restored on exit.
55
+
56
+ **Inline Viewport**: Preserves scrollback, fixed-height widget area, output remains visible after exit.
57
+
58
+ ## Core Pattern
59
+
60
+ The managed loop pattern handles terminal lifecycle:
61
+
62
+ ```ruby
63
+ RatatuiRuby.run do |tui|
64
+ loop do
65
+ # 1. Draw UI
66
+ tui.draw do |frame|
67
+ # Render widgets
68
+ end
69
+
70
+ # 2. Handle events
71
+ case tui.poll_event
72
+ in {type: :key, code: "q"}
73
+ break
74
+ end
75
+ end
76
+ end
77
+ ```
78
+
79
+ ## Common Widgets
80
+
81
+ | Widget | Factory | Purpose |
82
+ |--------|---------|---------|
83
+ | Paragraph | `tui.paragraph(text:)` | Text display |
84
+ | Block | `tui.block(title:, borders:)` | Borders, titles, padding |
85
+ | List | `tui.list(items:)` | Selectable item list |
86
+ | Table | `tui.table(rows:, widths:)` | Tabular data |
87
+ | Gauge | `tui.gauge(ratio:)` | Progress indication |
88
+ | Tabs | `tui.tabs(titles:)` | Tab navigation |
89
+ | Chart | `tui.chart(datasets:)` | Data visualization |
90
+ | Canvas | `tui.canvas { }` | Custom drawing |
91
+ | Scrollbar | `tui.scrollbar` | Scroll indication |
92
+
93
+ ### Stateless vs Stateful Widgets
94
+
95
+ **Stateless** (Paragraph, Block, Gauge):
96
+
97
+ ```ruby
98
+ widget = tui.paragraph(text: "Hello")
99
+ frame.render_widget(widget, frame.area)
100
+ ```
101
+
102
+ **Stateful** (List, Table):
103
+
104
+ ```ruby
105
+ # Create state once (outside draw loop)
106
+ list_state = tui.list_state(0)
107
+
108
+ # In draw block
109
+ list = tui.list(items: ["Item A", "Item B", "Item C"])
110
+ frame.render_stateful_widget(list, frame.area, list_state)
111
+
112
+ # Update state on input
113
+ list_state.select_next if event_down?
114
+ ```
115
+
116
+ ### Block Composition
117
+
118
+ Wrap widgets with Block for borders and titles:
119
+
120
+ ```ruby
121
+ block = tui.block(
122
+ title: "Main",
123
+ titles: [
124
+ {content: "Help: q", position: :bottom, alignment: :right}
125
+ ],
126
+ borders: [:all],
127
+ border_style: {fg: "cyan"}
128
+ )
129
+
130
+ paragraph = tui.paragraph(text: "Content", block:)
131
+ ```
132
+
133
+ ## Layout
134
+
135
+ Split areas using constraints:
136
+
137
+ ```ruby
138
+ layout = tui.layout(
139
+ direction: :vertical,
140
+ constraints: [
141
+ tui.constraint(:percentage, 20), # Header: 20%
142
+ tui.constraint(:min, 0), # Body: remaining
143
+ tui.constraint(:length, 3) # Footer: 3 rows
144
+ ]
145
+ )
146
+
147
+ chunks = layout.split(frame.area)
148
+ # chunks[0] -> header area
149
+ # chunks[1] -> body area
150
+ # chunks[2] -> footer area
151
+ ```
152
+
153
+ ### Constraint Types
154
+
155
+ | Type | Syntax | Behavior |
156
+ |------|--------|----------|
157
+ | Length | `tui.constraint(:length, 5)` | Fixed 5 rows/cols |
158
+ | Percentage | `tui.constraint(:percentage, 50)` | 50% of parent |
159
+ | Min | `tui.constraint(:min, 10)` | At least 10 |
160
+ | Max | `tui.constraint(:max, 20)` | At most 20 |
161
+ | Ratio | `tui.constraint(:ratio, 1, 3)` | 1/3 of space |
162
+ | Fill | `tui.constraint(:fill)` | Expand into excess |
163
+
164
+ ## Event Handling
165
+
166
+ ### Pattern Matching
167
+
168
+ ```ruby
169
+ case tui.poll_event
170
+ in {type: :key, code: "q"}
171
+ break
172
+ in {type: :key, code: "j"} | {type: :key, code: "down"}
173
+ list_state.select_next
174
+ in {type: :key, code: "k"} | {type: :key, code: "up"}
175
+ list_state.select_previous
176
+ in {type: :key, code: "c", modifiers: ["ctrl"]}
177
+ break
178
+ in {type: :mouse, kind: "down", button: "left", x:, y:}
179
+ handle_click(x, y)
180
+ in {type: :resize}
181
+ # Terminal resized, next draw adapts
182
+ end
183
+ ```
184
+
185
+ ### Event Helper Methods
186
+
187
+ ```ruby
188
+ event = tui.poll_event
189
+ break if event.ctrl_c?
190
+ list_state.select_next if event.down? || event.j?
191
+ ```
192
+
193
+ ## Styling
194
+
195
+ Hash-based syntax for colors and modifiers:
196
+
197
+ ```ruby
198
+ tui.paragraph(
199
+ text: "Styled text",
200
+ style: {fg: "green", bold: true},
201
+ block: tui.block(border_style: {fg: "cyan"})
202
+ )
203
+ ```
204
+
205
+ ### Text Composition
206
+
207
+ ```ruby
208
+ line = tui.line([
209
+ tui.span("Normal "),
210
+ tui.span("Bold", style: {bold: true}),
211
+ tui.span(" Red", style: {fg: "red"})
212
+ ])
213
+
214
+ tui.paragraph(text: line)
215
+ ```
216
+
217
+ ### Color Options
218
+
219
+ - Named: `"red"`, `"green"`, `"cyan"`, `"white"`
220
+ - Hex: `"#FF5733"`
221
+ - Indexed: 0-255 palette
222
+
223
+ ## Testing
224
+
225
+ ```ruby
226
+ require "ratatui_ruby/test_helper"
227
+
228
+ class MyAppTest < Minitest::Test
229
+ include RatatuiRuby::TestHelper
230
+
231
+ def test_renders_greeting
232
+ with_test_terminal(80, 24) do
233
+ RatatuiRuby.draw do |frame|
234
+ widget = RatatuiRuby::Widgets::Paragraph.new(text: "Hello")
235
+ frame.render_widget(widget, frame.area)
236
+ end
237
+
238
+ assert_snapshots("greeting") # Creates/compares snapshots/greeting.txt
239
+ end
240
+ end
241
+
242
+ def test_keyboard_navigation
243
+ with_test_terminal do
244
+ inject_keys("j", "j", "k") # Down, down, up
245
+ # Assert state changes
246
+ end
247
+ end
248
+ end
249
+ ```
250
+
251
+ ## Frameworks
252
+
253
+ ### Tea (MVU Architecture)
254
+
255
+ Functional, Elm-style architecture for predictable state:
256
+
257
+ ```ruby
258
+ require "ratatui_ruby/tea"
259
+
260
+ class Counter
261
+ include RatatuiRuby::Tea::App
262
+
263
+ def init
264
+ [Model.new(count: 0), nil]
265
+ end
266
+
267
+ def view(model, tui)
268
+ tui.paragraph(text: "Count: #{model.count}")
269
+ end
270
+
271
+ def update(message, model)
272
+ case message
273
+ in {type: :key, code: "q"}
274
+ [model, RatatuiRuby::Tea::Command.exit]
275
+ in {type: :key, code: "j"}
276
+ [model.with(count: model.count + 1), nil]
277
+ else
278
+ [model, nil]
279
+ end
280
+ end
281
+ end
282
+
283
+ Counter.new.run
284
+ ```
285
+
286
+ ## Best Practices
287
+
288
+ ### Do
289
+
290
+ - Use `RatatuiRuby.run` for managed terminal lifecycle
291
+ - Create state objects outside the draw loop
292
+ - Use pattern matching for event handling
293
+ - Use inline viewports for CLI "rich moments"
294
+ - Test with `RatatuiRuby::TestHelper`
295
+
296
+ ### Don't
297
+
298
+ - Handle Ctrl+C manually (use `event.ctrl_c?` helper)
299
+ - Forget to break the loop (leads to CPU spin)
300
+ - Render widgets before poll_event (blocks input)
301
+ - Use full-screen for simple progress indicators
302
+
303
+ ## Additional Resources
304
+
305
+ ### Reference Files
306
+
307
+ For detailed API documentation and patterns:
308
+
309
+ - **`references/core-concepts.md`** - Managed loop, terminal lifecycle, inline vs full-screen
310
+ - **`references/widgets.md`** - Complete widget catalog, composition patterns
311
+ - **`references/layout.md`** - Constraints, directions, nested layouts
312
+ - **`references/events.md`** - Keyboard, mouse, event handling patterns
313
+ - **`references/styling.md`** - Colors, modifiers, text composition
314
+ - **`references/testing.md`** - TestHelper, snapshots, event injection
315
+ - **`references/frameworks.md`** - Tea MVU, Kit components