@tutorialkit-rb/cli 0.1.5 → 0.1.8

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 +47 -38
  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
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import "yargs-parser";
14
14
  // package.json
15
15
  var package_default = {
16
16
  name: "@tutorialkit-rb/cli",
17
- version: "0.1.5",
17
+ version: "0.1.8",
18
18
  description: "Interactive tutorials powered by WebContainer API",
19
19
  author: "StackBlitz Inc.",
20
20
  type: "module",
@@ -675,30 +675,32 @@ import path4 from "node:path";
675
675
  import * as prompts3 from "@clack/prompts";
676
676
  import chalk4 from "chalk";
677
677
 
678
- // src/commands/create/hosting-config/_headers.txt?raw
679
- var headers_default = "/*\n Cross-Origin-Embedder-Policy: require-corp\n Cross-Origin-Opener-Policy: same-origin\n";
678
+ // src/commands/create/hosting-config/Dockerfile.txt?raw
679
+ var Dockerfile_default = `# Minimal nginx image to serve pre-built static files
680
+ # Build happens in GitHub Actions (with WASM caching), not here
680
681
 
681
- // src/commands/create/hosting-config/netlify_toml.txt?raw
682
- var netlify_toml_default = '[[headers]]\n for = "/*"\n [headers.values]\n Cross-Origin-Embedder-Policy = "require-corp"\n Cross-Origin-Opener-Policy = "same-origin"\n';
682
+ FROM nginx:alpine
683
683
 
684
- // src/commands/create/hosting-config/vercel.json?raw
685
- var vercel_default = {
686
- headers: [
687
- {
688
- source: "/(.*)",
689
- headers: [
690
- {
691
- key: "Cross-Origin-Embedder-Policy",
692
- value: "require-corp"
693
- },
694
- {
695
- key: "Cross-Origin-Opener-Policy",
696
- value: "same-origin"
697
- }
698
- ]
699
- }
700
- ]
701
- };
684
+ # Copy pre-built site from GitHub Actions
685
+ COPY dist /usr/share/nginx/html
686
+
687
+ # Add required headers for WebContainers (SharedArrayBuffer support)
688
+ RUN echo 'add_header Cross-Origin-Embedder-Policy "require-corp";' > /etc/nginx/conf.d/custom-headers.conf \\
689
+ && echo 'add_header Cross-Origin-Opener-Policy "same-origin";' >> /etc/nginx/conf.d/custom-headers.conf \\
690
+ && echo 'add_header Cross-Origin-Resource-Policy "cross-origin";' >> /etc/nginx/conf.d/custom-headers.conf
691
+
692
+ EXPOSE 80
693
+ CMD ["nginx", "-g", "daemon off;"]
694
+ `;
695
+
696
+ // src/commands/create/hosting-config/deploy_flyio_yml.txt?raw
697
+ var deploy_flyio_yml_default = "name: Build and Deploy\n\non:\n push:\n branches: [main]\n workflow_dispatch:\n\nconcurrency:\n group: deploy\n cancel-in-progress: false\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n\n - uses: actions-rust-lang/setup-rust-toolchain@v1\n with:\n toolchain: 1.74.0\n\n - name: Install wasi-vfs\n env:\n WASI_VFS_VERSION: 0.5.4\n run: |\n curl -LO \"https://github.com/kateinoigakukun/wasi-vfs/releases/download/v${WASI_VFS_VERSION}/wasi-vfs-cli-x86_64-unknown-linux-gnu.zip\"\n unzip wasi-vfs-cli-x86_64-unknown-linux-gnu.zip\n mv wasi-vfs /usr/local/bin/wasi-vfs\n\n - uses: ruby/setup-ruby@v1\n env:\n BUNDLE_GEMFILE: ruby-wasm/Gemfile\n BUNDLE_PATH: ruby-wasm/vendor\n with:\n ruby-version: 3.3\n bundler-cache: true\n\n - uses: actions/setup-node@v4\n with:\n node-version: 20\n cache: npm\n\n - name: Cache ruby.wasm build artifacts\n uses: actions/cache@v4\n with:\n path: |\n ruby-wasm/build\n ruby-wasm/rubies\n key: ${{ runner.os }}-ruby-wasm-${{ hashFiles('ruby-wasm/Gemfile.lock') }}\n restore-keys: |\n ${{ runner.os }}-ruby-wasm-\n\n - name: Build ruby.wasm\n run: npm run build:wasm\n\n - name: Build tutorial\n run: |\n npm install\n npm run build\n\n - name: Setup flyctl\n if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'\n uses: superfly/flyctl-actions/setup-flyctl@master\n\n - name: Deploy to Fly.io\n if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'\n run: flyctl deploy --local-only\n env:\n FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}\n";
698
+
699
+ // src/commands/create/hosting-config/fly_toml.txt?raw
700
+ var fly_toml_default = "# Fly.io configuration\n# Update 'app' to your Fly.io app name after running: fly apps create <name>\napp = 'my-tutorial'\nprimary_region = 'sjc'\n\n[build]\n\n[http_service]\n internal_port = 80\n force_https = true\n auto_stop_machines = 'stop'\n auto_start_machines = true\n min_machines_running = 0\n processes = ['app']\n\n[[vm]]\n size = 'shared-cpu-1x'\n";
701
+
702
+ // src/commands/create/hosting-config/netlify_toml.txt?raw
703
+ var netlify_toml_default = '[build]\n publish = "./dist"\n command = "# no build needed \u2014 CI handles the build"\n\n[[headers]]\n for = "/*"\n [headers.values]\n Cross-Origin-Embedder-Policy = "require-corp"\n Cross-Origin-Opener-Policy = "same-origin"\n';
702
704
 
703
705
  // src/commands/create/generate-hosting-config.ts
704
706
  async function generateHostingConfig(dest, flags) {
@@ -707,9 +709,11 @@ async function generateHostingConfig(dest, flags) {
707
709
  provider = await prompts3.select({
708
710
  message: "Select hosting providers for automatic configuration:",
709
711
  options: [
710
- { value: "Vercel", label: "Vercel" },
711
712
  { value: "Netlify", label: "Netlify" },
712
- { value: "Cloudflare", label: "Cloudflare" },
713
+ { value: "Fly.io", label: "Fly.io" },
714
+ // TODO: re-enable when full deployment pipelines are added
715
+ // { value: 'Vercel', label: 'Vercel' },
716
+ // { value: 'Cloudflare', label: 'Cloudflare' },
713
717
  { value: "skip", label: "Skip hosting configuration" }
714
718
  ],
715
719
  initialValue: DEFAULT_VALUES.provider
@@ -731,20 +735,20 @@ async function generateHostingConfig(dest, flags) {
731
735
  }
732
736
  let config;
733
737
  let filename;
738
+ let extraFiles;
734
739
  switch (provider.toLowerCase()) {
735
- case "vercel": {
736
- config = typeof vercel_default === "string" ? vercel_default : JSON.stringify(vercel_default, null, 2);
737
- filename = "vercel.json";
738
- break;
739
- }
740
740
  case "netlify": {
741
741
  config = netlify_toml_default;
742
742
  filename = "netlify.toml";
743
743
  break;
744
744
  }
745
- case "cloudflare": {
746
- config = headers_default;
747
- filename = "_headers";
745
+ case "fly.io": {
746
+ config = fly_toml_default;
747
+ filename = "fly.toml";
748
+ extraFiles = [
749
+ { filename: "Dockerfile", content: Dockerfile_default },
750
+ { filename: path4.join(".github", "workflows", "deploy.yml"), content: deploy_flyio_yml_default }
751
+ ];
748
752
  break;
749
753
  }
750
754
  }
@@ -756,7 +760,16 @@ async function generateHostingConfig(dest, flags) {
756
760
  task: async () => {
757
761
  const filepath = path4.join(resolvedDest, filename);
758
762
  fs3.writeFileSync(filepath, config);
759
- return `Added ${filepath}`;
763
+ const added = [filepath];
764
+ if (extraFiles) {
765
+ for (const extra of extraFiles) {
766
+ const extraPath = path4.join(resolvedDest, extra.filename);
767
+ fs3.mkdirSync(path4.dirname(extraPath), { recursive: true });
768
+ fs3.writeFileSync(extraPath, extra.content);
769
+ added.push(extraPath);
770
+ }
771
+ }
772
+ return added.map((f) => `Added ${f}`).join("\n");
760
773
  }
761
774
  });
762
775
  }
@@ -1122,10 +1135,6 @@ function updatePackageJson(dest, projectName, flags, provider) {
1122
1135
  pkgJson.name = projectName;
1123
1136
  updateWorkspaceVersions(pkgJson.dependencies, TUTORIALKIT_VERSION);
1124
1137
  updateWorkspaceVersions(pkgJson.devDependencies, TUTORIALKIT_VERSION);
1125
- if (provider.toLowerCase() === "cloudflare") {
1126
- pkgJson.scripts = pkgJson.scripts || {};
1127
- pkgJson.scripts.postbuild = "cp _headers ./dist/";
1128
- }
1129
1138
  fs7.writeFileSync(pkgPath, JSON.stringify(pkgJson, null, 2));
1130
1139
  try {
1131
1140
  const pkgLockPath = path8.resolve(dest, "package-lock.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tutorialkit-rb/cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.8",
4
4
  "description": "Interactive tutorials powered by WebContainer API",
5
5
  "author": "StackBlitz Inc.",
6
6
  "type": "module",
@@ -0,0 +1,211 @@
1
+ ---
2
+ name: rails-file-management
3
+ description: |
4
+ Use this skill whenever organizing files for Rails tutorial lessons — _files/, _solution/,
5
+ templates, workspace paths. Trigger on: 'create _files', 'add _solution', 'template
6
+ inheritance', '.tk-config.json', 'extends path', 'workspace path', 'which files go where',
7
+ 'add a gem', 'Gemfile', 'build-wasm', 'create a template', 'protected files', 'do not
8
+ override', or asking where to put Rails app code — even without mentioning file management.
9
+ Covers the three-layer merge model, built-in templates, `.tk-config.json` path formula for
10
+ different nesting depths, protected infrastructure files, and WASM gem build workflow. Do
11
+ NOT attempt file organization without this skill. Do NOT use for frontmatter (use
12
+ tutorial-lesson-config) or content hierarchy (use tutorial-content-structure).
13
+ ---
14
+
15
+ # Rails File Management
16
+
17
+ How to organize files across templates, `_files`, and `_solution` directories for Rails tutorial lessons.
18
+
19
+ ## The File Layering Model
20
+
21
+ When a lesson loads, TutorialKit merges files from multiple sources in this order:
22
+
23
+ ```
24
+ Template (base) ← Runtime infrastructure, package.json, bin/, lib/
25
+ ↓ overlaid by
26
+ _files/ (lesson) ← Lesson-specific Rails app code
27
+ ↓ replaced on "Solve"
28
+ _solution/ (lesson) ← Completed/answer code
29
+ ```
30
+
31
+ Files from each layer **overlay** the previous — same-path files are replaced, new files are added.
32
+
33
+ **Two template mechanisms (don't confuse them):**
34
+ - **`template` frontmatter field** (e.g., `template: default`) — selects which base template JSON is loaded as the "Template (base)" layer. Almost always set in the part's `meta.md`. The tutorial's root `meta.md` contains `rails-app`—a minimal demo app used in the tutotiral.
35
+ - **`_files/.tk-config.json` with `extends`** — extends the `_files` layer with files from another template directory. These provide the source code that overlays on top of the template used. Use it to share many files between lessons. Otherwise, prefer duplication and the `template` frontmatter..
36
+
37
+ IMPORTANT: Files added via the `template` frontmatter are not visible in the editor; only the `_files` contents (including `_files/.tk-config.json`'s contents) are shown in the editor.
38
+
39
+ ## Templates
40
+
41
+ Templates live in `src/templates/` and provide the base project structure for WebContainer.
42
+
43
+ ### Built-in Templates
44
+
45
+ | Template | Purpose | Inherits From |
46
+ |----------|---------|---------------|
47
+ | `default` | Base Rails WASM runtime — Node.js wrappers, Express server, PGLite, WASM loader. Selected via `template: default` in frontmatter (the default). | (none) |
48
+ | `rails-app` | Pre-generated Rails app at `workspace/`. Update this template to set up the base Rails app for the tutorial | `default` |
49
+
50
+ ### Template Inheritance
51
+
52
+ Templates can extend other templates via `.tk-config.json`:
53
+
54
+ ```json
55
+ {
56
+ "extends": "../rails-app"
57
+ }
58
+ ```
59
+
60
+ The child template's files overlay on top of the parent's files.
61
+
62
+ ### Creating a New Template
63
+
64
+ Create a directory under `src/templates/` with:
65
+
66
+ 1. A `.tk-config.json` extending the appropriate parent
67
+ 2. The files that differ from the parent
68
+
69
+ **Example:** A template for a blog tutorial:
70
+
71
+ ```
72
+ src/templates/blog-app/
73
+ .tk-config.json → { "extends": "../rails-app" }
74
+ workspace/
75
+ app/
76
+ models/post.rb
77
+ controllers/posts_controller.rb
78
+ views/posts/
79
+ index.html.erb
80
+ show.html.erb
81
+ config/routes.rb
82
+ db/
83
+ migrate/001_create_posts.rb
84
+ seeds.rb
85
+ ```
86
+
87
+ ### What Belongs in a Template vs. a Lesson
88
+
89
+ | In a Template | In a Lesson's `_files/` |
90
+ |---------------|------------------------|
91
+ | Shared app structure used by multiple lessons | Files specific to one lesson |
92
+ | Models, migrations, seeds for a baseline state | Modifications the user will build upon |
93
+ | Routes and config shared across a chapter | One-off configuration changes |
94
+ | Complete working app states (scaffolded code) | Starter/skeleton files the user will complete |
95
+
96
+ **Rule of thumb:** If 3+ lessons need the same base state, create a template. If only one lesson uses it, put it in `_files/`.
97
+
98
+ ## `_files/` Directory
99
+
100
+ The `_files/` directory contains files shown to the user when the lesson loads. These are the **starting state** for the lesson.
101
+
102
+ ### Path Convention
103
+
104
+ All Rails app files **must** live under `workspace/` to match the WASI preopen at `/workspace`:
105
+
106
+ ```
107
+ _files/
108
+ workspace/
109
+ app/
110
+ controllers/
111
+ products_controller.rb
112
+ models/
113
+ product.rb
114
+ views/
115
+ products/
116
+ index.html.erb
117
+ config/
118
+ routes.rb
119
+ ```
120
+
121
+ ### `.tk-config.json` Path Formula
122
+
123
+ The `extends` path in `.tk-config.json` is **relative to the directory containing the `.tk-config.json` file**. To reach `src/templates/<name>`, count the `../` segments based on your content nesting depth:
124
+
125
+ | Content structure | `../` count | Example `extends` value |
126
+ |-------------------|-------------|------------------------|
127
+ | `tutorial/lesson/_files/` | 4 | `"../../../../templates/rails-app"` |
128
+ | `tutorial/part/lesson/_files/` | 5 | `"../../../../../templates/rails-app"` |
129
+ | `tutorial/part/chapter/lesson/_files/` | 6 | `"../../../../../../templates/rails-app"` |
130
+
131
+ The count is: 1 (`_files/`) + 1 (lesson dir) + N (intermediate dirs: part, chapter) + 1 (`tutorial/`) + 1 (`content/`) = **4 + number of intermediate levels**.
132
+
133
+ For the standard `tutorial/part/lesson/_files/` structure used by most Rails tutorials, the path is always `"../../../../../templates/<name>"` (5 segments).
134
+
135
+ ## `_solution/` Directory
136
+
137
+ The `_solution/` directory contains the **completed code** for the lesson. When the user clicks "Solve", solution files replace the corresponding `_files/`.
138
+
139
+ ### Structure
140
+
141
+ Mirror the `_files/` directory structure:
142
+
143
+ ```
144
+ _solution/
145
+ workspace/
146
+ store/
147
+ app/
148
+ controllers/
149
+ products_controller.rb ← completed version
150
+ ```
151
+
152
+ ### When to Use
153
+
154
+ - **Code-editing lessons** where the user modifies files — provide the finished version
155
+ - **Not needed** for terminal-only lessons (e.g., running `rails new`, using the console)
156
+
157
+ ### Tips
158
+
159
+ - `_solution/` can include files not present in `_files/` (e.g., a new file the user was asked to create)
160
+ - Only include files that differ from the template + `_files/` — unchanged files don't need to appear
161
+
162
+ ## Do Not Override These Files
163
+
164
+ The following files are **runtime infrastructure** provided by the `default` template. Lessons should never include these in `_files/` or `_solution/`:
165
+
166
+ | Path | Purpose |
167
+ |------|---------|
168
+ | `package.json` | npm dependencies (WASM runtime, Express, PGLite) |
169
+ | `bin/rails`, `bin/ruby`, `bin/console`, `bin/rackup` | Node.js CLI wrappers |
170
+ | `lib/rails.js`, `lib/server.js`, `lib/database.js` | WASM runtime, HTTP bridge, DB |
171
+ | `lib/boot-progress.js`, `lib/commands.js`, `lib/irb.js` | Boot progress, command dispatch, IRB |
172
+ | `lib/server/frame_location_middleware.js` | Express middleware for frame location |
173
+ | `lib/patches/authentication.rb` | Auto-login patch |
174
+ | `lib/patches/app_generator.rb` | Rails generator WASM compat patch |
175
+ | `scripts/` | Build and automation scripts |
176
+ | `pgdata/` | PGLite database storage |
177
+
178
+ ## Managing Gems
179
+
180
+ ### Adding Gems
181
+
182
+ Gems are compiled into the WASM binary. To add a gem:
183
+
184
+ 1. Edit `ruby-wasm/Gemfile` in the project root
185
+ 2. Run `bin/build-wasm` to rebuild the WASM binary
186
+
187
+ ```ruby
188
+ # ruby-wasm/Gemfile
189
+ source "https://rubygems.org"
190
+
191
+ gem "wasmify-rails", "~> 0.4.0"
192
+ gem "rails", "~> 8.0.0"
193
+
194
+ # Add your gems here
195
+ gem "devise" # Authentication
196
+ gem "friendly_id" # Slugs
197
+ gem "pagy" # Pagination
198
+ ```
199
+
200
+ ### Gem Constraints
201
+
202
+ - **Pure Ruby gems** work without issues
203
+ - **Gems with native C extensions** need WASM-compatible builds — many common ones are already shimmed (see `rails-wasm-author-constraints` skill)
204
+ - Rebuilding the WASM binary takes several minutes
205
+ - The resulting binary is ~80MB and includes all gems
206
+
207
+ ### Gemfile in Templates
208
+
209
+ The `ruby-wasm/Gemfile` is the **single source of truth** for which gems are available. Individual lessons cannot add gems at runtime — the Gemfile is baked into the WASM binary at build time.
210
+
211
+ If your tutorial teaches the user to "add a gem to the Gemfile", be aware that this is a **conceptual exercise** — the gem must already be compiled into the WASM binary for it to actually work when they run `bundle install`.