multiplat-factory 0.1.0 → 0.1.1

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 (410) hide show
  1. package/README.md +63 -274
  2. package/dist/chunk-A5ZTNRTN.js +65 -0
  3. package/dist/chunk-A5ZTNRTN.js.map +1 -0
  4. package/dist/dashboard/200.html +1 -0
  5. package/dist/dashboard/404.html +1 -0
  6. package/dist/dashboard/_nuxt/2mTACkJa.js +3 -0
  7. package/dist/dashboard/_nuxt/B1MQUUmA.js +1 -0
  8. package/dist/dashboard/_nuxt/BD1XFGRw.js +1 -0
  9. package/dist/dashboard/_nuxt/BPFatz28.js +1 -0
  10. package/dist/dashboard/_nuxt/BSEomhaZ.js +3 -0
  11. package/dist/dashboard/_nuxt/BVtebpBS.js +1 -0
  12. package/dist/dashboard/_nuxt/BeCRcAqm.js +1 -0
  13. package/dist/dashboard/_nuxt/Bk10jxDt.js +1 -0
  14. package/dist/dashboard/_nuxt/Bn8sQoBR.js +1 -0
  15. package/dist/dashboard/_nuxt/C0GreNxE.js +1 -0
  16. package/dist/dashboard/_nuxt/C6vzBuMd.js +1 -0
  17. package/dist/dashboard/_nuxt/CCMCJdY2.js +1 -0
  18. package/dist/dashboard/_nuxt/CFi3g9tC.js +1 -0
  19. package/dist/dashboard/_nuxt/CNarvKcN.js +2 -0
  20. package/dist/dashboard/_nuxt/CYHUjCqZ.js +3 -0
  21. package/dist/dashboard/_nuxt/CccylW6m.js +1 -0
  22. package/dist/dashboard/_nuxt/ClOgtTzr.js +1 -0
  23. package/dist/dashboard/_nuxt/Cp6G1JDJ.js +1 -0
  24. package/dist/dashboard/_nuxt/CqvIBbxm.js +1 -0
  25. package/dist/dashboard/_nuxt/CxmPv0Xg.js +1 -0
  26. package/dist/dashboard/_nuxt/D2giJ0zb.js +1 -0
  27. package/dist/dashboard/_nuxt/DD-d0mIO.js +1 -0
  28. package/dist/dashboard/_nuxt/DSm1cKIU.js +1 -0
  29. package/dist/dashboard/_nuxt/DYvEcPgU.js +1 -0
  30. package/dist/dashboard/_nuxt/DkcIzcLy.js +57 -0
  31. package/dist/dashboard/_nuxt/DlF4qIFG.js +1 -0
  32. package/dist/dashboard/_nuxt/DoctorView.BWn3KKZd.css +1 -0
  33. package/dist/dashboard/_nuxt/DxWip8Ok.js +1 -0
  34. package/dist/dashboard/_nuxt/G-IHUB_t.js +1 -0
  35. package/dist/dashboard/_nuxt/RgvLL8ZL.js +1 -0
  36. package/dist/dashboard/_nuxt/ZdpP1Ud-.js +1 -0
  37. package/dist/dashboard/_nuxt/_id_.BL_xkUo3.css +1 -0
  38. package/dist/dashboard/_nuxt/builds/latest.json +1 -0
  39. package/dist/dashboard/_nuxt/builds/meta/1d285342-32d1-4f36-aa29-aea4bf9510b7.json +1 -0
  40. package/dist/dashboard/_nuxt/default.BwIDAPER.css +1 -0
  41. package/dist/dashboard/_nuxt/dnzJofwx.js +1 -0
  42. package/dist/dashboard/_nuxt/entry.DRmJCmhL.css +1 -0
  43. package/dist/dashboard/_nuxt/error-404.Bq-TOqNj.css +1 -0
  44. package/dist/dashboard/_nuxt/error-500.xaLU7Cq-.css +1 -0
  45. package/dist/dashboard/_nuxt/ex3ztmkP.js +1 -0
  46. package/dist/dashboard/_nuxt/new.BbydyAru.css +1 -0
  47. package/dist/dashboard/_nuxt/pages.DbXUtV-_.css +1 -0
  48. package/dist/dashboard/_nuxt/release.CN2gX7ob.css +1 -0
  49. package/dist/dashboard/_nuxt/tests.BKp4ZmZa.css +1 -0
  50. package/dist/dashboard/_nuxt/u4OLurSP.js +1 -0
  51. package/dist/dashboard/doctor/index.html +1 -0
  52. package/dist/dashboard/index.html +1 -0
  53. package/dist/dashboard/runs/new/index.html +1 -0
  54. package/dist/exec-S73NAGZS.js +11 -0
  55. package/dist/exec-S73NAGZS.js.map +1 -0
  56. package/dist/main.js +16779 -0
  57. package/dist/main.js.map +1 -0
  58. package/package.json +60 -33
  59. package/.editorconfig +0 -9
  60. package/.github/workflows/ci.yml +0 -124
  61. package/.nvmrc +0 -1
  62. package/.prettierrc +0 -6
  63. package/CLAUDE.md +0 -162
  64. package/PRODUCT.md +0 -63
  65. package/README.plain.md +0 -294
  66. package/apps/cli/README.md +0 -83
  67. package/apps/cli/package.json +0 -69
  68. package/apps/cli/scripts/bundle-dashboard.mjs +0 -18
  69. package/apps/cli/src/commands/control.ts +0 -115
  70. package/apps/cli/src/commands/doctor.ts +0 -37
  71. package/apps/cli/src/commands/port.ts +0 -168
  72. package/apps/cli/src/commands/runs.ts +0 -18
  73. package/apps/cli/src/commands/serve.ts +0 -33
  74. package/apps/cli/src/commands/setup.ts +0 -133
  75. package/apps/cli/src/commands/shared.ts +0 -35
  76. package/apps/cli/src/main.ts +0 -37
  77. package/apps/cli/src/version.ts +0 -16
  78. package/apps/cli/test/deps.test.ts +0 -39
  79. package/apps/cli/test/version.test.ts +0 -14
  80. package/apps/cli/tsconfig.json +0 -4
  81. package/apps/cli/tsup.config.ts +0 -21
  82. package/apps/dashboard/.impeccable/surfaces/apps-dashboard.md +0 -24
  83. package/apps/dashboard/app/app.vue +0 -5
  84. package/apps/dashboard/app/assets/board.css +0 -980
  85. package/apps/dashboard/app/assets/main.css +0 -1416
  86. package/apps/dashboard/app/assets/review.css +0 -379
  87. package/apps/dashboard/app/components/AppMark.vue +0 -15
  88. package/apps/dashboard/app/components/BoardHero.vue +0 -18
  89. package/apps/dashboard/app/components/BoardProjects.vue +0 -112
  90. package/apps/dashboard/app/components/BoardRunOverview.vue +0 -191
  91. package/apps/dashboard/app/components/BoardTestSummary.vue +0 -98
  92. package/apps/dashboard/app/components/BoardThemeToggle.vue +0 -26
  93. package/apps/dashboard/app/components/BudgetTape.vue +0 -43
  94. package/apps/dashboard/app/components/DashboardStyleSwitcher.vue +0 -35
  95. package/apps/dashboard/app/components/DesignMockup.vue +0 -329
  96. package/apps/dashboard/app/components/DesignReview.vue +0 -429
  97. package/apps/dashboard/app/components/DoctorView.vue +0 -237
  98. package/apps/dashboard/app/components/NetlifySetup.vue +0 -180
  99. package/apps/dashboard/app/components/PanelTitle.vue +0 -14
  100. package/apps/dashboard/app/components/PreviewAnnotator.vue +0 -273
  101. package/apps/dashboard/app/components/ProjectChat.vue +0 -392
  102. package/apps/dashboard/app/components/ReviewMarks.vue +0 -42
  103. package/apps/dashboard/app/components/RunTimeline.vue +0 -137
  104. package/apps/dashboard/app/components/StatusAnnunciator.vue +0 -14
  105. package/apps/dashboard/app/components/StatusLamp.vue +0 -12
  106. package/apps/dashboard/app/components/TranscriptList.vue +0 -69
  107. package/apps/dashboard/app/components/UiIcon.vue +0 -84
  108. package/apps/dashboard/app/composables/useApi.ts +0 -21
  109. package/apps/dashboard/app/composables/useBoardTheme.ts +0 -34
  110. package/apps/dashboard/app/composables/useClock.ts +0 -11
  111. package/apps/dashboard/app/composables/useCore.ts +0 -97
  112. package/apps/dashboard/app/composables/useDashboardStyle.ts +0 -21
  113. package/apps/dashboard/app/composables/useHealth.ts +0 -32
  114. package/apps/dashboard/app/composables/useProjectReview.ts +0 -140
  115. package/apps/dashboard/app/composables/useRunView.ts +0 -72
  116. package/apps/dashboard/app/layouts/default.vue +0 -185
  117. package/apps/dashboard/app/pages/doctor.vue +0 -3
  118. package/apps/dashboard/app/pages/index.vue +0 -245
  119. package/apps/dashboard/app/pages/runs/[id]/core.vue +0 -144
  120. package/apps/dashboard/app/pages/runs/[id]/critic.vue +0 -108
  121. package/apps/dashboard/app/pages/runs/[id]/doctor.vue +0 -17
  122. package/apps/dashboard/app/pages/runs/[id]/index.vue +0 -562
  123. package/apps/dashboard/app/pages/runs/[id]/plan.vue +0 -252
  124. package/apps/dashboard/app/pages/runs/[id]/release.vue +0 -488
  125. package/apps/dashboard/app/pages/runs/[id]/spec.vue +0 -309
  126. package/apps/dashboard/app/pages/runs/[id]/tests.vue +0 -323
  127. package/apps/dashboard/app/pages/runs/new.vue +0 -596
  128. package/apps/dashboard/app/plugins/webmcp.client.ts +0 -42
  129. package/apps/dashboard/app/utils/board.ts +0 -101
  130. package/apps/dashboard/app/utils/format.ts +0 -116
  131. package/apps/dashboard/app/utils/reviewTools.ts +0 -447
  132. package/apps/dashboard/app/utils/signals.ts +0 -34
  133. package/apps/dashboard/nuxt.config.ts +0 -35
  134. package/apps/dashboard/package.json +0 -25
  135. package/apps/dashboard/tsconfig.json +0 -3
  136. package/apps/server/package.json +0 -26
  137. package/apps/server/src/app.ts +0 -208
  138. package/apps/server/src/daemon.ts +0 -148
  139. package/apps/server/src/index.ts +0 -11
  140. package/apps/server/src/review.ts +0 -152
  141. package/apps/server/test/app.test.ts +0 -144
  142. package/apps/server/test/review.test.ts +0 -587
  143. package/apps/server/tsconfig.json +0 -4
  144. package/apps/server/tsup.config.ts +0 -12
  145. package/docs/ABI.md +0 -76
  146. package/docs/ADAPTERS.md +0 -74
  147. package/docs/ARCHITECTURE.md +0 -403
  148. package/docs/HOSTED-SEAM.md +0 -83
  149. package/docs/PROJECT-REVIEW.md +0 -56
  150. package/docs/evals/project-review.json +0 -171
  151. package/eslint.config.js +0 -55
  152. package/fixtures/inhouse/android-views-java-notes/README.md +0 -17
  153. package/fixtures/inhouse/android-views-java-notes/app/build.gradle +0 -31
  154. package/fixtures/inhouse/android-views-java-notes/app/src/main/AndroidManifest.xml +0 -21
  155. package/fixtures/inhouse/android-views-java-notes/app/src/main/java/dev/multiplat/fixtures/notes/MainActivity.java +0 -113
  156. package/fixtures/inhouse/android-views-java-notes/app/src/main/java/dev/multiplat/fixtures/notes/Note.java +0 -44
  157. package/fixtures/inhouse/android-views-java-notes/app/src/main/java/dev/multiplat/fixtures/notes/NoteEditorActivity.java +0 -52
  158. package/fixtures/inhouse/android-views-java-notes/app/src/main/java/dev/multiplat/fixtures/notes/NoteStore.java +0 -131
  159. package/fixtures/inhouse/android-views-java-notes/app/src/main/java/dev/multiplat/fixtures/notes/PrefsStorage.java +0 -23
  160. package/fixtures/inhouse/android-views-java-notes/app/src/main/res/layout/activity_main.xml +0 -40
  161. package/fixtures/inhouse/android-views-java-notes/app/src/main/res/layout/activity_note_editor.xml +0 -42
  162. package/fixtures/inhouse/android-views-java-notes/app/src/main/res/layout/item_note.xml +0 -20
  163. package/fixtures/inhouse/android-views-java-notes/app/src/main/res/values/strings.xml +0 -13
  164. package/fixtures/inhouse/android-views-java-notes/app/src/main/res/values/themes.xml +0 -4
  165. package/fixtures/inhouse/android-views-java-notes/app/src/test/java/dev/multiplat/fixtures/notes/NoteStoreTest.java +0 -76
  166. package/fixtures/inhouse/android-views-java-notes/build.gradle +0 -3
  167. package/fixtures/inhouse/android-views-java-notes/gradle.properties +0 -2
  168. package/fixtures/inhouse/android-views-java-notes/settings.gradle +0 -16
  169. package/fixtures/inhouse/ios-objc-notes/README.md +0 -16
  170. package/fixtures/inhouse/ios-objc-notes/Sources/AppDelegate.h +0 -4
  171. package/fixtures/inhouse/ios-objc-notes/Sources/AppDelegate.m +0 -18
  172. package/fixtures/inhouse/ios-objc-notes/Sources/MPFNote.h +0 -17
  173. package/fixtures/inhouse/ios-objc-notes/Sources/MPFNote.m +0 -54
  174. package/fixtures/inhouse/ios-objc-notes/Sources/MPFNoteEditorViewController.h +0 -8
  175. package/fixtures/inhouse/ios-objc-notes/Sources/MPFNoteEditorViewController.m +0 -91
  176. package/fixtures/inhouse/ios-objc-notes/Sources/MPFNoteStore.h +0 -27
  177. package/fixtures/inhouse/ios-objc-notes/Sources/MPFNoteStore.m +0 -131
  178. package/fixtures/inhouse/ios-objc-notes/Sources/MPFNotesListViewController.h +0 -8
  179. package/fixtures/inhouse/ios-objc-notes/Sources/MPFNotesListViewController.m +0 -98
  180. package/fixtures/inhouse/ios-objc-notes/Sources/SceneDelegate.h +0 -5
  181. package/fixtures/inhouse/ios-objc-notes/Sources/SceneDelegate.m +0 -17
  182. package/fixtures/inhouse/ios-objc-notes/Sources/main.m +0 -8
  183. package/fixtures/inhouse/ios-objc-notes/Tests/MPFNoteStoreTests.m +0 -54
  184. package/fixtures/inhouse/ios-objc-notes/project.yml +0 -48
  185. package/fixtures/inhouse/ios-uikit-notes/README.md +0 -19
  186. package/fixtures/inhouse/ios-uikit-notes/Sources/AppDelegate.swift +0 -21
  187. package/fixtures/inhouse/ios-uikit-notes/Sources/Note.swift +0 -8
  188. package/fixtures/inhouse/ios-uikit-notes/Sources/NoteEditorViewController.swift +0 -84
  189. package/fixtures/inhouse/ios-uikit-notes/Sources/NoteStore.swift +0 -95
  190. package/fixtures/inhouse/ios-uikit-notes/Sources/NotesListViewController.swift +0 -89
  191. package/fixtures/inhouse/ios-uikit-notes/Sources/SceneDelegate.swift +0 -14
  192. package/fixtures/inhouse/ios-uikit-notes/Tests/NoteStoreTests.swift +0 -49
  193. package/fixtures/inhouse/ios-uikit-notes/project.yml +0 -44
  194. package/fixtures/inhouse/web-vanilla-cart/README.md +0 -13
  195. package/fixtures/inhouse/web-vanilla-cart/index.html +0 -21
  196. package/fixtures/inhouse/web-vanilla-cart/main.js +0 -186
  197. package/fixtures/inhouse/web-vanilla-cart/pricing.js +0 -178
  198. package/fixtures/inhouse/web-vanilla-cart/styles.css +0 -60
  199. package/fixtures/inhouse/web-vanilla-counter/README.md +0 -12
  200. package/fixtures/inhouse/web-vanilla-counter/index.html +0 -20
  201. package/fixtures/inhouse/web-vanilla-counter/main.js +0 -106
  202. package/fixtures/inhouse/web-vanilla-counter/styles.css +0 -58
  203. package/fixtures/manifest.json +0 -175
  204. package/packages/adapters/package.json +0 -23
  205. package/packages/adapters/src/core/android.ts +0 -37
  206. package/packages/adapters/src/core/embed.ts +0 -96
  207. package/packages/adapters/src/core/ios.ts +0 -44
  208. package/packages/adapters/src/core/kotlin.ts +0 -208
  209. package/packages/adapters/src/core/swift.ts +0 -232
  210. package/packages/adapters/src/core/ts.ts +0 -217
  211. package/packages/adapters/src/core/web.ts +0 -24
  212. package/packages/adapters/src/index.ts +0 -118
  213. package/packages/adapters/src/registry.ts +0 -46
  214. package/packages/adapters/src/source/cross.ts +0 -68
  215. package/packages/adapters/src/source/mobile.ts +0 -618
  216. package/packages/adapters/src/source/pm.ts +0 -72
  217. package/packages/adapters/src/source/web.ts +0 -541
  218. package/packages/adapters/src/target/android/index.ts +0 -466
  219. package/packages/adapters/src/target/android/template.ts +0 -517
  220. package/packages/adapters/src/target/gradle.ts +0 -84
  221. package/packages/adapters/src/target/ios/index.ts +0 -491
  222. package/packages/adapters/src/target/ios/template.ts +0 -368
  223. package/packages/adapters/src/target/junit.ts +0 -93
  224. package/packages/adapters/src/target/maestro-evidence.ts +0 -283
  225. package/packages/adapters/src/target/maestro.ts +0 -925
  226. package/packages/adapters/src/target/registry.ts +0 -11
  227. package/packages/adapters/src/target/start-screen.ts +0 -25
  228. package/packages/adapters/src/target/web/executor.ts +0 -812
  229. package/packages/adapters/src/target/web/index.ts +0 -456
  230. package/packages/adapters/src/target/web/template.ts +0 -609
  231. package/packages/adapters/src/target/xcodebuild.ts +0 -90
  232. package/packages/adapters/src/types.ts +0 -310
  233. package/packages/adapters/test/core-embed.test.ts +0 -279
  234. package/packages/adapters/test/detect.test.ts +0 -144
  235. package/packages/adapters/test/executor.test.ts +0 -726
  236. package/packages/adapters/test/fixture-matrix.test.ts +0 -29
  237. package/packages/adapters/test/generic-observe.test.ts +0 -89
  238. package/packages/adapters/test/ios.test.ts +0 -93
  239. package/packages/adapters/test/maestro-evidence.test.ts +0 -460
  240. package/packages/adapters/test/source-tests.test.ts +0 -138
  241. package/packages/adapters/test/target.test.ts +0 -1531
  242. package/packages/adapters/test/web-flavors.test.ts +0 -100
  243. package/packages/adapters/test/web.test.ts +0 -74
  244. package/packages/adapters/tsconfig.json +0 -4
  245. package/packages/analyzer/package.json +0 -27
  246. package/packages/analyzer/src/candidacy.ts +0 -61
  247. package/packages/analyzer/src/index.ts +0 -39
  248. package/packages/analyzer/src/inventory.ts +0 -671
  249. package/packages/analyzer/src/observe/web.ts +0 -12
  250. package/packages/analyzer/src/prompts.ts +0 -240
  251. package/packages/analyzer/src/stages/behaviors.ts +0 -130
  252. package/packages/analyzer/src/stages/ingest.ts +0 -161
  253. package/packages/analyzer/src/stages/inventory.ts +0 -41
  254. package/packages/analyzer/src/stages/observe.ts +0 -439
  255. package/packages/analyzer/src/stages/plan.ts +0 -147
  256. package/packages/analyzer/src/stages/spec.ts +0 -221
  257. package/packages/analyzer/src/validate.ts +0 -117
  258. package/packages/analyzer/src/worker.ts +0 -190
  259. package/packages/analyzer/test/duplicates.test.ts +0 -98
  260. package/packages/analyzer/test/inventory.test.ts +0 -161
  261. package/packages/analyzer/test/observe.test.ts +0 -54
  262. package/packages/analyzer/test/prompts.test.ts +0 -129
  263. package/packages/analyzer/test/source-tests.test.ts +0 -117
  264. package/packages/analyzer/test/validate-routes.test.ts +0 -51
  265. package/packages/analyzer/test/validate.test.ts +0 -228
  266. package/packages/analyzer/test/worker.test.ts +0 -151
  267. package/packages/analyzer/tsconfig.json +0 -4
  268. package/packages/core/package.json +0 -32
  269. package/packages/core/src/config.ts +0 -68
  270. package/packages/core/src/cost/prices.json +0 -16
  271. package/packages/core/src/cost/prices.ts +0 -34
  272. package/packages/core/src/events/bus.ts +0 -49
  273. package/packages/core/src/ids.ts +0 -9
  274. package/packages/core/src/index.ts +0 -9
  275. package/packages/core/src/paths.ts +0 -35
  276. package/packages/core/src/reducer.ts +0 -149
  277. package/packages/core/src/schemas/appspec.ts +0 -159
  278. package/packages/core/src/schemas/artifact.ts +0 -42
  279. package/packages/core/src/schemas/behaviorspec.ts +0 -96
  280. package/packages/core/src/schemas/common.ts +0 -29
  281. package/packages/core/src/schemas/config.ts +0 -47
  282. package/packages/core/src/schemas/corevectors.ts +0 -71
  283. package/packages/core/src/schemas/design.ts +0 -40
  284. package/packages/core/src/schemas/event.ts +0 -99
  285. package/packages/core/src/schemas/index.ts +0 -34
  286. package/packages/core/src/schemas/inventory.ts +0 -76
  287. package/packages/core/src/schemas/portplan.ts +0 -67
  288. package/packages/core/src/schemas/review.ts +0 -135
  289. package/packages/core/src/schemas/run.ts +0 -90
  290. package/packages/core/src/schemas/stage.ts +0 -97
  291. package/packages/core/src/schemas/testresult.ts +0 -47
  292. package/packages/core/src/schemas/toolchain.ts +0 -32
  293. package/packages/core/src/schemas/verdict.ts +0 -43
  294. package/packages/core/src/stages/catalog.ts +0 -139
  295. package/packages/core/src/store/index.ts +0 -2
  296. package/packages/core/src/store/sqlite.ts +0 -185
  297. package/packages/core/src/store/types.ts +0 -27
  298. package/packages/core/test/reducer.test.ts +0 -101
  299. package/packages/core/test/routes.test.ts +0 -23
  300. package/packages/core/test/schemas.test.ts +0 -86
  301. package/packages/core/test/store-events.test.ts +0 -36
  302. package/packages/core/test/store-shared.test.ts +0 -17
  303. package/packages/core/test/store.test.ts +0 -105
  304. package/packages/core/tsconfig.json +0 -4
  305. package/packages/critic/package.json +0 -21
  306. package/packages/critic/src/decide.ts +0 -115
  307. package/packages/critic/src/index.ts +0 -12
  308. package/packages/critic/src/prompt.ts +0 -140
  309. package/packages/critic/src/rubrics.ts +0 -73
  310. package/packages/critic/test/decide.test.ts +0 -121
  311. package/packages/critic/tsconfig.json +0 -4
  312. package/packages/harness/package.json +0 -23
  313. package/packages/harness/src/devices/android.ts +0 -498
  314. package/packages/harness/src/devices/ios.ts +0 -356
  315. package/packages/harness/src/devices/maestro.ts +0 -32
  316. package/packages/harness/src/devices/remember.ts +0 -96
  317. package/packages/harness/src/devserver.ts +0 -222
  318. package/packages/harness/src/exec.ts +0 -97
  319. package/packages/harness/src/index.ts +0 -81
  320. package/packages/harness/src/lanelock.ts +0 -237
  321. package/packages/harness/src/lanes.ts +0 -117
  322. package/packages/harness/src/stage.ts +0 -70
  323. package/packages/harness/src/toolchain/checks/android.ts +0 -297
  324. package/packages/harness/src/toolchain/checks/basics.ts +0 -112
  325. package/packages/harness/src/toolchain/checks/llm.ts +0 -90
  326. package/packages/harness/src/toolchain/checks/playwright.ts +0 -154
  327. package/packages/harness/src/toolchain/checks/xcode.ts +0 -67
  328. package/packages/harness/src/toolchain/doctor.ts +0 -84
  329. package/packages/harness/src/toolchain/rust.ts +0 -47
  330. package/packages/harness/src/toolchain/types.ts +0 -36
  331. package/packages/harness/test/devices.test.ts +0 -281
  332. package/packages/harness/test/devserver.test.ts +0 -73
  333. package/packages/harness/test/doctor.test.ts +0 -82
  334. package/packages/harness/test/gradle-jdk.test.ts +0 -42
  335. package/packages/harness/test/ios-dismiss.test.ts +0 -31
  336. package/packages/harness/test/jdk-drop-in.test.ts +0 -40
  337. package/packages/harness/test/lanes.test.ts +0 -249
  338. package/packages/harness/test/playwright.test.ts +0 -183
  339. package/packages/harness/test/remember.test.ts +0 -55
  340. package/packages/harness/tsconfig.json +0 -4
  341. package/packages/llm/package.json +0 -23
  342. package/packages/llm/src/agent.ts +0 -219
  343. package/packages/llm/src/backend.ts +0 -20
  344. package/packages/llm/src/fake/recorder.ts +0 -20
  345. package/packages/llm/src/index.ts +0 -11
  346. package/packages/llm/src/messages.ts +0 -66
  347. package/packages/llm/src/models.ts +0 -15
  348. package/packages/llm/src/permissions.ts +0 -210
  349. package/packages/llm/test/permissions.test.ts +0 -191
  350. package/packages/llm/tsconfig.json +0 -4
  351. package/packages/orchestrator/package.json +0 -27
  352. package/packages/orchestrator/src/design-review.ts +0 -69
  353. package/packages/orchestrator/src/graph.ts +0 -69
  354. package/packages/orchestrator/src/impeccable.ts +0 -59
  355. package/packages/orchestrator/src/index.ts +0 -20
  356. package/packages/orchestrator/src/machine.ts +0 -159
  357. package/packages/orchestrator/src/review.ts +0 -670
  358. package/packages/orchestrator/src/runner.ts +0 -701
  359. package/packages/orchestrator/src/stages/critic.ts +0 -324
  360. package/packages/orchestrator/src/stages/fake.ts +0 -167
  361. package/packages/orchestrator/src/stages/placeholders.ts +0 -69
  362. package/packages/orchestrator/src/stages/prompts.ts +0 -107
  363. package/packages/orchestrator/src/stages/real.ts +0 -39
  364. package/packages/orchestrator/src/stages/report.ts +0 -758
  365. package/packages/orchestrator/src/stages/target.ts +0 -607
  366. package/packages/orchestrator/src/types.ts +0 -1
  367. package/packages/orchestrator/test/cost.test.ts +0 -117
  368. package/packages/orchestrator/test/critic.test.ts +0 -286
  369. package/packages/orchestrator/test/driver-lock.test.ts +0 -38
  370. package/packages/orchestrator/test/graph.test.ts +0 -57
  371. package/packages/orchestrator/test/lanes.test.ts +0 -65
  372. package/packages/orchestrator/test/machine.test.ts +0 -137
  373. package/packages/orchestrator/test/real.test.ts +0 -342
  374. package/packages/orchestrator/test/report.test.ts +0 -468
  375. package/packages/orchestrator/test/runner.test.ts +0 -165
  376. package/packages/orchestrator/test/target.test.ts +0 -558
  377. package/packages/orchestrator/tsconfig.json +0 -4
  378. package/packages/wasm-core/package.json +0 -26
  379. package/packages/wasm-core/src/cargo.ts +0 -134
  380. package/packages/wasm-core/src/host.ts +0 -105
  381. package/packages/wasm-core/src/index.ts +0 -38
  382. package/packages/wasm-core/src/prompts.ts +0 -145
  383. package/packages/wasm-core/src/source-tools.ts +0 -81
  384. package/packages/wasm-core/src/stages.ts +0 -661
  385. package/packages/wasm-core/src/template.ts +0 -327
  386. package/packages/wasm-core/src/vectors.ts +0 -90
  387. package/packages/wasm-core/test/host.test.ts +0 -108
  388. package/packages/wasm-core/test/prompts.test.ts +0 -136
  389. package/packages/wasm-core/test/source-tools.test.ts +0 -79
  390. package/packages/wasm-core/test/template.test.ts +0 -114
  391. package/packages/wasm-core/tsconfig.json +0 -7
  392. package/pnpm-workspace.yaml +0 -56
  393. package/scripts/compile-flows.ts +0 -65
  394. package/scripts/fixtures-sync.ts +0 -103
  395. package/scripts/release.ts +0 -55
  396. package/scripts/review-demo.ts +0 -81
  397. package/scripts/run-control.ts +0 -161
  398. package/scripts/spike-agent-sdk.ts +0 -37
  399. package/scripts/spike-target-scaffold.ts +0 -252
  400. package/tsconfig.base.json +0 -19
  401. package/turbo.json +0 -19
  402. package/vitest.config.ts +0 -8
  403. /package/{apps/dashboard/public → dist/dashboard}/board/plan.webp +0 -0
  404. /package/{apps/dashboard/public → dist/dashboard}/board/progress.webp +0 -0
  405. /package/{apps/dashboard/public → dist/dashboard}/board/projects.webp +0 -0
  406. /package/{apps/dashboard/public → dist/dashboard}/board/release.webp +0 -0
  407. /package/{apps/dashboard/public → dist/dashboard}/board/setup.webp +0 -0
  408. /package/{apps/dashboard/public → dist/dashboard}/board/tests.webp +0 -0
  409. /package/{apps/dashboard/public → dist/dashboard}/board/update.webp +0 -0
  410. /package/{apps/dashboard/public → dist/dashboard}/board/updatePlan.webp +0 -0
package/README.md CHANGED
@@ -1,294 +1,83 @@
1
- # ⚙️ Multiplat Factory
1
+ # multiplat-factory
2
2
 
3
- If you have an app that runs on one platform: the web, iOS, or Android, this Multiplat Factory will help you publish to the others. Point this tool at the app, name the platforms you want, and it builds working native apps for them, tests that they behave and look like the original, and writes up what it did. It even has design, annotation, and implementation, which can be useful for going from small screen to large.
3
+ Port an app from one platform (web, iOS, Android) to the others. Give the factory a source
4
+ project, pick the targets, and it analyzes the app, verifies its behaviors on the original (a dev
5
+ server plus Chromium for web, a simulator or emulator plus Maestro for mobile), plans the port,
6
+ generates native projects (SwiftUI, Jetpack Compose, or a web app in vanilla, React, Vue, or
7
+ Svelte), builds and tests them against the same behaviors, has an independent critic review the
8
+ result, and writes a `PORT.md` per target. Optionally it extracts the shared, non-UI logic into a
9
+ Rust core compiled to WebAssembly that runs unchanged on all three platforms with golden-vector
10
+ parity tests.
4
11
 
5
- It runs on your own machine, uses your own agent accounts, and you decide on the budget cap.
6
-
7
- https://github.com/user-attachments/assets/145f78c3-7b22-4a36-b986-779a44c52e98
8
-
9
- ## How it works
10
-
11
- The factory reads your app's code and works out what the app does: its screens, its controls, and the things a user can do with it (we call those **behaviors**). It then runs the original app and checks each behavior really works there, so it will infer what "correct" looks like. Next it plans the port, and Claude writes the code for each new platform. The factory builds that code, installs it on a simulator or emulator or in a browser, and runs the same behaviors against it. You can speak directly to the agent within the run as well.
12
-
13
- Failures go back to the agent to fix, a limited number of times. A second, independent agent session reviews the finished port and scores it. Finally a `PORT.md` file per platform records what shipped, what passed, what failed, and what you chose to accept.
14
-
15
- There are a couple of design principles:
16
-
17
- - **An agent never decides what happens next.** The order of steps, the retry limits, the budget, and the pass or fail decisions are fixed code. The agent only writes files.
18
- - **Everything is checked against the real app.** Behaviors are verified on the original before anything is written, and the ports are tested with real behaviors.
19
- - **Leverage WASM for Portability** When your app has a good amount of logic that is not user interface (pricing rules, validation, data handling), the factory can pull that logic out into one **shared core**: a small Rust program compiled to WebAssembly that runs unchanged on web, iOS, and Android. It records the original app's real outputs for many inputs and checks the core gives the same answers on every platform.
20
-
21
- A dashboard in your browser shows all of this live: where a run is, what it is spending, what is failing, and when it needs a decision from you.
22
-
23
- ## What it supports
24
-
25
- | Your app is on | It can read | It produces |
26
- | -------------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
27
- | Web | Vite, Next, Nuxt, SvelteKit, Angular, any npm project with build or dev scripts, or plain static HTML | A Vite app in vanilla JavaScript, React 19, Vue 3, or Svelte 5 (you choose, or the plan does) |
28
- | iOS | Xcode projects and workspaces, XcodeGen `project.yml` files, Swift packages. Swift or Objective-C. UIKit or SwiftUI. | A SwiftUI app, built and tested on the iPhone simulator |
29
- | Android | Gradle projects. Kotlin or Java. Views or Jetpack Compose. Multi-module and flavored builds are fine. | A Jetpack Compose app, built and tested on a headless emulator |
30
-
31
- _Apps already built with a cross-platform framework (React Native, Flutter, Capacitor) are not supported_
32
-
33
- ## Install
34
-
35
- The package is not on npm yet. Once it is:
36
-
37
- ```bash
38
- npm i -g multiplat-factory
39
- mpf doctor
40
- ```
41
-
42
- From a packed tarball (what the release script produces, see [Releasing and publishing](#releasing-and-publishing)):
12
+ ## Quick start
43
13
 
44
14
  ```bash
45
- npm i -g dist-release/multiplat-factory-0.1.0.tgz
46
- mpf doctor
15
+ npm i -g multiplat-factory # or: npx multiplat-factory doctor
16
+ mpf doctor # what toolchains are present, what each gap disables (--json)
17
+ mpf setup # install what doctor reports missing, asking before each step (--yes, --dry-run)
18
+ mpf serve # the dashboard and API on http://127.0.0.1:4310 (--port, --host)
19
+ mpf port ./my-app --to ios,android --budget 30
20
+ mpf runs # runs so far; `mpf status <runId>` for one
47
21
  ```
48
22
 
49
- From source, every `mpf` command runs as `pnpm mpf <command>`:
50
-
51
- ```bash
52
- git clone https://github.com/sdras/multiplat-factory.git
53
- cd multiplat-factory
54
- nvm use
55
- pnpm install
56
- pnpm mpf doctor
57
- ```
58
-
59
- ## Step by step instructions
60
-
61
- 1. **Check the machine.** `mpf doctor` prints one line per tool and says which platforms this machine can build for. `mpf doctor --json` prints the same report as JSON.
62
- 2. **Install what is missing.** `mpf setup` walks through the gaps doctor found and asks before running each install command (Homebrew on macOS, apt on Linux, winget on Windows). Anything that needs `sudo` runs only after you confirm that exact command. `mpf setup --yes` accepts every command that does not need sudo and prints the rest for you to run yourself. `mpf setup --dry-run` shows what would run and stops.
63
- 3. **Start the dashboard.** `mpf serve` runs the API and the dashboard on one address, `http://127.0.0.1:4310` by default (change it with `--port`, `--host`, or `MPF_PORT`). Leave it running. If you skip this, each command starts its own copy for as long as the command runs, so the dashboard disappears when the command ends.
64
- 4. **Port something.** `mpf port ./my-app --to ios,android` takes a folder or a git URL. It creates the run, prints a link to the run's dashboard page, and follows the run in the terminal until it finishes. The command exits with an error code unless the run succeeded or stopped where you told it to with `--until`. The first pause is plan approval: open the dashboard page, or run `mpf answer <runId> plan approve`.
65
- 5. **Read the result.** Each platform's project is in `~/.mpf/runs/<runId>/workspace/<target>/`. Its `PORT.md` lists what shipped, every behavior and whether its test passed, the reviewer's verdict, any deviations you accepted, and the parts of the shared core that were written by hand rather than recorded from the original. `report.json` in the run's folder holds the same data for tools. `mpf status <runId>` prints a one-line summary.
66
-
67
- To rehearse the whole thing for free, with no Claude calls and no build tools:
68
-
69
- ```bash
70
- mpf port fixtures/inhouse/web-vanilla-counter --to web,android --fake --yes
71
- ```
72
-
73
- ## Commands
74
-
75
- | Command | What it does |
76
- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
77
- | `mpf doctor [--json]` | Checks Node, git, the package manager, Claude access, Xcode, Java, the Android SDK, Gradle, Rust, Maestro, and Playwright's Chromium. Says which platforms you can build for and what each gap costs you. |
78
- | `mpf setup [--yes] [--dry-run]` | Installs what doctor reports missing, asking before each step. |
79
- | `mpf serve [--port <n>] [--host <h>]` | Runs the daemon (the API, the live event stream, and the dashboard) until you stop it. |
80
- | `mpf port <source> --to <targets> [options]` | Ports a folder or git URL. Options are listed below. |
81
- | `mpf runs` | Lists runs with their status, source, targets, and cost. |
82
- | `mpf status <runId>` | Shows one run's stages and its summary. |
83
- | `mpf resume <runId>` | Continues a paused run, including one that stopped at `--until`. |
84
- | `mpf answer <runId> <stageId> <choice> [--note]` | Answers a stage that is waiting for you. Stage ids look like `plan`, `test:android`, or `critique:ios`. |
85
- | `mpf rerun <runId> <stageId>` | Runs one stage again, plus everything after it. The rerun stages get a fresh allowance of fix rounds. |
86
- | `mpf instruct <runId> <stageId> "<text>"` | Leaves a note for the Claude session that will work on that stage next. |
87
-
88
- Options for `mpf port`:
89
-
90
- - `--to web,ios,android`: the platforms to build.
91
- - `--budget <usd>`: the spending cap. Default 30, at most 500.
92
- - `--core auto|always|never`: whether to build the shared core. `auto` says no when only one platform is requested, since there is nothing to share.
93
- - `--web-framework vanilla|react|vue|svelte`: the framework for a web port.
94
- - `--until <stage>`: pause after that stage.
95
- - `--yes`: skip the plan approval and the pause before fix rounds.
96
- - `--detach`: create the run and return right away instead of following it.
97
- - `--fake`: run the rehearsal pipeline with no Claude calls and no build tools.
98
-
99
- A run's stages, in order (use these names for `--until` and in stage ids): `ingest`, `inventory`, `spec`, `behaviors`, `observe`, `plan`, `core.extract`, `core.vectors`, `core.build`, then for each platform `generate`, `build`, `test`, `critique`, `fix`, and finally `report`.
100
-
101
- ## What you need
102
-
103
- - **Node 22.13 or newer.** The repo's `.nvmrc` asks for 24, so run `nvm use` in a source checkout. On an older Node, `pnpm` itself can fail before anything starts with the error `Cannot find matching keyid`. That is the old Corepack shim; switching Node fixes it.
104
- - **A way to use agents.** Either set `ANTHROPIC_API_KEY`, or log in to Claude Code on this machine (`claude`, then `/login`). Every call goes through your account and every run has a spending cap.
105
- - **The build tools for the platforms you want.** `mpf doctor` lists what is present. `mpf setup` installs missing build tools. In short:
106
- - Web needs Playwright's Chromium, so the factory can watch the original app run.
107
- - Android needs a JDK between 17 and 24, the Android SDK with an emulator and an `android-36` system image, Gradle, and Maestro (the tool that taps and types on mobile devices).
108
- - iOS needs macOS with Xcode, an iOS simulator, XcodeGen, and Maestro.
109
- - A shared core needs Rust with the `wasm32-unknown-unknown` target.
110
-
111
- A platform whose tools are missing is skipped, and the run explain this.
23
+ Requires Node 22.13 or newer. The factory uses your own Claude credentials: `ANTHROPIC_API_KEY`,
24
+ or the Claude Code login on this machine. iOS targets need macOS with Xcode; web and Android
25
+ targets also run on Linux. A target whose toolchain is missing is skipped with a plain reason.
112
26
 
113
- iOS output can only be built on macOS. Web and Android output can be built on macOS or Linux.
27
+ The dashboard runs on your machine next to the daemon, because the factory needs this machine's
28
+ simulators, emulators, toolchains, and Claude login. There is nothing to host. Commands start an
29
+ embedded daemon for their own lifetime when `mpf serve` is not running.
114
30
 
115
- ## Pauses, budgets, and stopping early
31
+ ## Porting
116
32
 
117
- A run can pause and wait for you at three points. Answer from the run's dashboard page or with `mpf answer`.
33
+ `mpf port <path or git URL> --to web,ios,android` creates a run, prints its dashboard link, and
34
+ follows it in the terminal. Options: `--budget <usd>` (default 30, at most 500; the run pauses
35
+ when it is reached), `--core auto|always|never` for the shared WASM core (`auto` declines when
36
+ only one target is requested), `--web-framework vanilla|react|vue|svelte`, `--until <stage>`
37
+ (pause after that stage, for example `plan`), `--yes` (skip the plan approval and fix-loop
38
+ checkpoints), `--detach` (create the run and return), and `--fake` (a pipeline with no LLM and
39
+ no toolchains, to exercise the factory). Without `--detach` it exits non-zero unless the run
40
+ succeeded or stopped where `--until` asked.
118
41
 
119
- - **Plan approval** (on by default; `--yes` skips it) pauses after `plan`. Choices: `approve` or `reject`.
120
- - **Before the first fix round** (`--yes` skips it) pauses `build` and `test` before they spend money on fixes. The pause shows the failures and how much the fix is allowed to cost. `continue` goes ahead, rebuilding or retesting first. `reject` cancels the run.
121
- - **Reviewer escalation** pauses when the independent review cannot settle a platform within its rounds. `accept` records the remaining problems as deviations you have chosen to live with, marked as such in `PORT.md`. `reject` cancels the run.
42
+ Stage kinds, in order: `ingest`, `inventory`, `spec`, `behaviors`, `observe`, `plan`,
43
+ `core.extract`, `core.vectors`, `core.build`, then per target `generate`, `build`, `test`,
44
+ `critique`, `fix`, and finally `report`. Stage ids look like `plan`, `test:android`, or
45
+ `critique:ios`.
122
46
 
123
- `--until <stage>` pauses a run after that stage. Use it to read the analysis before anything is generated (`--until plan`), or to stop after the shared core is built (`--until core.build`). `mpf resume` carries on. If the named stage is the last one, the run finishes instead of pausing.
47
+ Each target's project lands in `~/.mpf/runs/<runId>/workspace/<target>/` with its `PORT.md`:
48
+ what shipped, every behavior and whether its flow passed, the critic's verdict, and any accepted
49
+ deviations.
124
50
 
125
- Every run has a spending cap: `--budget`, or the configured default of $30. The ceiling is $500. Cost is charged the moment a Claude session returns, and the run pauses when it reaches the cap. The CLI and the dashboard cannot raise a paused run's budget yet. From a source checkout, `pnpm tsx scripts/run-control.ts <runId> resume --budget <usd>` does it. For scale: a two-screen app costs about $15 to $40 per platform, and a dozen-screen app that talks to a live backend about $100 to $150.
51
+ ## Checkpoints
126
52
 
127
- ## The dashboard
128
-
129
- `mpf serve` serves the dashboard and the API from one process on your machine. There is nothing to deploy. The factory needs the simulators, emulators, build tools, and Claude login of the machine it runs on, so each person runs it locally. A hosted version for many users would need the six replacement points listed in `docs/HOSTED-SEAM.md`. None of that exists yet.
130
-
131
- The pages:
132
-
133
- - `/`: all runs.
134
- - `/runs/new`: start a new run.
135
- - `/runs/<id>`: one run, with its stage graph, live transcript, budget, and controls.
136
- - `spec`: the app's screens and behaviors, marked with which ones were verified on the original.
137
- - `plan`: the port plan, with approve and reject.
138
- - `tests`: results per platform, with flows, screenshots, failure evidence, and the shared-core parity table.
139
- - `critic`: the independent review's verdicts and findings.
140
- - `core`: a playground for the shared core.
141
- - `release`: a release checklist.
142
- - `doctor`: the tool report.
143
-
144
- The dashboard also has review tools for a project: a chat about the project that can propose changes, a design review that drafts three concept mockups to pick from, annotation of recorded screenshots with pins and notes, and the release checklist. Applying a proposal creates a new version of the project and reruns the stages it touches. These actions are also available to browser agents as WebMCP tools on the page. `docs/PROJECT-REVIEW.md` describes all of it, and `pnpm tsx scripts/review-demo.ts <screenshot.png> <port>` runs a demo against a throwaway database. The "Host on Netlify" card on the release page is for the generated web app, not for the factory itself.
145
-
146
- When developing from source, the dashboard is not prebuilt: run the API with `pnpm mpf serve` on 4310 and the dashboard with `pnpm dev`, which reloads on changes at `http://localhost:4311` and forwards `/api` calls to the daemon. Use `localhost`, not `127.0.0.1`; the dev server listens on IPv6.
147
-
148
- ## Where data lives
149
-
150
- Run data never goes inside your project. The data folder is `$MPF_HOME` if set, else `$XDG_DATA_HOME/mpf`, else `~/.mpf`. Inside:
151
-
152
- - `mpf.sqlite`: the run database. Everything that happened is stored as a log of events, and every view is rebuilt from that log.
153
- - `daemon.json`: the running daemon's address, so the other commands can find it.
154
- - `runs/<runId>/`: one folder per run:
155
- - `source/`: a copy of the original app
156
- - `detection.json`, `inventory.json`, `appspec.json`, `behaviorspec.json`, `portplan.json`: the analysis and the plan
157
- - `observe/`: screenshots of the original app and its own test results
158
- - `core/`: the shared core's Rust code, the built WebAssembly module, and the recorded inputs and outputs in `vectors/*.json`
159
- - `workspace/<target>/`: the generated project, its `PORT.md`, and the test flows with their evidence
160
- - `report.json`: the run's report
161
- - `review/`: screenshot annotations
162
- - `locks/`: one lock per run and one per device. Only one process drives a run at a time, and only one process uses the simulator or the emulator at a time.
163
- - `jdks/`: JDKs you unpack here are found like installed ones. Some older apps come with a Gradle wrapper that needs an old JDK (Gradle 4 to 7 want JDK 8 to 16), and this lets you provide one without a package manager or sudo.
164
- - `cache/`.
165
-
166
- ## Configuration
167
-
168
- Settings come in layers, and later layers win: built-in defaults, then `config.json` in the data folder, then `mpf.config.ts`, `.js`, or `.json` in the current folder, then environment variables.
169
-
170
- ```json
171
- {
172
- "models": { "generate": "claude-opus-5", "critic": "claude-opus-5", "cheap": "claude-sonnet-5" },
173
- "effort": { "generate": "xhigh", "critic": "high" },
174
- "budget": { "defaultUsd": 30 },
175
- "concurrency": {
176
- "agents": 3,
177
- "lanes": { "xcodebuild": 1, "gradle": 1, "simulator": 1, "emulator": 1, "web": 2 }
178
- },
179
- "toolchain": {
180
- "androidHome": "/path/to/android-sdk",
181
- "javaHome": "/path/to/jdk",
182
- "developerDir": "/Applications/Xcode.app/Contents/Developer",
183
- "maestro": "/path/to/maestro",
184
- "cargo": "/path/to/cargo"
185
- },
186
- "server": { "host": "127.0.0.1", "port": 4310 },
187
- "dataDir": "/somewhere/else"
188
- }
189
- ```
190
-
191
- The values shown are the defaults, except the `toolchain` entries. Those are empty by default because the tools are found automatically (`DEVELOPER_DIR` or `xcode-select`, `JAVA_HOME`, `ANDROID_HOME`, Homebrew folders, PATH). Fill one in only when discovery picks the wrong thing.
192
-
193
- Environment variables:
194
-
195
- - `MPF_HOME`: the data folder
196
- - `MPF_PORT` and `MPF_HOST`: the daemon's address
197
- - `MPF_BUDGET_USD`: the default budget
198
- - `MPF_MODEL_GENERATE`, `MPF_MODEL_CRITIC`, `MPF_MODEL_CHEAP`: which Claude models to use
199
- - `ANDROID_HOME` or `ANDROID_SDK_ROOT`, `JAVA_HOME`, `DEVELOPER_DIR`: where the build tools are
200
- - `ANTHROPIC_API_KEY`: Claude access
201
- - `MPF_ANDROID_AVD`: which emulator image to start. Otherwise the first one found, or `mpf-api36` if there is none.
202
- - `MPF_KEEP_DEVICES=1`: leave the emulator or simulator running after a run that started it
203
- - `PLAYWRIGHT_BROWSERS_PATH`: where Playwright keeps its browsers
204
-
205
- ## Run stages
206
-
207
- 1. `ingest` copies the app and works out its platform and framework.
208
- 2. `inventory` parses the code and collects the test ids the app already has, so tests can find its controls.
209
- 3. `spec` and `behaviors` describe the app in platform-neutral terms: its screens, controls, navigation, and the things a user can do.
210
- 4. `observe` runs the original app and checks each behavior against it, takes screenshots, and runs the app's own test suite as a baseline. Web apps run in Chromium from a dev server or a production build. iOS and Android apps run on a simulator or emulator, driven by Maestro.
211
- 5. `plan` decides how to build each port and whether a shared core is worth it.
212
- 6. If so, `core.extract`, `core.vectors`, and `core.build` pull out the shared logic, record the original app's real outputs for many inputs, and compile the core.
213
- 7. For each platform: the project is scaffolded, Claude writes the code, the factory builds it and runs the same behaviors against it, the independent reviewer scores it, and problems go back for fixing within fixed limits (three build-fix rounds, two test-fix rounds, two review rounds). A `PORT.md` sums it up.
214
- 8. `report` runs once every platform has finished, however it finished.
215
-
216
- Devices are handled for you. The factory starts a headless emulator or an iPhone simulator when none is running, reuses one that is, clears stale system dialogs before each launch, and shuts down the devices it started when the run ends.
217
-
218
- ## Troubleshooting
219
-
220
- - **`mpf doctor` says there is no Claude login, but you are logged in.** nvm keeps global commands per Node version, so a `claude` installed under another Node is not on PATH. The factory still finds the login. Either install `claude` under this Node, or ignore the line.
221
- - **Xcode is installed, but `xcode-select` points at the Command Line Tools.** The factory finds Xcode itself and keeps working. `sudo xcode-select -s /Applications/Xcode.app` makes Xcode the default for everything else too.
222
- - **Gradle fails with `java.lang.IllegalArgumentException: 27` or similar.** The JDK is newer than Gradle supports. The factory's own builds need JDK 17 to 24. Homebrew's plain `openjdk` tracks the newest release, so install `openjdk@21` instead. Run `gradle --stop` after switching JDKs. The original app's own wrapper may need an older JDK still; the factory picks one by the wrapper's version, and a JDK unpacked under `<data folder>/jdks` counts.
223
- - **`brew install maestro` installs the wrong thing.** The mobile testing tool is `brew trust mobile-dev-inc/tap && brew install --formula mobile-dev-inc/tap/maestro`.
224
- - **Web observation says "unverified".** Playwright's Chromium is missing. Run `npx playwright install chromium` (from a source checkout, `pnpm --filter @mpf/analyzer exec playwright install chromium`). The port still runs; the behaviors just are not checked against the original.
225
- - **Every iOS test fails at once.** A stale "Open in …?" dialog from an earlier launch is hiding the whole app. The factory dismisses it before each launch. If one is stuck, shut the simulator down.
226
- - **Every Android test fails at once.** A stale system dialog (an "isn't responding" prompt, a crash dialog, or an "Open with" chooser) is covering the app. The factory detects and dismisses these. If an "Open with" chooser keeps coming back, uninstall leftover `app.mpf.*` packages from the emulator.
227
- - **"already being driven by another process".** Only one process drives a run at a time. Stop the other one (a daemon or a `run-control` script) before resuming or rerunning here.
228
- - **Tests die part-way when the machine is busy.** Maestro gives up without a report under heavy load. Keep the machine quiet during device runs.
229
- - **The run paused on its budget.** Raise it as described under [Pauses, budgets, and stopping early](#pauses-budgets-and-stopping-early), then resume.
230
-
231
- ## Developing the factory
53
+ A run pauses for you at three checkpoints: plan approval (`approve` or `reject`), before the first
54
+ paid fix round of a build or test stage (`continue` or `reject`; `--yes` skips both of these), and
55
+ critic escalation, when the critic cannot settle a target within its rounds (`accept` turns the
56
+ remaining findings into accepted deviations recorded in `PORT.md`; `reject` cancels). Answer from
57
+ the dashboard or with the control commands:
232
58
 
233
59
  ```bash
234
- nvm use
235
- pnpm install
236
- pnpm mpf serve # the API on 4310
237
- pnpm dev # the dashboard with hot reload on http://localhost:4311
238
- pnpm test # vitest across every package; tests that need a missing tool skip themselves
239
- pnpm typecheck
240
- pnpm lint
241
- pnpm format # Prettier; `pnpm format:check` is what CI runs
242
- pnpm mpf <command> # run the CLI from source
60
+ mpf answer <runId> <stageId> <choice> [--note "..."] # e.g. mpf answer 01M... plan approve
61
+ mpf resume <runId> # continue a paused run, or past a --until stop
62
+ mpf rerun <runId> <stageId> # re-run one stage and everything after it
63
+ mpf instruct <runId> <stageId> "text" # queue an instruction for a stage's next worker
243
64
  ```
244
65
 
245
- To drive a run without the daemon while you work on a stage, use `scripts/run-control.ts`:
246
-
247
- - `pnpm tsx scripts/run-control.ts <runId> resume|rerun <stageId>|answer <stageId> <choice> [note]|status` drives an existing run.
248
- - `resume` also takes `--until <stage>`, `--budget <usd>`, and `--core auto|always|never`. `rerun` takes `--approve`.
249
- - `pnpm tsx scripts/run-control.ts new <source> --to ios,android [--until <stage>] [--budget <usd>] [--core …] [--yes]` creates a run in this process, on the current code.
250
-
251
- Point `MPF_HOME` at the same folder the daemon serves and the run appears in the dashboard. Two more scripts help. `scripts/compile-flows.ts <runId> <target>` recompiles a platform's test flows from the stored behaviors without a device. `scripts/spike-target-scaffold.ts --target android|ios|web [--framework <flavor>] [--e2e] [--core]` scaffolds, builds, launches, runs one flow, and checks shared-core parity with no Claude calls.
252
-
253
- **Fixtures** are the sample apps the factory is tested against. `fixtures/manifest.json` pins public apps by git URL, branch, and commit. `pnpm fixtures:sync` clones them into `fixtures/.cache` and fills in missing commits; `--id <fixture>` syncs one, and `--repin` moves the pins forward. The manifest also lists the in-house fixtures under `fixtures/inhouse/`:
254
-
255
- - `web-vanilla-counter`: tiny; the shared core stays off
256
- - `web-vanilla-cart`: about 150 lines of pure pricing logic, made for the shared core
257
- - `ios-uikit-notes`: Swift, UIKit, XcodeGen spec, XCTest
258
- - `ios-objc-notes`: Objective-C, XCTest
259
- - `android-views-java-notes`: Java, XML layouts, JUnit
260
-
261
- Every in-house fixture has a test id on every interactive element, and the mobile ones ship their own unit tests, which the `observe` stage records as the `source` result. `packages/adapters/test/detect.test.ts` checks the in-house fixtures, and `fixture-matrix.test.ts` checks the synced public ones.
262
-
263
- **CI** (`.github/workflows/ci.yml`) runs on Linux and macOS: lint, the Prettier check, typecheck, the tests (with Rust and Chromium installed so the core and observe tests run), the rehearsal pipeline end to end, the dashboard build, and the bundled CLI. It also builds the four web flavors and runs one Playwright flow on each, builds the in-house mobile fixtures and runs their own tests, and syncs the public fixtures and checks they are detected correctly. Runs that use real Claude calls or real devices are not in CI.
264
-
265
- To add a platform or framework, implement `SourceAdapter` or `TargetAdapter` from `packages/adapters/src/types.ts`, register it, and add a fixture. `docs/ADAPTERS.md` walks through it. `docs/ARCHITECTURE.md` describes the design and where the code differs from it. `docs/ABI.md` is the contract between the shared core and its hosts.
266
-
267
- ## Releasing and publishing
268
-
269
- The published package is `multiplat-factory`. It is built from `apps/cli`, with every `@mpf/*` package bundled in and the static dashboard copied next to it. Its npm page is `apps/cli/README.md`.
270
-
271
- 1. Bump the version in `apps/cli/package.json`. The CLI reads it from there at run time, so that is the only place it lives.
272
- 2. Build and pack: `pnpm tsx scripts/release.ts`. It builds the dashboard, bundles the CLI, runs `mpf --help` from the bundle, and packs the tarball into `dist-release/`. It prints the tarball's contents (about 1.2 MB, with source maps).
273
- 3. Try the tarball in a clean project: `mkdir /tmp/try && cd /tmp/try && npm init -y && npm i <repo>/dist-release/multiplat-factory-<version>.tgz && npx mpf doctor && npx mpf serve`.
274
- 4. Log in to npm with `npm login` and confirm it with `npm whoami`. The name `multiplat-factory` is unscoped, so no organization is needed.
275
- 5. Publish: `pnpm tsx scripts/release.ts --publish`. It rebuilds, then runs `pnpm publish --access public --no-git-checks` from `apps/cli`. With two-factor auth on, npm asks for a one-time code.
276
- 6. Check from another machine: `npm view multiplat-factory version` and `npx multiplat-factory doctor`.
277
-
278
- The package metadata and this README link to the GitHub repository. Make it public before publishing, or expect dead links.
66
+ ## Data and configuration
279
67
 
280
- ## Layout
68
+ Run data lives under `~/.mpf` (or `$MPF_HOME`, or `$XDG_DATA_HOME/mpf`), never inside your
69
+ project: the SQLite run store, `runs/<runId>/` with the copied source, the analysis, the
70
+ observation screenshots, the shared core, and each target's workspace, plus `jdks/` for JDKs you
71
+ unpack by hand for old Gradle wrappers.
281
72
 
282
- - `apps/cli`: the `mpf` command
283
- - `apps/server`: the HTTP API, the live event stream, the run daemon, and the project review routes
284
- - `apps/dashboard`: the Nuxt dashboard
285
- - `packages/core`: schemas, the event log, the SQLite store, config, the view reducer, the stage catalog
286
- - `packages/orchestrator`: the stage graph, state machine, scheduler, the per-platform stages, review and fix, report, project revisions
287
- - `packages/harness`: finding build tools, running processes, starting and stopping devices, lanes
288
- - `packages/adapters`: everything that knows about a specific platform or framework: reading sources, generating targets, compiling test flows, and the shared core's host bindings
289
- - `packages/analyzer`: the inventory, the spec and behaviors extraction, observing the original app, planning
290
- - `packages/llm`: the Claude session wrapper, its sandbox rules, cost accounting, and a recorder
291
- - `packages/critic`: the independent review
292
- - `packages/wasm-core`: the Rust core template, its build runner, the Node host, the recorded vectors, and the core stages
73
+ Toolchains are discovered (`DEVELOPER_DIR` or `xcode-select`, `JAVA_HOME`, `ANDROID_HOME`,
74
+ Homebrew prefixes, PATH) with overrides in `~/.mpf/config.json`: `models` (`generate`, `critic`,
75
+ `cheap`), `effort`, `budget.defaultUsd`, `concurrency`, `toolchain` (`androidHome`, `javaHome`,
76
+ `developerDir`, `maestro`, `cargo`), `server` (`host`, `port`), and `dataDir`. Environment
77
+ variables override the file: `MPF_HOME`, `MPF_PORT`, `MPF_HOST`, `MPF_BUDGET_USD`,
78
+ `MPF_MODEL_GENERATE`, `MPF_MODEL_CRITIC`, `MPF_MODEL_CHEAP`, `ANDROID_HOME`, `JAVA_HOME`,
79
+ `DEVELOPER_DIR`, `MPF_ANDROID_AVD` (the emulator image to boot), and `MPF_KEEP_DEVICES=1` (leave
80
+ the devices a run booted running).
293
81
 
294
- Licensed under Apache-2.0; see `LICENSE`.
82
+ Source code, the full guide with troubleshooting, the architecture, and the adapter interface for
83
+ adding frameworks: https://github.com/sdras/multiplat-factory
@@ -0,0 +1,65 @@
1
+ // ../../packages/harness/src/exec.ts
2
+ import { execa } from "execa";
3
+ async function runProcess(opts) {
4
+ const started = Date.now();
5
+ const limit = opts.captureLimit ?? 2e6;
6
+ let out = "";
7
+ let err = "";
8
+ const execaOpts = {
9
+ cwd: opts.cwd,
10
+ env: opts.env,
11
+ reject: false,
12
+ timeout: opts.timeoutMs,
13
+ cancelSignal: opts.signal,
14
+ killSignal: "SIGTERM",
15
+ forceKillAfterDelay: 5e3,
16
+ cleanup: true,
17
+ stdin: "ignore",
18
+ stdout: "pipe",
19
+ stderr: "pipe"
20
+ };
21
+ const child = execa(opts.command, opts.args ?? [], execaOpts);
22
+ child.stdout?.on("data", (b) => {
23
+ const s = b.toString();
24
+ opts.onStdout?.(s);
25
+ if (out.length < limit) out += s;
26
+ });
27
+ child.stderr?.on("data", (b) => {
28
+ const s = b.toString();
29
+ opts.onStderr?.(s);
30
+ if (err.length < limit) err += s;
31
+ });
32
+ const result = await child;
33
+ return {
34
+ command: [opts.command, ...opts.args ?? []].join(" "),
35
+ exitCode: result.exitCode ?? null,
36
+ stdout: out,
37
+ stderr: err,
38
+ durationMs: Date.now() - started,
39
+ timedOut: Boolean(result.timedOut),
40
+ cancelled: Boolean(result.isCanceled)
41
+ };
42
+ }
43
+ async function which(bin, env = process.env) {
44
+ const cmd = process.platform === "win32" ? "where" : "which";
45
+ const r = await runProcess({ command: cmd, args: [bin], env, timeoutMs: 5e3 });
46
+ if (r.exitCode !== 0) return void 0;
47
+ const first = r.stdout.split(/\r?\n/).find((l) => l.trim().length > 0);
48
+ return first?.trim();
49
+ }
50
+ async function tryOutput(command, args = [], env = process.env, timeoutMs = 15e3) {
51
+ try {
52
+ const r = await runProcess({ command, args, env, timeoutMs });
53
+ if (r.exitCode !== 0) return void 0;
54
+ return r.stdout.trim();
55
+ } catch {
56
+ return void 0;
57
+ }
58
+ }
59
+
60
+ export {
61
+ runProcess,
62
+ which,
63
+ tryOutput
64
+ };
65
+ //# sourceMappingURL=chunk-A5ZTNRTN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../packages/harness/src/exec.ts"],"sourcesContent":["import { execa, type Options as ExecaOptions } from 'execa'\n\nexport interface RunProcessOptions {\n command: string\n args?: string[]\n cwd?: string\n env?: NodeJS.ProcessEnv\n timeoutMs?: number\n signal?: AbortSignal\n onStdout?: (chunk: string) => void\n onStderr?: (chunk: string) => void\n /** Cap on captured output per stream, in characters. Streaming callbacks still see everything. */\n captureLimit?: number\n}\n\nexport interface ProcessResult {\n command: string\n exitCode: number | null\n stdout: string\n stderr: string\n durationMs: number\n timedOut: boolean\n cancelled: boolean\n}\n\n/**\n * Runs a child process with a timeout, kill-tree on abort, and streaming output.\n * Never throws for a non-zero exit; inspect `exitCode`.\n */\nexport async function runProcess(opts: RunProcessOptions): Promise<ProcessResult> {\n const started = Date.now()\n const limit = opts.captureLimit ?? 2_000_000\n let out = ''\n let err = ''\n const execaOpts: ExecaOptions = {\n cwd: opts.cwd,\n env: opts.env,\n reject: false,\n timeout: opts.timeoutMs,\n cancelSignal: opts.signal,\n killSignal: 'SIGTERM',\n forceKillAfterDelay: 5000,\n cleanup: true,\n stdin: 'ignore',\n stdout: 'pipe',\n stderr: 'pipe',\n }\n const child = execa(opts.command, opts.args ?? [], execaOpts)\n child.stdout?.on('data', (b: Buffer) => {\n const s = b.toString()\n opts.onStdout?.(s)\n if (out.length < limit) out += s\n })\n child.stderr?.on('data', (b: Buffer) => {\n const s = b.toString()\n opts.onStderr?.(s)\n if (err.length < limit) err += s\n })\n const result = await child\n return {\n command: [opts.command, ...(opts.args ?? [])].join(' '),\n exitCode: result.exitCode ?? null,\n stdout: out,\n stderr: err,\n durationMs: Date.now() - started,\n timedOut: Boolean(result.timedOut),\n cancelled: Boolean(result.isCanceled),\n }\n}\n\n/** Resolve a binary on PATH; returns undefined when absent. */\nexport async function which(\n bin: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<string | undefined> {\n const cmd = process.platform === 'win32' ? 'where' : 'which'\n const r = await runProcess({ command: cmd, args: [bin], env, timeoutMs: 5000 })\n if (r.exitCode !== 0) return undefined\n const first = r.stdout.split(/\\r?\\n/).find((l) => l.trim().length > 0)\n return first?.trim()\n}\n\n/** Run a command and return trimmed stdout, or undefined on any failure. */\nexport async function tryOutput(\n command: string,\n args: string[] = [],\n env: NodeJS.ProcessEnv = process.env,\n timeoutMs = 15_000,\n): Promise<string | undefined> {\n try {\n const r = await runProcess({ command, args, env, timeoutMs })\n if (r.exitCode !== 0) return undefined\n return r.stdout.trim()\n } catch {\n return undefined\n }\n}\n"],"mappings":";AAAA,SAAS,aAA2C;AA6BpD,eAAsB,WAAW,MAAiD;AAChF,QAAM,UAAU,KAAK,IAAI;AACzB,QAAM,QAAQ,KAAK,gBAAgB;AACnC,MAAI,MAAM;AACV,MAAI,MAAM;AACV,QAAM,YAA0B;AAAA,IAC9B,KAAK,KAAK;AAAA,IACV,KAAK,KAAK;AAAA,IACV,QAAQ;AAAA,IACR,SAAS,KAAK;AAAA,IACd,cAAc,KAAK;AAAA,IACnB,YAAY;AAAA,IACZ,qBAAqB;AAAA,IACrB,SAAS;AAAA,IACT,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACA,QAAM,QAAQ,MAAM,KAAK,SAAS,KAAK,QAAQ,CAAC,GAAG,SAAS;AAC5D,QAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc;AACtC,UAAM,IAAI,EAAE,SAAS;AACrB,SAAK,WAAW,CAAC;AACjB,QAAI,IAAI,SAAS,MAAO,QAAO;AAAA,EACjC,CAAC;AACD,QAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc;AACtC,UAAM,IAAI,EAAE,SAAS;AACrB,SAAK,WAAW,CAAC;AACjB,QAAI,IAAI,SAAS,MAAO,QAAO;AAAA,EACjC,CAAC;AACD,QAAM,SAAS,MAAM;AACrB,SAAO;AAAA,IACL,SAAS,CAAC,KAAK,SAAS,GAAI,KAAK,QAAQ,CAAC,CAAE,EAAE,KAAK,GAAG;AAAA,IACtD,UAAU,OAAO,YAAY;AAAA,IAC7B,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,YAAY,KAAK,IAAI,IAAI;AAAA,IACzB,UAAU,QAAQ,OAAO,QAAQ;AAAA,IACjC,WAAW,QAAQ,OAAO,UAAU;AAAA,EACtC;AACF;AAGA,eAAsB,MACpB,KACA,MAAyB,QAAQ,KACJ;AAC7B,QAAM,MAAM,QAAQ,aAAa,UAAU,UAAU;AACrD,QAAM,IAAI,MAAM,WAAW,EAAE,SAAS,KAAK,MAAM,CAAC,GAAG,GAAG,KAAK,WAAW,IAAK,CAAC;AAC9E,MAAI,EAAE,aAAa,EAAG,QAAO;AAC7B,QAAM,QAAQ,EAAE,OAAO,MAAM,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC;AACrE,SAAO,OAAO,KAAK;AACrB;AAGA,eAAsB,UACpB,SACA,OAAiB,CAAC,GAClB,MAAyB,QAAQ,KACjC,YAAY,MACiB;AAC7B,MAAI;AACF,UAAM,IAAI,MAAM,WAAW,EAAE,SAAS,MAAM,KAAK,UAAU,CAAC;AAC5D,QAAI,EAAE,aAAa,EAAG,QAAO;AAC7B,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1 @@
1
+ <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>multiplat-factory</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkcIzcLy.js"}}</script><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=B612:ital,wght@0,400;0,700;1,400&family=B612+Mono:wght@400;700&family=Big+Shoulders+Display:wght@500;600;700&family=Familjen+Grotesk:wght@400;500;600;700&display=swap"><link rel="stylesheet" href="/_nuxt/entry.DRmJCmhL.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkcIzcLy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BSEomhaZ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CCMCJdY2.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B1MQUUmA.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BVtebpBS.js"><script type="module" src="/_nuxt/DkcIzcLy.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/_nuxt/default.BwIDAPER.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/CFi3g9tC.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/G-IHUB_t.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/D2giJ0zb.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BeCRcAqm.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DxWip8Ok.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZdpP1Ud-.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/C6vzBuMd.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.Bq-TOqNj.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/dnzJofwx.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.xaLU7Cq-.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/RgvLL8ZL.js"><meta name="color-scheme" content="dark"><meta name="theme-color" content="#1a1917"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{apiBase:"/api/v1"},app:{baseURL:"/",buildId:"1d285342-32d1-4f36-aa29-aea4bf9510b7",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793581709535,false]</script></body></html>
@@ -0,0 +1 @@
1
+ <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>multiplat-factory</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkcIzcLy.js"}}</script><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=B612:ital,wght@0,400;0,700;1,400&family=B612+Mono:wght@400;700&family=Big+Shoulders+Display:wght@500;600;700&family=Familjen+Grotesk:wght@400;500;600;700&display=swap"><link rel="stylesheet" href="/_nuxt/entry.DRmJCmhL.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkcIzcLy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BSEomhaZ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CCMCJdY2.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B1MQUUmA.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BVtebpBS.js"><script type="module" src="/_nuxt/DkcIzcLy.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/_nuxt/default.BwIDAPER.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/CFi3g9tC.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/G-IHUB_t.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/D2giJ0zb.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BeCRcAqm.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DxWip8Ok.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZdpP1Ud-.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/C6vzBuMd.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.Bq-TOqNj.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/dnzJofwx.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.xaLU7Cq-.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/RgvLL8ZL.js"><meta name="color-scheme" content="dark"><meta name="theme-color" content="#1a1917"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{apiBase:"/api/v1"},app:{baseURL:"/",buildId:"1d285342-32d1-4f36-aa29-aea4bf9510b7",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793581709535,false]</script></body></html>
@@ -0,0 +1,3 @@
1
+ function e(e){return e.startsWith(`before launching`)}function t(e){if(!e)return{rest:``};let t=e.split(`
2
+ `),n=t.findIndex(e=>e.startsWith(`A system dialog covers the app:`));return n===-1?{rest:e}:{dialog:t[n],rest:t.filter((e,t)=>t!==n).join(`
3
+ `)}}function n(e){let t=e.split(`. `),n=t.findIndex(e=>/\breject\b/.test(e));return n<=0?[e]:[`${t.slice(0,n).join(`. `)}.`,t.slice(n).join(`. `)]}export{n,t as r,e as t};
@@ -0,0 +1 @@
1
+ import{Jt as e,Xt as t,Yt as n,a as r,bt as i,ft as a}from"./BSEomhaZ.js";import"./CCMCJdY2.js";var o=t({docsBase:e,reporters:n}),s=`$s`;function c(...e){let t=typeof e[e.length-1]==`string`?e.pop():void 0;typeof e[0]!=`string`&&e.unshift(t);let[n,c]=e;if(!n||typeof n!=`string`)throw o.NUXT_E7009({key:n});if(c!==void 0&&typeof c!=`function`)throw o.NUXT_E7007({type:typeof c});let l=s+n,u=r(),d=i(u.payload.state,l);if(c&&(u._state[l]??={_default:c}),d.value===void 0&&c){let e=c();if(a(e))return u.payload.state[l]=e,e;d.value=e}return d}export{c as t};
@@ -0,0 +1 @@
1
+ import{F as e,Gt as t,Ht as n,O as r,St as i,T as a,Z as o,w as s}from"./BSEomhaZ.js";import{f as c,o as l}from"./BeCRcAqm.js";var u=Object.assign(e({__name:`StatusLamp`,props:{status:{},label:{},strong:{type:Boolean},hollow:{type:Boolean}},setup(e){let u=e,d=s(()=>u.label??c(u.status));return(s,c)=>(o(),r(`span`,{class:n([`status`,`st-${e.status}`])},[a(`span`,{class:n([`lamp`,{live:(`isLive`in s?s.isLive:i(l))(e.status),hollow:e.hollow}])},null,2),a(`span`,{class:n({lbl:e.strong})},t(i(d)),3)],2))}}),{__name:`StatusLamp`});export{u as t};
@@ -0,0 +1 @@
1
+ import{D as e,F as t,Gt as n,Ht as r,M as i,N as a,O as o,St as s,T as c,Y as l,Z as u,et as d,gt as f,w as p,y as m}from"./BSEomhaZ.js";import{t as h}from"./BVtebpBS.js";import{t as g}from"#entry";import{g as _}from"./BeCRcAqm.js";import{t as v}from"./BD1XFGRw.js";import{t as y}from"./DlF4qIFG.js";var b={class:`doctor-page`},x={class:`page-head`},S={key:0,class:`readouts`},C={class:`readout`},w={class:`v mono`},T={class:`readout`},E={class:`v row`,style:{gap:`14px`}},D={class:`readout`},O={class:`v mono`},k={class:`readout`},A={class:`v`},j=[`disabled`],M={key:0,class:`callout bad`,style:{"margin-bottom":`16px`}},N={class:`body`},P={key:1,class:`boxed`},F={class:`cell`,style:{"line-height":`1.35`}},I={style:{"font-size":`13.5px`,"font-weight":`700`}},L={key:0,class:`mono small muted`},R={class:`stack check-detail`,style:{gap:`6px`}},z={key:0,class:`fix`},B=[`aria-label`,`onClick`],V={class:`tags affects`},H={key:2,class:`muted`},U=Object.assign(g(t({__name:`DoctorView`,setup(t){let g=h(),U=f(),W=f(),G=f(!1),K=f(),{now:q}=y(5e3);async function J(e=!1){G.value=!0;try{U.value=await g.get(`/doctor${e?`?refresh=1`:``}`),W.value=void 0}catch(e){W.value=e.message}finally{G.value=!1}}l(()=>J());let Y=[{id:`web`,name:`web`},{id:`ios`,name:`iOS`},{id:`android`,name:`android`}],X=p(()=>new Set(U.value?.availableTargets??[])),Z=p(()=>{if(!U.value)return``;let e=Math.max(0,Math.round((q.value-new Date(U.value.generatedAt).getTime())/1e3));return e<60?`just now`:`${Math.round(e/60)} min ago`}),Q=e=>e.fix?.brew??e.fix?.apt??e.fix?.winget??e.fix?.manual;async function $(e,t){try{await navigator.clipboard.writeText(e),K.value=t,setTimeout(()=>K.value=void 0,1500)}catch{K.value=void 0}}return(t,l)=>{let f=v,p=_;return u(),o(`div`,b,[c(`div`,x,[l[5]||=c(`div`,null,[c(`h1`,{class:`title`},`Doctor`),c(`p`,{class:`subtitle`},[i(` Toolchains found on this machine and what each one unlocks. Nothing here is configured by hand; overrides live in `),c(`code`,null,`~/.mpf/config.json`),i(`. `)])],-1),l[6]||=c(`div`,{class:`grow`},null,-1),s(U)?(u(),o(`div`,S,[c(`div`,C,[l[1]||=c(`span`,{class:`k`},`Host`,-1),c(`span`,w,n(s(U).host.os)+` / `+n(s(U).host.arch)+` · node `+n(s(U).host.node),1)]),c(`div`,T,[l[2]||=c(`span`,{class:`k`},`Targets available`,-1),c(`span`,E,[(u(),o(m,null,d(Y,e=>a(f,{key:e.id,status:s(X).has(e.id)?`ok`:`missing`,label:e.name,hollow:!s(X).has(e.id)},null,8,[`status`,`label`,`hollow`])),64))])]),c(`div`,D,[l[3]||=c(`span`,{class:`k`},`Claude backend`,-1),c(`span`,O,n(s(U).llmBackend),1)]),c(`div`,k,[l[4]||=c(`span`,{class:`k`},`Checked`,-1),c(`span`,A,n(s(Z)),1)])])):e(``,!0),c(`button`,{class:`btn`,type:`button`,disabled:s(G),onClick:l[0]||=e=>J(!0)},[a(p,{name:`refresh`}),i(n(s(G)?`Checking`:`Re-check`),1)],8,j)]),s(W)?(u(),o(`div`,M,[l[10]||=c(`span`,{class:`lamp`},null,-1),c(`div`,null,[l[9]||=c(`div`,{class:`head`},`Cannot reach the server`,-1),c(`div`,N,[i(n(s(W))+`. Start it with `,1),l[7]||=c(`code`,null,`pnpm mpf serve`,-1),l[8]||=i(`.`,-1)])])])):e(``,!0),s(U)?(u(),o(`div`,P,[l[12]||=c(`div`,{class:`head-row check-head`},[c(`span`,null,`Status`),c(`span`,null,`Check`),c(`span`,null,`Detail · fix`),c(`span`,{class:`affects`},`Affects`)],-1),(u(!0),o(m,null,d(s(U).checks,t=>(u(),o(`div`,{key:t.id,class:`check`},[a(f,{status:t.status,strong:``,hollow:t.status!==`ok`},null,8,[`status`,`hollow`]),c(`div`,F,[c(`span`,I,n(t.label),1),t.version?(u(),o(`span`,L,n(t.version),1)):e(``,!0)]),c(`div`,R,[c(`span`,{style:{"font-size":`13px`,"line-height":`1.45`},class:r(t.status===`ok`?`dim`:``)},n(t.detail),3),t.status!==`ok`&&Q(t)?(u(),o(`div`,z,[c(`code`,null,n(Q(t)),1),c(`button`,{class:`btn tiny`,type:`button`,"aria-label":`Copy the fix for ${t.label}`,onClick:e=>$(Q(t),t.id)},[a(p,{name:`copy`,size:11}),i(n(s(K)===t.id?`Copied`:`Copy`),1)],8,B)])):e(``,!0)]),c(`div`,V,[l[11]||=c(`span`,{class:`affects-label muted small`},`Used for`,-1),(u(!0),o(m,null,d(t.affects,e=>(u(),o(`span`,{key:e,class:`tag`},n(e),1))),128))])]))),128))])):s(W)?e(``,!0):(u(),o(`p`,H,`Checking toolchains…`))])}}}),[[`__scopeId`,`data-v-f46173f9`]]),{__name:`DoctorView`});export{U as t};