@tutorialkit-rb/cli 0.1.5 → 0.1.7

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 (185) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/template/.claude/skills/rails-file-management/SKILL.md +211 -0
  4. package/template/.claude/skills/rails-lesson-recipes/SKILL.md +415 -0
  5. package/template/.claude/skills/rails-wasm-author-constraints/SKILL.md +181 -0
  6. package/template/.claude/skills/tutorial-content-structure/SKILL.md +377 -0
  7. package/template/.claude/skills/tutorial-lesson-config/SKILL.md +389 -0
  8. package/template/.claude/skills/tutorial-quickstart/SKILL.md +440 -0
  9. package/template/.github/workflows/deploy.yml +85 -0
  10. package/template/.gitignore +10 -0
  11. package/template/CLAUDE.md +47 -0
  12. package/template/astro.config.ts +12 -0
  13. package/template/bin/build-pack +84 -0
  14. package/template/bin/build-ruby-base +180 -0
  15. package/template/bin/link-local +13 -0
  16. package/template/bin/unlink-local +7 -0
  17. package/template/cors-proxy/README.md +63 -0
  18. package/template/cors-proxy/package-lock.json +1504 -0
  19. package/template/cors-proxy/package.json +12 -0
  20. package/template/cors-proxy/src/index.ts +87 -0
  21. package/template/cors-proxy/wrangler.toml +7 -0
  22. package/template/netlify.toml +9 -0
  23. package/template/package.json +12 -4
  24. package/template/ruby-wasm/Gemfile +6 -0
  25. package/template/ruby-wasm/Gemfile.base +19 -0
  26. package/template/ruby-wasm/Gemfile.base.lock +50 -0
  27. package/template/ruby-wasm/Gemfile.lock +8 -0
  28. package/template/ruby-wasm/bin/pack-gems +368 -0
  29. package/template/ruby-wasm/package.json +6 -0
  30. package/template/src/components/FileManager.tsx +33 -0
  31. package/template/src/components/HeadTags.astro +6 -6
  32. package/template/src/components/HelpDropdown.tsx +1 -1
  33. package/template/src/components/RailsPathLinkHandler.tsx +2 -2
  34. package/template/src/components/ShellConfigurator.tsx +6 -1
  35. package/template/src/content/tutorial/1-getting-started/1-creating-your-first-rails-app/content.md +4 -4
  36. package/template/src/content/tutorial/1-getting-started/2-rails-console/content.md +4 -4
  37. package/template/src/content/tutorial/2-controllers/2-crud-operations/content.md +2 -2
  38. package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/.tk-config.json +3 -0
  39. package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/workspace/app/controllers/http_demo_controller.rb +65 -0
  40. package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/workspace/app/views/http_demo/index.html.erb +172 -0
  41. package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/workspace/config/routes.rb +8 -0
  42. package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/content.md +97 -0
  43. package/template/src/content/tutorial/9-outbound-http/meta.md +4 -0
  44. package/template/src/content/tutorial/meta.md +5 -0
  45. package/template/src/middleware.ts +14 -0
  46. package/template/src/templates/default/lib/boot-progress.js +49 -0
  47. package/template/src/templates/default/lib/http-bridge.js +55 -0
  48. package/template/src/templates/default/lib/patches/http_bridge.rb +167 -0
  49. package/template/src/templates/default/lib/rails.js +52 -5
  50. package/template/src/templates/default/lib/server.js +33 -1
  51. package/template/src/templates/default/package.json +4 -1
  52. package/template/src/templates/default/scripts/rails.js +1 -1
  53. package/template/src/templates/default/scripts/smoke-test.js +349 -0
  54. package/template/src/templates/default/scripts/wasi-loader.mjs +10 -0
  55. package/template/src/templates/default/workspace/_debug_app/.github/dependabot.yml +12 -0
  56. package/template/src/templates/default/workspace/_debug_app/.github/workflows/ci.yml +51 -0
  57. package/template/src/templates/default/workspace/_debug_app/.ruby-version +1 -0
  58. package/template/src/templates/default/workspace/_debug_app/Gemfile +37 -0
  59. package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/layouts/application.html.erb +1 -2
  60. package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/pwa/manifest.json.erb +2 -2
  61. package/template/src/templates/default/workspace/_debug_app/bin/dev +2 -0
  62. package/template/src/templates/default/workspace/_debug_app/bin/rake +4 -0
  63. package/template/src/templates/default/workspace/_debug_app/bin/setup +34 -0
  64. package/template/src/templates/default/workspace/_debug_app/config/application.rb +30 -0
  65. package/template/src/templates/default/workspace/_debug_app/config/cable.yml +10 -0
  66. package/template/src/templates/default/workspace/_debug_app/config/credentials.yml.enc +1 -0
  67. package/template/src/templates/default/workspace/_debug_app/config/master.key +1 -0
  68. package/template/src/templates/default/workspace/_debug_app/config/routes.rb +14 -0
  69. package/template/src/templates/default/workspace/_debug_app/test/models/.keep +0 -0
  70. package/template/src/templates/default/workspace/_debug_app/test/test_helper.rb +15 -0
  71. package/template/src/templates/default/workspace/_debug_app/tmp/.keep +0 -0
  72. package/template/src/templates/default/workspace/_debug_app/tmp/pids/.keep +0 -0
  73. package/template/src/templates/default/workspace/_debug_app/tmp/storage/.keep +0 -0
  74. package/template/src/templates/default/workspace/_debug_app/vendor/.keep +0 -0
  75. package/template/src/templates/rails-app/workspace/README.md +24 -0
  76. package/template/src/templates/rails-app/workspace/Rakefile +6 -0
  77. package/template/src/templates/rails-app/workspace/app/assets/images/.keep +0 -0
  78. package/template/src/templates/rails-app/workspace/app/assets/stylesheets/application.css +534 -0
  79. package/template/src/templates/rails-app/workspace/app/controllers/application_controller.rb +2 -0
  80. package/template/src/templates/rails-app/workspace/app/helpers/application_helper.rb +2 -0
  81. package/template/src/templates/rails-app/workspace/app/jobs/application_job.rb +7 -0
  82. package/template/src/templates/rails-app/workspace/app/mailers/application_mailer.rb +4 -0
  83. package/template/src/templates/rails-app/workspace/app/models/application_record.rb +3 -0
  84. package/template/src/templates/rails-app/workspace/app/models/concerns/.keep +0 -0
  85. package/template/src/templates/rails-app/workspace/app/views/layouts/application.html.erb +38 -0
  86. package/template/src/templates/rails-app/workspace/app/views/layouts/mailer.html.erb +13 -0
  87. package/template/src/templates/rails-app/workspace/app/views/layouts/mailer.text.erb +1 -0
  88. package/template/src/templates/rails-app/workspace/bin/rails +4 -0
  89. package/template/src/templates/rails-app/workspace/{store/config → config}/application.rb +1 -1
  90. package/template/src/templates/rails-app/workspace/config/boot.rb +3 -0
  91. package/template/src/templates/rails-app/workspace/config/database.yml +32 -0
  92. package/template/src/templates/rails-app/workspace/config/environment.rb +5 -0
  93. package/template/src/templates/rails-app/workspace/config/environments/development.rb +69 -0
  94. package/template/src/templates/rails-app/workspace/config/environments/production.rb +89 -0
  95. package/template/src/templates/rails-app/workspace/config/environments/test.rb +54 -0
  96. package/template/src/templates/rails-app/workspace/config/initializers/assets.rb +7 -0
  97. package/template/src/templates/rails-app/workspace/config/initializers/content_security_policy.rb +25 -0
  98. package/template/src/templates/rails-app/workspace/config/initializers/filter_parameter_logging.rb +8 -0
  99. package/template/src/templates/rails-app/workspace/config/initializers/inflections.rb +16 -0
  100. package/template/src/templates/rails-app/workspace/config/locales/en.yml +31 -0
  101. package/template/src/templates/rails-app/workspace/config/puma.rb +41 -0
  102. package/template/src/templates/rails-app/workspace/{store/config → config}/routes.rb +1 -1
  103. package/template/src/templates/rails-app/workspace/config/storage.yml +34 -0
  104. package/template/src/templates/rails-app/workspace/config.ru +6 -0
  105. package/template/src/templates/rails-app/workspace/db/seeds.rb +9 -0
  106. package/template/src/templates/rails-app/workspace/log/.keep +0 -0
  107. package/template/src/templates/rails-app/workspace/public/400.html +114 -0
  108. package/template/src/templates/rails-app/workspace/public/404.html +114 -0
  109. package/template/src/templates/rails-app/workspace/public/406-unsupported-browser.html +114 -0
  110. package/template/src/templates/rails-app/workspace/public/422.html +114 -0
  111. package/template/src/templates/rails-app/workspace/public/500.html +114 -0
  112. package/template/src/templates/rails-app/workspace/public/icon.png +0 -0
  113. package/template/src/templates/rails-app/workspace/public/icon.svg +3 -0
  114. package/template/src/templates/rails-app/workspace/public/robots.txt +1 -0
  115. package/template/src/templates/rails-app/workspace/script/.keep +0 -0
  116. package/template/src/templates/rails-app/workspace/storage/.keep +0 -0
  117. package/template/src/templates/rails-app/workspace/test/controllers/.keep +0 -0
  118. package/template/src/templates/rails-app/workspace/test/helpers/.keep +0 -0
  119. package/template/src/templates/rails-app/workspace/test/integration/.keep +0 -0
  120. package/template/src/templates/rails-app/workspace/tmp/.keep +0 -0
  121. package/template/src/templates/rails-app/workspace/tmp/cache/.keep +0 -0
  122. package/template/src/templates/rails-app/workspace/tmp/pids/.keep +0 -0
  123. package/template/src/templates/rails-app/workspace/tmp/sockets/.keep +0 -0
  124. package/template/src/templates/rails-app/workspace/tmp/storage/.keep +0 -0
  125. package/template/src/templates/rails-app/workspace/vendor/javascripts/.keep +0 -0
  126. package/template/tsconfig.json +3 -1
  127. package/template/uno.config.ts +17 -0
  128. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/README.md +0 -0
  129. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/Rakefile +0 -0
  130. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/assets/images/.keep +0 -0
  131. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/assets/stylesheets/application.css +0 -0
  132. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/controllers/application_controller.rb +0 -0
  133. /package/template/src/templates/{rails-app/workspace/store/app/models → default/workspace/_debug_app/app/controllers}/concerns/.keep +0 -0
  134. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/helpers/application_helper.rb +0 -0
  135. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/jobs/application_job.rb +0 -0
  136. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/mailers/application_mailer.rb +0 -0
  137. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/models/application_record.rb +0 -0
  138. /package/template/src/templates/{rails-app/workspace/store/log → default/workspace/_debug_app/app/models/concerns}/.keep +0 -0
  139. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/layouts/mailer.html.erb +0 -0
  140. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/layouts/mailer.text.erb +0 -0
  141. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/pwa/service-worker.js +0 -0
  142. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/bin/rails +0 -0
  143. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/boot.rb +0 -0
  144. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/database.yml +0 -0
  145. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environment.rb +0 -0
  146. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environments/development.rb +0 -0
  147. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environments/production.rb +0 -0
  148. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environments/test.rb +0 -0
  149. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/assets.rb +0 -0
  150. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/content_security_policy.rb +0 -0
  151. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/filter_parameter_logging.rb +0 -0
  152. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/inflections.rb +0 -0
  153. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/locales/en.yml +0 -0
  154. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/puma.rb +0 -0
  155. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/storage.yml +0 -0
  156. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config.ru +0 -0
  157. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/db/seeds.rb +0 -0
  158. /package/template/src/templates/{rails-app/workspace/store/script → default/workspace/_debug_app/lib/tasks}/.keep +0 -0
  159. /package/template/src/templates/{rails-app/workspace/store/storage → default/workspace/_debug_app/log}/.keep +0 -0
  160. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/400.html +0 -0
  161. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/404.html +0 -0
  162. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/406-unsupported-browser.html +0 -0
  163. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/422.html +0 -0
  164. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/500.html +0 -0
  165. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/icon.png +0 -0
  166. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/icon.svg +0 -0
  167. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/robots.txt +0 -0
  168. /package/template/src/templates/{rails-app/workspace/store/test/controllers → default/workspace/_debug_app/script}/.keep +0 -0
  169. /package/template/src/templates/{rails-app/workspace/store/test/helpers → default/workspace/_debug_app/storage}/.keep +0 -0
  170. /package/template/src/templates/{rails-app/workspace/store/test/integration → default/workspace/_debug_app/test/controllers}/.keep +0 -0
  171. /package/template/src/templates/{rails-app/workspace/store/tmp → default/workspace/_debug_app/test/fixtures/files}/.keep +0 -0
  172. /package/template/src/templates/{rails-app/workspace/store/tmp/pids → default/workspace/_debug_app/test/helpers}/.keep +0 -0
  173. /package/template/src/templates/{rails-app/workspace/store/tmp/storage → default/workspace/_debug_app/test/integration}/.keep +0 -0
  174. /package/template/src/templates/{rails-app/workspace/store/vendor/javascripts → default/workspace/_debug_app/test/mailers}/.keep +0 -0
  175. /package/template/src/templates/rails-app/workspace/{store/.ruby-version → .ruby-version} +0 -0
  176. /package/template/src/templates/rails-app/workspace/{store/Gemfile → Gemfile} +0 -0
  177. /package/template/src/templates/rails-app/workspace/{store/app → app}/javascript/application.js +0 -0
  178. /package/template/src/templates/rails-app/workspace/{store/app → app}/javascript/controllers/application.js +0 -0
  179. /package/template/src/templates/rails-app/workspace/{store/app → app}/javascript/controllers/index.js +0 -0
  180. /package/template/src/templates/rails-app/workspace/{store/bin → bin}/importmap +0 -0
  181. /package/template/src/templates/rails-app/workspace/{store/config → config}/cable.yml +0 -0
  182. /package/template/src/templates/rails-app/workspace/{store/config → config}/credentials.yml.enc +0 -0
  183. /package/template/src/templates/rails-app/workspace/{store/config → config}/importmap.rb +0 -0
  184. /package/template/src/templates/rails-app/workspace/{store/config → config}/master.key +0 -0
  185. /package/template/src/templates/rails-app/workspace/{store/test → test}/test_helper.rb +0 -0
@@ -0,0 +1,181 @@
1
+ ---
2
+ name: rails-wasm-author-constraints
3
+ description: |
4
+ Use this skill whenever checking if a Rails feature or gem works in WASM, or understanding
5
+ what's real vs. conceptual in lessons. Trigger on: 'does X work in WASM', 'can I use this
6
+ gem', 'gem compatibility', 'WASM limitation', 'what works', 'supported features', 'PGLite',
7
+ 'threading', 'Net::HTTP', 'ActionCable', 'background jobs', 'can I teach X', 'bundle
8
+ install in WASM', 'conceptual vs real', or any Rails capability question — even without
9
+ mentioning WASM. Authoritative compatibility matrix: which features work, which are shimmed,
10
+ which are impossible, plus gem tiers, PGLite behavior, boot timing, and conceptual-vs-real
11
+ operations. General Rails knowledge is insufficient here. Do NOT use for file organization
12
+ (use rails-file-management) or frontmatter (use tutorial-lesson-config).
13
+ ---
14
+
15
+ # Rails WASM Author Constraints
16
+
17
+ What works, what doesn't, and how to design lessons around the WASM environment.
18
+
19
+ ## Quick Reference: What Can I Teach?
20
+
21
+ | Rails Feature | Works? | Notes |
22
+ |---------------|--------|-------|
23
+ | ActiveRecord (CRUD, queries, scopes) | Yes | Full support via PGLite |
24
+ | Migrations & schema management | Yes | Standard `rails db:migrate` |
25
+ | Controllers & routing | Yes | Full support |
26
+ | Views (ERB templates) | Yes | Full support |
27
+ | Form helpers & validations | Yes | Full support |
28
+ | Model associations | Yes | `has_many`, `belongs_to`, etc. |
29
+ | Scaffolding & generators | Yes | `rails generate scaffold`, etc. |
30
+ | Asset pipeline (Propshaft) | Yes | Importmaps, Stimulus, Turbo |
31
+ | Rails console | Yes | Interactive IRB via custom bridge |
32
+ | Authentication (basic) | Yes | `has_secure_password`, session-based |
33
+ | ActionMailer (define mailers) | Partial | Can define/configure but delivery is a no-op |
34
+ | Active Storage (upload) | Partial | Upload works but image processing is a no-op |
35
+ | Background jobs | No | Single-threaded; Solid Queue won't process |
36
+ | ActionCable / WebSockets | No | No `IO.select`, no real socket support |
37
+ | External HTTP requests | No | No outbound networking from Ruby — sockets are unimplemented at the WASI level |
38
+ | Threads / parallel processing | No | `Thread.new` uses fibers (cooperative, single-threaded) |
39
+ | System commands from Ruby | No | `system()`, backticks, `Open3` are non-functional |
40
+
41
+ ## Hard Limitations
42
+
43
+ These are **impossible to work around** in the WASM environment. Do not write lessons that depend on them:
44
+
45
+ ### No Outbound Networking
46
+
47
+ Socket operations (`TCPSocket`, `UDPSocket`, and all socket classes) fail because the underlying WASI syscalls are unimplemented. `Net::HTTP` and `open-uri` will raise errors when attempting connections. You cannot:
48
+ - Call external APIs from Ruby
49
+ - Download files from the internet
50
+ - Connect to external databases or services
51
+
52
+ **Workaround for lessons:** If you want to teach API consumption, focus on the controller/model patterns and mock the responses. Show the code structure without executing real HTTP calls.
53
+
54
+ ### No Process Spawning
55
+
56
+ `system()`, backticks, `exec`, `fork`, and `Open3` are non-functional. The `rails new` generator works because it's been patched, but arbitrary shell commands from tutorial code will not work.
57
+
58
+ ### No Threading
59
+
60
+ `Thread.new` is shimmed to use `Fiber.new` (cooperative, single-threaded). Code that relies on parallel execution behaves differently. Background job processing and concurrent operations don't work as expected.
61
+
62
+ ### No IO.select
63
+
64
+ The `poll_oneoff` WASI syscall is unimplemented. This breaks gems like nio4r, Puma, and ActionCable's EventMachine adapter.
65
+
66
+ ### No chmod/fchmod
67
+
68
+ POSIX permission calls are stubbed. Avoid `FileUtils.chmod` in tutorial code. The `rails new` generator is pre-patched to handle this.
69
+
70
+ ## Gem Compatibility
71
+
72
+ ### Adding Gems
73
+
74
+ Authors **can add gems** to their tutorial by editing `ruby-wasm/Gemfile` and running `bin/build-wasm` to rebuild the WASM binary. This bakes all gems into the binary at build time.
75
+
76
+ ### Compatibility Tiers
77
+
78
+ | Tier | Description | Examples |
79
+ |------|-------------|---------|
80
+ | **Works** | Pure Ruby gems, no native extensions | `devise`, `friendly_id`, `pagy`, `pundit`, `draper`, `kaminari` |
81
+ | **Shimmed** | Has native extensions but already patched | `nokogiri` (stub), `io-console` (stub), `bcrypt` |
82
+ | **Needs testing** | May work if extension compiles for WASM | Test with `bin/build-wasm` |
83
+ | **Won't work** | Requires unsupported syscalls or networking | `pg` (native), `mysql2`, `redis`, `sidekiq`, `puma` |
84
+
85
+ ### Pre-Shimmed Gems
86
+
87
+ These gems are already handled by the WASM runtime:
88
+
89
+ | Gem | Behavior |
90
+ |-----|----------|
91
+ | `nokogiri` | 165-line minimal stub; CSS selectors return `[]`; sufficient for sanitization but not real HTML parsing |
92
+ | `io-console` | Stubbed; `winsize` returns `[80, 24]`; `raw` yields without change |
93
+ | `nio4r` | `.so` stripped; loads as empty shim |
94
+ | `date`, `psych`, `bigdecimal` | `.so` stripped; Ruby falls back to pure-Ruby stdlib |
95
+ | `sqlite3` (native) | Replaced by PGLite adapter |
96
+
97
+ ### Gem Build Workflow
98
+
99
+ ```bash
100
+ # 1. Edit the Gemfile
101
+ vim ruby-wasm/Gemfile
102
+
103
+ # 2. Rebuild the WASM binary (takes several minutes)
104
+ bin/build-wasm
105
+
106
+ # 3. Test your tutorial locally
107
+ npm run dev
108
+ ```
109
+
110
+ ## Database: PGLite
111
+
112
+ The database is **PGLite** — an in-browser PostgreSQL implementation compiled to WASM.
113
+
114
+ ### What Works
115
+
116
+ - Standard ActiveRecord operations: `create`, `find`, `where`, `update`, `destroy`
117
+ - Migrations: `rails db:migrate`, `rails db:rollback`
118
+ - Seeds: `rails db:seed`
119
+ - PostgreSQL-compatible SQL syntax
120
+ - Multiple databases (development, test)
121
+ - Associations, joins, aggregations
122
+ - Indexes and constraints
123
+
124
+ ### What to Know
125
+
126
+ | Behavior | Detail |
127
+ |----------|--------|
128
+ | **Data does not survive page reloads** | WebContainer filesystem is in-memory; refreshing the browser resets everything |
129
+ | **Database adapter** | `pglite` (auto-configured by wasmify-rails; authors don't need to set this up) |
130
+ | **Setup command** | `node scripts/rails.js db:prepare` in `prepareCommands` |
131
+ | **Location** | `pgdata/<dbname>/` in WebContainer filesystem |
132
+ | **Performance** | Slower than native PostgreSQL; acceptable for tutorial-sized datasets |
133
+
134
+ ### Lesson Design Implications
135
+
136
+ - Always include `['node scripts/rails.js db:prepare', 'Prepare development database']` in `prepareCommands` for lessons that use the database
137
+ - Provide seeds in templates so users start with data
138
+ - Don't rely on data from a previous lesson persisting — each lesson should set up its own state via migrations + seeds
139
+
140
+ ## Boot Timing
141
+
142
+ The WASM runtime takes time to load. Set expectations for tutorial users:
143
+
144
+ | Phase | Typical Duration | What Happens |
145
+ |-------|-----------------|--------------|
146
+ | `npm install` | 10-30s | Downloads ~80MB WASM binary + dependencies |
147
+ | WASM compile | 2-5s | Browser compiles the binary |
148
+ | Rails bootstrap | 2-5s | Loads Rails framework from embedded VFS |
149
+ | Command execution | Varies | User's command runs |
150
+
151
+ **Total first-load time: 15-40 seconds** depending on network and browser.
152
+
153
+ ### Author Tips for Boot Experience
154
+
155
+ - Include a note in early lessons: "The Ruby runtime takes a moment to load — this is normal!"
156
+ - Use `prepareCommands` with descriptive labels so users see progress
157
+ - The `['output', 'Setup Logs']` terminal panel shows boot details
158
+ - Subsequent lesson navigation is faster if the WebContainer is already booted
159
+
160
+ ## Auto-Authentication
161
+
162
+ The runtime includes an auto-login patch: if `tmp/authenticated-user.txt` exists in the Rails app root, the first HTTP request auto-authenticates the user found by `User.find_by(email_address: <file contents>)`. The file should contain a single email address (e.g., `admin@example.com`).
163
+
164
+ **To use:** Place `workspace/tmp/authenticated-user.txt` in `_files/` or a template, containing the email of a seeded user. The first request calls `start_new_session_for(user)`, creating a session cookie for all subsequent requests. Runs once per VM lifetime (the `$__pre_authenticated` global flag prevents repeat attempts).
165
+
166
+ This requires the Rails 8 `Authentication` concern and a `User` model with an `email_address` column.
167
+
168
+ ## Filesystem Boundaries
169
+
170
+ Ruby code can only access `/workspace` (the WASI preopen). Attempting to access paths outside this boundary raises `Errno::ENOENT`. Tutorial code should never navigate above `/workspace` with `Dir.chdir("..")` or absolute paths outside the preopen.
171
+
172
+ ## Conceptual vs. Real Operations
173
+
174
+ Some tutorial operations are **conceptual** — they teach the correct pattern but the actual execution requires something different in the WASM environment:
175
+
176
+ | Operation in lesson content | Reality | What the author must do |
177
+ |-----------------------------|---------|------------------------|
178
+ | "Add `gem 'devise'` to your Gemfile" | Gems are baked into the WASM binary at build time | The gem must already be in `ruby-wasm/Gemfile` and rebuilt with `bin/build-wasm` **before** the tutorial is published |
179
+ | "Run `bundle install`" | `bundle install` is a no-op in WASM — all gems come from the binary | Include it for pedagogical completeness; it will appear to succeed |
180
+ | "Run `rails server`" | The Rails server runs through `node scripts/rails.js server` | Use `node scripts/rails.js server` in frontmatter `mainCommand`; in lesson content, show `rails server` since that's what the terminal wrapper understands |
181
+ | "Edit `database.yml`" | Database adapter is PGLite, auto-configured by wasmify-rails | Pre-configure in the template; showing a `database.yml` edit is fine for teaching but won't change the runtime behavior |
@@ -0,0 +1,377 @@
1
+ ---
2
+ name: tutorial-content-structure
3
+ description: |
4
+ Use this skill whenever organizing tutorial content into parts, chapters, and lessons.
5
+ Trigger when the user says 'create a lesson', 'create a chapter', 'create a part', 'add
6
+ a lesson', 'tutorial structure', 'content.md', 'meta.md', 'lesson ordering', 'callout',
7
+ ':::tip', ':::info', ':::warn', 'code import', 'expressive code', or asks about the
8
+ content hierarchy, directory naming, or markdown features — even if they don't explicitly
9
+ mention content structure. This skill documents TutorialKit's specific directory conventions,
10
+ metadata file roles (meta.md vs content.md), numeric prefix ordering, the recommended
11
+ tutorial root config for Rails, callout syntax with all attributes, code block file imports,
12
+ and expressive code features. These are framework-specific patterns that cannot be inferred
13
+ from general knowledge. Do NOT use for frontmatter option reference (use
14
+ tutorial-lesson-config) or file/template organization (use rails-file-management).
15
+ ---
16
+
17
+ # Tutorial Content Structure
18
+
19
+ How to organize interactive Ruby on Rails tutorials using TutorialKit's content hierarchy.
20
+
21
+ ## Content Hierarchy
22
+
23
+ Tutorials use a directory-based hierarchy under `src/content/tutorial/`:
24
+
25
+ ```
26
+ src/content/tutorial/
27
+ meta.md # Tutorial root config (type: tutorial)
28
+ 1-getting-started/ # Part
29
+ meta.md # Part config (type: part)
30
+ 1-first-rails-app/ # Lesson (or Chapter, if it contains lessons)
31
+ content.md # Lesson content (type: lesson)
32
+ _files/ # Initial code files
33
+ _solution/ # Solution code files
34
+ 2-rails-console/
35
+ content.md
36
+ _files/
37
+ ```
38
+
39
+ The full hierarchy is **Tutorial > Parts > Chapters > Lessons**, but you can skip levels:
40
+
41
+ | Structure | When to Use |
42
+ |-----------|-------------|
43
+ | Parts > Chapters > Lessons | Large tutorials with major sections and subsections |
44
+ | Parts > Lessons | Medium tutorials grouped by topic |
45
+ | Lessons only | Small tutorials or quick guides |
46
+
47
+ ## Directory Naming
48
+
49
+ Directories use **numeric prefixes** for ordering: `1-basics/`, `2-controllers/`, `3-views/`.
50
+
51
+ - The number determines display order in navigation
52
+ - The text after the number becomes part of the URL slug
53
+ - Use kebab-case: `1-creating-your-first-rails-app/`
54
+
55
+ ## Metadata Files
56
+
57
+ ### `meta.md` — Tutorial Root
58
+
59
+ Every tutorial needs a root `meta.md` at `src/content/tutorial/meta.md`. This sets **inherited defaults** for all lessons:
60
+
61
+ ```yaml
62
+ ---
63
+ type: tutorial
64
+ prepareCommands:
65
+ - ['npm install', 'Preparing Ruby runtime']
66
+ terminalBlockingPrepareCommandsCount: 1
67
+ previews: false
68
+ filesystem:
69
+ watch: ['/*.json', '/workspace/**/*']
70
+ terminal:
71
+ open: true
72
+ activePanel: 0
73
+ panels:
74
+ - type: terminal
75
+ id: 'cmds'
76
+ title: 'Command Line'
77
+ allowRedirects: true
78
+ - ['output', 'Setup Logs']
79
+ ---
80
+ ```
81
+
82
+ **Important for Rails tutorials:** The config above is the recommended baseline. It sets up `npm install` as a blocking prepare command (downloads the ~80MB WASM runtime), watches `/workspace/**/*` for file changes, and configures a persistent terminal with setup log output.
83
+
84
+ ### `meta.md` — Parts and Chapters
85
+
86
+ Parts and chapters each get a `meta.md`:
87
+
88
+ ```yaml
89
+ ---
90
+ type: part
91
+ title: Getting Started with Rails
92
+ ---
93
+ ```
94
+
95
+ ```yaml
96
+ ---
97
+ type: chapter
98
+ title: Models and Databases
99
+ ---
100
+ ```
101
+
102
+ Parts and chapters can also set inherited configuration (same options as tutorial root). Any setting here overrides the tutorial default for all lessons within.
103
+
104
+ ### `content.md` — Lessons
105
+
106
+ Each lesson directory contains a `content.md` with frontmatter and markdown body:
107
+
108
+ ```yaml
109
+ ---
110
+ type: lesson
111
+ title: Creating your first Rails app
112
+ editor: false
113
+ custom:
114
+ shell:
115
+ workdir: "/workspace"
116
+ ---
117
+
118
+ # Your lesson content here
119
+
120
+ Write instructions, explanations, and code examples in standard markdown.
121
+ ```
122
+
123
+ ## Explicit Ordering
124
+
125
+ Instead of relying on numeric prefixes, you can explicitly order children in `meta.md`:
126
+
127
+ ```yaml
128
+ ---
129
+ type: tutorial
130
+ parts:
131
+ - getting-started # matches folder name (without numeric prefix)
132
+ - controllers
133
+ - views
134
+ ---
135
+ ```
136
+
137
+ ```yaml
138
+ ---
139
+ type: part
140
+ title: Getting Started
141
+ lessons:
142
+ - creating-your-first-rails-app
143
+ - rails-console
144
+ ---
145
+ ```
146
+
147
+ When using explicit ordering, folder names don't need numeric prefixes.
148
+
149
+ ## Markdown Features
150
+
151
+ ### Callouts
152
+
153
+ ```markdown
154
+ :::tip
155
+ Rails follows RESTful conventions, making your applications predictable.
156
+ :::
157
+
158
+ :::info
159
+ You can customize the application Rails generates by using flags.
160
+ :::
161
+
162
+ :::warn
163
+ This operation will reset the database.
164
+ :::
165
+
166
+ :::danger
167
+ Never expose your secret_key_base in production.
168
+ :::
169
+
170
+ :::success
171
+ Your Rails app is running!
172
+ :::
173
+ ```
174
+
175
+ Callouts support optional attributes:
176
+
177
+ ```markdown
178
+ :::tip{title="Pro Tip"}
179
+ Custom title for the callout.
180
+ :::
181
+
182
+ :::info{noBorder=true}
183
+ Borderless callout style.
184
+ :::
185
+
186
+ :::warn{hideTitle=true}
187
+ Only the message, no title bar.
188
+ :::
189
+
190
+ :::danger{hideIcon=true}
191
+ Title shown but no icon.
192
+ :::
193
+
194
+ :::tip{class="my-custom-class"}
195
+ Custom CSS class on the callout container.
196
+ :::
197
+ ```
198
+
199
+ | Attribute | Effect |
200
+ |-----------|--------|
201
+ | `title` | Custom title text (replaces default like "Tip", "Info") |
202
+ | `noBorder` | `"true"` removes the left border |
203
+ | `hideTitle` | `"true"` hides the entire title bar |
204
+ | `hideIcon` | `"true"` hides the icon but keeps the title |
205
+ | `class` | Additional CSS classes on the callout container |
206
+
207
+ ### Code Block File Imports
208
+
209
+ Inline the contents of lesson files directly in your markdown:
210
+
211
+ ~~~markdown
212
+ ```file:/workspace/app/controllers/products_controller.rb
213
+ ```
214
+ ~~~
215
+
216
+ This renders the contents of the file from the lesson's `_files/` directory. For solution files:
217
+
218
+ ~~~markdown
219
+ ```solution:/workspace/app/controllers/products_controller.rb
220
+ ```
221
+ ~~~
222
+
223
+ ### Expressive Code Attributes
224
+
225
+ Code blocks support highlighting, line annotations, and framing:
226
+
227
+ ~~~markdown
228
+ ```ruby title="app/models/product.rb" showLineNumbers
229
+ class Product < ApplicationRecord
230
+ validates :name, presence: true # [!code highlight]
231
+ validates :price, numericality: { greater_than: 0 }
232
+ end
233
+ ```
234
+ ~~~
235
+
236
+ Available attributes:
237
+
238
+ | Attribute | Example | Effect |
239
+ |-----------|---------|--------|
240
+ | `title` | `title="config/routes.rb"` | Shows filename header |
241
+ | `showLineNumbers` | `showLineNumbers` | Display line numbers |
242
+ | `ins={lines}` | `ins={2-3}` | Mark lines as inserted (green) |
243
+ | `del={lines}` | `del={5}` | Mark lines as deleted (red) |
244
+ | `{lines}` | `{1,3-5}` | Highlight specific lines |
245
+ | `collapse={range}` | `collapse={1-5}` | Collapse line range |
246
+ | `frame="terminal"` | `frame="terminal"` | Terminal-style frame |
247
+
248
+ ### Rails path links
249
+
250
+ Inline code matching Rails path patterns (`app/`, `db/`, `config/`, `test/`) is automatically converted into clickable buttons that open the file in the editor:
251
+
252
+ ```markdown
253
+ Open `app/models/user.rb` to see the User model.
254
+ ```
255
+
256
+ This is handled by the `remarkRailsPathLinks` plugin. Only paths starting with `app/`, `db/`, `config/`, or `test/` are matched.
257
+
258
+ ### Links
259
+
260
+ Standard markdown links work. For linking to the running app preview:
261
+
262
+ ```markdown
263
+ Visit [the home page](http://localhost:3000) to see the result.
264
+ ```
265
+
266
+ ### MDX Support
267
+
268
+ Rename `content.md` to `content.mdx` to use MDX features (component imports, JSX in markdown).
269
+
270
+ ## Styling Rails Views in Lessons
271
+
272
+ The `rails-app` template includes a CSS design system with BEM components and CSS custom properties. When writing ERB views in `_files/` or `_solution/`, use these classes for consistent styling without adding custom CSS.
273
+
274
+ ### Common Patterns
275
+
276
+ **Page with a list (index view):**
277
+
278
+ ```erb
279
+ <div class="page-header">
280
+ <h1>Tickets</h1>
281
+ <%= link_to "New Ticket", new_ticket_path, class: "btn btn--primary" %>
282
+ </div>
283
+
284
+ <div class="card">
285
+ <table class="table">
286
+ <thead>
287
+ <tr>
288
+ <th>Title</th>
289
+ <th>Status</th>
290
+ <th></th>
291
+ </tr>
292
+ </thead>
293
+ <tbody>
294
+ <% @tickets.each do |ticket| %>
295
+ <tr>
296
+ <td><%= link_to ticket.title, ticket %></td>
297
+ <td><span class="badge badge--primary"><%= ticket.status %></span></td>
298
+ <td class="inline-actions">
299
+ <%= link_to "Edit", edit_ticket_path(ticket), class: "btn btn--small" %>
300
+ </td>
301
+ </tr>
302
+ <% end %>
303
+ </tbody>
304
+ </table>
305
+ </div>
306
+ ```
307
+
308
+ **Detail page (show view):**
309
+
310
+ ```erb
311
+ <div class="page-header">
312
+ <h1><%= @ticket.title %></h1>
313
+ <div class="inline-actions">
314
+ <%= link_to "Edit", edit_ticket_path(@ticket), class: "btn btn--small" %>
315
+ <%= link_to "Back", tickets_path, class: "btn btn--small" %>
316
+ </div>
317
+ </div>
318
+
319
+ <div class="card mb-md">
320
+ <div class="card__body">
321
+ <p><%= @ticket.description %></p>
322
+ <p class="text-muted text-sm">
323
+ Created by <%= @ticket.user.name %> &middot;
324
+ <span class="badge badge--primary"><%= @ticket.status %></span>
325
+ </p>
326
+ </div>
327
+ </div>
328
+ ```
329
+
330
+ **Form:**
331
+
332
+ ```erb
333
+ <div class="card">
334
+ <div class="card__header">
335
+ <h2>New Ticket</h2>
336
+ </div>
337
+ <div class="card__body">
338
+ <%= form_with model: @ticket do |form| %>
339
+ <div class="form__group">
340
+ <%= form.label :title, class: "form__label" %>
341
+ <%= form.text_field :title, class: "input" %>
342
+ </div>
343
+
344
+ <div class="form__group">
345
+ <%= form.label :description, class: "form__label" %>
346
+ <%= form.text_area :description, class: "input" %>
347
+ </div>
348
+
349
+ <div class="form__actions">
350
+ <%= form.submit "Create", class: "btn btn--primary" %>
351
+ <%= link_to "Cancel", tickets_path, class: "btn" %>
352
+ </div>
353
+ <% end %>
354
+ </div>
355
+ </div>
356
+ ```
357
+
358
+ **Flash messages** are handled automatically by the layout. Controllers just use `redirect_to ..., notice: "..."` or `alert: "..."`.
359
+
360
+ ### Quick Reference
361
+
362
+ | Need | Classes |
363
+ |------|---------|
364
+ | Primary action button | `btn btn--primary` |
365
+ | Destructive action | `btn btn--danger` |
366
+ | Small inline button | `btn btn--small` |
367
+ | Text input / textarea / select | `input` |
368
+ | Wrap content in a box | `card` > `card__body` |
369
+ | Box with title | `card` > `card__header` + `card__body` |
370
+ | Status label | `badge badge--primary` (or `--success`, `--danger`, `--warning`) |
371
+ | Data table | `table` |
372
+ | Title + action row | `page-header` |
373
+ | Form field wrapper | `form__group` > `form__label` + `input` |
374
+ | Muted helper text | `text-muted text-sm` |
375
+ | Row of buttons/links | `inline-actions` |
376
+
377
+ See the `tutorial-quickstart` skill for the full CSS custom properties reference and the complete component list.