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,385 @@
1
+ # DragonRuby Core Reference
2
+
3
+ ## Game Loop
4
+
5
+ DragonRuby runs at **60 FPS**. The `tick` method executes every frame (~16ms).
6
+
7
+ ```ruby
8
+ def boot args
9
+ args.state = {} # Initialize state (required in future versions)
10
+ end
11
+
12
+ def tick args
13
+ # Called 60 times per second
14
+ # All game logic goes here
15
+ end
16
+
17
+ def reset args
18
+ # Called BEFORE $gtk.reset executes
19
+ end
20
+ ```
21
+
22
+ ### Time Conversions
23
+
24
+ ```ruby
25
+ FPS = 60
26
+
27
+ # Seconds to ticks
28
+ timer = 30 * FPS # 1800 ticks = 30 seconds
29
+
30
+ # Ticks to seconds
31
+ remaining = (args.state.timer / FPS).round
32
+
33
+ # Helper methods
34
+ 1.seconds # => 60 frames
35
+ 5.seconds # => 300 frames
36
+ ```
37
+
38
+ ### Tick Counters
39
+
40
+ ```ruby
41
+ Kernel.tick_count # Resets on $gtk.reset
42
+ Kernel.global_tick_count # Never resets
43
+ ```
44
+
45
+ ## Coordinate System
46
+
47
+ - **Resolution**: 1280×720 (auto-scaled)
48
+ - **Origin**: Bottom-left `(0, 0)`
49
+ - **Top-right**: `(1280, 720)`
50
+ - **Y increases upward** (unlike most engines)
51
+
52
+ ```
53
+ (0, 720) (1280, 720)
54
+ ┌─────────────────────┐
55
+ │ │
56
+ │ 1280 × 720 │
57
+ │ │
58
+ └─────────────────────┘
59
+ (0, 0) (1280, 0)
60
+ ```
61
+
62
+ ### Movement Directions
63
+
64
+ ```ruby
65
+ player.x += speed # Right
66
+ player.x -= speed # Left
67
+ player.y += speed # Up
68
+ player.y -= speed # Down
69
+ ```
70
+
71
+ ### Grid Helpers
72
+
73
+ ```ruby
74
+ args.grid.w # 1280
75
+ args.grid.h # 720
76
+ args.grid.left # 0
77
+ args.grid.right # 1280
78
+ args.grid.top # 720
79
+ args.grid.bottom # 0
80
+
81
+ # Positioning from top
82
+ y: 30.from_top # 30 pixels from top
83
+ ```
84
+
85
+ ## args Object
86
+
87
+ The `args` parameter contains everything needed for the game:
88
+
89
+ | Property | Purpose |
90
+ |----------|---------|
91
+ | `args.outputs` | Render to screen |
92
+ | `args.state` | Persistent game data |
93
+ | `args.inputs` | Keyboard/mouse/controller |
94
+ | `args.grid` | Screen dimensions |
95
+ | `Geometry` | Collision helpers (also `args.geometry`) |
96
+ | `args.audio` | Sound playback |
97
+
98
+ ## args.state - Game State
99
+
100
+ Persistent data storage across ticks. Use `||=` for lazy initialization.
101
+
102
+ ```ruby
103
+ args.state.player ||= { x: 120, y: 280, health: 100 }
104
+ args.state.enemies ||= []
105
+ args.state.score ||= 0
106
+ args.state.scene ||= :title
107
+
108
+ # Arbitrary nesting
109
+ args.state.player.x += 5
110
+ args.state.level.enemies ||= []
111
+ ```
112
+
113
+ ### Entity Properties (Auto-Added)
114
+
115
+ When storing objects in `args.state`, they get these properties:
116
+ - `entity_id` - Unique identifier
117
+ - `entity_type` - Symbol type
118
+ - `created_at` - Tick when created
119
+ - `created_at_elapsed` - Ticks since creation
120
+
121
+ ## args.outputs - Rendering
122
+
123
+ ### Render Order (bottom to top)
124
+
125
+ 1. `solids` - Filled rectangles
126
+ 2. `sprites` - Images
127
+ 3. `primitives` - Mixed types
128
+ 4. `labels` - Text
129
+ 5. `lines` - Line segments
130
+ 6. `borders` - Outline rectangles
131
+ 7. `debug` - Debug only (not in production)
132
+
133
+ ### Sprites
134
+
135
+ ```ruby
136
+ # Hash syntax (RECOMMENDED)
137
+ args.outputs.sprites << {
138
+ x: 100, y: 100,
139
+ w: 128, h: 128,
140
+ path: "sprites/player.png",
141
+
142
+ # Anchoring (default: bottom-left)
143
+ anchor_x: 0.5, anchor_y: 0.5, # Center
144
+
145
+ # Rotation
146
+ angle: 45,
147
+ angle_anchor_x: 0.5, angle_anchor_y: 0.5,
148
+
149
+ # Color/opacity
150
+ r: 255, g: 255, b: 255, a: 255,
151
+
152
+ # Flipping
153
+ flip_horizontally: false,
154
+ flip_vertically: false,
155
+
156
+ # Blending
157
+ blendmode_enum: 1 # 0=none, 1=alpha, 2=additive
158
+ }
159
+ ```
160
+
161
+ ### Labels
162
+
163
+ ```ruby
164
+ args.outputs.labels << {
165
+ x: 640, y: 360,
166
+ text: "Score: #{score}",
167
+ size_enum: 0, # -2=18px, -1=20px, 0=22px, 1=24px, 2=26px
168
+ size_px: 22, # Or exact pixels
169
+ alignment_enum: 1, # 0=left, 1=center, 2=right
170
+ vertical_alignment_enum: 1, # 0=bottom, 1=center, 2=top
171
+ anchor_x: 0.5, anchor_y: 0.5,
172
+ r: 255, g: 255, b: 255, a: 255,
173
+ font: "fonts/custom.ttf"
174
+ }
175
+ ```
176
+
177
+ **Note**: Label default anchor is TOP-LEFT (unlike sprites which use bottom-left).
178
+
179
+ ### Solids and Borders
180
+
181
+ ```ruby
182
+ # Filled rectangle
183
+ args.outputs.solids << { x: 0, y: 0, w: 100, h: 100, r: 255, g: 0, b: 0 }
184
+
185
+ # Outline rectangle
186
+ args.outputs.borders << { x: 0, y: 0, w: 100, h: 100, r: 0, g: 0, b: 0 }
187
+
188
+ # BETTER: Use sprite with :solid (cached, better performance)
189
+ args.outputs.sprites << { x: 0, y: 0, w: 100, h: 100, path: :solid, r: 255, g: 0, b: 0 }
190
+ ```
191
+
192
+ ### Lines
193
+
194
+ ```ruby
195
+ args.outputs.lines << {
196
+ x: 100, y: 100,
197
+ x2: 200, y2: 200,
198
+ r: 0, g: 0, b: 0, a: 255
199
+ }
200
+ ```
201
+
202
+ ### Batch Rendering
203
+
204
+ ```ruby
205
+ # Push multiple items efficiently
206
+ args.outputs.sprites << [player, *enemies, *fireballs]
207
+ ```
208
+
209
+ ## Project Structure
210
+
211
+ ```
212
+ mygame/
213
+ app/
214
+ main.rb # Entry point with def tick(args)
215
+ sprites/ # PNG, JPG images
216
+ sounds/ # WAV, OGG audio
217
+ fonts/ # TTF fonts
218
+ data/ # Game data files
219
+ metadata/
220
+ game_metadata.txt
221
+ ```
222
+
223
+ ### Asset Paths
224
+
225
+ ```ruby
226
+ # Relative to mygame/
227
+ path: "sprites/player.png" # mygame/sprites/player.png
228
+ font: "fonts/custom.ttf" # mygame/fonts/custom.ttf
229
+ ```
230
+
231
+ ## Hot Reload
232
+
233
+ Code auto-reloads on save. State persists unless reset.
234
+
235
+ ```ruby
236
+ # Force state reset during development
237
+ def tick args
238
+ # ... game logic
239
+ end
240
+
241
+ $gtk.reset # Add at end of file
242
+
243
+ # Or reset before next tick (safer during tick)
244
+ $gtk.reset_next_tick
245
+ ```
246
+
247
+ ## Tick Structure Pattern
248
+
249
+ ```ruby
250
+ def tick args
251
+ # 1. Initialize state
252
+ args.state.player ||= { x: 640, y: 360 }
253
+
254
+ # 2. Handle input
255
+ args.state.player.x += 5 if args.inputs.right
256
+
257
+ # 3. Update game logic
258
+ update_enemies(args)
259
+ check_collisions(args)
260
+
261
+ # 4. Render (always last!)
262
+ args.outputs.sprites << args.state.player
263
+ end
264
+ ```
265
+
266
+ ## Class-Based Development
267
+
268
+ ```ruby
269
+ class Game
270
+ attr_gtk # Adds state, inputs, outputs, etc.
271
+
272
+ def tick
273
+ @player ||= { x: 0, y: 0 }
274
+ @player.x += 1
275
+ outputs.sprites << @player # No args. prefix needed
276
+ end
277
+ end
278
+
279
+ def tick args
280
+ $game ||= Game.new
281
+ $game.args = args
282
+ $game.tick
283
+ end
284
+
285
+ def reset args
286
+ $game = nil
287
+ end
288
+ ```
289
+
290
+ ## Debug Output
291
+
292
+ ```ruby
293
+ # Debug labels (not in production builds)
294
+ args.outputs.debug << "Frame: #{Kernel.tick_count}"
295
+ args.outputs.debug << { x: 10, y: 10, text: "FPS: #{$gtk.current_framerate}" }
296
+
297
+ # Watch variables
298
+ args.outputs.debug.watch args.state.player
299
+ ```
300
+
301
+ ## Common Antipatterns
302
+
303
+ ### State Persistence During Hot Reload
304
+
305
+ ```ruby
306
+ # WRONG - changed value doesn't apply
307
+ args.state.player_x ||= 200 # Still uses old value
308
+
309
+ # CORRECT - add $gtk.reset at end of file
310
+ ```
311
+
312
+ ### Magic Numbers
313
+
314
+ ```ruby
315
+ # WRONG
316
+ args.state.timer = 30 * 60
317
+
318
+ # CORRECT
319
+ FPS = 60
320
+ args.state.timer = 30 * FPS
321
+ ```
322
+
323
+ ### Infinite Collections
324
+
325
+ ```ruby
326
+ # WRONG - memory leak
327
+ args.state.fireballs.each { |f| f.x += 5 }
328
+
329
+ # CORRECT - remove offscreen
330
+ args.state.fireballs.each do |f|
331
+ f.x += 5
332
+ f.dead = true if f.x > args.grid.w
333
+ end
334
+ args.state.fireballs.reject! { |f| f.dead }
335
+ ```
336
+
337
+ ### Render Before Update
338
+
339
+ ```ruby
340
+ # WRONG - off by 1 frame
341
+ args.outputs.sprites << player
342
+ player.x += 5
343
+
344
+ # CORRECT - update then render
345
+ player.x += 5
346
+ args.outputs.sprites << player
347
+ ```
348
+
349
+ ### Missing Early Return
350
+
351
+ ```ruby
352
+ # WRONG - game still runs after game over
353
+ if game_over?
354
+ show_game_over(args)
355
+ end
356
+ handle_input(args) # Still executes!
357
+
358
+ # CORRECT
359
+ if game_over?
360
+ show_game_over(args)
361
+ return
362
+ end
363
+ handle_input(args)
364
+ ```
365
+
366
+ ## Decision Tree
367
+
368
+ ```
369
+ What are you trying to do?
370
+ ├─ Display text → examples/core/labels.rb
371
+ ├─ Show an image → examples/core/sprites.rb
372
+ ├─ Store game data → examples/core/state_management.rb
373
+ ├─ Basic game structure → examples/core/hello_world.rb
374
+ └─ Understand coordinates → examples/core/coordinate_system.rb
375
+ ```
376
+
377
+ ## Examples
378
+
379
+ | File | Demonstrates |
380
+ |------|--------------|
381
+ | `examples/core/hello_world.rb` | Minimal tick method, basic rendering |
382
+ | `examples/core/sprites.rb` | Image rendering, anchors, rotation, color |
383
+ | `examples/core/labels.rb` | Text display, alignment, sizing, fonts |
384
+ | `examples/core/state_management.rb` | args.state, ||= initialization, persistence |
385
+ | `examples/core/coordinate_system.rb` | Grid helpers, positioning, movement directions |