create-cmp-cli 0.7.0 → 0.7.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-cmp-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Create production mobile apps (Android + iOS, one Kotlin codebase) with AI — the delivery harness for Compose Multiplatform, the current generation of cross-platform (Google-backed KMP, iOS stable since May 2025). A deterministic, non-interactive generator that scaffolds a green-building app in minutes, then holds AI-driven changes to a machine-enforced verify lane with a committed evidence receipt. Every app carries a device-free UI preview loop (real screens rendered headlessly on save; changed-screen attribution and compile-error surfacing for coding agents, a live gallery for humans) plus agent-first docs (CLAUDE.md + AGENTS.md). Installs the `create-cmp` command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/lib/tabs.mjs
CHANGED
|
@@ -378,8 +378,10 @@ ${imports.join("\n")}
|
|
|
378
378
|
*
|
|
379
379
|
* The \`@Preview\` analog for the create-cmp inspector: the registry makes "render screen
|
|
380
380
|
* X" a closed, enumerable operation. The scaffolder regenerates the tab entries from the
|
|
381
|
-
* configured \`tabs
|
|
382
|
-
*
|
|
381
|
+
* configured \`tabs\`, and the feature stamper (\`qa/scaffold-feature.mjs\`, via the
|
|
382
|
+
* \`add-feature\`/\`add-screen\` skills) auto-appends a stamped screen at the
|
|
383
|
+
* \`// cmp:anchor preview-registry\` marker below; when you add a screen by hand, add it
|
|
384
|
+
* there too — the renderScreens harness, the gallery, and golden baselines pick it up by id.
|
|
383
385
|
*
|
|
384
386
|
* State variants (the Storybook "story" analog): a screen in a specific UI state is just
|
|
385
387
|
* another entry with a derived id — e.g. \`ScreenPreview("home@empty", "Home — empty")\`
|
|
@@ -405,6 +407,7 @@ ${infos.map(previewTabArg).join("\n")}
|
|
|
405
407
|
},
|
|
406
408
|
${infos.map(previewEntry).join("\n")}
|
|
407
409
|
ScreenPreview("detail", "Detail (nav destination)") { DetailScreen(itemId = "1", onBack = {}) },
|
|
410
|
+
// cmp:anchor preview-registry
|
|
408
411
|
)
|
|
409
412
|
|
|
410
413
|
/**
|
package/template/CLAUDE.md
CHANGED
|
@@ -75,8 +75,10 @@ and tells you exactly what your edit changed.
|
|
|
75
75
|
`composeApp/build/previews/<id>/{screen.png, tree.json}` (`-Pscreen=<id>` for one);
|
|
76
76
|
`node qa/preview-gallery.mjs` builds a self-contained gallery page from the output.
|
|
77
77
|
|
|
78
|
-
Screens come from `inspector/PreviewRegistry.kt` (desktopMain).
|
|
79
|
-
|
|
78
|
+
Screens come from `inspector/PreviewRegistry.kt` (desktopMain). The `add-feature`/`add-screen`
|
|
79
|
+
stamper **auto-registers** a stamped screen there (at the `// cmp:anchor preview-registry`
|
|
80
|
+
marker). **When you add a screen by hand, register it there** — a forced-state variant is just
|
|
81
|
+
another entry (`"home@empty"`).
|
|
80
82
|
Assert on the `tree.json` structure; never read PNG bytes (pixels are for humans).
|
|
81
83
|
<!-- <<< cmp:feature inspector -->
|
|
82
84
|
<!-- >>> cmp:feature dev-client -->
|
package/template/composeApp/src/desktopMain/kotlin/com/example/app/inspector/PreviewRegistry.kt
CHANGED
|
@@ -18,8 +18,10 @@ import __PACKAGE__.presentation.profile.ProfileScreen
|
|
|
18
18
|
*
|
|
19
19
|
* The `@Preview` analog for the create-cmp inspector: the registry makes "render screen
|
|
20
20
|
* X" a closed, enumerable operation. The scaffolder regenerates the tab entries from the
|
|
21
|
-
* configured `tabs
|
|
22
|
-
*
|
|
21
|
+
* configured `tabs`, and the feature stamper (`qa/scaffold-feature.mjs`, via the
|
|
22
|
+
* `add-feature`/`add-screen` skills) auto-appends a stamped screen at the
|
|
23
|
+
* `// cmp:anchor preview-registry` marker below; when you add a screen by hand, add it
|
|
24
|
+
* there too — the renderScreens harness, the gallery, and golden baselines pick it up by id.
|
|
23
25
|
*
|
|
24
26
|
* State variants (the Storybook "story" analog): a screen in a specific UI state is just
|
|
25
27
|
* another entry with a derived id — e.g. `ScreenPreview("home@empty", "Home — empty")`
|
|
@@ -47,6 +49,7 @@ fun previewRegistry(): List<ScreenPreview> = listOf(
|
|
|
47
49
|
ScreenPreview("home", "Home tab") { TabHost { HomeScreen(onItemClick = {}) } },
|
|
48
50
|
ScreenPreview("profile", "Profile tab") { TabHost { ProfileScreen() } },
|
|
49
51
|
ScreenPreview("detail", "Detail (nav destination)") { DetailScreen(itemId = "1", onBack = {}) },
|
|
52
|
+
// cmp:anchor preview-registry
|
|
50
53
|
)
|
|
51
54
|
|
|
52
55
|
/**
|
|
@@ -446,6 +446,11 @@ const plan = {
|
|
|
446
446
|
const APP_MODULE = path.join(SRC("commonMain"), "di/AppModule.kt");
|
|
447
447
|
const SCREEN_KT = path.join(SRC("commonMain"), "presentation/navigation/Screen.kt");
|
|
448
448
|
const APP_NAV_HOST = path.join(SRC("commonMain"), "presentation/navigation/AppNavHost.kt");
|
|
449
|
+
// Optional (present only when the inspector feature is enabled): the preview
|
|
450
|
+
// registry lives in desktopMain. A stamped pushed-destination screen is
|
|
451
|
+
// registered here so `renderScreens`, the gallery, and golden baselines pick it
|
|
452
|
+
// up with zero hand edits — the same reason we wire nav/DI automatically.
|
|
453
|
+
const PREVIEW_REGISTRY = path.join(SRC("desktopMain"), "inspector/PreviewRegistry.kt");
|
|
449
454
|
|
|
450
455
|
// Each step is tagged with the presets it belongs to, same mechanism as
|
|
451
456
|
// FILES above: `repository` gets repo+usecase DI (+ imports) only; `screen`
|
|
@@ -478,14 +483,30 @@ const ALL_INJECTION_PLANS = [
|
|
|
478
483
|
{ presets: ["feature", "screen"], apply: (c) => injectAtAnchor(c, APP_NAV_HOST, "nav-destinations", `composable(Screen.${F}.route) { ${F}Screen(onItemClick = {}) }`) },
|
|
479
484
|
],
|
|
480
485
|
},
|
|
486
|
+
{
|
|
487
|
+
// Optional: only wired when the inspector feature shipped PreviewRegistry.kt.
|
|
488
|
+
// Registers the stamped screen exactly as the NavHost hosts it (pushed
|
|
489
|
+
// destination → standalone, matching DetailScreen), so preview parity holds.
|
|
490
|
+
filePath: PREVIEW_REGISTRY,
|
|
491
|
+
optional: true,
|
|
492
|
+
steps: [
|
|
493
|
+
{ presets: ["feature", "screen"], apply: (c) => injectImport(c, PREVIEW_REGISTRY, `import ${PACKAGE}.presentation.${f}.${F}Screen`) },
|
|
494
|
+
{ presets: ["feature", "screen"], apply: (c) => injectAtAnchor(c, PREVIEW_REGISTRY, "preview-registry", `ScreenPreview("${f}", "${F} (nav destination)") { ${F}Screen(onItemClick = {}) },`) },
|
|
495
|
+
],
|
|
496
|
+
},
|
|
481
497
|
];
|
|
482
498
|
|
|
483
499
|
// Filter steps by active preset; drop any file plan left with zero steps
|
|
484
500
|
// (e.g. Screen.kt / AppNavHost.kt entirely for `repository`).
|
|
485
501
|
const fileInjectionPlans = ALL_INJECTION_PLANS.map((p) => ({
|
|
486
502
|
filePath: p.filePath,
|
|
503
|
+
optional: p.optional === true,
|
|
487
504
|
steps: p.steps.filter((s) => s.presets.includes(preset)).map((s) => s.apply),
|
|
488
|
-
}))
|
|
505
|
+
}))
|
|
506
|
+
.filter((p) => p.steps.length > 0)
|
|
507
|
+
// An optional shared file (PreviewRegistry.kt when the inspector is disabled)
|
|
508
|
+
// simply isn't wired — a required file that's missing still dies in applyInjectionSteps.
|
|
509
|
+
.filter((p) => !(p.optional && !fs.existsSync(p.filePath)));
|
|
489
510
|
|
|
490
511
|
const fileResults = fileInjectionPlans.map((p) => applyInjectionSteps(p.filePath, p.steps));
|
|
491
512
|
|
|
@@ -520,6 +541,11 @@ if (dryRun) {
|
|
|
520
541
|
`\n${F}Screen.kt is stamped wrapped in BaseScreen (SHELL-05 — pushed destinations wrap their own content).`,
|
|
521
542
|
);
|
|
522
543
|
}
|
|
544
|
+
if (fileInjectionPlans.some((p) => p.filePath === PREVIEW_REGISTRY)) {
|
|
545
|
+
console.log(
|
|
546
|
+
`\n${F}Screen is auto-registered in inspector/PreviewRegistry.kt (renderScreens + gallery + golden baseline pick it up).`,
|
|
547
|
+
);
|
|
548
|
+
}
|
|
523
549
|
if (writesSpec) {
|
|
524
550
|
console.log(`\nspecs/${f}.spec.md will be written with default clauses ${F_UPPER}-01..06.`);
|
|
525
551
|
} else {
|