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,414 @@
1
+ # Input Handling Reference
2
+
3
+ DragonRuby provides unified input through `args.inputs`, supporting keyboard, mouse, controller (up to 4), and touch devices.
4
+
5
+ ## Unified Directional Input
6
+
7
+ Combine keyboard (arrows + WASD) and controller_one automatically:
8
+
9
+ ```ruby
10
+ # Boolean checks - true if any matching input active
11
+ args.inputs.up # Arrow up, W, dpad up, analog up
12
+ args.inputs.down # Arrow down, S, dpad down, analog down
13
+ args.inputs.left # Arrow left, A, dpad left, analog left
14
+ args.inputs.right # Arrow right, D, dpad right, analog right
15
+
16
+ # Numeric values: -1, 0, or +1
17
+ args.inputs.left_right # Horizontal: -1 left, 0 neutral, +1 right
18
+ args.inputs.up_down # Vertical: -1 down, 0 neutral, +1 up
19
+
20
+ # Float values: -1.0 to +1.0 (analog-aware)
21
+ args.inputs.left_right_perc # Prioritizes analog stick magnitude
22
+ args.inputs.up_down_perc # Prioritizes analog stick magnitude
23
+
24
+ # Normalized vector for diagonal-safe movement
25
+ args.inputs.directional_vector # { x: 0.707, y: 0.707 } or nil
26
+ args.inputs.directional_angle # Degrees 0-360 or nil
27
+ ```
28
+
29
+ ### Directional Variants
30
+
31
+ | Method | WASD | Arrows | D-Pad | Analog | Returns |
32
+ |--------|------|--------|-------|--------|---------|
33
+ | `left_right` | ✓ | ✓ | ✓ | ✓ (60%) | -1, 0, +1 |
34
+ | `left_right_perc` | ✓ | ✓ | ✓ | ✓ | -1.0 to +1.0 |
35
+ | `left_right_directional` | ✗ | ✓ | ✓ | ✗ | -1, 0, +1 |
36
+ | `left_right_directional_perc` | ✗ | ✓ | ✓ | ✓ | -1.0 to +1.0 |
37
+
38
+ Same pattern for `up_down*` variants.
39
+
40
+ ---
41
+
42
+ ## Keyboard Input
43
+
44
+ Access via `args.inputs.keyboard`:
45
+
46
+ ### Key States
47
+
48
+ ```ruby
49
+ args.inputs.keyboard.key_down.space # First frame pressed
50
+ args.inputs.keyboard.key_held.space # Every frame while held
51
+ args.inputs.keyboard.key_up.space # Frame released
52
+ args.inputs.keyboard.space # Down OR held (shorthand)
53
+ args.inputs.keyboard.key_repeat.space # OS key repeat rate
54
+ ```
55
+
56
+ ### Dynamic Key Queries
57
+
58
+ ```ruby
59
+ args.inputs.keyboard.key_down?(:enter)
60
+ args.inputs.keyboard.key_held?(:enter)
61
+ args.inputs.keyboard.key_up?(:enter)
62
+ args.inputs.keyboard.key_down_or_held?(:enter)
63
+ ```
64
+
65
+ ### Key Names Reference
66
+
67
+ | Category | Keys |
68
+ |----------|------|
69
+ | Letters | `a` through `z` |
70
+ | Numbers | `zero`, `one`, ... `nine` |
71
+ | Arrows | `left`, `right`, `up`, `down` (aliases for `left_arrow`, etc.) |
72
+ | Modifiers | `shift`, `ctrl`, `alt`, `meta` (+ `_left`, `_right` variants) |
73
+ | Function | `f1` through `f12` |
74
+ | Special | `space`, `enter`, `escape`, `tab`, `backspace`, `delete` |
75
+ | Navigation | `home`, `end`, `page_up`, `page_down`, `insert` |
76
+ | Numpad | `kp_zero` ... `kp_nine`, `kp_plus`, `kp_enter`, etc. |
77
+ | WASD Scancode | `w_scancode`, `a_scancode`, `s_scancode`, `d_scancode` |
78
+
79
+ ### Modifier Combinations
80
+
81
+ ```ruby
82
+ args.inputs.keyboard.ctrl_s # Ctrl+S
83
+ args.inputs.keyboard.shift_a # Shift+A
84
+ args.inputs.keyboard.alt_enter # Alt+Enter
85
+ ```
86
+
87
+ ### Keyboard Helpers
88
+
89
+ ```ruby
90
+ args.inputs.keyboard.truthy_keys # Array of currently pressed keys
91
+ args.inputs.keyboard.has_focus # true if game has keyboard focus
92
+ args.inputs.keyboard.active # tick_count if any key pressed
93
+ args.inputs.text # String of last key pressed
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Controller Input
99
+
100
+ Access via `args.inputs.controller_one` (through `controller_four`):
101
+
102
+ ### Connection
103
+
104
+ ```ruby
105
+ args.inputs.controller_one.connected # true if controller present
106
+ args.inputs.controller_one.name # Controller name string
107
+ ```
108
+
109
+ ### Buttons
110
+
111
+ ```ruby
112
+ # Same state pattern as keyboard
113
+ args.inputs.controller_one.key_down.a # Just pressed
114
+ args.inputs.controller_one.key_held.a # Being held
115
+ args.inputs.controller_one.key_up.a # Just released
116
+ args.inputs.controller_one.a # Down or held
117
+ ```
118
+
119
+ | Category | Buttons |
120
+ |----------|---------|
121
+ | Face | `a`, `b`, `x`, `y` |
122
+ | D-Pad | `dpad_up`, `dpad_down`, `dpad_left`, `dpad_right` |
123
+ | Shoulder | `l1`, `r1`, `l2`, `r2` |
124
+ | Sticks | `l3` (left click), `r3` (right click) |
125
+ | System | `start`, `select`, `home` |
126
+
127
+ ### Analog Sticks
128
+
129
+ ```ruby
130
+ # Raw values: -32767 to +32767
131
+ args.inputs.controller_one.left_analog_x_raw
132
+ args.inputs.controller_one.left_analog_y_raw
133
+
134
+ # Percentage: -1.0 to +1.0
135
+ args.inputs.controller_one.left_analog_x_perc
136
+ args.inputs.controller_one.left_analog_y_perc
137
+
138
+ # Angle in degrees (nil if centered)
139
+ args.inputs.controller_one.left_analog_angle
140
+ args.inputs.controller_one.right_analog_angle
141
+
142
+ # Active check with threshold
143
+ args.inputs.controller_one.left_analog_active?(threshold_perc: 0.2)
144
+ ```
145
+
146
+ ### Directional Helpers (Controller-specific)
147
+
148
+ ```ruby
149
+ # Combines D-pad + analog
150
+ args.inputs.controller_one.up # D-pad or analog up
151
+ args.inputs.controller_one.left_right # -1, 0, +1
152
+ args.inputs.controller_one.up_down # -1, 0, +1
153
+ ```
154
+
155
+ ### Dead Zone
156
+
157
+ ```ruby
158
+ args.inputs.controller_one.analog_dead_zone = 3600 # Default raw value
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Mouse Input
164
+
165
+ Access via `args.inputs.mouse`:
166
+
167
+ ### Position
168
+
169
+ ```ruby
170
+ args.inputs.mouse.x # Current x
171
+ args.inputs.mouse.y # Current y
172
+ args.inputs.mouse.moved # true if moved this frame
173
+ args.inputs.mouse.relative_x # Delta from previous frame
174
+ args.inputs.mouse.relative_y # Delta from previous frame
175
+ ```
176
+
177
+ ### Button States
178
+
179
+ ```ruby
180
+ # Simple boolean
181
+ args.inputs.mouse.button_left # true if left down
182
+ args.inputs.mouse.button_middle
183
+ args.inputs.mouse.button_right
184
+
185
+ # Key state pattern
186
+ args.inputs.mouse.key_down.left # Just pressed
187
+ args.inputs.mouse.key_held.left # Being held
188
+ args.inputs.mouse.key_up.left # Just released
189
+ ```
190
+
191
+ ### Click Events
192
+
193
+ ```ruby
194
+ if (click = args.inputs.mouse.click)
195
+ click.x # Click x position
196
+ click.y # Click y position
197
+ click.created_at # Tick when clicked
198
+ end
199
+ ```
200
+
201
+ ### Click vs Drag Detection
202
+
203
+ ```ruby
204
+ button = args.inputs.mouse.buttons.left
205
+
206
+ button.buffered_click # true if quick click (not drag)
207
+ button.buffered_held # true if held/dragging
208
+ button.click_at # When click started
209
+ button.up_at # When released
210
+ ```
211
+
212
+ ### Scroll Wheel
213
+
214
+ ```ruby
215
+ if (wheel = args.inputs.mouse.wheel)
216
+ wheel.x # Horizontal scroll
217
+ wheel.y # Vertical scroll (positive = up)
218
+ end
219
+ ```
220
+
221
+ ### Collision Helpers
222
+
223
+ ```ruby
224
+ args.inputs.mouse.inside_rect?({ x: 100, y: 100, w: 50, h: 50 })
225
+ args.inputs.mouse.inside_circle?({ x: 640, y: 360 }, radius: 100)
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Touch Input
231
+
232
+ For mobile/touch devices:
233
+
234
+ ```ruby
235
+ # Simple finger access (nil if not touching)
236
+ args.inputs.finger_one # { x:, y: }
237
+ args.inputs.finger_two # { x:, y: }
238
+
239
+ # Side-based helpers
240
+ args.inputs.finger_left # Touch on left side
241
+ args.inputs.finger_right # Touch on right side
242
+
243
+ # Multi-touch hash
244
+ args.inputs.touch.each do |id, touch|
245
+ touch.x
246
+ touch.y
247
+ touch.touch_order # Order of touch (0-indexed)
248
+ touch.moved # Boolean
249
+ end
250
+
251
+ # Pinch zoom
252
+ args.inputs.pinch_zoom # Pinch zoom amount
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Grid Boundaries
258
+
259
+ Use `args.grid` for screen boundaries:
260
+
261
+ ```ruby
262
+ args.grid.w # 1280 (width)
263
+ args.grid.h # 720 (height)
264
+ args.grid.left # 0
265
+ args.grid.right # 1280
266
+ args.grid.top # 720
267
+ args.grid.bottom # 0
268
+ args.grid.center_x # 640
269
+ args.grid.center_y # 360
270
+ ```
271
+
272
+ ### Clamping Pattern
273
+
274
+ ```ruby
275
+ # Keep entity within screen
276
+ entity.x = entity.x.clamp(args.grid.left, args.grid.right - entity.w)
277
+ entity.y = entity.y.clamp(args.grid.bottom, args.grid.top - entity.h)
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Input Source Detection
283
+
284
+ ```ruby
285
+ args.inputs.last_active # :keyboard, :mouse, or :controller
286
+ args.inputs.last_active_at # Tick count of last input
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Best Practices
292
+
293
+ ### Support Multiple Input Types
294
+
295
+ ```ruby
296
+ # Accept action from any source
297
+ if args.inputs.keyboard.key_down.space ||
298
+ args.inputs.keyboard.key_down.enter ||
299
+ args.inputs.controller_one.key_down.a
300
+ fire_action
301
+ end
302
+ ```
303
+
304
+ ### Use Unified Helpers for Movement
305
+
306
+ ```ruby
307
+ # Instead of checking each key
308
+ speed = 5
309
+ player.x += args.inputs.left_right * speed
310
+ player.y += args.inputs.up_down * speed
311
+ ```
312
+
313
+ ### Normalize Diagonal Movement
314
+
315
+ ```ruby
316
+ # Prevent faster diagonal movement
317
+ if (vector = args.inputs.directional_vector)
318
+ player.x += vector.x * speed
319
+ player.y += vector.y * speed
320
+ end
321
+ ```
322
+
323
+ ### Adapt UI to Input Type
324
+
325
+ ```ruby
326
+ if args.inputs.last_active == :controller
327
+ show_prompt "Press [A]"
328
+ else
329
+ show_prompt "Press [Space]"
330
+ end
331
+ ```
332
+
333
+ ---
334
+
335
+ ## Common Antipatterns
336
+
337
+ ### Checking D-Pad AND Analog Separately
338
+
339
+ ```ruby
340
+ # WRONG - redundant checks
341
+ if args.inputs.controller_one.dpad_left ||
342
+ args.inputs.controller_one.left_analog_x_perc < -0.5
343
+ move_left
344
+ end
345
+
346
+ # CORRECT - use built-in helper
347
+ if args.inputs.controller_one.left
348
+ move_left
349
+ end
350
+ ```
351
+
352
+ ### Not Using Grid for Boundaries
353
+
354
+ ```ruby
355
+ # WRONG - hardcoded values
356
+ if player.x > 1280
357
+ player.x = 1280
358
+ end
359
+
360
+ # CORRECT - use grid constants
361
+ if player.x + player.w > args.grid.right
362
+ player.x = args.grid.right - player.w
363
+ end
364
+ ```
365
+
366
+ ### Forgetting Diagonal Speed
367
+
368
+ ```ruby
369
+ # WRONG - diagonal is ~1.41x faster
370
+ player.x += args.inputs.left_right * speed
371
+ player.y += args.inputs.up_down * speed
372
+
373
+ # CORRECT - normalized diagonal
374
+ if (v = args.inputs.directional_vector)
375
+ player.x += v.x * speed
376
+ player.y += v.y * speed
377
+ end
378
+ ```
379
+
380
+ ---
381
+
382
+ ## Decision Tree
383
+
384
+ ```
385
+ What type of movement?
386
+ ├─ Simple 4-directional → examples/input/directional_input.rb
387
+ ├─ With boundary clamping → examples/input/movement_with_bounds.rb
388
+ ├─ Smooth analog → examples/input/analog_movement.rb
389
+ └─ Normalized diagonal → examples/input/normalized_movement.rb
390
+
391
+ What type of action trigger?
392
+ ├─ One-shot (fire, jump) → use key_down, examples/input/action_triggers.rb
393
+ ├─ Continuous (hold to run) → use key_held, examples/input/keyboard_input.rb
394
+ └─ On release (charge attacks) → use key_up, examples/input/keyboard_input.rb
395
+
396
+ Mouse interaction?
397
+ └─ Click handling → examples/input/mouse_click.rb
398
+
399
+ Controller input?
400
+ └─ Buttons and analog → examples/input/controller_input.rb
401
+ ```
402
+
403
+ ## Examples
404
+
405
+ | File | Demonstrates |
406
+ |------|--------------|
407
+ | `examples/input/directional_input.rb` | 4-directional movement with unified input |
408
+ | `examples/input/movement_with_bounds.rb` | Clamping player to screen boundaries |
409
+ | `examples/input/analog_movement.rb` | Smooth analog stick movement |
410
+ | `examples/input/normalized_movement.rb` | Normalized diagonal movement |
411
+ | `examples/input/action_triggers.rb` | One-shot actions with key_down |
412
+ | `examples/input/keyboard_input.rb` | Key states: down, held, up |
413
+ | `examples/input/mouse_click.rb` | Mouse click detection and position |
414
+ | `examples/input/controller_input.rb` | Controller buttons and analog sticks |