openuispec 0.2.10 → 0.2.12

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 (237) hide show
  1. package/README.md +3 -1
  2. package/check/index.ts +17 -0
  3. package/cli/index.ts +21 -3
  4. package/cli/init.ts +224 -10
  5. package/docs/cli.md +13 -8
  6. package/docs/file-formats.md +36 -0
  7. package/docs/implementation-notes.md +7 -0
  8. package/drift/index.ts +281 -40
  9. package/mcp-server/index.ts +179 -119
  10. package/mcp-server/screenshot.ts +19 -4
  11. package/package.json +5 -2
  12. package/prepare/index.ts +155 -18
  13. package/schema/openuispec.schema.json +59 -0
  14. package/schema/semantic-lint.ts +25 -1
  15. package/scripts/take-all-screenshots.ts +507 -0
  16. package/spec/openuispec-v0.1.md +13 -0
  17. package/status/index.ts +72 -2
  18. package/examples/social-app/.mcp.json +0 -10
  19. package/examples/social-app/AGENTS.md +0 -124
  20. package/examples/social-app/CLAUDE.md +0 -124
  21. package/examples/social-app/backend/.gitkeep +0 -1
  22. package/examples/social-app/generated/android/social-app/app/.paparazzi-hashes.json +0 -3
  23. package/examples/social-app/generated/android/social-app/app/build.gradle.kts +0 -94
  24. package/examples/social-app/generated/android/social-app/app/src/main/AndroidManifest.xml +0 -26
  25. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/AppContainer.kt +0 -20
  26. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/MainActivity.kt +0 -35
  27. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/SocialAppApplication.kt +0 -13
  28. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/MockData.kt +0 -98
  29. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/AppPreferences.kt +0 -19
  30. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/DataStorePreferencesRepository.kt +0 -68
  31. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/PreferencesRepository.kt +0 -15
  32. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/model/Models.kt +0 -34
  33. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/MainShell.kt +0 -390
  34. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/Components.kt +0 -234
  35. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/ContractPrimitives.kt +0 -641
  36. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/navigation/RootComponent.kt +0 -113
  37. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ChatDetailScreen.kt +0 -212
  38. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/CreatePostScreen.kt +0 -113
  39. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/DiscoverScreen.kt +0 -137
  40. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/EditProfileScreen.kt +0 -180
  41. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/HomeFeedScreen.kt +0 -169
  42. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/MessagesInboxScreen.kt +0 -85
  43. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/NotificationsScreen.kt +0 -74
  44. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/PostDetailScreen.kt +0 -293
  45. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ProfileSelfScreen.kt +0 -116
  46. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SearchResultsScreen.kt +0 -161
  47. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsScreen.kt +0 -164
  48. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsStore.kt +0 -95
  49. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/UserProfileScreen.kt +0 -123
  50. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Color.kt +0 -33
  51. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Shape.kt +0 -41
  52. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Spacing.kt +0 -20
  53. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Theme.kt +0 -82
  54. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Type.kt +0 -60
  55. package/examples/social-app/generated/android/social-app/app/src/main/res/drawable/ic_launcher_foreground.xml +0 -9
  56. package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -5
  57. package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -5
  58. package/examples/social-app/generated/android/social-app/app/src/main/res/values/strings.xml +0 -91
  59. package/examples/social-app/generated/android/social-app/app/src/main/res/values/themes.xml +0 -10
  60. package/examples/social-app/generated/android/social-app/app/src/main/res/values-ru/strings.xml +0 -79
  61. package/examples/social-app/generated/android/social-app/app/src/main/res/values-uz/strings.xml +0 -79
  62. package/examples/social-app/generated/android/social-app/app/src/main/xml/AndroidManifest.xml +0 -23
  63. package/examples/social-app/generated/android/social-app/app/src/test/kotlin/com/social/app/screenshots/HomeFeedScreenshotTest.kt +0 -34
  64. package/examples/social-app/generated/android/social-app/build.gradle.kts +0 -7
  65. package/examples/social-app/generated/android/social-app/gradle/libs.versions.toml +0 -50
  66. package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.jar +0 -0
  67. package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.properties +0 -8
  68. package/examples/social-app/generated/android/social-app/gradle.properties +0 -11
  69. package/examples/social-app/generated/android/social-app/gradlew +0 -248
  70. package/examples/social-app/generated/android/social-app/settings.gradle.kts +0 -27
  71. package/examples/social-app/generated/web/social-app/index.html +0 -12
  72. package/examples/social-app/generated/web/social-app/package-lock.json +0 -2517
  73. package/examples/social-app/generated/web/social-app/package.json +0 -27
  74. package/examples/social-app/generated/web/social-app/src/app/App.tsx +0 -58
  75. package/examples/social-app/generated/web/social-app/src/components/Shell.tsx +0 -259
  76. package/examples/social-app/generated/web/social-app/src/components/cards.tsx +0 -317
  77. package/examples/social-app/generated/web/social-app/src/components/ui.tsx +0 -340
  78. package/examples/social-app/generated/web/social-app/src/flows/CreatePostFlow.tsx +0 -86
  79. package/examples/social-app/generated/web/social-app/src/i18n.tsx +0 -59
  80. package/examples/social-app/generated/web/social-app/src/lib/icons.tsx +0 -85
  81. package/examples/social-app/generated/web/social-app/src/lib/tokens.ts +0 -70
  82. package/examples/social-app/generated/web/social-app/src/lib/utils.ts +0 -97
  83. package/examples/social-app/generated/web/social-app/src/locales/en.json +0 -67
  84. package/examples/social-app/generated/web/social-app/src/locales/ru.json +0 -67
  85. package/examples/social-app/generated/web/social-app/src/locales/uz.json +0 -67
  86. package/examples/social-app/generated/web/social-app/src/main.tsx +0 -16
  87. package/examples/social-app/generated/web/social-app/src/screens/ChatDetailScreen.tsx +0 -90
  88. package/examples/social-app/generated/web/social-app/src/screens/DiscoverScreen.tsx +0 -86
  89. package/examples/social-app/generated/web/social-app/src/screens/EditProfileScreen.tsx +0 -57
  90. package/examples/social-app/generated/web/social-app/src/screens/HomeFeedScreen.tsx +0 -103
  91. package/examples/social-app/generated/web/social-app/src/screens/MessagesInboxScreen.tsx +0 -52
  92. package/examples/social-app/generated/web/social-app/src/screens/NotificationsScreen.tsx +0 -41
  93. package/examples/social-app/generated/web/social-app/src/screens/PostDetailScreen.tsx +0 -115
  94. package/examples/social-app/generated/web/social-app/src/screens/ProfileSelfScreen.tsx +0 -57
  95. package/examples/social-app/generated/web/social-app/src/screens/ProfileUserScreen.tsx +0 -76
  96. package/examples/social-app/generated/web/social-app/src/screens/SearchResultsScreen.tsx +0 -96
  97. package/examples/social-app/generated/web/social-app/src/screens/SettingsScreen.tsx +0 -79
  98. package/examples/social-app/generated/web/social-app/src/state/store.ts +0 -592
  99. package/examples/social-app/generated/web/social-app/src/styles.css +0 -125
  100. package/examples/social-app/generated/web/social-app/src/vite-env.d.ts +0 -1
  101. package/examples/social-app/generated/web/social-app/tsconfig.json +0 -22
  102. package/examples/social-app/generated/web/social-app/tsconfig.node.json +0 -13
  103. package/examples/social-app/generated/web/social-app/tsconfig.node.tsbuildinfo +0 -1
  104. package/examples/social-app/generated/web/social-app/tsconfig.tsbuildinfo +0 -1
  105. package/examples/social-app/generated/web/social-app/vite.config.d.ts +0 -2
  106. package/examples/social-app/generated/web/social-app/vite.config.js +0 -6
  107. package/examples/social-app/generated/web/social-app/vite.config.ts +0 -7
  108. package/examples/social-app/package.json +0 -13
  109. package/examples/social-app/take-web-screenshots.ts +0 -97
  110. package/examples/taskflow/.codex/config.toml +0 -4
  111. package/examples/taskflow/.mcp.json +0 -10
  112. package/examples/taskflow/AGENTS.md +0 -124
  113. package/examples/taskflow/CLAUDE.md +0 -124
  114. package/examples/taskflow/backend/.gitkeep +0 -1
  115. package/examples/taskflow/generated/android/TaskFlow/README.md +0 -43
  116. package/examples/taskflow/generated/android/TaskFlow/app/build.gradle.kts +0 -76
  117. package/examples/taskflow/generated/android/TaskFlow/app/proguard-rules.pro +0 -1
  118. package/examples/taskflow/generated/android/TaskFlow/app/src/main/AndroidManifest.xml +0 -21
  119. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/MainActivity.kt +0 -19
  120. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/TaskFlowApp.kt +0 -283
  121. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/DomainModels.kt +0 -106
  122. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/SampleData.kt +0 -57
  123. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/components/Common.kt +0 -109
  124. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/HomeScreen.kt +0 -112
  125. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/ProjectsScreen.kt +0 -61
  126. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/SettingsScreen.kt +0 -82
  127. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/TaskDetailScreen.kt +0 -111
  128. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/sheets/Sheets.kt +0 -77
  129. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Color.kt +0 -30
  130. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Theme.kt +0 -86
  131. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Type.kt +0 -57
  132. package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/strings.xml +0 -155
  133. package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/themes.xml +0 -4
  134. package/examples/taskflow/generated/android/TaskFlow/build.gradle.kts +0 -5
  135. package/examples/taskflow/generated/android/TaskFlow/gradle/gradle-daemon-jvm.properties +0 -12
  136. package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.jar +0 -0
  137. package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.properties +0 -7
  138. package/examples/taskflow/generated/android/TaskFlow/gradle.properties +0 -4
  139. package/examples/taskflow/generated/android/TaskFlow/gradlew +0 -18
  140. package/examples/taskflow/generated/android/TaskFlow/gradlew.bat +0 -12
  141. package/examples/taskflow/generated/android/TaskFlow/settings.gradle.kts +0 -18
  142. package/examples/taskflow/generated/ios/TaskFlow/README.md +0 -21
  143. package/examples/taskflow/generated/ios/TaskFlow/Resources/en.lproj/Localizable.strings +0 -115
  144. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/App/TaskFlowApp.swift +0 -24
  145. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Components/AppChrome.swift +0 -150
  146. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Flows/TaskEditorSheet.swift +0 -220
  147. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Models/DomainModels.swift +0 -122
  148. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/CalendarView.swift +0 -21
  149. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/HomeView.swift +0 -201
  150. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProfileEditView.swift +0 -48
  151. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectDetailView.swift +0 -59
  152. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectsView.swift +0 -63
  153. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/SettingsView.swift +0 -85
  154. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/TaskDetailView.swift +0 -219
  155. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppModel.swift +0 -320
  156. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppSupport.swift +0 -41
  157. package/examples/taskflow/generated/ios/TaskFlow/project.yml +0 -31
  158. package/examples/taskflow/generated/web/TaskFlow/README.md +0 -19
  159. package/examples/taskflow/generated/web/TaskFlow/index.html +0 -12
  160. package/examples/taskflow/generated/web/TaskFlow/package-lock.json +0 -1908
  161. package/examples/taskflow/generated/web/TaskFlow/package.json +0 -24
  162. package/examples/taskflow/generated/web/TaskFlow/src/App.tsx +0 -58
  163. package/examples/taskflow/generated/web/TaskFlow/src/AppShell.tsx +0 -55
  164. package/examples/taskflow/generated/web/TaskFlow/src/components/Common.tsx +0 -82
  165. package/examples/taskflow/generated/web/TaskFlow/src/components/Modals.tsx +0 -191
  166. package/examples/taskflow/generated/web/TaskFlow/src/components/Nav.tsx +0 -41
  167. package/examples/taskflow/generated/web/TaskFlow/src/generated/messages.ts +0 -131
  168. package/examples/taskflow/generated/web/TaskFlow/src/hooks.ts +0 -25
  169. package/examples/taskflow/generated/web/TaskFlow/src/i18n.ts +0 -39
  170. package/examples/taskflow/generated/web/TaskFlow/src/locales.en.json +0 -111
  171. package/examples/taskflow/generated/web/TaskFlow/src/main.tsx +0 -13
  172. package/examples/taskflow/generated/web/TaskFlow/src/screens/HomeScreen.tsx +0 -111
  173. package/examples/taskflow/generated/web/TaskFlow/src/screens/ProjectsScreen.tsx +0 -82
  174. package/examples/taskflow/generated/web/TaskFlow/src/screens/SettingsScreens.tsx +0 -132
  175. package/examples/taskflow/generated/web/TaskFlow/src/screens/TaskDetail.tsx +0 -105
  176. package/examples/taskflow/generated/web/TaskFlow/src/store.ts +0 -216
  177. package/examples/taskflow/generated/web/TaskFlow/src/styles.css +0 -617
  178. package/examples/taskflow/generated/web/TaskFlow/src/types.ts +0 -64
  179. package/examples/taskflow/generated/web/TaskFlow/src/utils.ts +0 -78
  180. package/examples/taskflow/generated/web/TaskFlow/tsconfig.json +0 -21
  181. package/examples/taskflow/generated/web/TaskFlow/vite.config.ts +0 -6
  182. package/examples/todo-orbit/.codex/config.toml +0 -4
  183. package/examples/todo-orbit/.mcp.json +0 -10
  184. package/examples/todo-orbit/AGENTS.md +0 -124
  185. package/examples/todo-orbit/CLAUDE.md +0 -124
  186. package/examples/todo-orbit/backend/.gitkeep +0 -1
  187. package/examples/todo-orbit/generated/android/Todo Orbit/README.md +0 -14
  188. package/examples/todo-orbit/generated/android/Todo Orbit/app/build.gradle.kts +0 -58
  189. package/examples/todo-orbit/generated/android/Todo Orbit/app/proguard-rules.pro +0 -1
  190. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/AndroidManifest.xml +0 -20
  191. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/MainActivity.kt +0 -14
  192. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/TodoOrbitApp.kt +0 -345
  193. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/AppLogic.kt +0 -231
  194. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Models.kt +0 -169
  195. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Strings.kt +0 -8
  196. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/components/CommonComponents.kt +0 -236
  197. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/AnalyticsScreen.kt +0 -193
  198. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/SettingsScreen.kt +0 -102
  199. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/TasksScreen.kt +0 -347
  200. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/sheets/EditorSheets.kt +0 -347
  201. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/theme/TodoOrbitTheme.kt +0 -59
  202. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values/strings.xml +0 -149
  203. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values-ru/strings.xml +0 -155
  204. package/examples/todo-orbit/generated/android/Todo Orbit/build.gradle.kts +0 -4
  205. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.jar +0 -0
  206. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.properties +0 -7
  207. package/examples/todo-orbit/generated/android/Todo Orbit/gradle.properties +0 -4
  208. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew +0 -248
  209. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew.bat +0 -93
  210. package/examples/todo-orbit/generated/android/Todo Orbit/settings.gradle.kts +0 -18
  211. package/examples/todo-orbit/generated/ios/Todo Orbit/.screenshot-uitest/Sources/ScreenshotUITest.swift +0 -36
  212. package/examples/todo-orbit/generated/ios/Todo Orbit/README.md +0 -29
  213. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/en.lproj/Localizable.strings +0 -119
  214. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/ru.lproj/Localizable.strings +0 -119
  215. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/App/TodoOrbitApp.swift +0 -50
  216. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/OrbitChrome.swift +0 -204
  217. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/SchedulePreviewView.swift +0 -126
  218. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/TrendChartView.swift +0 -70
  219. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/RecurringRuleSheet.swift +0 -126
  220. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/TaskEditorSheet.swift +0 -61
  221. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Models/DomainModels.swift +0 -238
  222. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/AnalyticsView.swift +0 -94
  223. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/SettingsView.swift +0 -76
  224. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/TasksHomeView.swift +0 -364
  225. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Support/AppModel.swift +0 -324
  226. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.pbxproj +0 -439
  227. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  228. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/xcshareddata/xcschemes/TodoOrbit.xcscheme +0 -89
  229. package/examples/todo-orbit/generated/ios/Todo Orbit/project.yml +0 -32
  230. package/examples/todo-orbit/generated/web/Todo Orbit/index.html +0 -16
  231. package/examples/todo-orbit/generated/web/Todo Orbit/package-lock.json +0 -1087
  232. package/examples/todo-orbit/generated/web/Todo Orbit/package.json +0 -24
  233. package/examples/todo-orbit/generated/web/Todo Orbit/src/App.tsx +0 -2167
  234. package/examples/todo-orbit/generated/web/Todo Orbit/src/main.tsx +0 -13
  235. package/examples/todo-orbit/generated/web/Todo Orbit/src/styles.css +0 -926
  236. package/examples/todo-orbit/generated/web/Todo Orbit/tsconfig.json +0 -19
  237. package/examples/todo-orbit/generated/web/Todo Orbit/vite.config.ts +0 -6
package/prepare/index.ts CHANGED
@@ -21,10 +21,17 @@ import {
21
21
  readManifest,
22
22
  readProjectName,
23
23
  readStatus,
24
+ hasDriftChanges,
25
+ readSharedLayerState,
26
+ readTargetStructure,
24
27
  resolveOutputDir,
28
+ sharedLayersForTarget,
29
+ specCategory,
30
+ computeSharedDrift,
25
31
  type FileExplanation,
26
32
  type ExplainResult,
27
33
  type SemanticChange,
34
+ type SharedLayerConfig,
28
35
  } from "../drift/index.js";
29
36
 
30
37
  interface PrepareItem {
@@ -116,6 +123,20 @@ interface PrepareBootstrapBundle {
116
123
  reference_examples: string[];
117
124
  }
118
125
 
126
+ interface SharedLayerInfo {
127
+ name: string;
128
+ platforms: string[];
129
+ language: string;
130
+ root: string;
131
+ paths: Record<string, string>;
132
+ scope: string;
133
+ tracks: string[];
134
+ already_generated: boolean;
135
+ generated_by_target: string | null;
136
+ has_drift: boolean;
137
+ guidance: string;
138
+ }
139
+
119
140
  interface SpecFileContent {
120
141
  path: string;
121
142
  category: string;
@@ -135,6 +156,11 @@ export interface PrepareResult {
135
156
  commit: string | null;
136
157
  branch: string | null;
137
158
  };
159
+ baseline_status?: {
160
+ output_exists: boolean;
161
+ snapshot_exists: boolean;
162
+ action_needed: string | null;
163
+ };
138
164
  summary: {
139
165
  changed: number;
140
166
  added: number;
@@ -143,6 +169,7 @@ export interface PrepareResult {
143
169
  changes_available: boolean;
144
170
  explanation_note?: string;
145
171
  items: PrepareItem[];
172
+ shared_layers?: SharedLayerInfo[];
146
173
  bootstrap?: PrepareBootstrapBundle;
147
174
  spec_contents?: SpecFileContent[];
148
175
  next_steps: string[];
@@ -316,13 +343,88 @@ function resolveBackendRoot(projectDir: string, manifest: Record<string, any>):
316
343
  return resolve(projectDir, backendRoot);
317
344
  }
318
345
 
319
- function categorizeSpecFile(relPath: string): string {
320
- if (relPath === "openuispec.yaml") return "manifest";
321
- const group = relPath.split("/")[0];
322
- return group || "other";
346
+ // Use specCategory from drift/index.ts (imported above) instead of a local duplicate.
347
+ const categorizeSpecFile = specCategory;
348
+
349
+ function buildSharedLayerInfos(projectDir: string, target: string, layers?: SharedLayerConfig[]): SharedLayerInfo[] {
350
+ const resolvedLayers = layers ?? sharedLayersForTarget(projectDir, target);
351
+ if (resolvedLayers.length === 0) return [];
352
+
353
+ return resolvedLayers.map((layer) => {
354
+ const state = readSharedLayerState(layer);
355
+ const alreadyGenerated = state !== null;
356
+
357
+ // Only compute hash-based drift when tracks are configured
358
+ let hasDrift = false;
359
+ if (layer.tracks.length > 0) {
360
+ const driftResult = computeSharedDrift(projectDir, layer);
361
+ hasDrift = driftResult.state !== null && hasDriftChanges(driftResult.drift);
362
+ }
363
+
364
+ let guidance: string;
365
+ if (alreadyGenerated && !hasDrift) {
366
+ guidance = `Shared code already generated by ${state!.generated_by_target} — read existing code, don't regenerate.`;
367
+ } else if (alreadyGenerated && hasDrift) {
368
+ guidance = `Generated by ${state!.generated_by_target} but spec has drifted — review shared code for needed updates.`;
369
+ } else {
370
+ guidance = `Generate shared layer alongside ${target} platform code.`;
371
+ }
372
+
373
+ return {
374
+ name: layer.name,
375
+ platforms: layer.platforms,
376
+ language: layer.language,
377
+ root: layer.root,
378
+ paths: layer.paths,
379
+ scope: layer.scope,
380
+ tracks: layer.tracks,
381
+ already_generated: alreadyGenerated,
382
+ generated_by_target: state?.generated_by_target ?? null,
383
+ has_drift: hasDrift,
384
+ guidance,
385
+ };
386
+ });
387
+ }
388
+
389
+ function collectSharedLayerPaths(layers: SharedLayerConfig[]): string[] {
390
+ const paths: string[] = [];
391
+ for (const layer of layers) {
392
+ paths.push(layer.root);
393
+ for (const p of Object.values(layer.paths)) {
394
+ paths.push(resolve(layer.root, p));
395
+ }
396
+ }
397
+ return paths;
398
+ }
399
+
400
+ function dedupeExistingPaths(candidates: string[]): string[] {
401
+ const seen = new Set<string>();
402
+ return candidates
403
+ .map((c) => resolve(c))
404
+ .filter((c) => existsSync(c))
405
+ .filter((c) => {
406
+ if (seen.has(c)) return false;
407
+ seen.add(c);
408
+ return true;
409
+ });
323
410
  }
324
411
 
325
- function suggestCodeRoots(target: string, outputDir: string): string[] {
412
+ function suggestCodeRoots(target: string, outputDir: string, projectDir?: string, sharedLayers?: SharedLayerConfig[]): string[] {
413
+ const layers = sharedLayers ?? (projectDir ? sharedLayersForTarget(projectDir, target) : []);
414
+
415
+ // When generation.structure is defined for the target, use it instead of heuristics
416
+ if (projectDir) {
417
+ const structure = readTargetStructure(projectDir, target);
418
+ if (structure) {
419
+ const candidates = [
420
+ structure.root,
421
+ ...Object.values(structure.paths).map((p) => resolve(structure.root, p)),
422
+ ...collectSharedLayerPaths(layers),
423
+ ];
424
+ return dedupeExistingPaths(candidates);
425
+ }
426
+ }
427
+
326
428
  const candidates: string[] = [];
327
429
 
328
430
  if (target === "web") {
@@ -340,15 +442,8 @@ function suggestCodeRoots(target: string, outputDir: string): string[] {
340
442
  candidates.push(outputDir);
341
443
  }
342
444
 
343
- const seen = new Set<string>();
344
- return candidates
345
- .map((candidate) => resolve(candidate))
346
- .filter((candidate) => existsSync(candidate))
347
- .filter((candidate) => {
348
- if (seen.has(candidate)) return false;
349
- seen.add(candidate);
350
- return true;
351
- });
445
+ candidates.push(...collectSharedLayerPaths(layers));
446
+ return dedupeExistingPaths(candidates);
352
447
  }
353
448
 
354
449
  function walkFiles(root: string, files: string[], depth = 0): void {
@@ -506,7 +601,7 @@ function buildBootstrapNotes(category: string, target: string, specStatus: strin
506
601
  return notes;
507
602
  }
508
603
 
509
- function generationRules(target: string, outputDir: string, manifest: Record<string, any>): string[] {
604
+ function generationRules(target: string, outputDir: string, manifest: Record<string, any>, sharedLayers?: SharedLayerInfo[]): string[] {
510
605
  const outputFormat = manifest.generation?.output_format?.[target] ?? {};
511
606
  const rules = [
512
607
  "Read openuispec.yaml first, then follow the referenced spec files instead of inventing structure from memory.",
@@ -523,6 +618,31 @@ function generationRules(target: string, outputDir: string, manifest: Record<str
523
618
  );
524
619
  }
525
620
 
621
+ if (sharedLayers && sharedLayers.length > 0) {
622
+ for (const layer of sharedLayers) {
623
+ if (layer.already_generated) {
624
+ rules.push(
625
+ `Shared layer "${layer.name}" already generated by ${layer.generated_by_target}. Read the existing code under ${layer.root} instead of regenerating it.`
626
+ );
627
+ } else {
628
+ rules.push(
629
+ `Generate shared layer "${layer.name}" (${layer.language}) under ${layer.root} alongside ${target} platform code.`
630
+ );
631
+ }
632
+ if (layer.scope) {
633
+ rules.push(
634
+ `Shared layer "${layer.name}" scope: ${layer.scope}`
635
+ );
636
+ }
637
+ }
638
+ }
639
+
640
+ // Include target structure scope when defined
641
+ const structure = manifest.generation?.structure?.[target];
642
+ if (structure?.scope) {
643
+ rules.push(`Target "${target}" scope: ${structure.scope}`);
644
+ }
645
+
526
646
  return rules;
527
647
  }
528
648
 
@@ -1065,8 +1185,10 @@ function buildBootstrapPrepareResult(cwd: string, target: string, includeContent
1065
1185
  const platformDef = readPlatformDefinition(projectDir, manifest, target);
1066
1186
  const platformConfig = buildPlatformConfig(target, platformDef);
1067
1187
  const outputFormat = manifest.generation?.output_format?.[target] ?? {};
1068
- const codeRoots = suggestCodeRoots(target, outputDir);
1188
+ const sharedLayerConfigs = sharedLayersForTarget(projectDir, target);
1189
+ const codeRoots = suggestCodeRoots(target, outputDir, projectDir, sharedLayerConfigs);
1069
1190
  const missingDecisions = missingPlatformDecisions(target, platformDef);
1191
+ const sharedLayerInfos = buildSharedLayerInfos(projectDir, target, sharedLayerConfigs);
1070
1192
  const backendRoot = resolveBackendRoot(projectDir, manifest);
1071
1193
  const backendContextReady = true; // backend is optional — not a generation blocker
1072
1194
  const pendingUserConfirmation = platformConfig.stack_confirmation.requires_user_confirmation;
@@ -1103,6 +1225,10 @@ function buildBootstrapPrepareResult(cwd: string, target: string, includeContent
1103
1225
  );
1104
1226
  }
1105
1227
 
1228
+ const outputDirExists = existsSync(outputDir);
1229
+ const snapshotPath = join(outputDir, ".openuispec-state.json");
1230
+ const snapshotFileExists = existsSync(snapshotPath);
1231
+
1106
1232
  return {
1107
1233
  mode: "bootstrap",
1108
1234
  project: projectName,
@@ -1116,6 +1242,13 @@ function buildBootstrapPrepareResult(cwd: string, target: string, includeContent
1116
1242
  commit: null,
1117
1243
  branch: null,
1118
1244
  },
1245
+ baseline_status: {
1246
+ output_exists: outputDirExists,
1247
+ snapshot_exists: snapshotFileExists,
1248
+ action_needed: outputDirExists && !snapshotFileExists
1249
+ ? `Baseline pending — when satisfied with the generated output, run: openuispec drift --snapshot --target ${target}`
1250
+ : null,
1251
+ },
1119
1252
  summary: {
1120
1253
  changed: 0,
1121
1254
  added: 0,
@@ -1124,6 +1257,7 @@ function buildBootstrapPrepareResult(cwd: string, target: string, includeContent
1124
1257
  changes_available: false,
1125
1258
  explanation_note: "No snapshot exists yet. This is a first-time generation bundle.",
1126
1259
  items: [],
1260
+ ...(sharedLayerInfos.length > 0 ? { shared_layers: sharedLayerInfos } : {}),
1127
1261
  ...(includeContents ? { spec_contents: readAllSpecContents(projectDir) } : {}),
1128
1262
  bootstrap: {
1129
1263
  output_exists: existsSync(outputDir),
@@ -1141,7 +1275,7 @@ function buildBootstrapPrepareResult(cwd: string, target: string, includeContent
1141
1275
  supported_locales: manifest.i18n?.supported_locales ?? [],
1142
1276
  },
1143
1277
  spec_files: bootstrapSpecFiles(projectDir, target),
1144
- generation_rules: generationRules(target, outputDir, manifest),
1278
+ generation_rules: generationRules(target, outputDir, manifest, sharedLayerInfos),
1145
1279
  generation_constraints: generationConstraints(target, platformConfig),
1146
1280
  generation_warnings: generationWarnings(target, platformConfig),
1147
1281
  reference_examples: referenceExamples(),
@@ -1153,8 +1287,10 @@ function buildBootstrapPrepareResult(cwd: string, target: string, includeContent
1153
1287
  function buildUpdatePrepareResult(cwd: string, target: string, includeContents: boolean = false): PrepareResult {
1154
1288
  const { projectDir, projectName, result } = loadTargetDrift(cwd, target, false, true);
1155
1289
  const outputDir = resolveOutputDir(projectDir, projectName, target);
1156
- const codeRoots = suggestCodeRoots(target, outputDir);
1290
+ const sharedLayerConfigs = sharedLayersForTarget(projectDir, target);
1291
+ const codeRoots = suggestCodeRoots(target, outputDir, projectDir, sharedLayerConfigs);
1157
1292
  const manifest = readManifest(projectDir);
1293
+ const sharedLayerInfos = buildSharedLayerInfos(projectDir, target, sharedLayerConfigs);
1158
1294
  const platformDef = readPlatformDefinition(projectDir, manifest, target);
1159
1295
  const platformConfig = buildPlatformConfig(target, platformDef);
1160
1296
  const outputFormat = manifest.generation?.output_format?.[target] ?? {};
@@ -1195,6 +1331,7 @@ function buildUpdatePrepareResult(cwd: string, target: string, includeContents:
1195
1331
  changes_available: result.explanation?.available ?? false,
1196
1332
  explanation_note: result.explanation?.note,
1197
1333
  items,
1334
+ ...(sharedLayerInfos.length > 0 ? { shared_layers: sharedLayerInfos } : {}),
1198
1335
  ...(includeContents ? { spec_contents: readAllSpecContents(projectDir) } : {}),
1199
1336
  next_steps: nextSteps,
1200
1337
  };
@@ -152,6 +152,65 @@
152
152
  },
153
153
  "additionalProperties": true
154
154
  }
155
+ },
156
+ "shared": {
157
+ "type": "object",
158
+ "description": "Named shared code layers that span multiple targets (e.g. KMP common modules)",
159
+ "additionalProperties": {
160
+ "type": "object",
161
+ "properties": {
162
+ "platforms": {
163
+ "type": "array",
164
+ "items": { "type": "string" }
165
+ },
166
+ "language": {
167
+ "type": "string"
168
+ },
169
+ "root": {
170
+ "type": "string"
171
+ },
172
+ "paths": {
173
+ "type": "object",
174
+ "additionalProperties": { "type": "string" }
175
+ },
176
+ "tracks": {
177
+ "type": "array",
178
+ "description": "Spec categories this shared layer tracks for drift. Must be explicit — choose which categories actually affect the shared code.",
179
+ "items": {
180
+ "type": "string",
181
+ "enum": ["manifest", "tokens", "contracts", "screens", "flows", "platform", "locales"]
182
+ }
183
+ },
184
+ "scope": {
185
+ "type": "string",
186
+ "description": "What code belongs in this shared layer (e.g. 'Business logic, data models, repositories, API clients, view models. No UI rendering.')"
187
+ }
188
+ },
189
+ "required": ["platforms", "language", "root", "scope"],
190
+ "additionalProperties": false
191
+ }
192
+ },
193
+ "structure": {
194
+ "type": "object",
195
+ "description": "Per-target output directory structure (overrides heuristic code root discovery)",
196
+ "additionalProperties": {
197
+ "type": "object",
198
+ "properties": {
199
+ "root": {
200
+ "type": "string"
201
+ },
202
+ "paths": {
203
+ "type": "object",
204
+ "additionalProperties": { "type": "string" }
205
+ },
206
+ "scope": {
207
+ "type": "string",
208
+ "description": "What code belongs in this target directory (e.g. 'Pure SwiftUI views and navigation. All business logic comes from the shared layer.')"
209
+ }
210
+ },
211
+ "required": ["root"],
212
+ "additionalProperties": false
213
+ }
155
214
  }
156
215
  },
157
216
  "required": [
@@ -5,6 +5,23 @@ import { listFiles, readManifest } from "../drift/index.js";
5
5
 
6
6
  type UnknownRecord = Record<string, unknown>;
7
7
 
8
+ /** Collect all locale keys from a JSON object, supporting both flat dotted keys and nested objects. */
9
+ function collectLocaleKeys(data: unknown, prefix = ""): string[] {
10
+ if (!data || typeof data !== "object" || Array.isArray(data)) return [];
11
+ const keys: string[] = [];
12
+ for (const [key, value] of Object.entries(data as UnknownRecord)) {
13
+ if (key.startsWith("$")) continue; // skip $locale, $direction
14
+ const fullKey = prefix ? `${prefix}.${key}` : key;
15
+ if (typeof value === "string") {
16
+ keys.push(fullKey);
17
+ } else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
18
+ // Nested object — recurse to flatten
19
+ keys.push(...collectLocaleKeys(value, fullKey));
20
+ }
21
+ }
22
+ return keys;
23
+ }
24
+
8
25
  export interface Includes {
9
26
  tokens: string;
10
27
  contracts: string;
@@ -212,7 +229,14 @@ function buildContext(projectDir: string, includes: Includes, manifest: UnknownR
212
229
  for (const filePath of listFiles(localeDir, ".json")) {
213
230
  const localeName = basename(filePath, ".json");
214
231
  const data = loadJson(filePath);
215
- localeFiles.set(localeName, new Set(Object.keys(data)));
232
+ // Support both flat dotted keys ("nav.home": "Home") and nested objects ({ nav: { home: "Home" } })
233
+ const flatKeys = Object.keys(data as object).filter(k => !k.startsWith("$"));
234
+ const hasNestedObjects = flatKeys.some(k => {
235
+ const v = (data as UnknownRecord)[k];
236
+ return typeof v === "object" && v !== null && !Array.isArray(v);
237
+ });
238
+ const allKeys = hasNestedObjects ? collectLocaleKeys(data) : flatKeys;
239
+ localeFiles.set(localeName, new Set(allKeys));
216
240
  }
217
241
 
218
242
  const formatterNames = new Set<string>([