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.
- checksums.yaml +4 -4
- data/.reek.yml +27 -1
- data/CHANGELOG.md +19 -0
- data/README.md +213 -43
- data/agents/codebase-analyzer.md +88 -0
- data/agents/codebase-pattern-finder.md +83 -0
- data/agents/documentation-researcher.md +59 -0
- data/agents/thoughts-analyzer.md +102 -0
- data/agents/web-search-researcher.md +71 -0
- data/anima-core.gemspec +3 -0
- data/app/channels/session_channel.rb +195 -45
- data/app/decorators/user_message_decorator.rb +16 -5
- data/app/jobs/agent_request_job.rb +55 -2
- data/app/jobs/analytical_brain_job.rb +33 -0
- data/app/jobs/count_event_tokens_job.rb +15 -4
- data/app/models/concerns/event/broadcasting.rb +81 -0
- data/app/models/event.rb +20 -1
- data/app/models/goal.rb +91 -0
- data/app/models/session.rb +366 -21
- data/config/application.rb +2 -0
- data/config/initializers/event_subscribers.rb +0 -1
- data/config/routes.rb +0 -6
- data/db/migrate/20260313010000_add_status_to_events.rb +8 -0
- data/db/migrate/20260313020000_add_processing_to_sessions.rb +7 -0
- data/db/migrate/20260314075248_add_subagent_support_to_sessions.rb +6 -0
- data/db/migrate/20260314112417_add_granted_tools_to_sessions.rb +5 -0
- data/db/migrate/20260314140000_add_name_to_sessions.rb +7 -0
- data/db/migrate/20260314150000_add_viewport_event_ids_to_sessions.rb +7 -0
- data/db/migrate/20260315100000_add_active_skills_to_sessions.rb +7 -0
- data/db/migrate/20260315140843_create_goals.rb +16 -0
- data/db/migrate/20260315144837_add_completed_at_to_goals.rb +5 -0
- data/db/migrate/20260315191105_add_active_workflow_to_sessions.rb +5 -0
- data/lib/agent_loop.rb +65 -6
- data/lib/agents/definition.rb +116 -0
- data/lib/agents/registry.rb +106 -0
- data/lib/analytical_brain/runner.rb +276 -0
- data/lib/analytical_brain/tools/activate_skill.rb +52 -0
- data/lib/analytical_brain/tools/deactivate_skill.rb +43 -0
- data/lib/analytical_brain/tools/deactivate_workflow.rb +34 -0
- data/lib/analytical_brain/tools/everything_is_ready.rb +28 -0
- data/lib/analytical_brain/tools/finish_goal.rb +62 -0
- data/lib/analytical_brain/tools/read_workflow.rb +58 -0
- data/lib/analytical_brain/tools/rename_session.rb +63 -0
- data/lib/analytical_brain/tools/set_goal.rb +60 -0
- data/lib/analytical_brain/tools/update_goal.rb +60 -0
- data/lib/analytical_brain.rb +23 -0
- data/lib/anima/cli/mcp/secrets.rb +76 -0
- data/lib/anima/cli/mcp.rb +197 -0
- data/lib/anima/cli.rb +5 -40
- data/lib/anima/installer.rb +168 -0
- data/lib/anima/settings.rb +226 -0
- data/lib/anima/version.rb +1 -1
- data/lib/anima.rb +9 -0
- data/lib/credential_store.rb +103 -0
- data/lib/environment_probe.rb +232 -0
- data/lib/events/subscribers/persister.rb +1 -0
- data/lib/events/user_message.rb +17 -0
- data/lib/llm/client.rb +29 -10
- data/lib/mcp/client_manager.rb +86 -0
- data/lib/mcp/config.rb +213 -0
- data/lib/mcp/health_check.rb +77 -0
- data/lib/mcp/secrets.rb +73 -0
- data/lib/mcp/stdio_transport.rb +206 -0
- data/lib/providers/anthropic.rb +11 -20
- data/lib/shell_session.rb +11 -10
- data/lib/skills/definition.rb +97 -0
- data/lib/skills/registry.rb +105 -0
- data/lib/tools/edit.rb +226 -0
- data/lib/tools/mcp_tool.rb +114 -0
- data/lib/tools/read.rb +151 -0
- data/lib/tools/registry.rb +14 -12
- data/lib/tools/request_feature.rb +121 -0
- data/lib/tools/return_result.rb +81 -0
- data/lib/tools/spawn_specialist.rb +109 -0
- data/lib/tools/spawn_subagent.rb +111 -0
- data/lib/tools/subagent_prompts.rb +12 -0
- data/lib/tools/web_get.rb +8 -9
- data/lib/tools/write.rb +86 -0
- data/lib/tui/app.rb +985 -26
- data/lib/tui/cable_client.rb +69 -31
- data/lib/tui/message_store.rb +103 -8
- data/lib/tui/screens/chat.rb +293 -45
- data/lib/workflows/definition.rb +97 -0
- data/lib/workflows/registry.rb +89 -0
- data/skills/activerecord/SKILL.md +255 -0
- data/skills/activerecord/examples/associations/association_extensions.rb +298 -0
- data/skills/activerecord/examples/associations/basic_associations.rb +118 -0
- data/skills/activerecord/examples/associations/counter_caches.rb +215 -0
- data/skills/activerecord/examples/associations/polymorphic_associations.rb +217 -0
- data/skills/activerecord/examples/associations/self_referential.rb +302 -0
- data/skills/activerecord/examples/associations/through_associations.rb +203 -0
- data/skills/activerecord/examples/basics/crud_operations.rb +209 -0
- data/skills/activerecord/examples/basics/dirty_tracking.rb +218 -0
- data/skills/activerecord/examples/basics/inheritance.rb +377 -0
- data/skills/activerecord/examples/basics/type_casting.rb +317 -0
- data/skills/activerecord/examples/callbacks/alternatives_to_callbacks.rb +447 -0
- data/skills/activerecord/examples/callbacks/conditional_callbacks.rb +353 -0
- data/skills/activerecord/examples/callbacks/lifecycle_callbacks.rb +280 -0
- data/skills/activerecord/examples/callbacks/transaction_callbacks.rb +340 -0
- data/skills/activerecord/examples/migrations/indexes_and_constraints.rb +337 -0
- data/skills/activerecord/examples/migrations/reversible_patterns.rb +403 -0
- data/skills/activerecord/examples/migrations/safe_patterns.rb +420 -0
- data/skills/activerecord/examples/migrations/schema_changes.rb +277 -0
- data/skills/activerecord/examples/querying/batch_processing.rb +226 -0
- data/skills/activerecord/examples/querying/eager_loading.rb +259 -0
- data/skills/activerecord/examples/querying/finder_methods.rb +170 -0
- data/skills/activerecord/examples/querying/optimization.rb +275 -0
- data/skills/activerecord/examples/querying/scopes.rb +260 -0
- data/skills/activerecord/examples/validations/built_in_validators.rb +277 -0
- data/skills/activerecord/examples/validations/conditional_validations.rb +288 -0
- data/skills/activerecord/examples/validations/custom_validators.rb +381 -0
- data/skills/activerecord/examples/validations/database_constraints.rb +432 -0
- data/skills/activerecord/examples/validations/validation_contexts.rb +367 -0
- data/skills/activerecord/references/associations.md +709 -0
- data/skills/activerecord/references/basics.md +622 -0
- data/skills/activerecord/references/callbacks.md +738 -0
- data/skills/activerecord/references/migrations.md +657 -0
- data/skills/activerecord/references/querying.md +655 -0
- data/skills/activerecord/references/validations.md +596 -0
- data/skills/dragonruby/SKILL.md +250 -0
- data/skills/dragonruby/examples/audio/audio_events.rb +55 -0
- data/skills/dragonruby/examples/audio/background_music.rb +29 -0
- data/skills/dragonruby/examples/audio/crossfade.rb +51 -0
- data/skills/dragonruby/examples/audio/music_controls.rb +51 -0
- data/skills/dragonruby/examples/audio/sound_effects.rb +30 -0
- data/skills/dragonruby/examples/core/coordinate_system.rb +27 -0
- data/skills/dragonruby/examples/core/hello_world.rb +24 -0
- data/skills/dragonruby/examples/core/labels.rb +22 -0
- data/skills/dragonruby/examples/core/sprites.rb +35 -0
- data/skills/dragonruby/examples/core/state_management.rb +29 -0
- data/skills/dragonruby/examples/distribution/background_pause.rb +42 -0
- data/skills/dragonruby/examples/distribution/build_workflow.sh +26 -0
- data/skills/dragonruby/examples/distribution/cvars_production.txt +16 -0
- data/skills/dragonruby/examples/distribution/game_metadata_hd.txt +23 -0
- data/skills/dragonruby/examples/distribution/game_metadata_minimal.txt +9 -0
- data/skills/dragonruby/examples/distribution/game_metadata_mobile.txt +31 -0
- data/skills/dragonruby/examples/distribution/platform_detection.rb +36 -0
- data/skills/dragonruby/examples/distribution/steam_metadata.txt +19 -0
- data/skills/dragonruby/examples/entities/collision_detection.rb +43 -0
- data/skills/dragonruby/examples/entities/entity_lifecycle.rb +68 -0
- data/skills/dragonruby/examples/entities/entity_storage.rb +38 -0
- data/skills/dragonruby/examples/entities/factory_methods.rb +45 -0
- data/skills/dragonruby/examples/entities/random_spawning.rb +50 -0
- data/skills/dragonruby/examples/game-logic/reset_patterns.rb +98 -0
- data/skills/dragonruby/examples/game-logic/save_load.rb +101 -0
- data/skills/dragonruby/examples/game-logic/scoring.rb +104 -0
- data/skills/dragonruby/examples/game-logic/state_transitions.rb +103 -0
- data/skills/dragonruby/examples/game-logic/timers.rb +87 -0
- data/skills/dragonruby/examples/input/action_triggers.rb +36 -0
- data/skills/dragonruby/examples/input/analog_movement.rb +28 -0
- data/skills/dragonruby/examples/input/controller_input.rb +28 -0
- data/skills/dragonruby/examples/input/directional_input.rb +24 -0
- data/skills/dragonruby/examples/input/keyboard_input.rb +28 -0
- data/skills/dragonruby/examples/input/mouse_click.rb +26 -0
- data/skills/dragonruby/examples/input/movement_with_bounds.rb +22 -0
- data/skills/dragonruby/examples/input/normalized_movement.rb +32 -0
- data/skills/dragonruby/examples/rendering/frame_animation.rb +32 -0
- data/skills/dragonruby/examples/rendering/labels.rb +32 -0
- data/skills/dragonruby/examples/rendering/layering.rb +51 -0
- data/skills/dragonruby/examples/rendering/solids.rb +61 -0
- data/skills/dragonruby/examples/rendering/sprites.rb +33 -0
- data/skills/dragonruby/examples/rendering/spritesheet_animation.rb +39 -0
- data/skills/dragonruby/examples/scenes/case_dispatch.rb +60 -0
- data/skills/dragonruby/examples/scenes/class_based.rb +150 -0
- data/skills/dragonruby/examples/scenes/pause_overlay.rb +100 -0
- data/skills/dragonruby/examples/scenes/safe_transitions.rb +68 -0
- data/skills/dragonruby/examples/scenes/scene_transitions.rb +98 -0
- data/skills/dragonruby/examples/scenes/send_dispatch.rb +88 -0
- data/skills/dragonruby/references/audio.md +396 -0
- data/skills/dragonruby/references/core.md +385 -0
- data/skills/dragonruby/references/distribution.md +434 -0
- data/skills/dragonruby/references/entities.md +516 -0
- data/skills/dragonruby/references/game-logic/persistence.md +386 -0
- data/skills/dragonruby/references/game-logic/state.md +389 -0
- data/skills/dragonruby/references/input.md +414 -0
- data/skills/dragonruby/references/rendering/animation.md +467 -0
- data/skills/dragonruby/references/rendering/primitives.md +403 -0
- data/skills/dragonruby/references/scenes.md +443 -0
- data/skills/draper-decorators/SKILL.md +344 -0
- data/skills/draper-decorators/examples/application_decorator.rb +61 -0
- data/skills/draper-decorators/examples/decorator_spec.rb +253 -0
- data/skills/draper-decorators/examples/model_decorator.rb +152 -0
- data/skills/draper-decorators/references/anti-patterns.md +640 -0
- data/skills/draper-decorators/references/patterns.md +507 -0
- data/skills/draper-decorators/references/testing.md +559 -0
- data/skills/gh-issue.md +182 -0
- data/skills/mcp-server/SKILL.md +177 -0
- data/skills/mcp-server/examples/dynamic_tools.rb +36 -0
- data/skills/mcp-server/examples/file_manager_tool.rb +85 -0
- data/skills/mcp-server/examples/http_client.rb +48 -0
- data/skills/mcp-server/examples/http_server.rb +97 -0
- data/skills/mcp-server/examples/rails_integration.rb +88 -0
- data/skills/mcp-server/examples/stdio_server.rb +108 -0
- data/skills/mcp-server/examples/streaming_client.rb +95 -0
- data/skills/mcp-server/references/gotchas.md +183 -0
- data/skills/mcp-server/references/prompts.md +98 -0
- data/skills/mcp-server/references/resources.md +53 -0
- data/skills/mcp-server/references/server.md +140 -0
- data/skills/mcp-server/references/tools.md +146 -0
- data/skills/mcp-server/references/transport.md +104 -0
- data/skills/ratatui-ruby/SKILL.md +315 -0
- data/skills/ratatui-ruby/references/core-concepts.md +340 -0
- data/skills/ratatui-ruby/references/events.md +387 -0
- data/skills/ratatui-ruby/references/frameworks.md +522 -0
- data/skills/ratatui-ruby/references/layout.md +423 -0
- data/skills/ratatui-ruby/references/styling.md +268 -0
- data/skills/ratatui-ruby/references/testing.md +433 -0
- data/skills/ratatui-ruby/references/widgets.md +532 -0
- data/skills/rspec/SKILL.md +340 -0
- data/skills/rspec/examples/core/basic_structure.rb +69 -0
- data/skills/rspec/examples/core/configuration.rb +126 -0
- data/skills/rspec/examples/core/hooks.rb +126 -0
- data/skills/rspec/examples/core/memoized_helpers.rb +139 -0
- data/skills/rspec/examples/core/metadata_filtering.rb +144 -0
- data/skills/rspec/examples/core/shared_examples.rb +145 -0
- data/skills/rspec/examples/factory_bot/associations.rb +314 -0
- data/skills/rspec/examples/factory_bot/build_strategies.rb +272 -0
- data/skills/rspec/examples/factory_bot/callbacks.rb +320 -0
- data/skills/rspec/examples/factory_bot/custom_construction.rb +328 -0
- data/skills/rspec/examples/factory_bot/factory_definition.rb +191 -0
- data/skills/rspec/examples/factory_bot/inheritance.rb +314 -0
- data/skills/rspec/examples/factory_bot/traits.rb +293 -0
- data/skills/rspec/examples/factory_bot/transients.rb +229 -0
- data/skills/rspec/examples/matchers/change.rb +115 -0
- data/skills/rspec/examples/matchers/collections.rb +154 -0
- data/skills/rspec/examples/matchers/comparisons.rb +79 -0
- data/skills/rspec/examples/matchers/composing.rb +155 -0
- data/skills/rspec/examples/matchers/custom_matchers.rb +197 -0
- data/skills/rspec/examples/matchers/equality.rb +58 -0
- data/skills/rspec/examples/matchers/errors.rb +136 -0
- data/skills/rspec/examples/matchers/output.rb +103 -0
- data/skills/rspec/examples/matchers/predicates.rb +87 -0
- data/skills/rspec/examples/matchers/truthiness.rb +101 -0
- data/skills/rspec/examples/matchers/types.rb +82 -0
- data/skills/rspec/examples/matchers/yield.rb +147 -0
- data/skills/rspec/examples/mocks/any_instance.rb +172 -0
- data/skills/rspec/examples/mocks/argument_matchers.rb +206 -0
- data/skills/rspec/examples/mocks/constants.rb +177 -0
- data/skills/rspec/examples/mocks/doubles.rb +139 -0
- data/skills/rspec/examples/mocks/expectations.rb +137 -0
- data/skills/rspec/examples/mocks/message_chains.rb +173 -0
- data/skills/rspec/examples/mocks/ordering.rb +144 -0
- data/skills/rspec/examples/mocks/receive_counts.rb +181 -0
- data/skills/rspec/examples/mocks/responses.rb +223 -0
- data/skills/rspec/examples/mocks/spies.rb +149 -0
- data/skills/rspec/examples/mocks/stubbing.rb +133 -0
- data/skills/rspec/examples/rails/channels.rb +250 -0
- data/skills/rspec/examples/rails/controller_specs.rb +302 -0
- data/skills/rspec/examples/rails/helper_specs.rb +245 -0
- data/skills/rspec/examples/rails/job_specs.rb +256 -0
- data/skills/rspec/examples/rails/mailer_specs.rb +228 -0
- data/skills/rspec/examples/rails/matchers.rb +374 -0
- data/skills/rspec/examples/rails/model_specs.rb +193 -0
- data/skills/rspec/examples/rails/request_specs.rb +275 -0
- data/skills/rspec/examples/rails/routing_specs.rb +276 -0
- data/skills/rspec/examples/rails/system_specs.rb +294 -0
- data/skills/rspec/examples/rails/transactions.rb +254 -0
- data/skills/rspec/examples/rails/view_specs.rb +252 -0
- data/skills/rspec/references/core.md +816 -0
- data/skills/rspec/references/factory_bot.md +641 -0
- data/skills/rspec/references/matchers.md +516 -0
- data/skills/rspec/references/mocks.md +381 -0
- data/skills/rspec/references/rails.md +528 -0
- data/templates/soul.md +40 -0
- data/workflows/commit.md +45 -0
- data/workflows/create_handoff.md +98 -0
- data/workflows/create_note.md +82 -0
- data/workflows/create_plan.md +457 -0
- data/workflows/decompose_ticket.md +109 -0
- data/workflows/feature.md +91 -0
- data/workflows/implement_plan.md +87 -0
- data/workflows/iterate_plan.md +247 -0
- data/workflows/research_codebase.md +210 -0
- data/workflows/resume_handoff.md +217 -0
- data/workflows/review_pr.md +320 -0
- data/workflows/thoughts_init.md +71 -0
- data/workflows/validate_plan.md +166 -0
- metadata +290 -3
- data/app/controllers/api/sessions_controller.rb +0 -25
- 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
|