create-cmp-cli 0.2.0

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 (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +232 -0
  3. package/bin/create-cmp.mjs +91 -0
  4. package/options.schema.json +110 -0
  5. package/package.json +52 -0
  6. package/src/bootstrap/checks.mjs +283 -0
  7. package/src/bootstrap/exec.mjs +90 -0
  8. package/src/commands/clean.mjs +195 -0
  9. package/src/commands/create.mjs +223 -0
  10. package/src/commands/doctor.mjs +256 -0
  11. package/src/commands/upgrade.mjs +196 -0
  12. package/src/commands/verify.mjs +94 -0
  13. package/src/doctor.mjs +109 -0
  14. package/src/lib/args.mjs +38 -0
  15. package/src/lib/clean.mjs +72 -0
  16. package/src/lib/fsutil.mjs +90 -0
  17. package/src/lib/log.mjs +40 -0
  18. package/src/lib/project-doctor.mjs +329 -0
  19. package/src/lib/registry.mjs +102 -0
  20. package/src/lib/rename.mjs +116 -0
  21. package/src/lib/schema.mjs +123 -0
  22. package/src/lib/toggle.mjs +130 -0
  23. package/src/lib/tokens.mjs +96 -0
  24. package/src/lib/toml.mjs +137 -0
  25. package/src/lib/upgrade.mjs +159 -0
  26. package/src/lib/verify.mjs +97 -0
  27. package/src/scaffold.mjs +347 -0
  28. package/src/verify.mjs +4 -0
  29. package/src/versions/registry.json +42 -0
  30. package/template/.github/workflows/verify.yml +70 -0
  31. package/template/build.gradle.kts +17 -0
  32. package/template/composeApp/build.gradle.kts +257 -0
  33. package/template/composeApp/google-services.json +29 -0
  34. package/template/composeApp/proguard-rules.pro +34 -0
  35. package/template/composeApp/src/androidDebug/AndroidManifest.xml +13 -0
  36. package/template/composeApp/src/androidDebug/kotlin/com/example/app/inspector/ComposeRootRegistry.kt +40 -0
  37. package/template/composeApp/src/androidDebug/kotlin/com/example/app/inspector/InspectorCatalog.kt +80 -0
  38. package/template/composeApp/src/androidDebug/kotlin/com/example/app/inspector/InspectorHttpServer.kt +322 -0
  39. package/template/composeApp/src/androidDebug/kotlin/com/example/app/inspector/InspectorInit.kt +16 -0
  40. package/template/composeApp/src/androidDebug/kotlin/com/example/app/inspector/LiveSemanticsJson.kt +102 -0
  41. package/template/composeApp/src/androidDebug/kotlin/com/example/app/inspector/RemoteControlPage.kt +106 -0
  42. package/template/composeApp/src/androidDebug/res/xml/debug_network_security_config.xml +9 -0
  43. package/template/composeApp/src/androidMain/AndroidManifest.xml +28 -0
  44. package/template/composeApp/src/androidMain/kotlin/com/example/app/AppApplication.kt +72 -0
  45. package/template/composeApp/src/androidMain/kotlin/com/example/app/MainActivity.kt +24 -0
  46. package/template/composeApp/src/androidMain/kotlin/com/example/app/core/connectivity/NetworkMonitor.kt +42 -0
  47. package/template/composeApp/src/androidMain/kotlin/com/example/app/data/local/DatabaseBuilder.android.kt +13 -0
  48. package/template/composeApp/src/androidMain/kotlin/com/example/app/di/AndroidModule.kt +9 -0
  49. package/template/composeApp/src/androidMain/kotlin/com/example/app/presentation/components/TestTagAutomation.android.kt +11 -0
  50. package/template/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml +9 -0
  51. package/template/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
  52. package/template/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
  53. package/template/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png +0 -0
  54. package/template/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
  55. package/template/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  56. package/template/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png +0 -0
  57. package/template/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
  58. package/template/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  59. package/template/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png +0 -0
  60. package/template/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
  61. package/template/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  62. package/template/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  63. package/template/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
  64. package/template/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  65. package/template/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  66. package/template/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
  67. package/template/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  68. package/template/composeApp/src/androidMain/res/values/colors.xml +6 -0
  69. package/template/composeApp/src/androidMain/res/values/themes.xml +9 -0
  70. package/template/composeApp/src/androidMain/res/values-v31/themes.xml +8 -0
  71. package/template/composeApp/src/androidRelease/kotlin/com/example/app/inspector/InspectorInit.kt +13 -0
  72. package/template/composeApp/src/commonMain/composeResources/font/DMSans_Bold.ttf +0 -0
  73. package/template/composeApp/src/commonMain/composeResources/font/DMSans_Medium.ttf +0 -0
  74. package/template/composeApp/src/commonMain/composeResources/font/DMSans_Regular.ttf +0 -0
  75. package/template/composeApp/src/commonMain/composeResources/font/DMSans_SemiBold.ttf +0 -0
  76. package/template/composeApp/src/commonMain/kotlin/com/example/app/App.kt +7 -0
  77. package/template/composeApp/src/commonMain/kotlin/com/example/app/core/connectivity/NetworkMonitor.kt +7 -0
  78. package/template/composeApp/src/commonMain/kotlin/com/example/app/data/local/AppDatabase.kt +28 -0
  79. package/template/composeApp/src/commonMain/kotlin/com/example/app/data/local/DatabaseBuilder.kt +14 -0
  80. package/template/composeApp/src/commonMain/kotlin/com/example/app/data/local/ItemDao.kt +18 -0
  81. package/template/composeApp/src/commonMain/kotlin/com/example/app/data/remote/FirebaseConfig.kt +5 -0
  82. package/template/composeApp/src/commonMain/kotlin/com/example/app/data/remote/ItemRepositoryImpl.kt +22 -0
  83. package/template/composeApp/src/commonMain/kotlin/com/example/app/di/AppModule.kt +23 -0
  84. package/template/composeApp/src/commonMain/kotlin/com/example/app/domain/model/Item.kt +8 -0
  85. package/template/composeApp/src/commonMain/kotlin/com/example/app/domain/repository/ItemRepository.kt +8 -0
  86. package/template/composeApp/src/commonMain/kotlin/com/example/app/domain/usecase/GetItemsUseCase.kt +12 -0
  87. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/App.kt +13 -0
  88. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/components/BaseScreen.kt +69 -0
  89. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/components/TestTagAutomation.kt +18 -0
  90. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/home/DetailScreen.kt +39 -0
  91. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/home/HomeScreen.kt +87 -0
  92. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/home/HomeViewModel.kt +35 -0
  93. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/navigation/AppNavHost.kt +43 -0
  94. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/navigation/AppShell.kt +144 -0
  95. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/navigation/AppTab.kt +25 -0
  96. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/navigation/Screen.kt +15 -0
  97. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/profile/ProfileScreen.kt +43 -0
  98. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/theme/DesignToken.kt +25 -0
  99. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/theme/Shape.kt +12 -0
  100. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/theme/Theme.kt +64 -0
  101. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/theme/Tokens.kt +19 -0
  102. package/template/composeApp/src/commonMain/kotlin/com/example/app/presentation/theme/Typography.kt +36 -0
  103. package/template/composeApp/src/desktopMain/kotlin/com/example/app/core/connectivity/NetworkMonitor.desktop.kt +9 -0
  104. package/template/composeApp/src/desktopMain/kotlin/com/example/app/data/local/DatabaseBuilder.desktop.kt +13 -0
  105. package/template/composeApp/src/desktopMain/kotlin/com/example/app/di/DesktopModule.kt +32 -0
  106. package/template/composeApp/src/desktopMain/kotlin/com/example/app/main.kt +29 -0
  107. package/template/composeApp/src/desktopMain/kotlin/com/example/app/presentation/components/TestTagAutomation.desktop.kt +6 -0
  108. package/template/composeApp/src/iosMain/kotlin/com/example/app/KoinHelper.kt +50 -0
  109. package/template/composeApp/src/iosMain/kotlin/com/example/app/MainViewController.kt +10 -0
  110. package/template/composeApp/src/iosMain/kotlin/com/example/app/core/connectivity/NetworkMonitor.kt +32 -0
  111. package/template/composeApp/src/iosMain/kotlin/com/example/app/data/local/DatabaseBuilder.ios.kt +10 -0
  112. package/template/composeApp/src/iosMain/kotlin/com/example/app/presentation/components/TestTagAutomation.ios.kt +11 -0
  113. package/template/docs/dev-client.md +52 -0
  114. package/template/gitignore +21 -0
  115. package/template/gradle/libs.versions.toml +100 -0
  116. package/template/gradle/wrapper/gradle-wrapper.jar +0 -0
  117. package/template/gradle/wrapper/gradle-wrapper.properties +7 -0
  118. package/template/gradle.properties +9 -0
  119. package/template/gradlew +152 -0
  120. package/template/gradlew.bat +94 -0
  121. package/template/iosApp/Podfile +27 -0
  122. package/template/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png +0 -0
  123. package/template/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json +14 -0
  124. package/template/iosApp/iosApp/Assets.xcassets/Contents.json +6 -0
  125. package/template/iosApp/iosApp/ContentView.swift +17 -0
  126. package/template/iosApp/iosApp/GoogleService-Info.plist +34 -0
  127. package/template/iosApp/iosApp/Info.plist +43 -0
  128. package/template/iosApp/iosApp/PrivacyInfo.xcprivacy +42 -0
  129. package/template/iosApp/iosApp/iOSApp.swift +33 -0
  130. package/template/iosApp/project.yml +56 -0
  131. package/template/local.properties.example +2 -0
  132. package/template/manifest.json +125 -0
  133. package/template/qa/appium/README.md +23 -0
  134. package/template/qa/appium/lib/appium-client.mjs +225 -0
  135. package/template/qa/appium/package.json +8 -0
  136. package/template/qa/appium/run-android-smoke.mjs +39 -0
  137. package/template/settings.gradle.kts +34 -0
  138. package/template/tests/appium/cmp/conftest.py +96 -0
  139. package/template/tests/appium/cmp/test_smoke.py +17 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Karel van der Merwe and create-cmp contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,232 @@
1
+ <div align="center">
2
+
3
+ # create-cmp
4
+
5
+ **Scaffold a green-building Kotlin/Compose Multiplatform app (Android + iOS) in minutes — not hours.**
6
+
7
+ Toolchain auto-bootstrapped · Navigation & insets pre-solved · Clean Architecture wired · Appium harness ready.
8
+
9
+ [![CI](https://github.com/kvdm-co-pilot/create-cmp/actions/workflows/ci.yml/badge.svg)](https://github.com/kvdm-co-pilot/create-cmp/actions/workflows/ci.yml)
10
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
11
+ [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](./CONTRIBUTING.md)
12
+ [![Kotlin Multiplatform](https://img.shields.io/badge/Kotlin-Multiplatform-7F52FF.svg?logo=kotlin&logoColor=white)](https://kotlinlang.org/docs/multiplatform.html)
13
+ [![Claude Code plugin](https://img.shields.io/badge/Claude%20Code-plugin-D97757.svg)](#use-it-from-claude-code)
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ Starting a Compose Multiplatform project is slow and flaky — not because the knowledge is hard, but
20
+ because it isn't *reproducible*. Kotlin ↔ KSP ↔ CMP ↔ Room ↔ AGP must all agree or the build dies;
21
+ the iOS shell (XcodeGen + CocoaPods + Firebase pods + AppDelegate + framework embedding) is a
22
+ multi-step minefield; edge-to-edge insets get re-debugged on every project; and the toolchain itself
23
+ is a cliff most scaffolders leave to you. That friction is why greenfield mobile apps quietly default
24
+ to React Native.
25
+
26
+ **`create-cmp` removes the friction.** It *stamps* a frozen, CI-verified golden template (it does not
27
+ freehand-generate your project), bootstraps the toolchain, and **proves a green build** before it
28
+ reports success.
29
+
30
+ ## Contents
31
+
32
+ - [Quick start](#quick-start)
33
+ - [Commands](#commands)
34
+ - [What you get](#what-you-get-the-pre-solved-moat)
35
+ - [Options](#options)
36
+ - [Use it from Claude Code](#use-it-from-claude-code)
37
+ - [Why CMP, not React Native](#why-cmp-not-react-native)
38
+ - [How it works](#how-it-works)
39
+ - [Requirements](#requirements)
40
+ - [Roadmap](#roadmap)
41
+ - [Contributing](#contributing)
42
+
43
+ > **Full usage in one place:** [`docs/USAGE.md`](./docs/USAGE.md) is the single entry point — setup,
44
+ > every CLI command, all 8 skills, the 14-tool `cmp-inspector` MCP, and the workflows that tie them
45
+ > together. Read it and you can drive create-cmp end to end (agents included).
46
+
47
+ ## Quick start
48
+
49
+ ```bash
50
+ npx create-cmp-cli@latest
51
+ ```
52
+
53
+ …or non-interactively:
54
+
55
+ ```bash
56
+ npx create-cmp-cli@latest --name Acme --package com.acme.app --yes --verify
57
+ ```
58
+
59
+ The scaffolder interviews you (or takes flags), runs the toolchain doctor, stamps the template, and
60
+ builds the app to prove it's green.
61
+
62
+ > **On the package name:** it publishes as `create-cmp-cli` — the bare name `create-cmp` is held by
63
+ > an unrelated placeholder, and `create-cmp-app` is a real, unrelated CMP generator, so we didn't
64
+ > reuse either. The installed *command* is still `create-cmp`. You can also run straight from GitHub
65
+ > with no install: `npx github:kvdm-co-pilot/create-cmp`, or use the
66
+ > [Claude Code plugin](#use-it-from-claude-code).
67
+
68
+ ## Commands
69
+
70
+ `create-cmp` is useful across the whole life of a project, not just day one — and every command
71
+ except `create` works on **any** KMP project, not only ones it scaffolded:
72
+
73
+ | Command | What it does |
74
+ |---|---|
75
+ | `create-cmp [dir]` / `create-cmp create` | Scaffold a new app from the frozen golden template (the default command). |
76
+ | `create-cmp doctor [--fix]` | Toolchain preflight (JDK/SDK/Xcode/Appium, consent-gated installs) **plus** project diagnosis when run inside a Gradle project: kotlin↔ksp lockstep, drift vs proven-green sets, the KSP2/iOS catch-22, sdk.dir, `~/.konan` bloat, disk space. `--fix` applies the safe heals. |
77
+ | `create-cmp upgrade [--dry-run]` | Migrate `gradle/libs.versions.toml` to the next proven-green version set — diff table first, surgical in-place edits with `.bak-upgrade` backups, kotlin↔ksp lockstep guardrail, `--verify` to prove the result. |
78
+ | `create-cmp clean` | Cache & build-output hygiene: stale `~/.konan` toolchains + project `build/`/`.gradle/` dirs (sizes shown, consent-gated); `~/.gradle/caches` is size-reported only. |
79
+ | `create-cmp verify [--target-dir .]` | Run the green-build gate (Android, and iOS on macOS) against an existing project. |
80
+
81
+ > **North-star (a goal, measured honestly — not a benchmark):** *time-to-green* — a running app on
82
+ > the Android emulator **and** the iOS simulator, smoke-passing, with zero manual steps modulo the
83
+ > one Xcode App Store install. Target: **under 5 minutes on a clean machine.** Every scaffold ends
84
+ > with a `--verify` build gate, so success is *proven*, not assumed.
85
+
86
+ ## What you get (the pre-solved moat)
87
+
88
+ Everything below is **stamped from a template that builds green on Android + iOS** — not regenerated
89
+ per run, so it can't silently drift:
90
+
91
+ - **A pinned, version-locked dependency set** — Kotlin, KSP, Compose Multiplatform, Room, AGP, Koin,
92
+ Ktor, GitLive Firebase KMP, Navigation Compose, Lifecycle — chosen to actually agree with each
93
+ other, including the iOS Room/KSP2 catch-22 (`ksp.useKSP2=true`). Frozen and CI-gated.
94
+ - **The iOS shell, in the order that builds** — `project.yml` (XcodeGen), `Podfile` (Firebase static
95
+ linkage), `Info.plist`, `iOSApp.swift` (AppDelegate + `FirebaseApp.configure()` before Koin),
96
+ `ContentView.swift` (ComposeUIViewController bridge).
97
+ - **The Android shell** — `AndroidManifest`, `MainActivity` with `enableEdgeToEdge()`, `Application`
98
+ starting Koin + Room, adaptive-icon structure.
99
+ - **Navigation + insets, solved once** — a generic `BaseScreen` Scaffold owning window insets /
100
+ status- and nav-bar padding, plus a data-driven `AppShell` + bottom nav fed by your `tabs` list
101
+ (not role-hardcoded), with type-safe routes.
102
+ - **Clean Architecture, wired** — `core / data{local,remote} / domain{model,repository,usecase} /
103
+ presentation{components,theme,navigation,<feature>} / di` with Koin modules registered and **one
104
+ example feature wired end-to-end** as the copy-paste pattern.
105
+ - **Theme & tokens** — `<Prefix>Theme`, `<Prefix>Tokens`, `<Prefix>Colors`, DM Sans.
106
+ - **An Appium harness** — an Appium client + smoke runner and a sample smoke asserting the bottom nav
107
+ renders, so "done" can be proven on a device.
108
+ - **A toolchain doctor** — diagnoses *and* heals JDK, Android SDK + AVD, Xcode/CLT, CocoaPods,
109
+ XcodeGen, Appium + drivers, Node. Idempotent, OS-aware, consent-gated.
110
+ - **AI-inspectable by default** — every generated app ships a debug-only live inspector
111
+ (`127.0.0.1:9500`, loopback, structurally absent from release). An agent runs `connect_live` and
112
+ reads the running UI as JSON — hierarchy, geometry, resolved design tokens, real navigation
113
+ state. Never screenshots. Toggle with `--no-inspector`; `create-cmp doctor` verifies it stays
114
+ debug-only and that every declared theme token is served on `/inspect/design-system`.
115
+ - **Live device view** — with the debug app running, open `http://127.0.0.1:9500/inspect/remote`
116
+ (after `adb forward tcp:9500 tcp:9500`) to watch the real device and click the image to tap it,
117
+ while the agent drives `navigate_and_inspect` on the semantics tree and proves every change with
118
+ `prove_change`. Pixels for the human, structure for the AI.
119
+ - **Desktop dev-client** — a phone-sized JVM window running the shared UI with Compose Hot Reload
120
+ attached (`./gradlew :composeApp:hotRunDesktop --auto`); Firebase never initializes on desktop
121
+ (offline DI fakes). Toggle with `--no-dev-client`.
122
+
123
+ `google-services.json` / `GoogleService-Info.plist` are intentionally **not** real — you get
124
+ placeholders and clear "drop your Firebase config here" instructions.
125
+
126
+ ## Options
127
+
128
+ | Option | Choices | Default |
129
+ |---|---|---|
130
+ | Platforms | Android (always) + iOS | iOS on |
131
+ | App name / package / iOS bundle id | — | required / derived |
132
+ | Firebase region | any Firebase region | `us-central1` |
133
+ | Firebase (GitLive KMP) | on / off | on |
134
+ | Auth | `email` / `phone` / `both` / `none` | `both` |
135
+ | Firebase services | Firestore · Storage · Functions · FCM | all on |
136
+ | Room local cache | on / off | on |
137
+ | Appium harness | on / off | on |
138
+ | Bottom-nav tabs | label + icon, any count | Home, Profile |
139
+
140
+ _(Web/PWA is intentionally out of scope — Android + iOS only.)_
141
+
142
+ ## Use it from Claude Code
143
+
144
+ `create-cmp` also ships as a [Claude Code](https://docs.claude.com/en/docs/claude-code) plugin — one
145
+ shared engine, two front doors. Install from the bundled marketplace:
146
+
147
+ ```text
148
+ /plugin marketplace add kvdm-co-pilot/create-cmp
149
+ /plugin install create-cmp
150
+ ```
151
+
152
+ It bundles eight skills (plus the `cmp-inspector` MCP server), each with a deterministic engine
153
+ behind it:
154
+
155
+ - **cmp-new** — conversational interview, then shells out to the same engine to scaffold, and
156
+ generates your tab screens from the example-feature pattern.
157
+ - **cmp-doctor** — toolchain bootstrap + project diagnosis on any KMP project (consent-gated).
158
+ - **cmp-upgrade** — migrate to the next proven-green version set (diff → apply → verify).
159
+ - **cmp-inspect** — see and drive a rendered Compose UI as structured JSON: hierarchy, geometry,
160
+ resolved design tokens, drift diffs, golden-tree snapshots, a11y audit, live navigation
161
+ (`navigate_and_inspect`), wireframe rendering, and `prove_change` — the verified dev loop.
162
+ - **cmp-dev-client** — run the shared UI in a phone-sized desktop window with Compose Hot Reload
163
+ (`./gradlew :composeApp:hotRunDesktop --auto`) — the Expo-style dev loop for KMP.
164
+ - **cmp-firebase-connect** — post-scaffold onboarding: create/reuse a Firebase project, register
165
+ the app, and replace the placeholder config via the Firebase CLI (consent-gated), proven by a
166
+ green build.
167
+ - **cmp-test** — generate the Appium regression suite by *observing* the app: read the running
168
+ UI's semantics tree and emit tests in the shipped harness style.
169
+ - **cmp-qa-prep** — brings up the emulator + Appium session + smoke.
170
+
171
+ ## Why CMP, not React Native
172
+
173
+ This isn't a knock on React Native — it's about defaults. The only place CMP loses to RN on a new
174
+ app is **time-to-first-green-build**, and that's a tooling problem, not a merits problem. With
175
+ `create-cmp`, CMP's `npx`-and-go is competitive:
176
+
177
+ - **One language, real native UI.** Kotlin shared logic *and* Compose UI across Android and iOS — no
178
+ JS bridge, no separate native-module dance for the common case.
179
+ - **Native performance and platform access** without the RN bridge tax.
180
+ - **Reproducible by construction.** A frozen, version-locked, CI-gated template means the build that
181
+ was green yesterday is green today — the exact property ad-hoc CMP setups lack.
182
+ - **Proven, not assumed.** Every scaffold ends on a real build (and an Appium smoke), with a
183
+ GREEN/FAIL verdict.
184
+
185
+ If `create-next-app` made React the default for the web by removing setup friction, the goal here is
186
+ the same for multiplatform mobile.
187
+
188
+ ## How it works
189
+
190
+ ```
191
+ Front doors: npx create-cmp-cli + Claude Code plugin (cmp-new / cmp-doctor / cmp-qa-prep)
192
+ │ one shared engine, two front doors
193
+ Engine (Node, deterministic): copy → token-replace → rename packages → toggle features → VERIFY
194
+ Golden template (frozen, CI'd): pinned versions · iOS shell · nav+insets · Clean Arch · DI · Appium
195
+ ```
196
+
197
+ The engine never puts an LLM in the hot path: it copies the template, replaces placeholders in file
198
+ contents *and* paths, atomically renames package directories, toggles features (Firebase / auth type
199
+ / Room / Appium), and runs the verify build. Determinism is the moat. See
200
+ [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) for the full design.
201
+
202
+ ## Requirements
203
+
204
+ - **Node.js ≥ 18** to run the scaffolder.
205
+ - **macOS** for iOS output (Xcode, CocoaPods, XcodeGen). Android output works on macOS or Linux.
206
+ - Everything else — JDK 17, Android SDK + emulator, CocoaPods, XcodeGen, Appium + drivers — the
207
+ built-in `doctor` detects and (with your consent) installs. Xcode itself must be installed from the
208
+ App Store; the doctor surfaces that as the one manual step.
209
+
210
+ ## Roadmap
211
+
212
+ The full plan — six pillars from scaffold to store release, and the sequencing — lives in
213
+ [`docs/ROADMAP.md`](./docs/ROADMAP.md). Near-term:
214
+
215
+ - [ ] Publish to npm as `create-cmp-cli` (`npx create-cmp-cli`) — release cut, publish pending.
216
+ - [ ] Record the asciinema demo (`npx create-cmp-cli` → green Android + iOS).
217
+ - [ ] Full Android + iOS build matrix in CI (currently CI runs the engine unit tests).
218
+ - [ ] More example features and nav shapes.
219
+ - **AI-native Compose inspector** — read a running app's hierarchy, geometry, and *resolved design
220
+ tokens* as structured JSON (no screenshots). **Phase 0 (headless host-JVM render → inspect) is
221
+ built and verified**; live-emulator inspection is next. See
222
+ [`docs/INSPECTOR-PLAN.md`](./docs/INSPECTOR-PLAN.md).
223
+
224
+ ## Contributing
225
+
226
+ Issues and PRs welcome — see [CONTRIBUTING.md](./CONTRIBUTING.md) and our
227
+ [Code of Conduct](./CODE_OF_CONDUCT.md). The golden template is CI-gated: an upstream version bump
228
+ must fail CI, not your generated project.
229
+
230
+ ## License
231
+
232
+ [MIT](./LICENSE) © Karel van der Merwe and create-cmp contributors.
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ // create-cmp — scaffold and maintain production-ready Kotlin/Compose
3
+ // Multiplatform apps.
4
+ //
5
+ // Thin subcommand dispatcher; each command lives in src/commands/<name>.mjs
6
+ // and the deterministic engine in src/. Two front doors (this CLI + the
7
+ // Claude Code plugin) share that one engine.
8
+ //
9
+ // npx create-cmp [target-dir] [flags] # scaffold (default command)
10
+ // npx create-cmp create [target-dir] [flags]
11
+ // npx create-cmp doctor [flags] # toolchain + project diagnosis
12
+ // npx create-cmp upgrade [flags] # migrate to a proven-green version set
13
+ // npx create-cmp clean [flags] # konan/Gradle cache & build-output hygiene
14
+ // npx create-cmp verify [flags] # green-build gate on an existing project
15
+
16
+ import { parseArgs } from "../src/lib/args.mjs";
17
+
18
+ const COMMANDS = new Set(["create", "doctor", "upgrade", "clean", "verify", "help"]);
19
+
20
+ async function main() {
21
+ const argv = process.argv.slice(2);
22
+ const { _: positionals, flags } = parseArgs(argv);
23
+
24
+ // Backward compatible dispatch: the first positional is a subcommand only if
25
+ // it names one; otherwise it is the scaffold target dir (bare `create-cmp`
26
+ // and all pre-router invocations keep working exactly as before).
27
+ const command = COMMANDS.has(positionals[0]) ? positionals[0] : "create";
28
+ const rest = COMMANDS.has(positionals[0]) ? positionals.slice(1) : positionals;
29
+
30
+ if (flags.help || flags.h || command === "help") {
31
+ printHelp();
32
+ process.exit(0);
33
+ }
34
+
35
+ switch (command) {
36
+ case "doctor": {
37
+ const { runDoctor } = await import("../src/commands/doctor.mjs");
38
+ await runDoctor(flags, rest[0]);
39
+ return;
40
+ }
41
+ case "upgrade": {
42
+ const { runUpgrade } = await import("../src/commands/upgrade.mjs");
43
+ await runUpgrade(flags, rest[0]);
44
+ return;
45
+ }
46
+ case "clean": {
47
+ const { runClean } = await import("../src/commands/clean.mjs");
48
+ await runClean(flags, rest[0]);
49
+ return;
50
+ }
51
+ case "verify": {
52
+ const { runVerifyCommand } = await import("../src/commands/verify.mjs");
53
+ await runVerifyCommand(flags, rest[0]);
54
+ return;
55
+ }
56
+ case "create":
57
+ default: {
58
+ const { runCreate } = await import("../src/commands/create.mjs");
59
+ await runCreate(flags, rest[0]);
60
+ }
61
+ }
62
+ }
63
+
64
+ function printHelp() {
65
+ process.stdout.write(
66
+ `create-cmp — scaffold & maintain Kotlin/Compose Multiplatform apps (Android + iOS)\n\n` +
67
+ `Usage:\n` +
68
+ ` npx create-cmp [target-dir] [flags] scaffold a new app (default command)\n` +
69
+ ` npx create-cmp create [target-dir] same, explicit\n` +
70
+ ` npx create-cmp doctor toolchain doctor + project diagnosis (any KMP project)\n` +
71
+ ` npx create-cmp upgrade migrate to the next proven-green version set\n` +
72
+ ` npx create-cmp clean ~/.konan + Gradle build-output hygiene (consent-gated)\n` +
73
+ ` npx create-cmp verify run the green-build gate on an existing project\n\n` +
74
+ `create (scaffold) flags:\n` +
75
+ ` --name --package --bundle-id --region --theme-prefix\n` +
76
+ ` --ios/--no-ios --firebase/--no-firebase --auth <email|phone|both|none>\n` +
77
+ ` --room/--no-room --appium/--no-appium --inspector/--no-inspector\n` +
78
+ ` --dev-client/--no-dev-client (desktop JVM window + Compose Hot Reload)\n` +
79
+ ` --tabs Home:home,Profile:person\n` +
80
+ ` --target-dir --verify/--no-verify --yes --force --dry-run-verify\n\n` +
81
+ `doctor flags: --yes --dry-run --no-ios --no-install --target-dir <dir> --fix\n` +
82
+ `upgrade flags: --target-dir <dir> --set <id> --dry-run --yes --verify\n` +
83
+ `clean flags: --target-dir <dir> --dry-run --yes\n` +
84
+ `verify flags: --target-dir <dir> --no-ios --dry-run\n`
85
+ );
86
+ }
87
+
88
+ main().catch((err) => {
89
+ process.stderr.write(`Fatal: ${err && err.stack ? err.stack : err}\n`);
90
+ process.exit(1);
91
+ });
@@ -0,0 +1,110 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/create-cmp/create-cmp/options.schema.json",
4
+ "title": "create-cmp engine config",
5
+ "description": "The config object built by the CLI/plugin front door and consumed by the scaffold engine.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "appName",
10
+ "package",
11
+ "iosBundleId",
12
+ "region",
13
+ "themePrefix",
14
+ "platforms",
15
+ "firebase",
16
+ "room",
17
+ "appium",
18
+ "inspector",
19
+ "devClient",
20
+ "tabs",
21
+ "targetDir"
22
+ ],
23
+ "properties": {
24
+ "appName": {
25
+ "type": "string",
26
+ "description": "Human display name.",
27
+ "minLength": 1,
28
+ "pattern": "^[^\\n\\r]+$"
29
+ },
30
+ "package": {
31
+ "type": "string",
32
+ "description": "Reverse-DNS package id, e.g. com.acme.app.",
33
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$"
34
+ },
35
+ "iosBundleId": {
36
+ "type": "string",
37
+ "description": "iOS bundle id, e.g. com.acme.app.",
38
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9-]*(\\.[A-Za-z0-9][A-Za-z0-9-]*)+$"
39
+ },
40
+ "region": {
41
+ "type": "string",
42
+ "description": "Firebase region, e.g. us-central1.",
43
+ "minLength": 1,
44
+ "pattern": "^[a-z]+(-[a-z]+[0-9]+)?$|^[a-z-]+[0-9]+$"
45
+ },
46
+ "themePrefix": {
47
+ "type": "string",
48
+ "description": "PascalCase theme symbol prefix, e.g. Acme.",
49
+ "pattern": "^[A-Z][A-Za-z0-9]*$"
50
+ },
51
+ "platforms": {
52
+ "type": "object",
53
+ "additionalProperties": false,
54
+ "required": ["android", "ios"],
55
+ "properties": {
56
+ "android": {
57
+ "type": "boolean",
58
+ "const": true,
59
+ "description": "Android is always enabled."
60
+ },
61
+ "ios": { "type": "boolean" }
62
+ }
63
+ },
64
+ "firebase": {
65
+ "type": "object",
66
+ "additionalProperties": false,
67
+ "required": ["enabled"],
68
+ "properties": {
69
+ "enabled": { "type": "boolean" },
70
+ "auth": {
71
+ "type": "string",
72
+ "enum": ["email", "phone", "both", "none"]
73
+ },
74
+ "firestore": { "type": "boolean" },
75
+ "storage": { "type": "boolean" },
76
+ "functions": { "type": "boolean" },
77
+ "fcm": { "type": "boolean" }
78
+ }
79
+ },
80
+ "room": { "type": "boolean" },
81
+ "appium": { "type": "boolean" },
82
+ "inspector": {
83
+ "type": "boolean",
84
+ "description": "Live on-device inspector: debug-only loopback HTTP server (127.0.0.1:9500) serving the Compose semantics tree + design-token catalog to the cmp-inspector MCP. Structurally absent from release builds."
85
+ },
86
+ "devClient": {
87
+ "type": "boolean",
88
+ "description": "Desktop dev-client: a jvm(\"desktop\") target running the shared commonMain UI in a phone-sized JVM window with Compose Hot Reload attached (./gradlew :composeApp:hotRunDesktop --auto). Firebase is never initialized on desktop; platform seams get JVM fakes/impls in desktopMain."
89
+ },
90
+ "tabs": {
91
+ "type": "array",
92
+ "minItems": 1,
93
+ "maxItems": 5,
94
+ "items": {
95
+ "type": "object",
96
+ "additionalProperties": false,
97
+ "required": ["label", "icon"],
98
+ "properties": {
99
+ "label": { "type": "string", "minLength": 1 },
100
+ "icon": { "type": "string", "minLength": 1 }
101
+ }
102
+ }
103
+ },
104
+ "targetDir": {
105
+ "type": "string",
106
+ "description": "Destination directory for the stamped project.",
107
+ "minLength": 1
108
+ }
109
+ }
110
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "create-cmp-cli",
3
+ "version": "0.2.0",
4
+ "description": "Scaffold a production-ready Kotlin/Compose Multiplatform app (Android + iOS) that builds green in minutes — toolchain auto-bootstrapped, navigation/insets pre-solved, Clean Architecture wired, Appium harness ready. Installs the `create-cmp` command.",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-cmp": "bin/create-cmp.mjs",
8
+ "create-cmp-cli": "bin/create-cmp.mjs"
9
+ },
10
+ "engines": {
11
+ "node": ">=18"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "src",
16
+ "template",
17
+ "options.schema.json"
18
+ ],
19
+ "scripts": {
20
+ "test": "node --test",
21
+ "prepublishOnly": "node --test",
22
+ "doctor": "node bin/create-cmp.mjs doctor"
23
+ },
24
+ "keywords": [
25
+ "create-cmp",
26
+ "create-cmp-cli",
27
+ "compose-multiplatform",
28
+ "kotlin-multiplatform",
29
+ "kmp",
30
+ "cmp",
31
+ "scaffold",
32
+ "android",
33
+ "ios"
34
+ ],
35
+ "license": "MIT",
36
+ "author": "Karel van der Merwe",
37
+ "homepage": "https://github.com/kvdm-co-pilot/create-cmp#readme",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/kvdm-co-pilot/create-cmp.git"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/kvdm-co-pilot/create-cmp/issues"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "dependencies": {
49
+ "prompts": "^2.4.2",
50
+ "picocolors": "^1.1.1"
51
+ }
52
+ }