@codeyam/codeyam-cli 0.1.23 → 0.1.25

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 (98) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +7 -1
  4. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
  5. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +7 -1
  6. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
  7. package/analyzer-template/project/runMultiScenarioServer.ts +26 -3
  8. package/background/src/lib/virtualized/project/runMultiScenarioServer.js +23 -3
  9. package/background/src/lib/virtualized/project/runMultiScenarioServer.js.map +1 -1
  10. package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js +30 -0
  11. package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js.map +1 -0
  12. package/codeyam-cli/src/commands/editor.js +349 -106
  13. package/codeyam-cli/src/commands/editor.js.map +1 -1
  14. package/codeyam-cli/src/data/designSystems.js +27 -0
  15. package/codeyam-cli/src/data/designSystems.js.map +1 -0
  16. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +44 -0
  17. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
  18. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js +6 -0
  19. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js.map +1 -1
  20. package/codeyam-cli/src/utils/editorApi.js +16 -0
  21. package/codeyam-cli/src/utils/editorApi.js.map +1 -1
  22. package/codeyam-cli/src/utils/editorScenarios.js +1 -0
  23. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  24. package/codeyam-cli/src/utils/entityChangeStatus.server.js +15 -0
  25. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  26. package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js +159 -0
  27. package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js.map +1 -0
  28. package/codeyam-cli/src/utils/queue/job.js +9 -1
  29. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  30. package/codeyam-cli/src/utils/scenariosManifest.js +8 -2
  31. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  32. package/codeyam-cli/src/utils/testResultCache.js +53 -0
  33. package/codeyam-cli/src/utils/testResultCache.js.map +1 -0
  34. package/codeyam-cli/src/utils/testResultCache.server.js +81 -0
  35. package/codeyam-cli/src/utils/testResultCache.server.js.map +1 -0
  36. package/codeyam-cli/src/utils/testResultCache.server.test.js +187 -0
  37. package/codeyam-cli/src/utils/testResultCache.server.test.js.map +1 -0
  38. package/codeyam-cli/src/utils/testResultCache.test.js +230 -0
  39. package/codeyam-cli/src/utils/testResultCache.test.js.map +1 -0
  40. package/codeyam-cli/src/utils/webappDetection.js +4 -2
  41. package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
  42. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js +98 -0
  43. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js.map +1 -0
  44. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +32 -0
  45. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
  46. package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js +34 -0
  47. package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js.map +1 -0
  48. package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-CKeQT5Ty.js → InteractivePreview-DtYTSPL2.js} +1 -1
  49. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-DUMfcNVK.js → ScenarioViewer-CefgqbCr.js} +1 -1
  50. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bc8BG-Lw.js +34 -0
  51. package/codeyam-cli/src/webserver/build/client/assets/{_index-BAWd-Xjf.js → _index-C1YkzTAV.js} +1 -1
  52. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BOARiB-g.js → activity.(_tab)-yH46LLUz.js} +1 -1
  53. package/codeyam-cli/src/webserver/build/client/assets/api.editor-verify-routes-l0sNRNKZ.js +1 -0
  54. package/codeyam-cli/src/webserver/build/client/assets/api.interactive-switch-scenario-l0sNRNKZ.js +1 -0
  55. package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-C8y4mmyv.js → dev.empty-CRepiabR.js} +1 -1
  56. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DLM1-ZMt.js +96 -0
  57. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-ByHz6rAQ.js → entity._sha._-DYJRGiDI.js} +1 -1
  58. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-CmLO432x.js → entity._sha.scenarios._scenarioId.dev-wdiwx5-Z.js} +1 -1
  59. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-Bz9sCUF_.js → entity._sha.scenarios._scenarioId.fullscreen-BrkN-40Y.js} +1 -1
  60. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-DQM8E7L4.js → entity._sha_.create-scenario-DxfhekTZ.js} +1 -1
  61. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-CAoXLsQr.js → entity._sha_.edit._scenarioId-CRXJWmpB.js} +1 -1
  62. package/codeyam-cli/src/webserver/build/client/assets/globals-9EkC9j9I.css +1 -0
  63. package/codeyam-cli/src/webserver/build/client/assets/manifest-7e749098.js +1 -0
  64. package/codeyam-cli/src/webserver/build/client/assets/{root-D2_tktnk.js → root-DGtly3mb.js} +2 -2
  65. package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-D9QZKaLJ.js +2 -0
  66. package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-By5slFjw.js → analysisRunner-CO8xocj3.js} +1 -1
  67. package/codeyam-cli/src/webserver/build/server/assets/{index-DXaOwBnm.js → index-QKPqlUgg.js} +1 -1
  68. package/codeyam-cli/src/webserver/build/server/assets/{init-CLG1LjQM.js → init-DlspChIk.js} +1 -1
  69. package/codeyam-cli/src/webserver/build/server/assets/server-build-ChzicV-B.js +689 -0
  70. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  71. package/codeyam-cli/src/webserver/build-info.json +5 -5
  72. package/codeyam-cli/src/webserver/idleDetector.js +12 -3
  73. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -1
  74. package/codeyam-cli/src/webserver/terminalServer.js +4 -3
  75. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  76. package/codeyam-cli/templates/design-systems/clean-dashboard-design-system.md +255 -0
  77. package/codeyam-cli/templates/design-systems/editorial-design-system.md +267 -0
  78. package/codeyam-cli/templates/design-systems/mono-brutalist-design-system.md +256 -0
  79. package/codeyam-cli/templates/design-systems/neo-brutalist-design-system.md +294 -0
  80. package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +115 -0
  81. package/codeyam-cli/templates/expo-react-native/__tests__/.gitkeep +0 -0
  82. package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +3 -2
  83. package/codeyam-cli/templates/expo-react-native/global.css +7 -0
  84. package/codeyam-cli/templates/expo-react-native/lib/theme.ts +73 -0
  85. package/codeyam-cli/templates/expo-react-native/package.json +16 -6
  86. package/codeyam-cli/templates/isolation-route/expo-router.tsx.template +54 -0
  87. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +10 -5
  88. package/codeyam-cli/templates/seed-adapters/supabase.ts +14 -5
  89. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +15 -0
  90. package/package.json +1 -1
  91. package/packages/database/src/lib/loadAnalysis.js +7 -1
  92. package/packages/database/src/lib/loadAnalysis.js.map +1 -1
  93. package/codeyam-cli/src/webserver/build/client/assets/Spinner-D0LgAaSa.js +0 -34
  94. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DMv5ESGo.js +0 -96
  95. package/codeyam-cli/src/webserver/build/client/assets/globals-oyPmV37k.css +0 -1
  96. package/codeyam-cli/src/webserver/build/client/assets/manifest-1a45e154.js +0 -1
  97. package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-BNd5hYuW.js +0 -2
  98. package/codeyam-cli/src/webserver/build/server/assets/server-build-NZmUqQv6.js +0 -688
@@ -13,6 +13,7 @@ import { installClaudeCodeSkills } from "../utils/install-skills.js";
13
13
  import { setupClaudeCodeSettings } from "../utils/setupClaudeCodeSettings.js";
14
14
  import { ensureAnalyzerFinalized, } from "../utils/analyzerFinalization.js";
15
15
  import { APP_FORMATS, TECH_STACKS } from "../data/techStacks.js";
16
+ import { DESIGN_SYSTEMS } from "../data/designSystems.js";
16
17
  import { getProjectRoot as getStateProjectRoot } from "../state.js";
17
18
  import initCommand from "./init.js";
18
19
  import { scanScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../utils/scenariosManifest.js";
@@ -188,6 +189,44 @@ function getProjectDimensions(root) {
188
189
  return { defaultName: 'Desktop', names: [] };
189
190
  }
190
191
  }
192
+ function getTechStackContext(root) {
193
+ const state = readState(root);
194
+ const techStackId = state?.techStackId || '';
195
+ const stack = TECH_STACKS.find((s) => s.id === techStackId);
196
+ const isExpo = techStackId === 'expo-react-native';
197
+ const isChromeExt = techStackId === 'chrome-extension-react';
198
+ const isNextjs = techStackId.startsWith('nextjs-') || (!isExpo && !isChromeExt);
199
+ return {
200
+ id: techStackId || 'nextjs-prisma-sqlite',
201
+ isExpo,
202
+ isNextjs,
203
+ isChromeExt,
204
+ hasDatabase: isNextjs,
205
+ testRunner: isNextjs ? 'vitest' : 'jest',
206
+ testRunCommand: isNextjs ? 'npx vitest run' : 'npx jest',
207
+ storageType: isExpo
208
+ ? 'asyncStorage'
209
+ : isChromeExt
210
+ ? 'chromeStorage'
211
+ : 'prisma',
212
+ routerImport: isExpo
213
+ ? 'expo-router'
214
+ : isChromeExt
215
+ ? 'react-router-dom'
216
+ : 'next/navigation',
217
+ componentPrimitives: isExpo
218
+ ? '<View>, <Text>, <ScrollView>'
219
+ : '<div>, <span>, <h1>',
220
+ rawPrimitivesList: isExpo
221
+ ? '<View>, <Text>, <Image>, <ScrollView>, <FlatList>'
222
+ : '<div>, <span>, <h1>, <p>, <img>, <ul>',
223
+ patternsFile: isExpo
224
+ ? 'MOBILE_SETUP.md'
225
+ : isChromeExt
226
+ ? 'EXTENSION_SETUP.md'
227
+ : 'FEATURE_PATTERNS.md',
228
+ };
229
+ }
191
230
  /**
192
231
  * Print dimension guidance when the project has multiple screen sizes.
193
232
  * Tells Claude to pick the right dimension for the content being previewed
@@ -280,7 +319,12 @@ function printAppScenarioInstructions(pageName, route) {
280
319
  console.log(chalk.dim(' For external APIs: also add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
281
320
  }
282
321
  else {
322
+ const appCtx = getTechStackContext(root);
283
323
  checkbox('Include data in every app scenario — without it the page will be empty:');
324
+ if (appCtx.isExpo) {
325
+ console.log(chalk.dim(' Use "localStorage":{"items":"[...]"} to pre-populate AsyncStorage (values are JSON strings)'));
326
+ console.log(chalk.dim(" AsyncStorage uses localStorage on web — CodeYam's injection works automatically."));
327
+ }
284
328
  console.log(chalk.dim(' Use "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
285
329
  console.log(chalk.dim(' For external APIs: add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
286
330
  }
@@ -355,9 +399,11 @@ function printExtractionPlanInstructions() {
355
399
  * Shared component capture instructions used by editor Step 7 and migration Steps 3/8.
356
400
  * Prints the full isolation route setup, codeyam-capture wrapper, register command, and error checking.
357
401
  */
358
- function printComponentCaptureInstructions() {
402
+ function printComponentCaptureInstructions(root) {
403
+ const ctx = root ? getTechStackContext(root) : undefined;
404
+ const isExpo = ctx?.isExpo ?? false;
359
405
  checkbox('Create isolation route dirs: `codeyam editor isolate ComponentA ComponentB ...`');
360
- console.log(chalk.dim(' This creates app/isolated-components/layout.tsx (with production notFound() guard) and'));
406
+ console.log(chalk.dim(` This creates app/isolated-components/layout.tsx (with production ${isExpo ? '__DEV__' : 'notFound()'} guard) and`));
361
407
  console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
362
408
  checkbox('For each visual component:');
363
409
  console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
@@ -369,14 +415,28 @@ function printComponentCaptureInstructions() {
369
415
  console.log(chalk.dim(' — Different visual states: loading, error, disabled, selected, hover'));
370
416
  console.log(chalk.dim(' — Boundary values: single item vs many, min/max ratings, very long names'));
371
417
  console.log(chalk.dim(' 3. Create ONE isolation route per component with a scenarios map and ?s= query param:'));
372
- console.log(chalk.dim(' Remix: app/routes/isolated-components.ComponentName.tsx → /isolated-components/ComponentName'));
373
- console.log(chalk.dim(' Next.js: app/isolated-components/ComponentName/page.tsx → /isolated-components/ComponentName'));
418
+ if (isExpo) {
419
+ console.log(chalk.dim(' Expo: app/isolated-components/ComponentName.tsx → /isolated-components/ComponentName'));
420
+ console.log(chalk.dim(' Use useLocalSearchParams() from expo-router to read ?s=ScenarioName.'));
421
+ }
422
+ else {
423
+ console.log(chalk.dim(' Remix: app/routes/isolated-components.ComponentName.tsx → /isolated-components/ComponentName'));
424
+ console.log(chalk.dim(' Next.js: app/isolated-components/ComponentName/page.tsx → /isolated-components/ComponentName'));
425
+ }
374
426
  console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
375
427
  console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
376
- console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
377
- console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block" }}>'));
378
- console.log(chalk.dim(' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
379
- console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth:"24rem", full-width component → omit maxWidth'));
428
+ if (isExpo) {
429
+ console.log(chalk.dim(' Wrap the component in a capture container with nativeID="codeyam-capture":'));
430
+ console.log(chalk.dim(' <View nativeID="codeyam-capture" style={{ display:"flex" }}>'));
431
+ }
432
+ else {
433
+ console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
434
+ console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block" }}>'));
435
+ }
436
+ console.log(chalk.dim(isExpo
437
+ ? ' <View style={{ width:"100%", maxWidth:... }}> ← match the app\'s container width'
438
+ : ' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
439
+ console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth: 384, full-width component → omit maxWidth'));
380
440
  console.log(chalk.dim(' The screenshot captures just this wrapper, so the component fills the image.'));
381
441
  console.log(chalk.dim(' Center the wrapper on the page (flexbox center both axes) and set a page background'));
382
442
  console.log(chalk.dim(' color that matches where the component normally appears (e.g. white for light UIs).'));
@@ -714,11 +774,17 @@ function printSetup(root) {
714
774
  console.log();
715
775
  // ── Design System ────────────────────────────────────────────────
716
776
  console.log(chalk.bold('Design System (ask FIRST):'));
717
- console.log(chalk.dim(' Ask: "Do you have a design system, brand guidelines, or style preferences you\'d like me to follow?"'));
777
+ console.log(chalk.dim(' Ask: "What visual style do you want? Pick a built-in design system or bring your own."'));
718
778
  console.log(chalk.dim(' Use AskUserQuestion with these EXACT option labels:'));
719
- console.log(chalk.yellow(' Option 1 label: "Yes, I\'ll paste my design system"'));
779
+ console.log();
780
+ for (const ds of DESIGN_SYSTEMS) {
781
+ console.log(chalk.yellow(` Option label: "${ds.name}"`) +
782
+ chalk.dim(` — ${ds.description}`));
783
+ console.log(chalk.dim(` → Run: codeyam editor design-system ${ds.id}`));
784
+ }
785
+ console.log(chalk.yellow(' Option label: "I\'ll paste my own design system"'));
720
786
  console.log(chalk.dim(' → Wait for paste, save to .codeyam/design-system.md, confirm with brief summary'));
721
- console.log(chalk.yellow(' Option 2 label: "No, use sensible defaults"'));
787
+ console.log(chalk.yellow(' Option label: "Skip use sensible defaults"'));
722
788
  console.log(chalk.dim(' → Skip'));
723
789
  console.log();
724
790
  console.log(chalk.bold('Checklist:'));
@@ -773,7 +839,7 @@ function printSetup(root) {
773
839
  console.log(chalk.yellow(' ( ) Mobile') + chalk.dim(' — 375 × 667'));
774
840
  console.log(chalk.yellow(' ( ) Custom') + chalk.dim(' — ask for width × height'));
775
841
  console.log();
776
- console.log(chalk.dim(' Pre-select based on app format: mobile-responsive-web-app/desktop-app → Desktop, mobile-app → Mobile, chrome-extension → Custom (400×600).'));
842
+ console.log(chalk.dim(' Pre-select based on app format: mobile-responsive-web-app/desktop-app → Desktop, mobile-app → iPhone 16 (393×852), chrome-extension → Custom (400×600).'));
777
843
  console.log(chalk.dim(' If only one obvious choice, confirm it rather than asking.'));
778
844
  console.log(chalk.dim(` Save the choice via: curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" -d '{"defaultScreenSize":{"name":"Desktop","width":1440,"height":900}}'`));
779
845
  console.log();
@@ -982,33 +1048,64 @@ function printStep2(root, feature) {
982
1048
  }
983
1049
  console.log('Get the project ready to build.');
984
1050
  console.log();
1051
+ const ctx = getTechStackContext(root);
985
1052
  // If no project exists yet, include scaffolding instructions first
986
1053
  if (!projectExists) {
987
1054
  console.log(chalk.bold('Scaffold the project:'));
988
1055
  checkbox('Run `codeyam editor template` to scaffold, install dependencies, init git, and configure CodeYam');
989
- console.log(chalk.dim(' This copies the Next.js + Prisma 7 + SQLite template, runs npm install,'));
1056
+ if (ctx.isExpo) {
1057
+ console.log(chalk.dim(' This copies the Expo + React Native template, runs npm install,'));
1058
+ }
1059
+ else if (ctx.isChromeExt) {
1060
+ console.log(chalk.dim(' This copies the Chrome Extension + React template, runs npm install,'));
1061
+ }
1062
+ else {
1063
+ console.log(chalk.dim(' This copies the Next.js + Prisma 7 + SQLite template, runs npm install,'));
1064
+ }
990
1065
  console.log(chalk.dim(' initializes git, runs codeyam init, and refreshes the editor — all in one command.'));
991
1066
  console.log();
992
- checkbox('Define your data models in `prisma/schema.prisma`');
993
- console.log(chalk.dim(" Replace the placeholder Todo model with your app's models"));
994
- console.log();
995
- checkbox('Push schema and seed the database');
996
- console.log(chalk.dim(' npm run db:push'));
997
- console.log(chalk.dim(' # Edit prisma/seed.ts with your seed data, then:'));
998
- console.log(chalk.dim(' npm run db:seed'));
999
- console.log(chalk.dim(` # After re-seeding, restart the dev server to pick up fresh data:`));
1000
- console.log(chalk.dim(` # codeyam editor dev-server '{"action":"restart"}'`));
1001
- console.log();
1002
- console.log(chalk.yellow(' IMPORTANT: When adding new required columns to existing tables,'));
1003
- console.log(chalk.yellow(' provide a @default(...) value so `db push` can fill existing rows.'));
1004
- console.log(chalk.dim(' Example: userId String @default("anonymous") existing rows get "anonymous"'));
1005
- console.log(chalk.dim(' Without a default, Prisma requires --force-reset which drops ALL data.'));
1006
- console.log(chalk.dim(' NEVER use --force-reset it is blocked in this environment.'));
1007
- console.log();
1008
- console.log(chalk.dim(' See DATABASE.md for Prisma patterns and important warnings.'));
1009
- console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
1010
- console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
1011
- console.log();
1067
+ if (ctx.isExpo) {
1068
+ // Expo: no database, use AsyncStorage + theme
1069
+ checkbox('Define your data types in `lib/types.ts`');
1070
+ console.log(chalk.dim(" Create TypeScript interfaces for your app's data models"));
1071
+ console.log();
1072
+ checkbox('Set up initial data using the storage helper in `lib/storage.ts`');
1073
+ console.log(chalk.dim(' import { storage } from "@/lib/storage";'));
1074
+ console.log(chalk.dim(' await storage.set("items", [{ id: "1", title: "First item" }]);'));
1075
+ console.log();
1076
+ console.log(chalk.dim(' Read MOBILE_SETUP.md for data storage, navigation, and testing patterns.'));
1077
+ console.log();
1078
+ }
1079
+ else if (ctx.isChromeExt) {
1080
+ // Chrome Extension: use chrome.storage
1081
+ checkbox('Set up data storage using chrome.storage (with localStorage fallback for dev)');
1082
+ console.log();
1083
+ console.log(chalk.dim(' Read EXTENSION_SETUP.md for storage, messaging, and manifest patterns.'));
1084
+ console.log();
1085
+ }
1086
+ else {
1087
+ // Next.js: Prisma + database
1088
+ checkbox('Define your data models in `prisma/schema.prisma`');
1089
+ console.log(chalk.dim(" Replace the placeholder Todo model with your app's models"));
1090
+ console.log();
1091
+ checkbox('Push schema and seed the database');
1092
+ console.log(chalk.dim(' npm run db:push'));
1093
+ console.log(chalk.dim(' # Edit prisma/seed.ts with your seed data, then:'));
1094
+ console.log(chalk.dim(' npm run db:seed'));
1095
+ console.log(chalk.dim(` # After re-seeding, restart the dev server to pick up fresh data:`));
1096
+ console.log(chalk.dim(` # codeyam editor dev-server '{"action":"restart"}'`));
1097
+ console.log();
1098
+ console.log(chalk.yellow(' IMPORTANT: When adding new required columns to existing tables,'));
1099
+ console.log(chalk.yellow(' provide a @default(...) value so `db push` can fill existing rows.'));
1100
+ console.log(chalk.dim(' Example: userId String @default("anonymous") — existing rows get "anonymous"'));
1101
+ console.log(chalk.dim(' Without a default, Prisma requires --force-reset which drops ALL data.'));
1102
+ console.log(chalk.dim(' NEVER use --force-reset — it is blocked in this environment.'));
1103
+ console.log();
1104
+ console.log(chalk.dim(' See DATABASE.md for Prisma patterns and important warnings.'));
1105
+ console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
1106
+ console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
1107
+ console.log();
1108
+ }
1012
1109
  printDataStructureInstructions();
1013
1110
  }
1014
1111
  else {
@@ -1050,26 +1147,42 @@ function printStep3(root, feature) {
1050
1147
  if (isResuming) {
1051
1148
  printResumptionHeader(3);
1052
1149
  }
1150
+ const ctx = getTechStackContext(root);
1053
1151
  console.log('Build fast with real data. Prioritize speed over quality.');
1054
1152
  console.log();
1055
1153
  console.log(chalk.bold('Checklist:'));
1056
- checkbox('Create API routes that read from the database via Prisma');
1057
- if (!projectExists) {
1058
- checkbox('Seed the database with demo data');
1059
- checkbox('Create `.codeyam/seed-adapter.ts` so CodeYam can seed the database for scenarios');
1060
- console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
1061
- console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
1062
- console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
1063
- console.log();
1064
- console.log(chalk.bold.cyan('Make seed data visually rich:'));
1065
- console.log(chalk.cyan(' • Use real placeholder images from Unsplash (https://images.unsplash.com/photo-<id>?w=400&h=300&fit=crop)'));
1066
- console.log(chalk.cyan(' • Use avatar services like i.pravatar.cc for user profile photos'));
1067
- console.log(chalk.cyan(' • Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
1068
- console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
1069
- console.log(chalk.cyan(' • Rich seed data makes the prototype look real and surfaces layout issues early'));
1154
+ if (ctx.isExpo) {
1155
+ checkbox('Build screens that read from AsyncStorage via `lib/storage.ts` or fetch from APIs');
1156
+ if (!projectExists) {
1157
+ checkbox('Populate initial data in AsyncStorage for development');
1158
+ console.log(chalk.dim(' import { storage } from "@/lib/storage";'));
1159
+ console.log(chalk.dim(' await storage.set("items", [{ id: "1", title: "Buy groceries" }]);'));
1160
+ console.log();
1161
+ console.log(chalk.bold.cyan('Make data visually rich:'));
1162
+ console.log(chalk.cyan(' Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
1163
+ console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
1164
+ console.log(chalk.cyan(' • Rich data makes the prototype look real and surfaces layout issues early'));
1165
+ }
1166
+ }
1167
+ else {
1168
+ checkbox('Create API routes that read from the database via Prisma');
1169
+ if (!projectExists) {
1170
+ checkbox('Seed the database with demo data');
1171
+ checkbox('Create `.codeyam/seed-adapter.ts` so CodeYam can seed the database for scenarios');
1172
+ console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
1173
+ console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
1174
+ console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
1175
+ console.log();
1176
+ console.log(chalk.bold.cyan('Make seed data visually rich:'));
1177
+ console.log(chalk.cyan(' • Use real placeholder images from Unsplash (https://images.unsplash.com/photo-<id>?w=400&h=300&fit=crop)'));
1178
+ console.log(chalk.cyan(' • Use avatar services like i.pravatar.cc for user profile photos'));
1179
+ console.log(chalk.cyan(' • Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
1180
+ console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
1181
+ console.log(chalk.cyan(' • Rich seed data makes the prototype look real and surfaces layout issues early'));
1182
+ }
1070
1183
  }
1071
1184
  checkbox('Verify the dev server shows the changes');
1072
- checkbox('If the feature involves auth, email, payments, or other common patterns: read FEATURE_PATTERNS.md');
1185
+ checkbox(`If the feature involves auth, email, payments, or other common patterns: read ${ctx.patternsFile}`);
1073
1186
  // Responsive design guidance when building a mobile-responsive web app
1074
1187
  if (prevState?.appFormats?.includes('mobile-responsive-web-app')) {
1075
1188
  console.log();
@@ -1086,16 +1199,29 @@ function printStep3(root, feature) {
1086
1199
  console.log();
1087
1200
  console.log(designSystem);
1088
1201
  console.log();
1089
- checkbox('Define ALL design tokens as CSS custom properties in globals.css — not just colors');
1090
- console.log(chalk.dim(' Colors: --bg-surface, --text-primary, --accent-green-a, etc.'));
1091
- console.log(chalk.dim(' Typography: --text-xs, --text-sm, --text-lg, --text-2xl (font-size values)'));
1092
- console.log(chalk.dim(' Font weights: --font-weight-normal, --font-weight-medium, --font-weight-semibold'));
1093
- console.log(chalk.dim(' Spacing: --spacing-xs, --spacing-sm, --spacing-md, --spacing-lg, etc.'));
1094
- console.log(chalk.dim(' Border radius, shadows, transitions — every value the design system defines.'));
1095
- checkbox('Reference tokens from components — ZERO hardcoded px values for font-size, spacing, or colors');
1096
- console.log(chalk.dim(' Bad: fontSize: 14, padding: "12px 16px", gap: 8'));
1097
- console.log(chalk.dim(' Good: fontSize: "var(--text-sm)", padding: "var(--spacing-md) var(--spacing-lg)", gap: "var(--spacing-sm)"'));
1098
- console.log(chalk.dim(' This ensures the entire app updates when the design system changes.'));
1202
+ if (ctx.isExpo) {
1203
+ checkbox('Define ALL design tokens in `lib/theme.ts` — this is the single source of truth');
1204
+ console.log(chalk.dim(' Colors: theme.colors.bgSurface, theme.colors.textPrimary, etc.'));
1205
+ console.log(chalk.dim(' Typography: theme.fontSize.sm, theme.fontSize.lg, theme.fontFamily.mono'));
1206
+ console.log(chalk.dim(' Spacing: theme.spacing.sm, theme.spacing.md, theme.spacing.lg, etc.'));
1207
+ console.log(chalk.dim(' Border radius: theme.borderRadius.sm, theme.borderRadius.lg, etc.'));
1208
+ checkbox('Import theme in every component — ZERO hardcoded color strings or pixel values');
1209
+ console.log(chalk.dim(' Bad: color: "#333", fontSize: 14, padding: 12'));
1210
+ console.log(chalk.dim(' Good: color: theme.colors.textPrimary, fontSize: theme.fontSize.sm, padding: theme.spacing.md'));
1211
+ console.log(chalk.dim(' Do NOT use CSS custom properties (var(--token)) they do not work in React Native.'));
1212
+ }
1213
+ else {
1214
+ checkbox('Define ALL design tokens as CSS custom properties in globals.css — not just colors');
1215
+ console.log(chalk.dim(' Colors: --bg-surface, --text-primary, --accent-green-a, etc.'));
1216
+ console.log(chalk.dim(' Typography: --text-xs, --text-sm, --text-lg, --text-2xl (font-size values)'));
1217
+ console.log(chalk.dim(' Font weights: --font-weight-normal, --font-weight-medium, --font-weight-semibold'));
1218
+ console.log(chalk.dim(' Spacing: --spacing-xs, --spacing-sm, --spacing-md, --spacing-lg, etc.'));
1219
+ console.log(chalk.dim(' Border radius, shadows, transitions — every value the design system defines.'));
1220
+ checkbox('Reference tokens from components — ZERO hardcoded px values for font-size, spacing, or colors');
1221
+ console.log(chalk.dim(' Bad: fontSize: 14, padding: "12px 16px", gap: 8'));
1222
+ console.log(chalk.dim(' Good: fontSize: "var(--text-sm)", padding: "var(--spacing-md) var(--spacing-lg)", gap: "var(--spacing-sm)"'));
1223
+ console.log(chalk.dim(' This ensures the entire app updates when the design system changes.'));
1224
+ }
1099
1225
  }
1100
1226
  console.log();
1101
1227
  console.log(chalk.bold.cyan('Keep the preview moving:'));
@@ -1132,14 +1258,13 @@ function printStep4(root, feature) {
1132
1258
  console.log('Verify everything works before presenting the prototype.');
1133
1259
  console.log();
1134
1260
  console.log(chalk.bold('Verify the dev server:'));
1135
- console.log(chalk.dim(` # Get dev server URL: codeyam editor dev-server`));
1136
- console.log(chalk.dim(' # Check page loads: curl -s -o /dev/null -w "%{http_code}" http://localhost:<dev-port>'));
1137
- console.log(chalk.dim(' # Check API routes: curl -s http://localhost:<dev-port>/api/your-route'));
1261
+ console.log(chalk.dim(' # Verify pages and API routes load:'));
1262
+ console.log(chalk.dim(` codeyam editor verify-routes '{"paths":["/your-page"],"apiRoutes":["/api/your-route"]}'`));
1138
1263
  console.log();
1139
1264
  console.log(chalk.bold('Verify before proceeding:'));
1140
1265
  console.log(chalk.yellow(' Verify everything works before presenting the prototype to the user.'));
1141
- checkbox('Verify the page loads: curl the dev server URL and confirm HTTP 200 (not an error page)');
1142
- checkbox('Verify API routes return valid JSON: curl each route and confirm no error responses');
1266
+ checkbox('Verify page and API routes: `codeyam editor verify-routes \'{"paths":["/"],"apiRoutes":["/api/your-route"]}\'`');
1267
+ console.log(chalk.dim(' Include ALL page paths you built and ALL API routes they depend on.'));
1143
1268
  checkbox('Check for broken images: `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1","url2"]}\'`');
1144
1269
  console.log(chalk.dim(' Pass ALL page paths and ALL image URLs you used in seed data / API responses.'));
1145
1270
  console.log(chalk.dim(' Client-rendered pages need imageUrls — the HTML shell has no images to scan.'));
@@ -1150,6 +1275,14 @@ function printStep4(root, feature) {
1150
1275
  console.log(chalk.dim(' The user is looking at the preview — a blank page means something is broken.'));
1151
1276
  console.log(chalk.dim(' If any check fails, fix the issue and re-verify before proceeding.'));
1152
1277
  console.log();
1278
+ const ctx4 = getTechStackContext(root);
1279
+ if (ctx4.isExpo) {
1280
+ console.log(chalk.magenta.bold(' EXPO WEB PREVIEW NOTE:'));
1281
+ console.log(chalk.magenta(' The preview renders via react-native-web in a browser. Some differences'));
1282
+ console.log(chalk.magenta(' from native devices are expected (fonts, SafeAreaView, shadows, Platform.OS).'));
1283
+ console.log(chalk.magenta(' The preview is for layout and data verification. Test final polish on device.'));
1284
+ console.log();
1285
+ }
1153
1286
  console.log(chalk.bold('Update README and setup script:'));
1154
1287
  checkbox('Update `README.md`: set the project name, write a one-line description, and list any setup prerequisites');
1155
1288
  checkbox('Update `npm run setup` in `package.json` if setup requires extra steps (e.g. env vars, external services)');
@@ -1182,7 +1315,7 @@ function printStep5(root, feature) {
1182
1315
  console.log();
1183
1316
  console.log(chalk.bold('Before presenting — verify everything works:'));
1184
1317
  checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\` — check the \`preview\` field for \`healthy: false\``);
1185
- checkbox('Verify API routes return valid data (curl each route)');
1318
+ checkbox('Verify routes: `codeyam editor verify-routes \'{"paths":["/"],"apiRoutes":["/api/your-route"]}\'`');
1186
1319
  console.log();
1187
1320
  console.log(chalk.bold.red(' Verify EVERY image loads (this is the #1 source of broken prototypes):'));
1188
1321
  checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1","url2"]}\'`');
@@ -1262,6 +1395,7 @@ function printStep7(root, feature) {
1262
1395
  if (isResuming) {
1263
1396
  printResumptionHeader(7);
1264
1397
  }
1398
+ const ctx = getTechStackContext(root);
1265
1399
  console.log('Execute your extraction plan from step 6.');
1266
1400
  console.log();
1267
1401
  console.log(chalk.bold('Components:'));
@@ -1275,18 +1409,23 @@ function printStep7(root, feature) {
1275
1409
  console.log(chalk.dim(' Cover: typical inputs, edge cases, empty/null inputs, error conditions'));
1276
1410
  console.log(chalk.dim(' Aim for 3-8 test cases per function depending on complexity'));
1277
1411
  console.log(chalk.dim(' Hooks count as functions — useDrinks, useAuth, etc. all need test files'));
1278
- checkbox('Place test files next to source: `app/lib/drinks.ts` → `app/lib/drinks.test.ts`');
1412
+ if (ctx.isExpo) {
1413
+ checkbox('Place test files next to source but OUTSIDE `app/` (Expo Router treats all files in app/ as routes): `lib/storage.ts` → `lib/storage.test.ts`, `app/hooks/useCounter.ts` → `__tests__/hooks/useCounter.test.ts`');
1414
+ }
1415
+ else {
1416
+ checkbox('Place test files next to source: `app/lib/drinks.ts` → `app/lib/drinks.test.ts`');
1417
+ }
1279
1418
  console.log(chalk.yellow(' Tests ARE the only coverage for library functions/hooks — step 9 only captures component screenshots.'));
1280
1419
  console.log();
1281
1420
  console.log(chalk.bold('Recursive pass:'));
1282
1421
  checkbox('Re-read EVERY new file you just created — extract components from components, functions from functions');
1283
1422
  checkbox('Keep going until every file is a thin shell: just imports and composition');
1284
- console.log(chalk.yellow(' Check: does any file contain raw <div>, <span>, <h1>, <p>, <img>, or <ul>?'));
1423
+ console.log(chalk.yellow(` Check: does any file contain raw ${ctx.rawPrimitivesList}?`));
1285
1424
  console.log(chalk.yellow(' If yes → that JSX section is a component waiting to be extracted.'));
1286
1425
  console.log();
1287
1426
  console.log(chalk.bold('Verify before proceeding:'));
1288
1427
  checkbox('Run all tests and verify they pass');
1289
- checkbox('Page files contain ONLY imports + component composition — no raw HTML tags');
1428
+ checkbox(`Page files contain ONLY imports + component composition — no raw ${ctx.isExpo ? 'React Native primitives' : 'HTML tags'}`);
1290
1429
  checkbox('Every component renders ONE thing or composes sub-components — no multi-section JSX');
1291
1430
  checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
1292
1431
  printDimensionGuidance(dim, dimNames);
@@ -1347,11 +1486,12 @@ function printStep9(root, feature) {
1347
1486
  console.log(chalk.dim(' Reuse and improve existing scenarios where possible — update mock data'));
1348
1487
  console.log(chalk.dim(' to reflect current changes. Add new scenarios only for genuinely new states.'));
1349
1488
  console.log(chalk.dim(' Ensure at least one scenario clearly demonstrates what changed in this session.'));
1350
- printComponentCaptureInstructions();
1489
+ const ctx9 = getTechStackContext(root);
1490
+ printComponentCaptureInstructions(root);
1351
1491
  console.log();
1352
1492
  console.log(chalk.bold('Library Functions — run tests:'));
1353
1493
  checkbox('Run ALL test files created in step 7');
1354
- console.log(chalk.dim(' Example: npx vitest run app/lib/drinks.test.ts'));
1494
+ console.log(chalk.dim(` Example: ${ctx9.testRunCommand} app/lib/drinks.test.ts`));
1355
1495
  checkbox('Verify every test passes');
1356
1496
  checkbox('If any test fails, fix the source code and re-run');
1357
1497
  console.log();
@@ -1832,7 +1972,7 @@ function printMigrateStep3(root) {
1832
1972
  console.log(chalk.dim(' Component scenarios use isolation routes with a codeyam-capture wrapper for tight screenshots.'));
1833
1973
  console.log(chalk.dim(' These supplement the app scenarios from step 2 with focused component-level views.'));
1834
1974
  console.log();
1835
- printComponentCaptureInstructions();
1975
+ printComponentCaptureInstructions(root);
1836
1976
  console.log();
1837
1977
  migrationStopGate(3, pageName, pageIndex, totalPages);
1838
1978
  }
@@ -1975,7 +2115,7 @@ function printMigrateStep8(root) {
1975
2115
  console.log();
1976
2116
  console.log(chalk.bold('Component Scenarios:'));
1977
2117
  console.log(chalk.dim(' For each visual component extracted in step 7, create isolation routes and register scenarios.'));
1978
- printComponentCaptureInstructions();
2118
+ printComponentCaptureInstructions(root);
1979
2119
  console.log();
1980
2120
  console.log(chalk.bold('Verify:'));
1981
2121
  checkbox('Run `codeyam editor analyze-imports` to populate import graph (or `codeyam editor analyze-imports path/to/file.tsx ...` for specific files)');
@@ -2516,18 +2656,10 @@ async function handleAnalyzeImports(options = {}) {
2516
2656
  }
2517
2657
  }
2518
2658
  }
2519
- else if (targetFilePaths.length > 0) {
2520
- // Couldn't parse specific entity names mark all target files
2521
- // that we attempted to analyze as potentially failed
2522
- for (const fp of targetFilePaths) {
2523
- const entry = glossary.find((e) => e.filePath === fp);
2524
- updatedFailures[fp] = {
2525
- entityName: entry?.name || path.basename(fp, path.extname(fp)),
2526
- error: `Automated analysis failed — see .codeyam/analysis-errors.txt`,
2527
- failedAt: now,
2528
- };
2529
- }
2530
- }
2659
+ // When we can't parse specific entity names from the error, DON'T mark
2660
+ // all target files as failed. The error may be non-fatal (e.g., a cache
2661
+ // miss logged as "CodeYam Error") and blanket-marking every file as
2662
+ // permanently failed blocks future audits from resolving them.
2531
2663
  writeAnalysisFailures(root, updatedFailures);
2532
2664
  }
2533
2665
  catch {
@@ -2726,43 +2858,87 @@ async function handleDelete(scenarioId) {
2726
2858
  * Creates isolation route directories and the layout guard file.
2727
2859
  * This avoids brace-expansion permission prompts in the embedded terminal.
2728
2860
  */
2861
+ function handleDesignSystem(designSystemId) {
2862
+ const root = process.cwd();
2863
+ const ds = DESIGN_SYSTEMS.find((d) => d.id === designSystemId);
2864
+ if (!ds) {
2865
+ console.error(chalk.red(`Error: Unknown design system "${designSystemId}". Valid options: ${DESIGN_SYSTEMS.map((d) => d.id).join(', ')}`));
2866
+ process.exit(1);
2867
+ }
2868
+ const srcPath = path.join(__dirname, '..', '..', 'templates', 'design-systems', ds.fileName);
2869
+ if (!fs.existsSync(srcPath)) {
2870
+ console.error(chalk.red(`Error: Design system file not found: ${srcPath}`));
2871
+ process.exit(1);
2872
+ }
2873
+ const destDir = path.join(root, '.codeyam');
2874
+ fs.mkdirSync(destDir, { recursive: true });
2875
+ const destPath = path.join(destDir, 'design-system.md');
2876
+ fs.copyFileSync(srcPath, destPath);
2877
+ console.log(chalk.green(`Installed "${ds.name}" design system → .codeyam/design-system.md`));
2878
+ }
2729
2879
  function handleIsolate(componentNames) {
2730
2880
  const root = process.cwd();
2881
+ const ctx = getTechStackContext(root);
2731
2882
  if (componentNames.length === 0) {
2732
2883
  console.error(chalk.red('Usage: codeyam editor isolate "ComponentA ComponentB ..."'));
2733
2884
  process.exit(1);
2734
2885
  }
2735
2886
  const isolateDir = path.join(root, 'app', 'isolated-components');
2736
2887
  // Create layout.tsx with production guard if missing
2737
- const layoutPath = path.join(isolateDir, 'layout.tsx');
2888
+ const layoutPath = path.join(isolateDir, ctx.isExpo ? '_layout.tsx' : 'layout.tsx');
2738
2889
  if (!fs.existsSync(layoutPath)) {
2739
2890
  fs.mkdirSync(isolateDir, { recursive: true });
2740
- fs.writeFileSync(layoutPath, [
2741
- 'import { notFound } from "next/navigation";',
2742
- '',
2743
- 'export default function CaptureLayout({ children }: { children: React.ReactNode }) {',
2744
- ' if (process.env.NODE_ENV === "production") notFound();',
2745
- ' return <>{children}</>;',
2746
- '}',
2747
- '',
2748
- ].join('\n'), 'utf8');
2749
- console.log(chalk.green(`Created layout guard: app/isolated-components/layout.tsx`));
2750
- }
2751
- // Create a directory for each component
2891
+ if (ctx.isExpo) {
2892
+ fs.writeFileSync(layoutPath, [
2893
+ 'import { Redirect, Slot } from "expo-router";',
2894
+ '',
2895
+ 'export default function CaptureLayout() {',
2896
+ ' if (!__DEV__) return <Redirect href="/" />;',
2897
+ ' return <Slot />;',
2898
+ '}',
2899
+ '',
2900
+ ].join('\n'), 'utf8');
2901
+ }
2902
+ else {
2903
+ fs.writeFileSync(layoutPath, [
2904
+ 'import { notFound } from "next/navigation";',
2905
+ '',
2906
+ 'export default function CaptureLayout({ children }: { children: React.ReactNode }) {',
2907
+ ' if (process.env.NODE_ENV === "production") notFound();',
2908
+ ' return <>{children}</>;',
2909
+ '}',
2910
+ '',
2911
+ ].join('\n'), 'utf8');
2912
+ }
2913
+ console.log(chalk.green(`Created layout guard: app/isolated-components/${ctx.isExpo ? '_layout.tsx' : 'layout.tsx'}`));
2914
+ }
2915
+ // Create isolation route for each component.
2916
+ // Expo Router uses flat files (ComponentName.tsx), Next.js uses subdirectories (ComponentName/page.tsx).
2752
2917
  const created = [];
2753
2918
  const existed = [];
2754
2919
  for (const name of componentNames) {
2755
- const dir = path.join(isolateDir, name);
2756
- if (fs.existsSync(dir)) {
2757
- existed.push(name);
2920
+ if (ctx.isExpo) {
2921
+ const filePath = path.join(isolateDir, `${name}.tsx`);
2922
+ if (fs.existsSync(filePath)) {
2923
+ existed.push(name);
2924
+ }
2925
+ else {
2926
+ created.push(name);
2927
+ }
2758
2928
  }
2759
2929
  else {
2760
- fs.mkdirSync(dir, { recursive: true });
2761
- created.push(name);
2930
+ const dir = path.join(isolateDir, name);
2931
+ if (fs.existsSync(dir)) {
2932
+ existed.push(name);
2933
+ }
2934
+ else {
2935
+ fs.mkdirSync(dir, { recursive: true });
2936
+ created.push(name);
2937
+ }
2762
2938
  }
2763
2939
  }
2764
2940
  if (created.length > 0) {
2765
- console.log(chalk.green(`Created ${created.length} isolation route dir(s): ${created.join(', ')}`));
2941
+ console.log(chalk.green(`Created ${created.length} isolation route(s): ${created.join(', ')}`));
2766
2942
  }
2767
2943
  if (existed.length > 0) {
2768
2944
  console.log(chalk.dim(`Already existed: ${existed.join(', ')}`));
@@ -2816,6 +2992,12 @@ function formatApiSubcommandResult(subcommand, data) {
2816
2992
  parts.push(`scenarioId="${data.scenarioId}"`);
2817
2993
  if (data.sessionsNotified != null)
2818
2994
  parts.push(`notified=${data.sessionsNotified}`);
2995
+ // Surface the HTTP health check from the dev server
2996
+ if (data.preview) {
2997
+ parts.push(`healthy=${data.preview.healthy}`);
2998
+ if (data.preview.error)
2999
+ parts.push(`error="${data.preview.error}"`);
3000
+ }
2819
3001
  return parts.join(' ');
2820
3002
  }
2821
3003
  case 'dev-server': {
@@ -2865,6 +3047,34 @@ function formatApiSubcommandResult(subcommand, data) {
2865
3047
  }
2866
3048
  return parts.join(' ');
2867
3049
  }
3050
+ case 'verify-routes': {
3051
+ const lines = [];
3052
+ lines.push(chalk.bold.yellow('━━━ Route Verification ━━━'));
3053
+ for (const [route, info] of Object.entries(data.routes || {})) {
3054
+ const r = info;
3055
+ if (r.ok) {
3056
+ lines.push(chalk.green(` ✓ ${route} — HTTP ${r.status}`));
3057
+ }
3058
+ else {
3059
+ lines.push(chalk.red(` ✗ ${route} — ${r.error || `HTTP ${r.status}`}`));
3060
+ }
3061
+ }
3062
+ for (const [route, info] of Object.entries(data.apiRoutes || {})) {
3063
+ const r = info;
3064
+ if (r.ok && r.isJSON) {
3065
+ lines.push(chalk.green(` ✓ ${route} — HTTP ${r.status}, valid JSON`));
3066
+ }
3067
+ else if (r.ok) {
3068
+ lines.push(chalk.yellow(` ⚠ ${route} — HTTP ${r.status}, not valid JSON`));
3069
+ }
3070
+ else {
3071
+ lines.push(chalk.red(` ✗ ${route} — ${r.error || `HTTP ${r.status}`}`));
3072
+ }
3073
+ }
3074
+ lines.push('');
3075
+ lines.push(data.ok ? chalk.green(data.summary) : chalk.red(data.summary));
3076
+ return lines.join('\n');
3077
+ }
2868
3078
  default:
2869
3079
  return null; // journal-list, show/hide-results: keep full JSON
2870
3080
  }
@@ -4327,7 +4537,7 @@ async function handleTemplate() {
4327
4537
  _: [],
4328
4538
  });
4329
4539
  console.log(chalk.green(' CodeYam initialized.'));
4330
- // 5. Verify config has startCommand
4540
+ // 5. Verify config has startCommand and set format-specific defaults
4331
4541
  const configPath = path.join(root, '.codeyam', 'config.json');
4332
4542
  if (fs.existsSync(configPath)) {
4333
4543
  try {
@@ -4338,6 +4548,26 @@ async function handleTemplate() {
4338
4548
  console.log(chalk.yellow(' Warning: No startCommand found in .codeyam/config.json webapps.'));
4339
4549
  console.log(chalk.dim(' You may need to add: "startCommand": { "command": "sh", "args": ["-c", "npm run dev -- --port $PORT"] }'));
4340
4550
  }
4551
+ // Store appFormats from the tech stack so the editor UI can adapt
4552
+ if (stack?.supportedFormats) {
4553
+ config.appFormats = stack.supportedFormats;
4554
+ }
4555
+ // Set mobile-first defaults for mobile-app projects
4556
+ if (stack?.supportedFormats?.includes('mobile-app')) {
4557
+ config.defaultScreenSize = {
4558
+ name: 'iPhone 16',
4559
+ width: 393,
4560
+ height: 852,
4561
+ };
4562
+ config.screenSizes = {
4563
+ 'iPhone 16': { width: 393, height: 852 },
4564
+ 'iPhone 16 Pro Max': { width: 430, height: 932 },
4565
+ 'iPhone SE': { width: 375, height: 667 },
4566
+ 'Pixel 8': { width: 412, height: 915 },
4567
+ 'iPad mini': { width: 744, height: 1133 },
4568
+ };
4569
+ }
4570
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
4341
4571
  }
4342
4572
  catch {
4343
4573
  // Config parse error is non-fatal
@@ -4372,7 +4602,15 @@ async function handleTemplate() {
4372
4602
  }
4373
4603
  console.log();
4374
4604
  console.log(chalk.green.bold('Project scaffolded and ready!'));
4375
- console.log(chalk.dim('Next: Define your Prisma models, push the schema, seed the database, and build your feature.'));
4605
+ if (stack?.id === 'expo-react-native') {
4606
+ console.log(chalk.dim('Next: Set up your data types, configure the theme in lib/theme.ts, and build your feature.'));
4607
+ }
4608
+ else if (stack?.id === 'chrome-extension-react') {
4609
+ console.log(chalk.dim('Next: Configure your extension manifest and build your feature.'));
4610
+ }
4611
+ else {
4612
+ console.log(chalk.dim('Next: Define your Prisma models, push the schema, seed the database, and build your feature.'));
4613
+ }
4376
4614
  }
4377
4615
  // ─── Sync subcommand ─────────────────────────────────────────────────
4378
4616
  /**
@@ -4874,8 +5112,8 @@ const editorCommand = {
4874
5112
  describe: 'Editor mode guided workflow',
4875
5113
  builder: (yargs) => {
4876
5114
  const stepDescription = IS_INTERNAL_BUILD
4877
- ? 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)'
4878
- : 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)';
5115
+ ? 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, debug, design-system, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)'
5116
+ : 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, design-system, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)';
4879
5117
  let builder = yargs
4880
5118
  .positional('step', {
4881
5119
  type: 'string',
@@ -5090,6 +5328,11 @@ const editorCommand = {
5090
5328
  await handleValidateSeed(argv.json || '');
5091
5329
  return;
5092
5330
  }
5331
+ // Subcommand: codeyam editor design-system <id>
5332
+ if (argv.step === 'design-system') {
5333
+ handleDesignSystem(argv.json || '');
5334
+ return;
5335
+ }
5093
5336
  // Subcommand: codeyam editor delete <scenarioId>
5094
5337
  if (argv.step === 'delete') {
5095
5338
  await handleDelete(argv.json || '');