create-disposabl-app 0.1.0 → 0.1.2

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 (390) hide show
  1. package/dist/index.js +21 -22
  2. package/dist/templates/default/apps/desktop/.oxfmtrc.json +5 -0
  3. package/dist/templates/default/apps/desktop/.oxlintrc.json +6 -0
  4. package/dist/templates/default/apps/desktop/README.md +58 -0
  5. package/dist/templates/default/apps/desktop/forge.config.mts +90 -0
  6. package/dist/templates/default/apps/desktop/index.html +12 -0
  7. package/dist/templates/default/apps/desktop/package.json +55 -1
  8. package/dist/templates/default/apps/desktop/src/declarations.d.ts +2 -0
  9. package/dist/templates/default/apps/desktop/src/index.css +8 -0
  10. package/dist/templates/default/apps/desktop/src/main.ts +58 -0
  11. package/dist/templates/default/apps/desktop/src/preload.ts +3 -0
  12. package/dist/templates/default/apps/desktop/src/renderer.ts +19 -0
  13. package/dist/templates/default/apps/desktop/tsconfig.json +14 -0
  14. package/dist/templates/default/apps/desktop/vite.main.config.mts +4 -0
  15. package/dist/templates/default/apps/desktop/vite.preload.config.mts +4 -0
  16. package/dist/templates/default/apps/desktop/vite.renderer.config.mts +4 -0
  17. package/dist/templates/default/apps/mobile/.agents/skills/agent-device/SKILL.md +22 -0
  18. package/dist/templates/default/apps/mobile/.agents/skills/android-emulator/SKILL.md +32 -0
  19. package/dist/templates/default/apps/mobile/.agents/skills/dogfood/SKILL.md +27 -0
  20. package/dist/templates/default/apps/mobile/.agents/skills/eas-app-stores/SKILL.md +176 -0
  21. package/dist/templates/default/apps/mobile/.agents/skills/eas-app-stores/agents/openai.yaml +4 -0
  22. package/dist/templates/default/apps/mobile/.agents/skills/eas-app-stores/references/app-store-metadata.md +497 -0
  23. package/dist/templates/default/apps/mobile/.agents/skills/eas-app-stores/references/ios-app-store.md +376 -0
  24. package/dist/templates/default/apps/mobile/.agents/skills/eas-app-stores/references/native-ios.md +167 -0
  25. package/dist/templates/default/apps/mobile/.agents/skills/eas-app-stores/references/play-store.md +244 -0
  26. package/dist/templates/default/apps/mobile/.agents/skills/eas-app-stores/references/testflight.md +62 -0
  27. package/dist/templates/default/apps/mobile/.agents/skills/eas-app-stores/references/workflows.md +120 -0
  28. package/dist/templates/default/apps/mobile/.agents/skills/eas-hosting/SKILL.md +431 -0
  29. package/dist/templates/default/apps/mobile/.agents/skills/eas-hosting/agents/openai.yaml +4 -0
  30. package/dist/templates/default/apps/mobile/.agents/skills/eas-observe/SKILL.md +46 -0
  31. package/dist/templates/default/apps/mobile/.agents/skills/eas-observe/agents/openai.yaml +4 -0
  32. package/dist/templates/default/apps/mobile/.agents/skills/eas-observe/references/metrics.md +98 -0
  33. package/dist/templates/default/apps/mobile/.agents/skills/eas-observe/references/queries.md +444 -0
  34. package/dist/templates/default/apps/mobile/.agents/skills/eas-observe/references/setup.md +529 -0
  35. package/dist/templates/default/apps/mobile/.agents/skills/eas-observe/references/third-party.md +136 -0
  36. package/dist/templates/default/apps/mobile/.agents/skills/eas-simulator/SKILL.md +237 -0
  37. package/dist/templates/default/apps/mobile/.agents/skills/eas-simulator/agents/openai.yaml +4 -0
  38. package/dist/templates/default/apps/mobile/.agents/skills/eas-simulator/references/controllers.md +139 -0
  39. package/dist/templates/default/apps/mobile/.agents/skills/eas-simulator/references/logs-and-crashes.md +101 -0
  40. package/dist/templates/default/apps/mobile/.agents/skills/eas-simulator/references/run-your-app.md +256 -0
  41. package/dist/templates/default/apps/mobile/.agents/skills/eas-simulator/references/troubleshooting.md +56 -0
  42. package/dist/templates/default/apps/mobile/.agents/skills/eas-update/SKILL.md +146 -0
  43. package/dist/templates/default/apps/mobile/.agents/skills/eas-update/agents/openai.yaml +4 -0
  44. package/dist/templates/default/apps/mobile/.agents/skills/eas-update-insights/SKILL.md +238 -0
  45. package/dist/templates/default/apps/mobile/.agents/skills/eas-update-insights/agents/openai.yaml +4 -0
  46. package/dist/templates/default/apps/mobile/.agents/skills/eas-update-insights/references/channel-insights-schema.md +47 -0
  47. package/dist/templates/default/apps/mobile/.agents/skills/eas-update-insights/references/update-insights-schema.md +69 -0
  48. package/dist/templates/default/apps/mobile/.agents/skills/eas-workflows/SKILL.md +99 -0
  49. package/dist/templates/default/apps/mobile/.agents/skills/eas-workflows/agents/openai.yaml +4 -0
  50. package/dist/templates/default/apps/mobile/.agents/skills/eas-workflows/scripts/fetch.js +109 -0
  51. package/dist/templates/default/apps/mobile/.agents/skills/eas-workflows/scripts/package.json +6 -0
  52. package/dist/templates/default/apps/mobile/.agents/skills/expo-animation/LICENSE +21 -0
  53. package/dist/templates/default/apps/mobile/.agents/skills/expo-animation/RECIPES.md +385 -0
  54. package/dist/templates/default/apps/mobile/.agents/skills/expo-animation/SKILL.md +274 -0
  55. package/dist/templates/default/apps/mobile/.agents/skills/expo-animation/agents/openai.yaml +4 -0
  56. package/dist/templates/default/apps/mobile/.agents/skills/expo-app-clip/SKILL.md +290 -0
  57. package/dist/templates/default/apps/mobile/.agents/skills/expo-app-clip/agents/openai.yaml +4 -0
  58. package/dist/templates/default/apps/mobile/.agents/skills/expo-app-clip/references/native-module.md +96 -0
  59. package/dist/templates/default/apps/mobile/.agents/skills/expo-brownfield/SKILL.md +69 -0
  60. package/dist/templates/default/apps/mobile/.agents/skills/expo-brownfield/agents/openai.yaml +4 -0
  61. package/dist/templates/default/apps/mobile/.agents/skills/expo-brownfield/references/brownfield-integrated.md +551 -0
  62. package/dist/templates/default/apps/mobile/.agents/skills/expo-brownfield/references/brownfield-isolated.md +449 -0
  63. package/dist/templates/default/apps/mobile/.agents/skills/expo-brownfield/references/comparison.md +63 -0
  64. package/dist/templates/default/apps/mobile/.agents/skills/expo-brownfield/references/feature-integration.md +163 -0
  65. package/dist/templates/default/apps/mobile/.agents/skills/expo-brownfield/references/troubleshooting.md +88 -0
  66. package/dist/templates/default/apps/mobile/.agents/skills/expo-brownfield/references/version-compatibility.md +40 -0
  67. package/dist/templates/default/apps/mobile/.agents/skills/expo-data-fetching/SKILL.md +478 -0
  68. package/dist/templates/default/apps/mobile/.agents/skills/expo-data-fetching/agents/openai.yaml +4 -0
  69. package/dist/templates/default/apps/mobile/.agents/skills/expo-data-fetching/references/expo-router-loaders.md +341 -0
  70. package/dist/templates/default/apps/mobile/.agents/skills/expo-data-fetching/references/offline-and-cancellation.md +68 -0
  71. package/dist/templates/default/apps/mobile/.agents/skills/expo-design-system/SKILL.md +376 -0
  72. package/dist/templates/default/apps/mobile/.agents/skills/expo-design-system/agents/openai.yaml +4 -0
  73. package/dist/templates/default/apps/mobile/.agents/skills/expo-design-system/references/audit.md +190 -0
  74. package/dist/templates/default/apps/mobile/.agents/skills/expo-design-system/references/native-slop.md +74 -0
  75. package/dist/templates/default/apps/mobile/.agents/skills/expo-dev-client/SKILL.md +184 -0
  76. package/dist/templates/default/apps/mobile/.agents/skills/expo-dev-client/agents/openai.yaml +4 -0
  77. package/dist/templates/default/apps/mobile/.agents/skills/expo-dom/SKILL.md +425 -0
  78. package/dist/templates/default/apps/mobile/.agents/skills/expo-dom/agents/openai.yaml +4 -0
  79. package/dist/templates/default/apps/mobile/.agents/skills/expo-examples/SKILL.md +106 -0
  80. package/dist/templates/default/apps/mobile/.agents/skills/expo-examples/agents/openai.yaml +4 -0
  81. package/dist/templates/default/apps/mobile/.agents/skills/expo-examples/references/catalog.md +105 -0
  82. package/dist/templates/default/apps/mobile/.agents/skills/expo-migrate-module/SKILL.md +124 -0
  83. package/dist/templates/default/apps/mobile/.agents/skills/expo-migrate-module/agents/openai.yaml +4 -0
  84. package/dist/templates/default/apps/mobile/.agents/skills/expo-migrate-module/references/compatibility.md +130 -0
  85. package/dist/templates/default/apps/mobile/.agents/skills/expo-migrate-module/references/example.md +212 -0
  86. package/dist/templates/default/apps/mobile/.agents/skills/expo-migrate-module/references/migration-map.md +457 -0
  87. package/dist/templates/default/apps/mobile/.agents/skills/expo-module/SKILL.md +151 -0
  88. package/dist/templates/default/apps/mobile/.agents/skills/expo-module/agents/openai.yaml +4 -0
  89. package/dist/templates/default/apps/mobile/.agents/skills/expo-module/references/config-plugin.md +90 -0
  90. package/dist/templates/default/apps/mobile/.agents/skills/expo-module/references/create-expo-module.md +206 -0
  91. package/dist/templates/default/apps/mobile/.agents/skills/expo-module/references/lifecycle.md +127 -0
  92. package/dist/templates/default/apps/mobile/.agents/skills/expo-module/references/module-config.md +48 -0
  93. package/dist/templates/default/apps/mobile/.agents/skills/expo-module/references/native-module.md +286 -0
  94. package/dist/templates/default/apps/mobile/.agents/skills/expo-module/references/native-view.md +171 -0
  95. package/dist/templates/default/apps/mobile/.agents/skills/expo-native-ui/SKILL.md +201 -0
  96. package/dist/templates/default/apps/mobile/.agents/skills/expo-native-ui/agents/openai.yaml +4 -0
  97. package/dist/templates/default/apps/mobile/.agents/skills/expo-native-ui/references/controls.md +231 -0
  98. package/dist/templates/default/apps/mobile/.agents/skills/expo-native-ui/references/gradients.md +106 -0
  99. package/dist/templates/default/apps/mobile/.agents/skills/expo-native-ui/references/icons.md +232 -0
  100. package/dist/templates/default/apps/mobile/.agents/skills/expo-native-ui/references/media.md +193 -0
  101. package/dist/templates/default/apps/mobile/.agents/skills/expo-native-ui/references/storage.md +121 -0
  102. package/dist/templates/default/apps/mobile/.agents/skills/expo-native-ui/references/visual-effects.md +198 -0
  103. package/dist/templates/default/apps/mobile/.agents/skills/expo-native-ui/references/webgpu-three.md +605 -0
  104. package/dist/templates/default/apps/mobile/.agents/skills/expo-overview/SKILL.md +120 -0
  105. package/dist/templates/default/apps/mobile/.agents/skills/expo-overview/agents/openai.yaml +4 -0
  106. package/dist/templates/default/apps/mobile/.agents/skills/expo-overview/references/launch-without-dev-menu.md +24 -0
  107. package/dist/templates/default/apps/mobile/.agents/skills/expo-project-structure/SKILL.md +114 -0
  108. package/dist/templates/default/apps/mobile/.agents/skills/expo-project-structure/agents/openai.yaml +4 -0
  109. package/dist/templates/default/apps/mobile/.agents/skills/expo-router/SKILL.md +240 -0
  110. package/dist/templates/default/apps/mobile/.agents/skills/expo-router/agents/openai.yaml +4 -0
  111. package/dist/templates/default/apps/mobile/.agents/skills/expo-router/references/form-sheet.md +253 -0
  112. package/dist/templates/default/apps/mobile/.agents/skills/expo-router/references/route-structure.md +229 -0
  113. package/dist/templates/default/apps/mobile/.agents/skills/expo-router/references/search.md +248 -0
  114. package/dist/templates/default/apps/mobile/.agents/skills/expo-router/references/tabs.md +433 -0
  115. package/dist/templates/default/apps/mobile/.agents/skills/expo-router/references/toolbar-and-headers.md +284 -0
  116. package/dist/templates/default/apps/mobile/.agents/skills/expo-router/references/zoom-transitions.md +158 -0
  117. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/SKILL.md +314 -0
  118. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/agents/visual-grader.md +52 -0
  119. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/references/design-rubric.md +45 -0
  120. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/references/runtime-matrix.md +34 -0
  121. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/scripts/check-static.sh +64 -0
  122. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/scripts/clean-fixture.sh +56 -0
  123. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/scripts/generate_viewer.py +450 -0
  124. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/scripts/latest-sdk.sh +46 -0
  125. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/scripts/make-fixture.sh +95 -0
  126. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/scripts/make-workspace.sh +31 -0
  127. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/scripts/snapshot-android.sh +284 -0
  128. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/scripts/snapshot-ios.sh +132 -0
  129. package/dist/templates/default/apps/mobile/.agents/skills/expo-skill-eval/scripts/snapshot-web.sh +61 -0
  130. package/dist/templates/default/apps/mobile/.agents/skills/expo-ui/SKILL.md +101 -0
  131. package/dist/templates/default/apps/mobile/.agents/skills/expo-ui/agents/openai.yaml +4 -0
  132. package/dist/templates/default/apps/mobile/.agents/skills/expo-ui/references/drop-in-replacements.md +27 -0
  133. package/dist/templates/default/apps/mobile/.agents/skills/expo-ui/references/jetpack-compose.md +73 -0
  134. package/dist/templates/default/apps/mobile/.agents/skills/expo-ui/references/swift-ui.md +73 -0
  135. package/dist/templates/default/apps/mobile/.agents/skills/expo-ui/references/universal.md +104 -0
  136. package/dist/templates/default/apps/mobile/.agents/skills/expo-ui/scripts/list-components.js +193 -0
  137. package/dist/templates/default/apps/mobile/.agents/skills/expo-upgrade/SKILL.md +150 -0
  138. package/dist/templates/default/apps/mobile/.agents/skills/expo-upgrade/agents/openai.yaml +4 -0
  139. package/dist/templates/default/apps/mobile/.agents/skills/expo-upgrade/references/expo-av-to-audio.md +132 -0
  140. package/dist/templates/default/apps/mobile/.agents/skills/expo-upgrade/references/expo-av-to-video.md +160 -0
  141. package/dist/templates/default/apps/mobile/.agents/skills/expo-upgrade/references/native-tabs.md +124 -0
  142. package/dist/templates/default/apps/mobile/.agents/skills/expo-upgrade/references/new-architecture.md +79 -0
  143. package/dist/templates/default/apps/mobile/.agents/skills/expo-upgrade/references/react-19.md +79 -0
  144. package/dist/templates/default/apps/mobile/.agents/skills/expo-upgrade/references/react-compiler.md +59 -0
  145. package/dist/templates/default/apps/mobile/.agents/skills/expo-upgrade/references/react-navigation-to-expo-router.md +61 -0
  146. package/dist/templates/default/apps/mobile/.agents/skills/expo-web-to-native/SKILL.md +91 -0
  147. package/dist/templates/default/apps/mobile/.agents/skills/expo-web-to-native/agents/openai.yaml +4 -0
  148. package/dist/templates/default/apps/mobile/.agents/skills/expo-web-to-native/references/false-friends.md +119 -0
  149. package/dist/templates/default/apps/mobile/.agents/skills/expo-web-to-native/references/native-patterns.md +39 -0
  150. package/dist/templates/default/apps/mobile/.agents/skills/expo-web-to-native/references/run-as-goal.md +48 -0
  151. package/dist/templates/default/apps/mobile/.agents/skills/expo-web-to-native/references/verify-on-device.md +45 -0
  152. package/dist/templates/default/apps/mobile/.agents/skills/ios-simulator/SKILL.md +32 -0
  153. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/POWER.md +172 -0
  154. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/SKILL.md +238 -0
  155. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/agents/openai.yaml +4 -0
  156. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/bundle-analyze-app.md +208 -0
  157. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/bundle-analyze-js.md +264 -0
  158. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/bundle-barrel-exports.md +250 -0
  159. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/bundle-code-splitting.md +255 -0
  160. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/bundle-hermes-mmap.md +167 -0
  161. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/bundle-library-size.md +164 -0
  162. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/bundle-native-assets.md +208 -0
  163. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/bundle-r8-android.md +231 -0
  164. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/bundle-tree-shaking.md +215 -0
  165. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/bundle-treemap-source-map-explorer.png +0 -0
  166. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/controlled-textinput-pingpong.png +0 -0
  167. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/devtools-flamegraph.png +0 -0
  168. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/emerge-xray-ios.png +0 -0
  169. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/expo-atlas-treemap.png +0 -0
  170. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/flashlight-flatlist-vs-flashlist.png +0 -0
  171. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/fps-drop-graph.png +0 -0
  172. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/memory-heap-snapshot.png +0 -0
  173. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/tti-warm-start-diagram.png +0 -0
  174. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/view-hierarchy-flattening.png +0 -0
  175. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/xcode-instruments-templates.png +0 -0
  176. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/images/xcode-thread-view.png +0 -0
  177. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/js-animations-reanimated.md +273 -0
  178. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/js-atomic-state.md +190 -0
  179. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/js-bottomsheet.md +339 -0
  180. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/js-concurrent-react.md +238 -0
  181. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/js-lists-flatlist-flashlist.md +223 -0
  182. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/js-measure-fps.md +178 -0
  183. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/js-memory-leaks.md +168 -0
  184. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/js-profile-react.md +138 -0
  185. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/js-react-compiler.md +261 -0
  186. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/js-uncontrolled-components.md +219 -0
  187. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/native-android-16kb-alignment.md +114 -0
  188. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/native-measure-tti.md +221 -0
  189. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/native-memory-leaks.md +206 -0
  190. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/native-memory-patterns.md +240 -0
  191. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/native-platform-setup.md +112 -0
  192. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/native-profiling.md +140 -0
  193. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/native-sdks-over-polyfills.md +192 -0
  194. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/native-threading-model.md +230 -0
  195. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/native-turbo-modules.md +210 -0
  196. package/dist/templates/default/apps/mobile/.agents/skills/react-native-best-practices/references/native-view-flattening.md +180 -0
  197. package/dist/templates/default/apps/mobile/AGENTS.md +41 -0
  198. package/dist/templates/default/apps/mobile/app.json +38 -0
  199. package/dist/templates/default/apps/mobile/assets/expo.icon/Assets/expo-symbol 2.svg +3 -0
  200. package/dist/templates/default/apps/mobile/assets/expo.icon/Assets/grid.png +0 -0
  201. package/dist/templates/default/apps/mobile/assets/expo.icon/icon.json +40 -0
  202. package/dist/templates/default/apps/mobile/assets/images/android-icon-background.png +0 -0
  203. package/dist/templates/default/apps/mobile/assets/images/android-icon-foreground.png +0 -0
  204. package/dist/templates/default/apps/mobile/assets/images/android-icon-monochrome.png +0 -0
  205. package/dist/templates/default/apps/mobile/assets/images/icon.png +0 -0
  206. package/dist/templates/default/apps/mobile/assets/images/splash-icon.png +0 -0
  207. package/dist/templates/default/apps/mobile/eslint.config.js +10 -0
  208. package/dist/templates/default/apps/mobile/package.json +43 -1
  209. package/dist/templates/default/apps/mobile/skills-lock.json +185 -0
  210. package/dist/templates/default/apps/mobile/src/app/_layout.tsx +5 -0
  211. package/dist/templates/default/apps/mobile/src/app/index.tsx +17 -0
  212. package/dist/templates/default/apps/mobile/tsconfig.json +20 -0
  213. package/dist/templates/default/apps/web/.agents/skills/frontend-design/SKILL.md +71 -0
  214. package/dist/templates/default/apps/web/.agents/skills/web-design-guidelines/SKILL.md +39 -0
  215. package/dist/templates/default/apps/web/.env.example +3 -0
  216. package/dist/templates/default/apps/web/components.json +23 -0
  217. package/dist/templates/default/apps/web/eslint.config.ts +21 -0
  218. package/dist/templates/default/apps/web/package.json +39 -1
  219. package/dist/templates/default/apps/web/public/disposabl-logo.svg +8 -0
  220. package/dist/templates/default/apps/web/public/favicon.ico +0 -0
  221. package/dist/templates/default/apps/web/public/manifest.json +25 -0
  222. package/dist/templates/default/apps/web/skills-lock.json +17 -0
  223. package/dist/templates/default/apps/web/src/index.css +92 -0
  224. package/dist/templates/default/apps/web/src/routeTree.gen.ts +282 -0
  225. package/dist/templates/default/apps/web/src/router/__root.tsx +63 -0
  226. package/dist/templates/default/apps/web/src/router/_authenticated/dashboard.tsx +9 -0
  227. package/dist/templates/default/apps/web/src/router/_authenticated/todos/$id.tsx +27 -0
  228. package/dist/templates/default/apps/web/src/router/_authenticated/todos/index.tsx +33 -0
  229. package/dist/templates/default/apps/web/src/router/index.tsx +15 -0
  230. package/dist/templates/default/apps/web/src/router.tsx +20 -0
  231. package/dist/templates/default/apps/web/tsconfig.json +33 -0
  232. package/dist/templates/default/apps/web/tsr.config.json +6 -0
  233. package/dist/templates/default/apps/web/vite.config.ts +65 -0
  234. package/dist/templates/default/bun.lock +3932 -0
  235. package/dist/templates/default/bunfig.toml +5 -0
  236. package/dist/templates/default/disposabl.json +9 -0
  237. package/dist/templates/default/gitignore +9 -1
  238. package/dist/templates/default/package.json +29 -4
  239. package/dist/templates/default/packages/backend/.agents/skills/convex/SKILL.md +69 -0
  240. package/dist/templates/default/packages/backend/.agents/skills/convex-add/SKILL.md +26 -0
  241. package/dist/templates/default/packages/backend/.agents/skills/convex-advisor/SKILL.md +32 -0
  242. package/dist/templates/default/packages/backend/.agents/skills/convex-agent/SKILL.md +25 -0
  243. package/dist/templates/default/packages/backend/.agents/skills/convex-auth/SKILL.md +29 -0
  244. package/dist/templates/default/packages/backend/.agents/skills/convex-authz/SKILL.md +36 -0
  245. package/dist/templates/default/packages/backend/.agents/skills/convex-backup/SKILL.md +33 -0
  246. package/dist/templates/default/packages/backend/.agents/skills/convex-billing/SKILL.md +69 -0
  247. package/dist/templates/default/packages/backend/.agents/skills/convex-cost/SKILL.md +29 -0
  248. package/dist/templates/default/packages/backend/.agents/skills/convex-create-component/SKILL.md +325 -0
  249. package/dist/templates/default/packages/backend/.agents/skills/convex-create-component/agents/openai.yaml +14 -0
  250. package/dist/templates/default/packages/backend/.agents/skills/convex-create-component/assets/icon.svg +3 -0
  251. package/dist/templates/default/packages/backend/.agents/skills/convex-create-component/references/advanced-patterns.md +146 -0
  252. package/dist/templates/default/packages/backend/.agents/skills/convex-create-component/references/hybrid-components.md +38 -0
  253. package/dist/templates/default/packages/backend/.agents/skills/convex-create-component/references/local-components.md +39 -0
  254. package/dist/templates/default/packages/backend/.agents/skills/convex-create-component/references/packaged-components.md +54 -0
  255. package/dist/templates/default/packages/backend/.agents/skills/convex-crons/SKILL.md +23 -0
  256. package/dist/templates/default/packages/backend/.agents/skills/convex-deploy-guard/SKILL.md +29 -0
  257. package/dist/templates/default/packages/backend/.agents/skills/convex-design/SKILL.md +30 -0
  258. package/dist/templates/default/packages/backend/.agents/skills/convex-docs/SKILL.md +31 -0
  259. package/dist/templates/default/packages/backend/.agents/skills/convex-domains/SKILL.md +26 -0
  260. package/dist/templates/default/packages/backend/.agents/skills/convex-env/SKILL.md +24 -0
  261. package/dist/templates/default/packages/backend/.agents/skills/convex-expert/SKILL.md +38 -0
  262. package/dist/templates/default/packages/backend/.agents/skills/convex-explain-app/SKILL.md +29 -0
  263. package/dist/templates/default/packages/backend/.agents/skills/convex-improve-convex-plugin/SKILL.md +24 -0
  264. package/dist/templates/default/packages/backend/.agents/skills/convex-insights/SKILL.md +32 -0
  265. package/dist/templates/default/packages/backend/.agents/skills/convex-launch-readiness/SKILL.md +35 -0
  266. package/dist/templates/default/packages/backend/.agents/skills/convex-migrate/SKILL.md +23 -0
  267. package/dist/templates/default/packages/backend/.agents/skills/convex-migrate-rehearse/SKILL.md +31 -0
  268. package/dist/templates/default/packages/backend/.agents/skills/convex-monitor/SKILL.md +22 -0
  269. package/dist/templates/default/packages/backend/.agents/skills/convex-optimize/SKILL.md +26 -0
  270. package/dist/templates/default/packages/backend/.agents/skills/convex-quickstart/SKILL.md +29 -0
  271. package/dist/templates/default/packages/backend/.agents/skills/convex-quickstart/agents/openai.yaml +14 -0
  272. package/dist/templates/default/packages/backend/.agents/skills/convex-quickstart/assets/icon.svg +4 -0
  273. package/dist/templates/default/packages/backend/.agents/skills/convex-reviewer/SKILL.md +26 -0
  274. package/dist/templates/default/packages/backend/.agents/skills/convex-seed/SKILL.md +23 -0
  275. package/dist/templates/default/packages/backend/.agents/skills/convex-self-heal/SKILL.md +38 -0
  276. package/dist/templates/default/packages/backend/.agents/skills/convex-sentinel/SKILL.md +25 -0
  277. package/dist/templates/default/packages/backend/.agents/skills/convex-suggest/SKILL.md +27 -0
  278. package/dist/templates/default/packages/backend/.agents/skills/convex-test/SKILL.md +23 -0
  279. package/dist/templates/default/packages/backend/.agents/skills/convex-verify/SKILL.md +34 -0
  280. package/dist/templates/default/packages/backend/.prettierignore +4 -0
  281. package/dist/templates/default/packages/backend/AGENTS.md +18 -0
  282. package/dist/templates/default/packages/backend/CLAUDE.md +13 -0
  283. package/dist/templates/default/packages/backend/README.md +33 -0
  284. package/dist/templates/default/packages/backend/convex/_generated/ai/ai-files.state.json +6 -0
  285. package/dist/templates/default/packages/backend/convex/_generated/ai/guidelines.md +286 -0
  286. package/dist/templates/default/packages/backend/convex/_generated/api.d.ts +66 -0
  287. package/dist/templates/default/packages/backend/convex/_generated/api.js +23 -0
  288. package/dist/templates/default/packages/backend/convex/_generated/dataModel.d.ts +60 -0
  289. package/dist/templates/default/packages/backend/convex/_generated/server.d.ts +164 -0
  290. package/dist/templates/default/packages/backend/convex/_generated/server.js +101 -0
  291. package/dist/templates/default/packages/backend/convex/auth.config.ts +14 -0
  292. package/dist/templates/default/packages/backend/convex/auth.ts +15 -0
  293. package/dist/templates/default/packages/backend/convex/convex.config.ts +30 -0
  294. package/dist/templates/default/packages/backend/convex/http.ts +5 -0
  295. package/dist/templates/default/packages/backend/convex/mcp.ts +8 -0
  296. package/dist/templates/default/packages/backend/convex/schema.ts +10 -0
  297. package/dist/templates/default/packages/backend/convex/todos/index.ts +26 -0
  298. package/dist/templates/default/packages/backend/convex/todos/internal.ts +43 -0
  299. package/dist/templates/default/packages/backend/convex/tsconfig.json +27 -0
  300. package/dist/templates/default/packages/backend/convex/users.ts +20 -0
  301. package/dist/templates/default/packages/backend/eslint.config.mjs +62 -0
  302. package/dist/templates/default/packages/backend/package.json +36 -0
  303. package/dist/templates/default/packages/backend/skills-lock.json +203 -0
  304. package/dist/templates/default/packages/disposabl-kit/client/components/ui/accordion.tsx +77 -0
  305. package/dist/templates/default/packages/disposabl-kit/client/components/ui/alert-dialog.tsx +185 -0
  306. package/dist/templates/default/packages/disposabl-kit/client/components/ui/alert.tsx +75 -0
  307. package/dist/templates/default/packages/disposabl-kit/client/components/ui/aspect-ratio.tsx +22 -0
  308. package/dist/templates/default/packages/disposabl-kit/client/components/ui/attachment.tsx +207 -0
  309. package/dist/templates/default/packages/disposabl-kit/client/components/ui/avatar.tsx +106 -0
  310. package/dist/templates/default/packages/disposabl-kit/client/components/ui/badge.tsx +51 -0
  311. package/dist/templates/default/packages/disposabl-kit/client/components/ui/breadcrumb.tsx +121 -0
  312. package/dist/templates/default/packages/disposabl-kit/client/components/ui/bubble.tsx +127 -0
  313. package/dist/templates/default/packages/disposabl-kit/client/components/ui/button-group.tsx +87 -0
  314. package/dist/templates/default/packages/disposabl-kit/client/components/ui/button.tsx +57 -0
  315. package/dist/templates/default/packages/disposabl-kit/client/components/ui/calendar.tsx +241 -0
  316. package/dist/templates/default/packages/disposabl-kit/client/components/ui/card.tsx +102 -0
  317. package/dist/templates/default/packages/disposabl-kit/client/components/ui/carousel.tsx +239 -0
  318. package/dist/templates/default/packages/disposabl-kit/client/components/ui/chart.tsx +368 -0
  319. package/dist/templates/default/packages/disposabl-kit/client/components/ui/checkbox.tsx +27 -0
  320. package/dist/templates/default/packages/disposabl-kit/client/components/ui/code-block.tsx +243 -0
  321. package/dist/templates/default/packages/disposabl-kit/client/components/ui/collapsible.tsx +19 -0
  322. package/dist/templates/default/packages/disposabl-kit/client/components/ui/combobox.tsx +300 -0
  323. package/dist/templates/default/packages/disposabl-kit/client/components/ui/command.tsx +194 -0
  324. package/dist/templates/default/packages/disposabl-kit/client/components/ui/context-menu.tsx +271 -0
  325. package/dist/templates/default/packages/disposabl-kit/client/components/ui/dialog.tsx +156 -0
  326. package/dist/templates/default/packages/disposabl-kit/client/components/ui/direction.tsx +4 -0
  327. package/dist/templates/default/packages/disposabl-kit/client/components/ui/drawer.tsx +224 -0
  328. package/dist/templates/default/packages/disposabl-kit/client/components/ui/dropdown-menu.tsx +269 -0
  329. package/dist/templates/default/packages/disposabl-kit/client/components/ui/empty.tsx +103 -0
  330. package/dist/templates/default/packages/disposabl-kit/client/components/ui/field.tsx +236 -0
  331. package/dist/templates/default/packages/disposabl-kit/client/components/ui/hover-card.tsx +50 -0
  332. package/dist/templates/default/packages/disposabl-kit/client/components/ui/input-group.tsx +158 -0
  333. package/dist/templates/default/packages/disposabl-kit/client/components/ui/input-otp.tsx +83 -0
  334. package/dist/templates/default/packages/disposabl-kit/client/components/ui/input.tsx +19 -0
  335. package/dist/templates/default/packages/disposabl-kit/client/components/ui/item.tsx +201 -0
  336. package/dist/templates/default/packages/disposabl-kit/client/components/ui/kbd.tsx +26 -0
  337. package/dist/templates/default/packages/disposabl-kit/client/components/ui/label.tsx +19 -0
  338. package/dist/templates/default/packages/disposabl-kit/client/components/ui/marker.tsx +70 -0
  339. package/dist/templates/default/packages/disposabl-kit/client/components/ui/menubar.tsx +284 -0
  340. package/dist/templates/default/packages/disposabl-kit/client/components/ui/message-scroller.tsx +128 -0
  341. package/dist/templates/default/packages/disposabl-kit/client/components/ui/message.tsx +91 -0
  342. package/dist/templates/default/packages/disposabl-kit/client/components/ui/native-select.tsx +64 -0
  343. package/dist/templates/default/packages/disposabl-kit/client/components/ui/navigation-menu.tsx +170 -0
  344. package/dist/templates/default/packages/disposabl-kit/client/components/ui/pagination.tsx +133 -0
  345. package/dist/templates/default/packages/disposabl-kit/client/components/ui/popover.tsx +87 -0
  346. package/dist/templates/default/packages/disposabl-kit/client/components/ui/progress.tsx +82 -0
  347. package/dist/templates/default/packages/disposabl-kit/client/components/ui/questionnaire.tsx +330 -0
  348. package/dist/templates/default/packages/disposabl-kit/client/components/ui/radio-group.tsx +35 -0
  349. package/dist/templates/default/packages/disposabl-kit/client/components/ui/resizable.tsx +49 -0
  350. package/dist/templates/default/packages/disposabl-kit/client/components/ui/scroll-area.tsx +51 -0
  351. package/dist/templates/default/packages/disposabl-kit/client/components/ui/select.tsx +201 -0
  352. package/dist/templates/default/packages/disposabl-kit/client/components/ui/separator.tsx +22 -0
  353. package/dist/templates/default/packages/disposabl-kit/client/components/ui/sheet.tsx +132 -0
  354. package/dist/templates/default/packages/disposabl-kit/client/components/ui/sidebar.tsx +723 -0
  355. package/dist/templates/default/packages/disposabl-kit/client/components/ui/skeleton.tsx +13 -0
  356. package/dist/templates/default/packages/disposabl-kit/client/components/ui/slider.tsx +51 -0
  357. package/dist/templates/default/packages/disposabl-kit/client/components/ui/spinner.tsx +16 -0
  358. package/dist/templates/default/packages/disposabl-kit/client/components/ui/switch.tsx +31 -0
  359. package/dist/templates/default/packages/disposabl-kit/client/components/ui/table.tsx +113 -0
  360. package/dist/templates/default/packages/disposabl-kit/client/components/ui/tabs.tsx +81 -0
  361. package/dist/templates/default/packages/disposabl-kit/client/components/ui/textarea.tsx +17 -0
  362. package/dist/templates/default/packages/disposabl-kit/client/components/ui/toast.tsx +229 -0
  363. package/dist/templates/default/packages/disposabl-kit/client/components/ui/toggle-group.tsx +87 -0
  364. package/dist/templates/default/packages/disposabl-kit/client/components/ui/toggle.tsx +42 -0
  365. package/dist/templates/default/packages/disposabl-kit/client/components/ui/tooltip.tsx +65 -0
  366. package/dist/templates/default/packages/disposabl-kit/client/components/ui/trace.tsx +161 -0
  367. package/dist/templates/default/packages/disposabl-kit/client/hooks/use-mobile.ts +21 -0
  368. package/dist/templates/default/packages/disposabl-kit/client/lib/prerender.ts +89 -0
  369. package/dist/templates/default/packages/disposabl-kit/client/lib/return-to.ts +12 -0
  370. package/dist/templates/default/packages/disposabl-kit/client/lib/utils.ts +1 -0
  371. package/dist/templates/default/packages/disposabl-kit/client/routes/_auth-pages/login.tsx +114 -0
  372. package/dist/templates/default/packages/disposabl-kit/client/routes/_auth-pages/signUp.tsx +120 -0
  373. package/dist/templates/default/packages/disposabl-kit/client/routes/_authenticated/route.tsx +38 -0
  374. package/dist/templates/default/packages/disposabl-kit/client/routes/consent.tsx +137 -0
  375. package/dist/templates/default/packages/disposabl-kit/client/routes/route.tsx +30 -0
  376. package/dist/templates/default/packages/disposabl-kit/client/styles/globals.css +60 -0
  377. package/dist/templates/default/packages/disposabl-kit/client/vite.config.ts +32 -0
  378. package/dist/templates/default/packages/disposabl-kit/components.json +23 -0
  379. package/dist/templates/default/packages/disposabl-kit/eslint.config.js +21 -0
  380. package/dist/templates/default/packages/disposabl-kit/package.json +59 -0
  381. package/dist/templates/default/packages/disposabl-kit/server/http.ts +42 -0
  382. package/dist/templates/default/packages/disposabl-kit/server/lib/functions.ts +46 -0
  383. package/dist/templates/default/packages/disposabl-kit/server/lib/mcp-tools.ts +47 -0
  384. package/dist/templates/default/packages/disposabl-kit/server/lib/platforms.ts +20 -0
  385. package/dist/templates/default/packages/disposabl-kit/tsconfig.json +17 -0
  386. package/dist/templates/default/patches/@convex-dev%2Fauth@2.0.0-beta.0.patch +310 -0
  387. package/dist/templates/default/patches/@convex-dev%2Fstatic-hosting@0.2.1.patch +187 -0
  388. package/dist/templates/default/tsconfig.json +9 -0
  389. package/dist/templates/default/turbo.json +51 -0
  390. package/package.json +1 -2
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env bash
2
+ # Reclaim disk after a fixture's screenshots are captured. Essential for
3
+ # dev-build runs, where each `expo run:<platform>` leaves multi-GB native build
4
+ # output (iOS Pods + DerivedData, Android Gradle build). Removes the heavy,
5
+ # regenerable artifacts and keeps the app source + git history, so the grader's
6
+ # `git diff` still works.
7
+ #
8
+ # Usage: clean-fixture.sh <project-path>
9
+ #
10
+ # Env: EXPO_SKILL_EVAL_KEEP_DERIVEDDATA=1 skips the iOS DerivedData sweep (set it
11
+ # only if you keep a real Xcode project literally named "fixture").
12
+ set -uo pipefail
13
+
14
+ APP="${1:?usage: clean-fixture.sh <project-path>}"
15
+
16
+ # Safety guard: only ever clean inside an expo-skill-eval workspace.
17
+ case "$APP" in
18
+ *expo-skill-eval-*) : ;;
19
+ *) echo "clean-fixture: refusing to clean '$APP' (not an expo-skill-eval fixture)" >&2; exit 1 ;;
20
+ esac
21
+ [[ -d "$APP" ]] || { echo "clean-fixture: $APP not found, skipping"; exit 0; }
22
+
23
+ # Stop any Metro / Expo dev server that may still be running for this fixture.
24
+ # The snapshot scripts' EXIT trap normally handles this, but cleaning up here
25
+ # catches leftover processes from crashes or interrupted runs.
26
+ # Ports 8081 (iOS) and 8082 (Android) are the two ports the eval harness
27
+ # reserves — freeing them here is safe because clean-fixture.sh only runs after
28
+ # all screenshots for this fixture have been captured.
29
+ #
30
+ # `-sTCP:LISTEN` is REQUIRED, not a refinement: without it, `lsof -ti tcp:8082`
31
+ # also matches the *established* connection the adb daemon holds from the
32
+ # `adb reverse tcp:8082` tunnel, and SIGKILLing the adb daemon crashes the
33
+ # emulator with a gfxstream gRPC SIGABRT (std::bad_function_call). Keep the flag
34
+ # so only the Metro listener is killed.
35
+ lsof -ti tcp:8081 -sTCP:LISTEN 2>/dev/null | xargs kill -9 2>/dev/null || true
36
+ lsof -ti tcp:8082 -sTCP:LISTEN 2>/dev/null | xargs kill -9 2>/dev/null || true
37
+
38
+ # Per-fixture heavy dirs — all gitignored / regenerable (node_modules, the
39
+ # prebuilt native projects incl. iOS Pods and Android Gradle output, caches).
40
+ rm -rf \
41
+ "$APP/node_modules" \
42
+ "$APP/ios" \
43
+ "$APP/android" \
44
+ "$APP/.expo" \
45
+ "$APP/dist" \
46
+ "$APP/web-build" 2>/dev/null || true
47
+
48
+ # iOS DerivedData for fixture builds. create-expo-app names the project
49
+ # "fixture", so its build output lives under DerivedData/fixture-<hash>. This is
50
+ # a cache (worst case a rebuild), so it's safe to drop between fixtures.
51
+ if [[ "${EXPO_SKILL_EVAL_KEEP_DERIVEDDATA:-0}" != "1" ]]; then
52
+ DD="$HOME/Library/Developer/Xcode/DerivedData"
53
+ [[ -d "$DD" ]] && rm -rf "$DD"/fixture-* 2>/dev/null || true
54
+ fi
55
+
56
+ echo "cleaned fixture: $APP"
@@ -0,0 +1,450 @@
1
+ #!/usr/bin/env python3
2
+ """Generate the self-contained HTML viewer for an expo-skill-eval run.
3
+
4
+ Usage: generate_viewer.py <workspace-root> [--artifact]
5
+ <workspace-root> Run root, e.g. /private/tmp/expo-skill-eval-expo-ui.
6
+ Iteration dirs (iteration-*) and trigger-evals/ live under it;
7
+ viewer.html is written there and opened in the browser.
8
+ --artifact Emit viewer_artifact.html (page-content only, no
9
+ <!DOCTYPE>/<html>/<head>/<body>) for the Artifact tool, and
10
+ do NOT open a browser. The standalone viewer.html is still
11
+ written on a normal (non-artifact) run.
12
+
13
+ Reads, per iteration, <iter>/evals.json plus each eval's
14
+ eval-<id>/<config>/{grading.json,static.json,outputs/<platform>.png}. Screenshots
15
+ are embedded as base64 data: URIs so the file is fully self-contained (and
16
+ satisfies the Artifact CSP). Self-paced by argv so it can be a checked-in script
17
+ rather than regenerated each run.
18
+ """
19
+
20
+ import base64
21
+ import json
22
+ import sys
23
+ import webbrowser
24
+ from pathlib import Path
25
+
26
+
27
+ def b64_img(path):
28
+ if not path:
29
+ return None
30
+ p = Path(path)
31
+ if not p.exists() or not p.is_file():
32
+ return None
33
+ with open(p, "rb") as f:
34
+ data = base64.b64encode(f.read()).decode()
35
+ ext = p.suffix.lower().lstrip(".")
36
+ mime = {"png": "image/png", "jpg": "image/jpeg", "jpeg": "image/jpeg"}.get(ext, "image/png")
37
+ return f"data:{mime};base64,{data}"
38
+
39
+
40
+ def score_color(score, max_score):
41
+ if not max_score:
42
+ return "#888"
43
+ pct = score / max_score
44
+ if pct >= 0.85:
45
+ return "#4ade80"
46
+ elif pct >= 0.65:
47
+ return "#fbbf24"
48
+ else:
49
+ return "#f87171"
50
+
51
+
52
+ def load_json(path, default=None):
53
+ p = Path(path)
54
+ if not p.exists():
55
+ return default
56
+ try:
57
+ with open(p) as f:
58
+ return json.load(f)
59
+ except Exception:
60
+ return default
61
+
62
+
63
+ def render_expectations(expectations):
64
+ if not expectations:
65
+ return ""
66
+ html = '<ul class="exp-list">'
67
+ for exp in expectations:
68
+ if isinstance(exp, dict):
69
+ passed = exp.get("passed", None)
70
+ text = exp.get("text", str(exp))
71
+ evidence = exp.get("evidence", "")
72
+ if passed is True:
73
+ badge = '<span class="badge pass">PASS</span>'
74
+ elif passed is False:
75
+ badge = '<span class="badge fail">FAIL</span>'
76
+ else:
77
+ badge = '<span class="badge unknown">?</span>'
78
+ ev_html = f'<div class="evidence">{evidence}</div>' if evidence else ""
79
+ html += f'<li>{badge} {text}{ev_html}</li>'
80
+ else:
81
+ html += f'<li><span class="badge unknown">?</span> {exp}</li>'
82
+ html += "</ul>"
83
+ return html
84
+
85
+
86
+ def render_quality(quality):
87
+ if not quality:
88
+ return ""
89
+ dims = quality.get("dimensions", [])
90
+ subtotal = quality.get("subtotal", 0)
91
+ max_total = quality.get("max", 0)
92
+ summary = quality.get("summary", "")
93
+ html = '<div class="quality-block">'
94
+ html += f'<div class="quality-header">Design Quality: <b>{subtotal}/{max_total}</b></div>'
95
+ for d in dims:
96
+ name = d.get("name", "")
97
+ score = d.get("score", 0)
98
+ mx = d.get("max", 3)
99
+ evidence = d.get("evidence", "")
100
+ pct = (score / mx * 100) if mx else 0
101
+ color = score_color(score, mx)
102
+ html += f'''<div class="quality-dim">
103
+ <div class="dim-label">{name} <span style="color:{color}">{score}/{mx}</span></div>
104
+ <div class="dim-bar-wrap"><div class="dim-bar" style="width:{pct:.0f}%;background:{color}"></div></div>
105
+ {f'<div class="dim-evidence">{evidence}</div>' if evidence else ""}
106
+ </div>'''
107
+ if summary:
108
+ html += f'<div class="quality-summary">{summary}</div>'
109
+ html += "</div>"
110
+ return html
111
+
112
+
113
+ def render_config_card(iter_dir, eval_id, config, eval_case, grading, static_result):
114
+ screenshots = []
115
+ for plat in eval_case.get("runtime", {}).get("platforms", ["ios"]):
116
+ img_path = iter_dir / f"eval-{eval_id}" / config / "outputs" / f"{plat}.png"
117
+ screenshots.append((plat, b64_img(img_path)))
118
+
119
+ static_pass = static_result and static_result.get("exit_code") == 0
120
+ static_badge = '<span class="badge pass">BUILD OK</span>' if static_pass else '<span class="badge fail">BUILD FAIL</span>'
121
+
122
+ score = grading.get("score", 0) if grading else None
123
+ max_score = grading.get("max_score", 1) if grading else 1
124
+ score_html = ""
125
+ if score is not None:
126
+ color = score_color(score, max_score)
127
+ score_html = f'<div class="score" style="color:{color}">{score}/{max_score}</div>'
128
+
129
+ label = "With Skill" if config == "with_skill" else "Without Skill"
130
+ html = '<div class="config-card">'
131
+ html += f'<div class="config-header"><span class="config-label {"with" if config == "with_skill" else "without"}">{label}</span>{static_badge}{score_html}</div>'
132
+
133
+ for plat, img_b64 in screenshots:
134
+ if img_b64:
135
+ html += f'<div class="screenshot-wrap"><div class="plat-label">{plat}</div>'
136
+ html += f'<img src="{img_b64}" class="screenshot" onclick="this.classList.toggle(\'zoomed\')" />'
137
+ html += '</div>'
138
+ else:
139
+ html += f'<div class="screenshot-wrap"><div class="plat-label">{plat}</div><div class="no-screenshot">No screenshot</div></div>'
140
+
141
+ if grading:
142
+ html += render_expectations(grading.get("expectations", []))
143
+
144
+ ref_match = grading.get("reference_match")
145
+ if ref_match:
146
+ rm_score = ref_match.get("score", 0)
147
+ rm_max = ref_match.get("max", 10)
148
+ rm_color = score_color(rm_score, rm_max)
149
+ html += f'<div class="ref-match"><b>Reference Match:</b> <span style="color:{rm_color}">{rm_score}/{rm_max}</span>'
150
+ if ref_match.get("evidence"):
151
+ html += f'<div class="evidence">{ref_match["evidence"]}</div>'
152
+ html += '</div>'
153
+
154
+ html += render_quality(grading.get("quality"))
155
+
156
+ notes = grading.get("user_notes_summary", {})
157
+ if notes and notes.get("notes"):
158
+ html += f'<div class="reviewer-notes"><b>Notes:</b> {notes["notes"]}</div>'
159
+
160
+ html += '</div>'
161
+ return html
162
+
163
+
164
+ def render_iteration(iter_dir):
165
+ evals = load_json(iter_dir / "evals.json")
166
+ if not evals:
167
+ return f"<p>No evals.json found in {iter_dir}</p>"
168
+
169
+ html = ""
170
+ total_with = total_without = 0
171
+ scored_with = scored_without = 0
172
+ qual_with = qual_without = 0
173
+ total_evals = 0
174
+
175
+ for ev in evals:
176
+ eid = ev["id"]
177
+ ref_img_b64 = b64_img(ev.get("reference_image", ""))
178
+
179
+ with_grading = load_json(iter_dir / f"eval-{eid}" / "with_skill" / "grading.json")
180
+ without_grading = load_json(iter_dir / f"eval-{eid}" / "without_skill" / "grading.json")
181
+ with_static = load_json(iter_dir / f"eval-{eid}" / "with_skill" / "static.json")
182
+ without_static = load_json(iter_dir / f"eval-{eid}" / "without_skill" / "static.json")
183
+
184
+ total_evals += 1
185
+
186
+ if with_grading and with_grading.get("max_score"):
187
+ scored_with += with_grading.get("score", 0)
188
+ total_with += with_grading.get("max_score", 0)
189
+ if without_grading and without_grading.get("max_score"):
190
+ scored_without += without_grading.get("score", 0)
191
+ total_without += without_grading.get("max_score", 0)
192
+ if with_grading and with_grading.get("quality"):
193
+ qual_with += with_grading["quality"].get("subtotal", 0)
194
+ if without_grading and without_grading.get("quality"):
195
+ qual_without += without_grading["quality"].get("subtotal", 0)
196
+
197
+ html += '<div class="eval-case">'
198
+ html += f'<div class="eval-header">Eval #{eid}</div>'
199
+ html += f'<div class="eval-prompt">{ev.get("prompt", "")[:200]}</div>'
200
+
201
+ if ref_img_b64:
202
+ html += '<div class="ref-image-wrap"><div class="ref-label">Target Reference</div>'
203
+ html += f'<img src="{ref_img_b64}" class="ref-image" onclick="this.classList.toggle(\'zoomed\')" />'
204
+ html += '</div>'
205
+
206
+ html += '<div class="configs-row">'
207
+ html += render_config_card(iter_dir, eid, "with_skill", ev, with_grading, with_static)
208
+ html += render_config_card(iter_dir, eid, "without_skill", ev, without_grading, without_static)
209
+ html += '</div>'
210
+ html += '</div>'
211
+
212
+ with_pct = (scored_with / total_with * 100) if total_with else 0
213
+ without_pct = (scored_without / total_without * 100) if total_without else 0
214
+ delta = with_pct - without_pct
215
+ delta_color = "#4ade80" if delta > 0 else "#f87171" if delta < 0 else "#888"
216
+
217
+ qual_html = ""
218
+ if qual_with or qual_without:
219
+ qdelta = qual_with - qual_without
220
+ qcolor = "#4ade80" if qdelta > 0 else "#f87171" if qdelta < 0 else "#888"
221
+ qual_html = f' <div class="summary-item">Quality Δ: <span style="color:{qcolor}">{qdelta:+d}</span></div>\n'
222
+
223
+ summary = f'''<div class="summary-bar">
224
+ <div class="summary-item">With Skill: <span style="color:{score_color(scored_with, total_with) if total_with else "#888"}">{with_pct:.0f}%</span></div>
225
+ <div class="summary-item">Without Skill: <span style="color:{score_color(scored_without, total_without) if total_without else "#888"}">{without_pct:.0f}%</span></div>
226
+ <div class="summary-item">Delta: <span style="color:{delta_color}">{delta:+.0f}pp</span></div>
227
+ {qual_html} <div class="summary-item">Evals: {total_evals}</div>
228
+ </div>'''
229
+
230
+ return summary + html
231
+
232
+
233
+ def render_trigger_table(results_path):
234
+ data = load_json(results_path)
235
+ if not data:
236
+ return ""
237
+
238
+ html = '<div class="trigger-section"><h2>Trigger Accuracy</h2>'
239
+ recall = data.get("recall", 0)
240
+ color = score_color(recall, 1)
241
+ passed_count = data.get("passed", data.get("triggered", 0))
242
+ html += f'<div class="trigger-summary">Recall: <span style="color:{color}">{100*recall:.0f}%</span> ({passed_count}/{data.get("total")} passed)</div>'
243
+ html += '<table class="trigger-table"><tr><th>#</th><th>Prompt</th><th>Should Trigger</th><th>Triggered</th><th>Result</th><th>Time</th></tr>'
244
+ for i, r in enumerate(data.get("results", []), 1):
245
+ # Support both schema variants: {id, prompt, should_trigger, pass} and {query, triggered, duration}
246
+ rid = r.get("id", i)
247
+ prompt = r.get("prompt", r.get("query", ""))
248
+ should_trigger = r.get("should_trigger", True) # default True for recall-only runs
249
+ triggered = r.get("triggered", False)
250
+ passed = r.get("pass", triggered if should_trigger else not triggered)
251
+ result_badge = '<span class="badge pass">PASS</span>' if passed else '<span class="badge fail">FAIL</span>'
252
+ should = "Yes" if should_trigger else "No"
253
+ triggered_str = "Yes" if triggered else "No"
254
+ elapsed = r.get("elapsed_s", r.get("duration", "?"))
255
+ html += f'<tr><td>{rid}</td><td class="prompt-cell">{prompt[:80]}</td><td>{should}</td><td>{triggered_str}</td><td>{result_badge}</td><td>{elapsed}s</td></tr>'
256
+ html += '</table></div>'
257
+ return html
258
+
259
+
260
+ CSS = """\
261
+ :root {
262
+ --ground: #090C14;
263
+ --surface: #0E1320;
264
+ --surface-hi: #131929;
265
+ --border: #1A2238;
266
+ --text: #D9E2F5;
267
+ --text-muted: #6B7A9E;
268
+ --text-dim: #3A4560;
269
+ --accent: #7B6FD3;
270
+ --accent-glow: rgba(123,111,211,0.12);
271
+ --pass: #4DB87C;
272
+ --pass-bg: rgba(77,184,124,0.10);
273
+ --fail: #E05555;
274
+ --fail-bg: rgba(224,85,85,0.10);
275
+ --score-with: #9B8EE8;
276
+ --score-without: #5A6480;
277
+ }
278
+ * { box-sizing: border-box; margin: 0; padding: 0; }
279
+ body { background: var(--ground); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-size: 14px; line-height: 1.5; padding: 20px 24px; }
280
+ h1 { font-size: 15px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 20px; }
281
+ h2 { font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); margin: 24px 0 10px; }
282
+
283
+ /* Tabs */
284
+ .tabs { display: flex; gap: 6px; margin-bottom: 20px; }
285
+ .tab { background: var(--surface); border: 1px solid var(--border); color: var(--text-muted); padding: 5px 14px; border-radius: 5px; cursor: pointer; font-size: 12px; font-weight: 500; transition: color .15s, border-color .15s; }
286
+ .tab:hover { color: var(--text); border-color: #2A3558; }
287
+ .tab.active { background: var(--surface-hi); border-color: var(--accent); color: var(--text); }
288
+
289
+ /* Summary strip */
290
+ .summary-bar { display: flex; gap: 0; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; margin-bottom: 20px; }
291
+ .summary-item { flex: 1; padding: 12px 16px; border-right: 1px solid var(--border); font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); }
292
+ .summary-item:last-child { border-right: none; }
293
+ .summary-item span { display: block; font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; color: var(--text); margin-top: 2px; }
294
+
295
+ /* Eval card */
296
+ .eval-case { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 24px; overflow: hidden; }
297
+ .eval-header { padding: 10px 16px; font-size: 11px; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-muted); border-bottom: 1px solid var(--border); }
298
+ .eval-prompt { padding: 12px 16px; font-size: 13px; color: var(--text); border-bottom: 1px solid var(--border); line-height: 1.55; }
299
+
300
+ /* Reference image */
301
+ .ref-image-wrap { padding: 16px; background: #07090F; border-bottom: 1px solid var(--border); }
302
+ .ref-label { font-size: 10px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 8px; }
303
+ .ref-image { max-height: 260px; width: auto; border-radius: 10px; border: 1px solid #1E2840; cursor: zoom-in; display: block; }
304
+ .ref-image.zoomed { max-height: none; cursor: zoom-out; }
305
+
306
+ /* Side-by-side config columns */
307
+ .configs-row { display: grid; grid-template-columns: 1fr 1fr; }
308
+ .config-card { padding: 16px; border-right: 1px solid var(--border); position: relative; }
309
+ .config-card:last-child { border-right: none; }
310
+
311
+ /* The aesthetic risk: the with_skill card gets a faint violet radial glow from its header edge */
312
+ .config-card:first-child::before {
313
+ content: "";
314
+ position: absolute;
315
+ top: 0; left: 0; right: 0; height: 180px;
316
+ background: radial-gradient(ellipse 80% 120px at 50% 0, var(--accent-glow), transparent 70%);
317
+ pointer-events: none;
318
+ }
319
+
320
+ .config-header { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
321
+ .config-label { font-size: 10px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; padding: 3px 8px; border-radius: 4px; }
322
+ .config-label.with { background: rgba(123,111,211,0.15); color: var(--accent); border: 1px solid rgba(123,111,211,0.25); }
323
+ .config-label.without { background: var(--surface-hi); color: var(--text-muted); border: 1px solid var(--border); }
324
+ .score { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; margin-left: auto; }
325
+ .config-card:first-child .score { color: var(--score-with); }
326
+ .config-card:last-child .score { color: var(--score-without); }
327
+
328
+ /* Badges */
329
+ .badge { display: inline-flex; align-items: center; font-size: 9px; font-weight: 700; padding: 2px 7px; border-radius: 3px; letter-spacing: 0.05em; text-transform: uppercase; }
330
+ .badge.pass { background: var(--pass-bg); color: var(--pass); border: 1px solid rgba(77,184,124,0.2); }
331
+ .badge.fail { background: var(--fail-bg); color: var(--fail); border: 1px solid rgba(224,85,85,0.2); }
332
+ .badge.unknown { background: var(--surface-hi); color: var(--text-dim); border: 1px solid var(--border); }
333
+
334
+ /* Screenshots */
335
+ .screenshot-wrap { margin-bottom: 14px; }
336
+ .plat-label { font-size: 9px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 6px; }
337
+ .screenshot { max-width: 100%; max-height: 380px; width: auto; border-radius: 14px; border: 1px solid #1A2540; cursor: zoom-in; display: block; box-shadow: 0 4px 24px rgba(0,0,0,0.5); }
338
+ .screenshot.zoomed { max-height: none; cursor: zoom-out; }
339
+ .no-screenshot { color: var(--text-dim); font-size: 11px; padding: 16px; background: var(--surface-hi); border-radius: 6px; text-align: center; border: 1px dashed var(--border); }
340
+
341
+ /* Expectations */
342
+ .exp-list { list-style: none; margin-top: 12px; }
343
+ .exp-list li { padding: 6px 0; border-bottom: 1px solid var(--border); font-size: 12px; color: var(--text-muted); display: flex; flex-wrap: wrap; gap: 6px; align-items: flex-start; }
344
+ .exp-list li:last-child { border-bottom: none; }
345
+ .evidence { font-size: 10px; color: var(--text-dim); width: 100%; line-height: 1.5; margin-top: 2px; }
346
+
347
+ /* Reference match block */
348
+ .ref-match { margin-top: 12px; font-size: 12px; color: var(--text-muted); background: var(--surface-hi); padding: 10px 12px; border-radius: 6px; border-left: 2px solid var(--accent); }
349
+ .ref-match b { color: var(--text); }
350
+
351
+ /* Quality rubric */
352
+ .quality-block { margin-top: 12px; background: var(--surface-hi); border: 1px solid var(--border); border-radius: 6px; padding: 12px; }
353
+ .quality-header { font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 10px; }
354
+ .quality-dim { margin-bottom: 10px; }
355
+ .quality-dim:last-of-type { margin-bottom: 0; }
356
+ .dim-label { font-size: 10px; color: var(--text-muted); margin-bottom: 4px; display: flex; justify-content: space-between; }
357
+ .dim-label span { font-variant-numeric: tabular-nums; }
358
+ .dim-bar-wrap { height: 3px; background: var(--border); border-radius: 2px; overflow: hidden; }
359
+ .dim-bar { height: 100%; border-radius: 2px; }
360
+ .dim-evidence { font-size: 10px; color: var(--text-dim); margin-top: 3px; line-height: 1.45; }
361
+ .quality-summary { font-size: 11px; color: var(--text-dim); margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
362
+ .reviewer-notes { margin-top: 10px; font-size: 11px; color: var(--text-dim); background: var(--ground); padding: 8px 10px; border-radius: 4px; }
363
+
364
+ /* Trigger table */
365
+ .trigger-section { margin-top: 32px; }
366
+ .trigger-summary { font-size: 13px; margin-bottom: 12px; color: var(--text-muted); }
367
+ .trigger-table { width: 100%; border-collapse: collapse; font-size: 12px; }
368
+ .trigger-table th { background: var(--surface); padding: 9px 10px; text-align: left; font-size: 10px; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-muted); border-bottom: 1px solid var(--border); }
369
+ .trigger-table td { padding: 8px 10px; border-bottom: 1px solid var(--border); color: var(--text-muted); vertical-align: top; }
370
+ .trigger-table tr:last-child td { border-bottom: none; }
371
+ .trigger-table tr:hover td { background: var(--surface-hi); }
372
+ .prompt-cell { max-width: 320px; word-break: break-word; color: var(--text); }
373
+ @media (prefers-reduced-motion: reduce) { .dim-bar { transition: none; } }
374
+ """
375
+
376
+ JS = """\
377
+ function showTab(i) {
378
+ document.querySelectorAll('.panel').forEach((p, idx) => { p.style.display = idx === i ? 'block' : 'none'; });
379
+ document.querySelectorAll('.tab').forEach((t, idx) => { t.classList.toggle('active', idx === i); });
380
+ localStorage.setItem('expo-eval-tab', i);
381
+ }
382
+ (function() {
383
+ const saved = parseInt(localStorage.getItem('expo-eval-tab') || '0', 10);
384
+ if (saved > 0) showTab(saved);
385
+ })();
386
+ """
387
+
388
+
389
+ def build_page(ws, title, artifact=False):
390
+ iterations = sorted(p for p in ws.glob("iteration-*") if p.is_dir())
391
+ if not iterations:
392
+ return f"<p>No iteration directories found under {ws}.</p>"
393
+
394
+ tabs_html = ""
395
+ panels_html = ""
396
+ for i, it in enumerate(iterations):
397
+ active = "active" if i == 0 else ""
398
+ tabs_html += f'<button class="tab {active}" onclick="showTab({i})" id="tab-{i}">{it.name}</button>'
399
+ content = render_iteration(it)
400
+ panels_html += f'<div class="panel" id="panel-{i}" style="display:{"block" if i == 0 else "none"}">{content}</div>'
401
+
402
+ trigger_html = render_trigger_table(ws / "trigger-evals" / "trigger_results.json")
403
+ body_content = f'<h1>{title}</h1>\n<div class="tabs">{tabs_html}</div>\n{panels_html}\n{trigger_html}'
404
+
405
+ if artifact:
406
+ return f'<title>{title}</title>\n<style>\n{CSS}</style>\n{body_content}\n<script>\n{JS}</script>'
407
+ return f'''<!DOCTYPE html>
408
+ <html lang="en">
409
+ <head>
410
+ <meta charset="UTF-8" />
411
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
412
+ <title>{title}</title>
413
+ <style>
414
+ {CSS}
415
+ </style>
416
+ </head>
417
+ <body>
418
+ {body_content}
419
+ <script>
420
+ {JS}
421
+ </script>
422
+ </body>
423
+ </html>'''
424
+
425
+
426
+ def main():
427
+ args = [a for a in sys.argv[1:] if a != "--artifact"]
428
+ artifact = "--artifact" in sys.argv
429
+ if not args:
430
+ print("usage: generate_viewer.py <workspace-root> [--artifact]", file=sys.stderr)
431
+ sys.exit(1)
432
+
433
+ ws = Path(args[0]).resolve()
434
+ skill = ws.name.replace("expo-skill-eval-", "")
435
+ title = f"{skill} Skill Eval"
436
+ html = build_page(ws, title, artifact=artifact)
437
+
438
+ if artifact:
439
+ out = ws / "viewer_artifact.html"
440
+ out.write_text(html)
441
+ print(f"Artifact viewer written: {out}")
442
+ else:
443
+ out = ws / "viewer.html"
444
+ out.write_text(html)
445
+ print(f"Viewer written: {out}")
446
+ webbrowser.open("file://" + str(out))
447
+
448
+
449
+ if __name__ == "__main__":
450
+ main()
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+ # Print the latest Expo SDK major version.
3
+ #
4
+ # Usage: latest-sdk.sh
5
+ #
6
+ # The `expo` package's major version IS the SDK version (expo@56.x -> SDK 56).
7
+ # Uses bun to run `npm view expo dist-tags --json` (so it honors the local npm
8
+ # registry / proxy / auth config rather than hardcoding registry.npmjs.org),
9
+ # JSON.parse the result, and take the major via the `semver` package (falling
10
+ # back to a numeric split if semver can't be resolved). Run via the skill's
11
+ # `bash *expo-skill-eval/scripts/*` rule so SDK detection stays prompt-free —
12
+ # the npm child runs under bun, not as its own Bash tool call.
13
+ set -euo pipefail
14
+
15
+ exec bun -e '
16
+ let out;
17
+ try {
18
+ out = await Bun.$`npm view expo dist-tags --json`.text();
19
+ } catch (e) {
20
+ console.error("error: `npm view expo` failed: " + (e?.message ?? e));
21
+ process.exit(1);
22
+ }
23
+ let latest;
24
+ try {
25
+ latest = JSON.parse(out)?.latest;
26
+ } catch {
27
+ console.error("error: could not JSON.parse `npm view` output");
28
+ process.exit(1);
29
+ }
30
+ if (!latest) {
31
+ console.error("error: no `latest` dist-tag for expo");
32
+ process.exit(1);
33
+ }
34
+ let major;
35
+ try {
36
+ const semver = await import("semver");
37
+ major = (semver.default ?? semver).major(latest);
38
+ } catch {
39
+ major = Number.parseInt(String(latest).split(".")[0], 10);
40
+ }
41
+ if (!Number.isInteger(major)) {
42
+ console.error(`error: could not parse SDK major from "${latest}"`);
43
+ process.exit(1);
44
+ }
45
+ console.log(major);
46
+ '
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env bash
2
+ # Create an Expo app fixture for an eval run.
3
+ #
4
+ # Usage: make-fixture.sh <project-path> [sdk-version] [variant]
5
+ # project-path Where the app should end up (must not exist yet)
6
+ # sdk-version Expo SDK major version, e.g. "56". Omit for the latest template.
7
+ # variant "clean" (default) runs the template's reset-project script so
8
+ # executors start from a blank app instead of the example
9
+ # screens. "full" keeps the example tabs (Home/Explore) - use
10
+ # it for evals whose prompt assumes an existing app.
11
+ #
12
+ # One pristine app is created per SDK version + variant with
13
+ # `bunx create-expo-app -t default@sdk-<version>` and cached under
14
+ # $EXPO_SKILL_EVAL_CACHE (default ~/.cache/expo-skill-eval/fixtures).
15
+ # Each call clones the cache with APFS copy-on-write, so only the first
16
+ # call per SDK version pays the install cost.
17
+ set -euo pipefail
18
+
19
+ PROJECT_PATH="${1:?usage: make-fixture.sh <project-path> [sdk-version] [variant]}"
20
+ SDK_VERSION="${2:-}"
21
+ VARIANT="${3:-clean}"
22
+ if [[ "$VARIANT" != "clean" && "$VARIANT" != "full" ]]; then
23
+ echo "error: variant must be 'clean' or 'full', got '$VARIANT'" >&2
24
+ exit 1
25
+ fi
26
+ CACHE_ROOT="${EXPO_SKILL_EVAL_CACHE:-$HOME/.cache/expo-skill-eval/fixtures}"
27
+
28
+ if [[ -e "$PROJECT_PATH" ]]; then
29
+ echo "error: $PROJECT_PATH already exists" >&2
30
+ exit 1
31
+ fi
32
+
33
+ TEMPLATE="default"
34
+ CACHE_KEY="latest"
35
+ if [[ -n "$SDK_VERSION" ]]; then
36
+ TEMPLATE="default@sdk-${SDK_VERSION}"
37
+ CACHE_KEY="sdk-${SDK_VERSION}"
38
+ fi
39
+ if [[ "$VARIANT" == "clean" ]]; then
40
+ CACHE_KEY="${CACHE_KEY}-clean"
41
+ fi
42
+ CACHE_DIR="$CACHE_ROOT/$CACHE_KEY"
43
+
44
+ if [[ ! -d "$CACHE_DIR" ]]; then
45
+ echo "building fixture cache for template '$TEMPLATE'..." >&2
46
+ mkdir -p "$CACHE_ROOT"
47
+ TMP_DIR="$(mktemp -d)"
48
+ trap 'rm -rf "$TMP_DIR"' EXIT
49
+ (cd "$TMP_DIR" && bunx create-expo-app --yes --template "$TEMPLATE" fixture) >&2
50
+ if [[ "$VARIANT" == "clean" ]]; then
51
+ # Strip the example screens so executors start from a blank app.
52
+ # Answer "n" to the "move to /example?" prompt - delete them instead.
53
+ (cd "$TMP_DIR/fixture" && echo n | bun run reset-project) >&2
54
+ fi
55
+ # Pin a stable iOS bundle identifier and Android package. The default template
56
+ # leaves both unset, so a dev-build run (`expo run:ios`/`expo run:android`)
57
+ # would prompt for them - which fails in the snapshot scripts' non-interactive
58
+ # mode - and the snapshot scripts need a known id to relaunch the app by.
59
+ (cd "$TMP_DIR/fixture" && bun -e '
60
+ const f = "app.json";
61
+ const j = await Bun.file(f).json();
62
+ j.expo ??= {};
63
+ j.expo.ios ??= {};
64
+ j.expo.android ??= {};
65
+ j.expo.ios.bundleIdentifier ??= "com.exposkilleval.fixture";
66
+ j.expo.android.package ??= "com.exposkilleval.fixture";
67
+ await Bun.write(f, JSON.stringify(j, null, 2) + "\n");
68
+ ') >&2
69
+ # The CLI normally generates expo-env.d.ts on first `expo start`; the static
70
+ # gate runs tsc before any start, so create it up front (it provides the
71
+ # CSS-module and Metro type declarations from expo/types).
72
+ printf '/// <reference types="expo/types" />\n' >"$TMP_DIR/fixture/expo-env.d.ts"
73
+ # expo-dev-client is required for dev-build snapshots: it registers the
74
+ # expo-development-client URL scheme and enables custom Metro port connections
75
+ # (expo run:ios/android --port N opens the app via a deep link that the dev
76
+ # client handles). Without it, a custom port deep link may not open the app.
77
+ (cd "$TMP_DIR/fixture" && bunx expo install expo-dev-client) >&2
78
+ # First `expo lint` self-installs eslint + eslint-config-expo and writes
79
+ # eslint.config.js if missing; it may exit non-zero on that bootstrap run.
80
+ (cd "$TMP_DIR/fixture" && CI=1 bunx expo lint) >&2 || true
81
+ printf '.eval-static/\n' >>"$TMP_DIR/fixture/.gitignore"
82
+ mv "$TMP_DIR/fixture" "$CACHE_DIR"
83
+ fi
84
+
85
+ mkdir -p "$(dirname "$PROJECT_PATH")"
86
+ # -c uses APFS clonefile (instant, no extra disk); fall back to a plain copy.
87
+ cp -Rc "$CACHE_DIR" "$PROJECT_PATH" 2>/dev/null || cp -R "$CACHE_DIR" "$PROJECT_PATH"
88
+
89
+ # Fresh git history so `git diff` in the app shows exactly what the executor changed.
90
+ rm -rf "$PROJECT_PATH/.git"
91
+ git -C "$PROJECT_PATH" init -q
92
+ git -C "$PROJECT_PATH" add -A
93
+ git -C "$PROJECT_PATH" commit -qm "fixture: pristine $TEMPLATE template" --no-verify
94
+
95
+ echo "$PROJECT_PATH"