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.0",
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\`; when you add a screen by hand, add it here — the renderScreens
382
- * harness, the gallery, and golden baselines pick it up by id.
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
  /**
@@ -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). **When you add a screen,
79
- register it there** a forced-state variant is just another entry (`"home@empty"`).
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 -->
@@ -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`; when you add a screen by hand, add it here — the renderScreens
22
- * harness, the gallery, and golden baselines pick it up by id.
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
- })).filter((p) => p.steps.length > 0);
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 {