@mr.dj2u/cli 0.1.14 → 0.1.16

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.
@@ -76,6 +76,7 @@ const CESS_QUESTIONS = [
76
76
  options: () => [
77
77
  { value: 'uniwind', label: 'Uniwind', hint: 'Default and MDS preference' },
78
78
  { value: 'nativewind', label: 'NativeWind' },
79
+ { value: 'nativewindui', label: 'NativeWindUI' },
79
80
  { value: 'tamagui', label: 'Tamagui' },
80
81
  { value: 'restyle', label: 'Restyle' },
81
82
  { value: 'stylesheet', label: 'StyleSheet only' },
@@ -350,137 +351,214 @@ const CESS_QUESTIONS = [
350
351
  ];
351
352
  const CESS_SNAPSHOT_START = '<!-- MDS_CESS_SNAPSHOT_START -->';
352
353
  const CESS_SNAPSHOT_END = '<!-- MDS_CESS_SNAPSHOT_END -->';
354
+ const SNAPSHOT_PRIORITY = 10;
355
+ const TECH_STACK_PRIORITY = 20;
356
+ const VISIBLE_SECTION_PRIORITY = 30;
357
+ const DERIVED_TITLE_PRIORITY = 40;
358
+ const EXPLICIT_INPUT_PRIORITY = 50;
353
359
  export function extractCessInfoFromMarkdown(input) {
354
360
  const infoMarkdown = normalizeLineEndings(input.infoMarkdown);
355
361
  const sections = parseMarkdownSections(infoMarkdown);
356
362
  const evidence = {};
357
363
  const prefilledAnswers = {};
358
364
  const ambiguousQuestionIds = new Set();
365
+ const assignedAnswerSources = new Map();
359
366
  const usedSections = new Set();
360
367
  const explicitAppName = normalizeText(input.appName);
361
368
  let derivedDisplayName = explicitAppName;
369
+ let derivedDisplayNamePriority = explicitAppName ? EXPLICIT_INPUT_PRIORITY : undefined;
370
+ let derivedDisplayNameNote = explicitAppName ? 'Explicit app name input' : undefined;
362
371
  let derivedFolderSlug = explicitAppName ? slugifyAppName(explicitAppName) : undefined;
363
372
  const recordEvidence = (key, note) => {
364
373
  evidence[key] = [...(evidence[key] ?? []), note];
365
374
  };
366
- const assignValue = (id, value, note) => {
375
+ const assignValue = (id, value, note, priority = VISIBLE_SECTION_PRIORITY) => {
367
376
  const normalized = normalizeExtractedAnswerValue(id, value);
368
377
  if (normalized === undefined) {
369
378
  return;
370
379
  }
380
+ if (isGenericExtractedAnswerValue(id, normalized)) {
381
+ recordEvidence(id, `${note} ignored because it looked like scaffold/default placeholder data`);
382
+ return;
383
+ }
371
384
  if (ambiguousQuestionIds.has(id)) {
385
+ const ambiguousSource = assignedAnswerSources.get(id);
386
+ if (priority > (ambiguousSource?.priority ?? -1)) {
387
+ ambiguousQuestionIds.delete(id);
388
+ prefilledAnswers[id] = normalized;
389
+ assignedAnswerSources.set(id, { priority, note });
390
+ recordEvidence(id, `${note} selected over earlier lower-priority ambiguous values`);
391
+ return;
392
+ }
393
+ recordEvidence(id, `${note} ignored because ${ambiguousSource?.note ?? 'another visible source'} already made this field ambiguous`);
372
394
  return;
373
395
  }
374
396
  const current = prefilledAnswers[id];
397
+ const currentSource = assignedAnswerSources.get(id);
375
398
  if (current !== undefined && !areAnswerValuesEquivalent(id, current, normalized)) {
399
+ if (priority > (currentSource?.priority ?? -1)) {
400
+ prefilledAnswers[id] = normalized;
401
+ assignedAnswerSources.set(id, { priority, note });
402
+ recordEvidence(id, `${note} selected over lower-priority value from ${currentSource?.note ?? 'earlier extraction'}`);
403
+ return;
404
+ }
405
+ if (priority < (currentSource?.priority ?? -1)) {
406
+ recordEvidence(id, `${note} ignored because ${currentSource?.note ?? 'an earlier source'} had a higher-priority value`);
407
+ return;
408
+ }
376
409
  delete prefilledAnswers[id];
377
410
  ambiguousQuestionIds.add(id);
411
+ assignedAnswerSources.set(id, {
412
+ priority,
413
+ note: `${currentSource?.note ?? 'earlier extraction'} and ${note}`,
414
+ });
378
415
  recordEvidence(id, `${note} (conflicts with earlier extracted value)`);
379
416
  return;
380
417
  }
381
418
  prefilledAnswers[id] = normalized;
419
+ assignedAnswerSources.set(id, { priority, note });
382
420
  recordEvidence(id, note);
383
421
  };
384
422
  const snapshot = parseCessSnapshot(infoMarkdown);
385
423
  if (snapshot) {
386
424
  recordEvidence('snapshot', 'Parsed machine-readable MDS snapshot block.');
387
- if (!derivedDisplayName) {
388
- derivedDisplayName = normalizeText(snapshot.displayAppName);
389
- }
390
- if (!derivedFolderSlug) {
391
- derivedFolderSlug = normalizeText(snapshot.folderSlug) ?? slugifyAppName(snapshot.displayAppName);
392
- }
393
- const snapshotAnswers = normalizeCessIntakeAnswers(snapshot.answers);
394
- for (const [key, value] of Object.entries(snapshotAnswers)) {
395
- assignValue(key, value, 'MDS snapshot');
425
+ }
426
+ const appNameSection = getMarkdownSection(sections, 'App Name');
427
+ if (!derivedDisplayName && appNameSection) {
428
+ const appNameFromSection = normalizeSectionText(appNameSection);
429
+ if (appNameFromSection && !isGenericTextValue(appNameFromSection)) {
430
+ derivedDisplayName = appNameFromSection;
431
+ derivedDisplayNamePriority = VISIBLE_SECTION_PRIORITY;
432
+ derivedDisplayNameNote = 'App Name section';
433
+ derivedFolderSlug = slugifyAppName(appNameFromSection);
434
+ recordEvidence('appName', `Derived app name from App Name section: ${appNameFromSection}`);
396
435
  }
436
+ usedSections.add('App Name');
397
437
  }
398
438
  const title = extractProjectTitle(infoMarkdown);
399
439
  if (!derivedDisplayName && title) {
400
440
  derivedDisplayName = title;
441
+ derivedDisplayNamePriority = DERIVED_TITLE_PRIORITY;
442
+ derivedDisplayNameNote = `Derived app name from title: ${title}`;
401
443
  recordEvidence('appName', `Derived app name from title: ${title}`);
402
444
  }
403
445
  if (!derivedFolderSlug && derivedDisplayName) {
404
446
  derivedFolderSlug = slugifyAppName(derivedDisplayName);
405
447
  }
406
- const targetUsers = sections.get('Target Users');
448
+ const targetUsers = getMarkdownSection(sections, 'Target Users');
407
449
  if (targetUsers) {
408
450
  usedSections.add('Target Users');
409
- assignValue('audience', normalizeSectionText(targetUsers), 'Target Users section');
451
+ assignValue('audience', normalizeSectionText(targetUsers), 'Target Users section', VISIBLE_SECTION_PRIORITY);
410
452
  }
411
453
  else {
412
- const overview = sections.get('Overview');
454
+ const overview = getMarkdownSection(sections, 'Overview');
413
455
  const overviewAudience = extractAudienceFromOverview(overview);
414
456
  if (overviewAudience) {
415
457
  usedSections.add('Overview');
416
- assignValue('audience', overviewAudience, 'Overview section');
458
+ assignValue('audience', overviewAudience, 'Overview section', VISIBLE_SECTION_PRIORITY);
459
+ }
460
+ }
461
+ const firstUserFlow = getMarkdownSection(sections, 'First User Flow');
462
+ const coreFlowsAndFeatures = getMarkdownSection(sections, 'Core Flows and Features');
463
+ const coreUserFlows = getMarkdownSection(sections, 'Core User Flows');
464
+ if (firstUserFlow || coreFlowsAndFeatures) {
465
+ const combinedFlows = [normalizeListSection(firstUserFlow), normalizeListSection(coreFlowsAndFeatures)]
466
+ .filter(Boolean)
467
+ .join('\n');
468
+ if (combinedFlows) {
469
+ assignValue('coreFlows', combinedFlows, 'First User Flow and Core Flows and Features sections', VISIBLE_SECTION_PRIORITY);
470
+ }
471
+ if (firstUserFlow) {
472
+ usedSections.add('First User Flow');
473
+ }
474
+ if (coreFlowsAndFeatures) {
475
+ usedSections.add('Core Flows and Features');
417
476
  }
418
477
  }
419
- const coreUserFlows = sections.get('Core User Flows');
420
- if (coreUserFlows) {
478
+ else if (coreUserFlows) {
421
479
  usedSections.add('Core User Flows');
422
- assignValue('coreFlows', normalizeListSection(coreUserFlows), 'Core User Flows section');
480
+ assignValue('coreFlows', normalizeListSection(coreUserFlows), 'Core User Flows section', VISIBLE_SECTION_PRIORITY);
423
481
  }
424
- const mustIncludeScreens = sections.get('Must-Include Screens Or Flows');
482
+ const mustIncludeScreens = getMarkdownSection(sections, 'Screens', 'Must-Include Screens Or Flows');
425
483
  if (mustIncludeScreens) {
426
- usedSections.add('Must-Include Screens Or Flows');
427
- assignValue('screens', normalizeListSection(mustIncludeScreens), 'Must-Include Screens Or Flows section');
484
+ usedSections.add(sections.has('Screens') ? 'Screens' : 'Must-Include Screens Or Flows');
485
+ assignValue('screens', normalizeListSection(mustIncludeScreens), 'Screens section', VISIBLE_SECTION_PRIORITY);
428
486
  }
429
- const dataAndBackend = sections.get('Data And Backend');
487
+ const dataAndBackend = getMarkdownSection(sections, 'Data And Backend');
430
488
  if (dataAndBackend) {
431
489
  usedSections.add('Data And Backend');
432
490
  const inferredDataNeeds = inferDataNeedSelections(dataAndBackend);
433
491
  if (inferredDataNeeds.length > 0) {
434
- assignValue('dataNeedSelections', inferredDataNeeds, 'Data And Backend section');
492
+ assignValue('dataNeedSelections', inferredDataNeeds, 'Data And Backend section', VISIBLE_SECTION_PRIORITY);
435
493
  }
436
494
  const dataStart = inferDataStart(dataAndBackend);
437
495
  if (dataStart) {
438
- assignValue('dataStart', dataStart, 'Data And Backend section');
496
+ assignValue('dataStart', dataStart, 'Data And Backend section', VISIBLE_SECTION_PRIORITY);
439
497
  }
440
498
  const authBackend = inferAuthBackend(dataAndBackend);
441
499
  if (authBackend) {
442
- assignValue('authBackend', authBackend, 'Data And Backend section');
500
+ assignValue('authBackend', authBackend, 'Data And Backend section', VISIBLE_SECTION_PRIORITY);
443
501
  }
444
502
  }
445
- const platforms = sections.get('Platforms');
503
+ const platforms = getMarkdownSection(sections, 'Platforms');
446
504
  if (platforms) {
447
505
  usedSections.add('Platforms');
448
- extractPlatformDecisions(platforms, assignValue);
506
+ extractPlatformDecisions(platforms, assignValue, VISIBLE_SECTION_PRIORITY);
449
507
  }
450
- const packageChoices = sections.get('Package Choices');
508
+ const packageChoices = getMarkdownSection(sections, 'Package Choices');
451
509
  if (packageChoices) {
452
510
  usedSections.add('Package Choices');
453
- extractPackageChoices(packageChoices, assignValue);
511
+ extractPackageChoices(packageChoices, assignValue, VISIBLE_SECTION_PRIORITY);
454
512
  }
455
- const releaseStrategy = sections.get('Release Strategy');
513
+ const releaseStrategy = getMarkdownSection(sections, 'Release Strategy');
456
514
  if (releaseStrategy) {
457
515
  usedSections.add('Release Strategy');
458
516
  const deploymentTarget = extractBulletValue(releaseStrategy, 'Deployment plan');
459
517
  if (deploymentTarget) {
460
- assignValue('deploymentTarget', deploymentTarget, 'Release Strategy section');
518
+ assignValue('deploymentTarget', deploymentTarget, 'Release Strategy section', VISIBLE_SECTION_PRIORITY);
461
519
  }
462
520
  const easUses = inferEasUses(extractBulletValue(releaseStrategy, 'EAS usage') ?? releaseStrategy);
463
521
  if (easUses.length > 0) {
464
- assignValue('easUses', easUses, 'Release Strategy section');
465
- assignValue('easSetup', true, 'Release Strategy section');
522
+ assignValue('easUses', easUses, 'Release Strategy section', VISIBLE_SECTION_PRIORITY);
523
+ assignValue('easSetup', true, 'Release Strategy section', VISIBLE_SECTION_PRIORITY);
466
524
  }
467
525
  const testToMain = parseBooleanValue(extractBulletValue(releaseStrategy, 'Test-to-main safeguards'));
468
526
  if (typeof testToMain === 'boolean') {
469
- assignValue('testToMainSafeguards', testToMain, 'Release Strategy section');
527
+ assignValue('testToMainSafeguards', testToMain, 'Release Strategy section', VISIBLE_SECTION_PRIORITY);
470
528
  }
471
529
  }
472
- const techStackSection = sections.get('Tech Stack & MDS Onboarding');
530
+ const techStackSection = getMarkdownSection(sections, 'Tech Stack & CESS Onboarding', 'Tech Stack & MDS Onboarding');
473
531
  if (techStackSection) {
474
- usedSections.add('Tech Stack & MDS Onboarding');
475
- extractTechStackDecisions(techStackSection, assignValue);
532
+ usedSections.add(sections.has('Tech Stack & CESS Onboarding') ? 'Tech Stack & CESS Onboarding' : 'Tech Stack & MDS Onboarding');
533
+ extractTechStackDecisions(techStackSection, assignValue, TECH_STACK_PRIORITY);
476
534
  }
477
535
  const onboardingDecisionsSection = sections.get('Onboarding Decisions');
478
536
  if (onboardingDecisionsSection) {
479
537
  usedSections.add('Onboarding Decisions');
480
- extractOnboardingDecisionLines(onboardingDecisionsSection, assignValue);
538
+ extractOnboardingDecisionLines(onboardingDecisionsSection, assignValue, VISIBLE_SECTION_PRIORITY);
539
+ }
540
+ if (!derivedDisplayName && snapshot?.displayAppName && !isGenericTextValue(snapshot.displayAppName)) {
541
+ derivedDisplayName = snapshot.displayAppName;
542
+ derivedDisplayNamePriority = SNAPSHOT_PRIORITY;
543
+ derivedDisplayNameNote = 'MDS snapshot metadata';
544
+ }
545
+ else if (!derivedDisplayName && snapshot?.displayAppName) {
546
+ recordEvidence('displayAppName', 'MDS snapshot metadata ignored because it looked like scaffold/default placeholder data');
547
+ }
548
+ if (!derivedFolderSlug && snapshot?.folderSlug && !isGenericTextValue(snapshot.folderSlug)) {
549
+ derivedFolderSlug = snapshot.folderSlug;
550
+ }
551
+ else if (!derivedFolderSlug && snapshot?.displayAppName && !isGenericTextValue(snapshot.displayAppName)) {
552
+ derivedFolderSlug = slugifyAppName(snapshot.displayAppName);
481
553
  }
482
554
  if (derivedDisplayName) {
483
- assignValue('displayAppName', derivedDisplayName, 'Derived app name');
555
+ assignValue('displayAppName', derivedDisplayName, derivedDisplayNameNote ?? 'Derived app name', derivedDisplayNamePriority ?? DERIVED_TITLE_PRIORITY);
556
+ }
557
+ if (snapshot) {
558
+ const snapshotAnswers = normalizeCessIntakeAnswers(snapshot.answers);
559
+ for (const [key, value] of Object.entries(snapshotAnswers)) {
560
+ assignValue(key, value, 'MDS snapshot', SNAPSHOT_PRIORITY);
561
+ }
484
562
  }
485
563
  const preservedNotes = [...sections.entries()]
486
564
  .filter(([heading, body]) => !usedSections.has(heading) && normalizeSectionText(body))
@@ -536,14 +614,16 @@ function extractProjectTitle(infoMarkdown) {
536
614
  }
537
615
  function parseMarkdownSections(markdown) {
538
616
  const sections = new Map();
539
- const matches = [...markdown.matchAll(/^##\s+(.+?)\s*$/gmu)];
617
+ const matches = [...markdown.matchAll(/^(#{1,2})\s+(.+?)\s*$/gmu)].filter((match) => {
618
+ return !(match.index === 0 && match[1] === '#');
619
+ });
540
620
  for (let index = 0; index < matches.length; index += 1) {
541
621
  const match = matches[index];
542
622
  const next = matches[index + 1];
543
623
  if (!match) {
544
624
  continue;
545
625
  }
546
- const heading = match?.[1]?.trim();
626
+ const heading = match?.[2]?.trim();
547
627
  if (!heading || match.index === undefined) {
548
628
  continue;
549
629
  }
@@ -553,6 +633,24 @@ function parseMarkdownSections(markdown) {
553
633
  }
554
634
  return sections;
555
635
  }
636
+ function getMarkdownSection(sections, ...headings) {
637
+ for (const heading of headings) {
638
+ const exact = sections.get(heading);
639
+ if (exact !== undefined) {
640
+ return exact;
641
+ }
642
+ const normalizedHeading = normalizeHeadingKey(heading);
643
+ for (const [candidate, body] of sections.entries()) {
644
+ if (normalizeHeadingKey(candidate) === normalizedHeading) {
645
+ return body;
646
+ }
647
+ }
648
+ }
649
+ return undefined;
650
+ }
651
+ function normalizeHeadingKey(value) {
652
+ return value.trim().toLowerCase().replace(/\s+/gu, ' ');
653
+ }
556
654
  function extractAudienceFromOverview(overview) {
557
655
  const text = normalizeSectionText(overview);
558
656
  if (!text) {
@@ -644,161 +742,290 @@ function inferAuthBackend(value) {
644
742
  }
645
743
  return undefined;
646
744
  }
647
- function extractPlatformDecisions(value, assignValue) {
745
+ function extractPlatformDecisions(value, assignValue, priority = VISIBLE_SECTION_PRIORITY) {
648
746
  const targetPlatforms = parsePlatformList(extractBulletValue(value, 'Target platforms'));
649
747
  if (targetPlatforms.length > 0) {
650
- assignValue('targetPlatforms', targetPlatforms, 'Platforms section');
748
+ assignValue('targetPlatforms', targetPlatforms, 'Platforms section', priority);
651
749
  }
652
750
  const firstTargetPlatform = normalizeChoice(extractBulletValue(value, 'First MVP platform'), PLATFORM_OPTIONS);
653
751
  if (firstTargetPlatform) {
654
- assignValue('firstTargetPlatform', firstTargetPlatform, 'Platforms section');
752
+ assignValue('firstTargetPlatform', firstTargetPlatform, 'Platforms section', priority);
655
753
  }
656
754
  const appDirectoryValue = extractBulletValue(value, 'Expo Router app directory');
657
755
  if (appDirectoryValue?.includes('src/app')) {
658
- assignValue('appDirectory', 'src', 'Platforms section');
756
+ assignValue('appDirectory', 'src', 'Platforms section', priority);
659
757
  }
660
758
  else if (appDirectoryValue?.includes('app')) {
661
- assignValue('appDirectory', 'root', 'Platforms section');
759
+ assignValue('appDirectory', 'root', 'Platforms section', priority);
662
760
  }
663
761
  const organization = extractBulletValue(value, 'Platform-specific organization') ?? '';
664
762
  if (organization.toLowerCase().includes('folder')) {
665
- assignValue('platformStrategy', 'folders', 'Platforms section');
763
+ assignValue('platformStrategy', 'folders', 'Platforms section', priority);
666
764
  }
667
765
  else if (organization.toLowerCase().includes('file')) {
668
- assignValue('platformStrategy', 'files-only', 'Platforms section');
766
+ assignValue('platformStrategy', 'files-only', 'Platforms section', priority);
669
767
  }
670
768
  const layoutMode = extractBulletValue(value, 'Platform layout mode');
671
769
  if (layoutMode?.toLowerCase().includes('platform-specific')) {
672
- assignValue('platformLayouts', 'platform-specific', 'Platforms section');
770
+ assignValue('platformLayouts', 'platform-specific', 'Platforms section', priority);
673
771
  }
674
772
  else if (layoutMode?.toLowerCase().includes('shared')) {
675
- assignValue('platformLayouts', 'shared', 'Platforms section');
773
+ assignValue('platformLayouts', 'shared', 'Platforms section', priority);
676
774
  }
677
775
  const webOutput = normalizeChoice(extractBulletValue(value, 'Web output'), ['static', 'server', 'spa', 'none']);
678
776
  if (webOutput) {
679
- assignValue('webOutput', webOutput, 'Platforms section');
777
+ assignValue('webOutput', webOutput, 'Platforms section', priority);
680
778
  }
681
779
  const deployedServer = (extractBulletValue(value, 'Deployed server') ?? '').toLowerCase();
682
780
  if (deployedServer.includes('no deployed server') || deployedServer === 'none') {
683
- assignValue('expoServerAdapter', 'none', 'Platforms section');
684
- assignValue('customBackend', false, 'Platforms section');
781
+ assignValue('expoServerAdapter', 'none', 'Platforms section', priority);
782
+ assignValue('customBackend', false, 'Platforms section', priority);
685
783
  }
686
784
  else if (deployedServer.includes('eas')) {
687
- assignValue('expoServerAdapter', 'eas', 'Platforms section');
785
+ assignValue('expoServerAdapter', 'eas', 'Platforms section', priority);
688
786
  }
689
787
  else if (deployedServer.includes('express')) {
690
- assignValue('expoServerAdapter', 'express', 'Platforms section');
788
+ assignValue('expoServerAdapter', 'express', 'Platforms section', priority);
691
789
  }
692
790
  else if (deployedServer.includes('bun')) {
693
- assignValue('expoServerAdapter', 'bun', 'Platforms section');
791
+ assignValue('expoServerAdapter', 'bun', 'Platforms section', priority);
694
792
  }
695
793
  else if (deployedServer.includes('custom')) {
696
- assignValue('expoServerAdapter', 'other', 'Platforms section');
697
- assignValue('customBackend', true, 'Platforms section');
794
+ assignValue('expoServerAdapter', 'other', 'Platforms section', priority);
795
+ assignValue('customBackend', true, 'Platforms section', priority);
698
796
  }
699
797
  const expoUi = parseBooleanValue(extractBulletValue(value, 'Expo UI'));
700
798
  if (typeof expoUi === 'boolean') {
701
- assignValue('usesExpoUi', expoUi, 'Platforms section');
799
+ assignValue('usesExpoUi', expoUi, 'Platforms section', priority);
702
800
  }
703
801
  const expoUiUniversal = parseBooleanValue(extractBulletValue(value, 'Expo UI Universal components'));
704
802
  if (typeof expoUiUniversal === 'boolean') {
705
- assignValue('usesExpoUiUniversalComponents', expoUiUniversal, 'Platforms section');
803
+ assignValue('usesExpoUiUniversalComponents', expoUiUniversal, 'Platforms section', priority);
706
804
  }
707
805
  const expoNativeTabs = parseBooleanValue(extractBulletValue(value, 'Expo Native Tabs'));
708
806
  if (typeof expoNativeTabs === 'boolean') {
709
- assignValue('usesExpoNativeTabs', expoNativeTabs, 'Platforms section');
807
+ assignValue('usesExpoNativeTabs', expoNativeTabs, 'Platforms section', priority);
710
808
  }
711
809
  }
712
- function extractPackageChoices(value, assignValue) {
810
+ function extractPackageChoices(value, assignValue, priority = VISIBLE_SECTION_PRIORITY) {
713
811
  const entries = normalizeListSection(value)
714
812
  ?.split('\n')
715
813
  .map((item) => item.trim().toLowerCase())
716
814
  .filter(Boolean) ?? [];
717
815
  for (const entry of entries) {
718
816
  if (entry === 'uniwind') {
719
- assignValue('stylingSystem', 'uniwind', 'Package Choices section');
817
+ assignValue('stylingSystem', 'uniwind', 'Package Choices section', priority);
720
818
  }
721
819
  else if (entry === 'nativewind') {
722
- assignValue('stylingSystem', 'nativewind', 'Package Choices section');
820
+ assignValue('stylingSystem', 'nativewind', 'Package Choices section', priority);
821
+ }
822
+ else if (entry === 'nativewindui') {
823
+ assignValue('stylingSystem', 'nativewindui', 'Package Choices section', priority);
723
824
  }
724
825
  else if (entry === 'tamagui') {
725
- assignValue('stylingSystem', 'tamagui', 'Package Choices section');
826
+ assignValue('stylingSystem', 'tamagui', 'Package Choices section', priority);
726
827
  }
727
828
  else if (entry === 'restyle') {
728
- assignValue('stylingSystem', 'restyle', 'Package Choices section');
829
+ assignValue('stylingSystem', 'restyle', 'Package Choices section', priority);
729
830
  }
730
831
  else if (entry === 'supabase') {
731
- assignValue('authBackend', 'supabase', 'Package Choices section');
832
+ assignValue('authBackend', 'supabase', 'Package Choices section', priority);
732
833
  }
733
834
  else if (entry === 'firebase') {
734
- assignValue('authBackend', 'firebase', 'Package Choices section');
835
+ assignValue('authBackend', 'firebase', 'Package Choices section', priority);
735
836
  }
736
837
  }
737
838
  }
738
- function extractTechStackDecisions(value, assignValue) {
839
+ function extractTechStackDecisions(value, assignValue, priority = TECH_STACK_PRIORITY) {
840
+ const typeScriptChoice = parseBooleanValue(extractKeyValue(value, 'TypeScript'));
841
+ if (typeof typeScriptChoice === 'boolean') {
842
+ assignValue('scriptLanguage', typeScriptChoice ? 'typescript' : 'javascript', 'Tech Stack & CESS Onboarding section', priority);
843
+ }
844
+ const packageManagerChoice = normalizeChoiceLabel(extractKeyValue(value, 'Package Manager'), ['npm', 'pnpm', 'yarn', 'bun']);
845
+ if (packageManagerChoice) {
846
+ assignValue('packageManager', packageManagerChoice, 'Tech Stack & CESS Onboarding section', priority);
847
+ }
848
+ const navigationChoice = (extractKeyValue(value, 'Navigation') ?? '').toLowerCase();
849
+ if (isConcreteChoiceValue(navigationChoice)) {
850
+ if (navigationChoice.includes('react navigation')) {
851
+ assignValue('navigationLibrary', 'react-navigation', 'Tech Stack & CESS Onboarding section', priority);
852
+ }
853
+ else if (navigationChoice.includes('expo router')) {
854
+ assignValue('navigationLibrary', 'expo-router', 'Tech Stack & CESS Onboarding section', priority);
855
+ }
856
+ }
857
+ const navigationType = (extractKeyValue(value, 'Type of Navigation') ?? '').toLowerCase();
858
+ if (isConcreteChoiceValue(navigationType)) {
859
+ if (navigationType.includes('drawer')) {
860
+ assignValue('reactNavigationLayout', 'drawer', 'Tech Stack & CESS Onboarding section', priority);
861
+ }
862
+ else if (navigationType.includes('tabs')) {
863
+ assignValue('reactNavigationLayout', 'tabs', 'Tech Stack & CESS Onboarding section', priority);
864
+ }
865
+ else if (navigationType.includes('stack')) {
866
+ assignValue('reactNavigationLayout', 'stack', 'Tech Stack & CESS Onboarding section', priority);
867
+ }
868
+ }
869
+ const appDirectoryValue = extractKeyValue(value, 'Expo Router app directory');
870
+ if (appDirectoryValue?.includes('src/app')) {
871
+ assignValue('appDirectory', 'src', 'Tech Stack & CESS Onboarding section', priority);
872
+ }
873
+ else if (appDirectoryValue === 'app' || appDirectoryValue?.includes('`app`')) {
874
+ assignValue('appDirectory', 'root', 'Tech Stack & CESS Onboarding section', priority);
875
+ }
876
+ const organization = (extractKeyValue(value, 'Platform-specific organization (folders, files, or inline)') ?? '').toLowerCase();
877
+ if (isConcreteChoiceValue(organization)) {
878
+ if (organization.includes('folder')) {
879
+ assignValue('platformStrategy', 'folders', 'Tech Stack & CESS Onboarding section', priority);
880
+ }
881
+ else if (organization.includes('file')) {
882
+ assignValue('platformStrategy', 'files-only', 'Tech Stack & CESS Onboarding section', priority);
883
+ }
884
+ }
885
+ const layoutMode = (extractKeyValue(value, 'Platform layout mode') ?? '').toLowerCase();
886
+ if (isConcreteChoiceValue(layoutMode)) {
887
+ if (layoutMode.includes('platform-specific')) {
888
+ assignValue('platformLayouts', 'platform-specific', 'Tech Stack & CESS Onboarding section', priority);
889
+ }
890
+ else if (layoutMode.includes('shared')) {
891
+ assignValue('platformLayouts', 'shared', 'Tech Stack & CESS Onboarding section', priority);
892
+ }
893
+ }
894
+ const webOutput = normalizeChoiceLabel(extractKeyValue(value, 'Web output'), ['static', 'server', 'spa', 'none']);
895
+ if (webOutput) {
896
+ assignValue('webOutput', webOutput, 'Tech Stack & CESS Onboarding section', priority);
897
+ }
898
+ const styleLibrary = (extractKeyValue(value, 'Style Library') ?? '').toLowerCase();
899
+ if (isConcreteChoiceValue(styleLibrary)) {
900
+ if (styleLibrary.includes('nativewindui')) {
901
+ assignValue('stylingSystem', 'nativewindui', 'Tech Stack & CESS Onboarding section', priority);
902
+ }
903
+ else if (styleLibrary.includes('nativewind')) {
904
+ assignValue('stylingSystem', 'nativewind', 'Tech Stack & CESS Onboarding section', priority);
905
+ }
906
+ else if (styleLibrary.includes('uniwind')) {
907
+ assignValue('stylingSystem', 'uniwind', 'Tech Stack & CESS Onboarding section', priority);
908
+ }
909
+ else if (styleLibrary.includes('tamagui')) {
910
+ assignValue('stylingSystem', 'tamagui', 'Tech Stack & CESS Onboarding section', priority);
911
+ }
912
+ else if (styleLibrary.includes('restyle')) {
913
+ assignValue('stylingSystem', 'restyle', 'Tech Stack & CESS Onboarding section', priority);
914
+ }
915
+ else if (styleLibrary.includes('stylesheet')) {
916
+ assignValue('stylingSystem', 'stylesheet', 'Tech Stack & CESS Onboarding section', priority);
917
+ }
918
+ }
919
+ assignBooleanKey(value, 'Components from create-expo-app', 'includeCreateExpoComponents', assignValue, priority);
920
+ assignBooleanKey(value, 'Expo UI', 'usesExpoUi', assignValue, priority);
921
+ assignBooleanKey(value, 'Expo UI Universal components', 'usesExpoUiUniversalComponents', assignValue, priority);
922
+ assignBooleanKey(value, 'Expo Native Tabs', 'usesExpoNativeTabs', assignValue, priority);
923
+ const stateManagementChoice = (extractKeyValue(value, 'State management library') ?? '').toLowerCase();
924
+ if (isConcreteChoiceValue(stateManagementChoice)) {
925
+ if (stateManagementChoice.includes('zustand')) {
926
+ assignValue('stateManagement', 'zustand', 'Tech Stack & CESS Onboarding section', priority);
927
+ }
928
+ else if (stateManagementChoice.includes('none')) {
929
+ assignValue('stateManagement', 'none', 'Tech Stack & CESS Onboarding section', priority);
930
+ }
931
+ }
932
+ const authChoice = inferAuthBackend(extractKeyValue(value, 'Auth') ?? '');
933
+ if (authChoice) {
934
+ assignValue('authBackend', authChoice, 'Tech Stack & CESS Onboarding section', priority);
935
+ }
936
+ else if ((extractKeyValue(value, 'Auth') ?? '').toLowerCase().includes('none')) {
937
+ assignValue('authBackend', 'none', 'Tech Stack & CESS Onboarding section', priority);
938
+ }
939
+ const dataCategories = extractKeyValue(value, 'Data Categories');
940
+ if (dataCategories) {
941
+ const inferredDataNeeds = inferDataNeedSelections(dataCategories);
942
+ if (inferredDataNeeds.length > 0) {
943
+ assignValue('dataNeedSelections', inferredDataNeeds, 'Tech Stack & CESS Onboarding section', priority);
944
+ }
945
+ }
946
+ const startingDataMode = inferDataStart(extractKeyValue(value, 'Starting Data mode') ?? '');
947
+ if (startingDataMode) {
948
+ assignValue('dataStart', startingDataMode, 'Tech Stack & CESS Onboarding section', priority);
949
+ }
950
+ const easChoice = parseBooleanValue(extractKeyValue(value, 'EAS'));
951
+ if (typeof easChoice === 'boolean') {
952
+ assignValue('easSetup', easChoice, 'Tech Stack & CESS Onboarding section', priority);
953
+ }
954
+ const newEasUses = inferEasUses(extractKeyValue(value, 'EAS Usage') ?? '');
955
+ if (newEasUses.length > 0) {
956
+ assignValue('easUses', newEasUses, 'Tech Stack & CESS Onboarding section', priority);
957
+ assignValue('easSetup', true, 'Tech Stack & CESS Onboarding section', priority);
958
+ }
959
+ extractPlatformDecisions(`- Deployed server: ${extractKeyValue(value, 'Deployed server') ?? ''}`, assignValue, priority);
960
+ const initialDeploymentPlan = extractKeyValue(value, 'Initial Deployment plan');
961
+ if (initialDeploymentPlan && isConcreteChoiceValue(initialDeploymentPlan)) {
962
+ assignValue('deploymentTarget', initialDeploymentPlan, 'Tech Stack & CESS Onboarding section', priority);
963
+ }
964
+ assignBooleanKey(value, 'Start with MDS project guidelines template', 'guidelinesTemplate', assignValue, priority);
965
+ assignBooleanKey(value, 'Use test-to-main safeguards', 'testToMainSafeguards', assignValue, priority);
739
966
  const language = normalizeChoice(extractKeyValue(value, 'Language'), ['typescript', 'javascript']);
740
967
  if (language) {
741
- assignValue('scriptLanguage', language, 'Tech Stack & MDS Onboarding section');
968
+ assignValue('scriptLanguage', language, 'Tech Stack & MDS Onboarding section', priority);
742
969
  }
743
970
  const packageManager = normalizeChoice(extractKeyValue(value, 'Package manager'), ['npm', 'pnpm', 'yarn', 'bun']);
744
971
  if (packageManager) {
745
- assignValue('packageManager', packageManager, 'Tech Stack & MDS Onboarding section');
972
+ assignValue('packageManager', packageManager, 'Tech Stack & MDS Onboarding section', priority);
746
973
  }
747
974
  const routing = (extractKeyValue(value, 'Routing') ?? '').toLowerCase();
748
975
  if (routing.includes('react navigation')) {
749
- assignValue('navigationLibrary', 'react-navigation', 'Tech Stack & MDS Onboarding section');
976
+ assignValue('navigationLibrary', 'react-navigation', 'Tech Stack & MDS Onboarding section', priority);
750
977
  if (routing.includes('tabs')) {
751
- assignValue('reactNavigationLayout', 'tabs', 'Tech Stack & MDS Onboarding section');
978
+ assignValue('reactNavigationLayout', 'tabs', 'Tech Stack & MDS Onboarding section', priority);
752
979
  }
753
980
  else if (routing.includes('drawer')) {
754
- assignValue('reactNavigationLayout', 'drawer', 'Tech Stack & MDS Onboarding section');
981
+ assignValue('reactNavigationLayout', 'drawer', 'Tech Stack & MDS Onboarding section', priority);
755
982
  }
756
983
  else {
757
- assignValue('reactNavigationLayout', 'stack', 'Tech Stack & MDS Onboarding section');
984
+ assignValue('reactNavigationLayout', 'stack', 'Tech Stack & MDS Onboarding section', priority);
758
985
  }
759
986
  }
760
987
  else if (routing.includes('expo router')) {
761
- assignValue('navigationLibrary', 'expo-router', 'Tech Stack & MDS Onboarding section');
988
+ assignValue('navigationLibrary', 'expo-router', 'Tech Stack & MDS Onboarding section', priority);
762
989
  }
763
990
  const styling = (extractKeyValue(value, 'Styling') ?? '').toLowerCase();
764
991
  if (styling.includes('uniwind')) {
765
- assignValue('stylingSystem', 'uniwind', 'Tech Stack & MDS Onboarding section');
992
+ assignValue('stylingSystem', 'uniwind', 'Tech Stack & MDS Onboarding section', priority);
766
993
  }
767
994
  else if (styling.includes('nativewind')) {
768
- assignValue('stylingSystem', 'nativewind', 'Tech Stack & MDS Onboarding section');
995
+ assignValue('stylingSystem', 'nativewind', 'Tech Stack & MDS Onboarding section', priority);
769
996
  }
770
997
  else if (styling.includes('tamagui')) {
771
- assignValue('stylingSystem', 'tamagui', 'Tech Stack & MDS Onboarding section');
998
+ assignValue('stylingSystem', 'tamagui', 'Tech Stack & MDS Onboarding section', priority);
772
999
  }
773
1000
  else if (styling.includes('restyle')) {
774
- assignValue('stylingSystem', 'restyle', 'Tech Stack & MDS Onboarding section');
1001
+ assignValue('stylingSystem', 'restyle', 'Tech Stack & MDS Onboarding section', priority);
775
1002
  }
776
1003
  else if (styling.includes('stylesheet')) {
777
- assignValue('stylingSystem', 'stylesheet', 'Tech Stack & MDS Onboarding section');
1004
+ assignValue('stylingSystem', 'stylesheet', 'Tech Stack & MDS Onboarding section', priority);
778
1005
  }
779
1006
  const stateManagement = (extractKeyValue(value, 'State management') ?? '').toLowerCase();
780
1007
  if (stateManagement.includes('zustand')) {
781
- assignValue('stateManagement', 'zustand', 'Tech Stack & MDS Onboarding section');
1008
+ assignValue('stateManagement', 'zustand', 'Tech Stack & MDS Onboarding section', priority);
782
1009
  }
783
1010
  else if (stateManagement.includes('none')) {
784
- assignValue('stateManagement', 'none', 'Tech Stack & MDS Onboarding section');
1011
+ assignValue('stateManagement', 'none', 'Tech Stack & MDS Onboarding section', priority);
785
1012
  }
786
1013
  const auth = inferAuthBackend(extractKeyValue(value, 'Auth') ?? '');
787
1014
  if (auth) {
788
- assignValue('authBackend', auth, 'Tech Stack & MDS Onboarding section');
1015
+ assignValue('authBackend', auth, 'Tech Stack & MDS Onboarding section', priority);
789
1016
  }
790
1017
  const distribution = extractKeyValue(value, 'Distribution');
791
1018
  if (distribution) {
792
- assignValue('deploymentTarget', distribution, 'Tech Stack & MDS Onboarding section');
1019
+ assignValue('deploymentTarget', distribution, 'Tech Stack & MDS Onboarding section', priority);
793
1020
  }
794
1021
  const easUses = inferEasUses(extractKeyValue(value, 'EAS') ?? '');
795
1022
  if (easUses.length > 0) {
796
- assignValue('easUses', easUses, 'Tech Stack & MDS Onboarding section');
797
- assignValue('easSetup', true, 'Tech Stack & MDS Onboarding section');
1023
+ assignValue('easUses', easUses, 'Tech Stack & MDS Onboarding section', priority);
1024
+ assignValue('easSetup', true, 'Tech Stack & MDS Onboarding section', priority);
798
1025
  }
799
- extractOnboardingDecisionLines(value, assignValue);
1026
+ extractOnboardingDecisionLines(value, assignValue, priority);
800
1027
  }
801
- function extractOnboardingDecisionLines(value, assignValue) {
1028
+ function extractOnboardingDecisionLines(value, assignValue, priority = VISIBLE_SECTION_PRIORITY) {
802
1029
  const lines = normalizeLineEndings(value)
803
1030
  .split('\n')
804
1031
  .map((line) => line.trim())
@@ -815,57 +1042,57 @@ function extractOnboardingDecisionLines(value, assignValue) {
815
1042
  continue;
816
1043
  }
817
1044
  if (loweredKey === 'create expo starter components') {
818
- assignValue('includeCreateExpoComponents', parseBooleanValue(rawValue), 'Onboarding decisions');
1045
+ assignValue('includeCreateExpoComponents', parseBooleanValue(rawValue), 'Onboarding decisions', priority);
819
1046
  }
820
1047
  else if (loweredKey === 'latest expo sdk preference') {
821
1048
  // Captured for human context only; SDK targeting is enforced by the generator.
822
1049
  continue;
823
1050
  }
824
1051
  else if (loweredKey === 'expo ui') {
825
- assignValue('usesExpoUi', parseBooleanValue(rawValue), 'Onboarding decisions');
1052
+ assignValue('usesExpoUi', parseBooleanValue(rawValue), 'Onboarding decisions', priority);
826
1053
  }
827
1054
  else if (loweredKey === 'expo ui universal components') {
828
- assignValue('usesExpoUiUniversalComponents', parseBooleanValue(rawValue), 'Onboarding decisions');
1055
+ assignValue('usesExpoUiUniversalComponents', parseBooleanValue(rawValue), 'Onboarding decisions', priority);
829
1056
  }
830
1057
  else if (loweredKey === 'expo native tabs') {
831
- assignValue('usesExpoNativeTabs', parseBooleanValue(rawValue), 'Onboarding decisions');
1058
+ assignValue('usesExpoNativeTabs', parseBooleanValue(rawValue), 'Onboarding decisions', priority);
832
1059
  }
833
1060
  else if (loweredKey === 'target platforms') {
834
1061
  const targetPlatforms = parsePlatformList(rawValue);
835
1062
  if (targetPlatforms.length > 0) {
836
- assignValue('targetPlatforms', targetPlatforms, 'Onboarding decisions');
1063
+ assignValue('targetPlatforms', targetPlatforms, 'Onboarding decisions', priority);
837
1064
  }
838
1065
  }
839
1066
  else if (loweredKey === 'first mvp platform') {
840
- assignValue('firstTargetPlatform', normalizeChoice(rawValue, PLATFORM_OPTIONS), 'Onboarding decisions');
1067
+ assignValue('firstTargetPlatform', normalizeChoice(rawValue, PLATFORM_OPTIONS), 'Onboarding decisions', priority);
841
1068
  }
842
1069
  else if (loweredKey === 'expo router app directory') {
843
- assignValue('appDirectory', rawValue.includes('src/app') ? 'src' : 'root', 'Onboarding decisions');
1070
+ assignValue('appDirectory', rawValue.includes('src/app') ? 'src' : 'root', 'Onboarding decisions', priority);
844
1071
  }
845
1072
  else if (loweredKey === 'platform-specific organization') {
846
- assignValue('platformStrategy', rawValue.toLowerCase().includes('folder') ? 'folders' : 'files-only', 'Onboarding decisions');
1073
+ assignValue('platformStrategy', rawValue.toLowerCase().includes('folder') ? 'folders' : 'files-only', 'Onboarding decisions', priority);
847
1074
  }
848
1075
  else if (loweredKey === 'platform layout mode') {
849
- assignValue('platformLayouts', rawValue.toLowerCase().includes('platform-specific') ? 'platform-specific' : 'shared', 'Onboarding decisions');
1076
+ assignValue('platformLayouts', rawValue.toLowerCase().includes('platform-specific') ? 'platform-specific' : 'shared', 'Onboarding decisions', priority);
850
1077
  }
851
1078
  else if (loweredKey === 'web output') {
852
- assignValue('webOutput', normalizeChoice(rawValue, ['static', 'server', 'spa', 'none']), 'Onboarding decisions');
1079
+ assignValue('webOutput', normalizeChoice(rawValue, ['static', 'server', 'spa', 'none']), 'Onboarding decisions', priority);
853
1080
  }
854
1081
  else if (loweredKey === 'deployed server') {
855
- extractPlatformDecisions(`- Deployed server: ${rawValue}`, assignValue);
1082
+ extractPlatformDecisions(`- Deployed server: ${rawValue}`, assignValue, priority);
856
1083
  }
857
1084
  else if (loweredKey === 'eas usage') {
858
1085
  const easUses = inferEasUses(rawValue);
859
1086
  if (easUses.length > 0) {
860
- assignValue('easUses', easUses, 'Onboarding decisions');
861
- assignValue('easSetup', true, 'Onboarding decisions');
1087
+ assignValue('easUses', easUses, 'Onboarding decisions', priority);
1088
+ assignValue('easSetup', true, 'Onboarding decisions', priority);
862
1089
  }
863
1090
  }
864
1091
  else if (loweredKey === 'data start') {
865
- assignValue('dataStart', inferDataStart(rawValue), 'Onboarding decisions');
1092
+ assignValue('dataStart', inferDataStart(rawValue), 'Onboarding decisions', priority);
866
1093
  }
867
1094
  else if (loweredKey === 'test-to-main safeguards') {
868
- assignValue('testToMainSafeguards', parseBooleanValue(rawValue), 'Onboarding decisions');
1095
+ assignValue('testToMainSafeguards', parseBooleanValue(rawValue), 'Onboarding decisions', priority);
869
1096
  }
870
1097
  }
871
1098
  }
@@ -877,9 +1104,29 @@ function extractKeyValue(value, label) {
877
1104
  const match = new RegExp(`^-\\s+(?:\\*\\*)?${escapeRegExp(label)}(?:\\*\\*)?:\\s+(.+)$`, 'imu').exec(value);
878
1105
  return normalizeText(match?.[1]);
879
1106
  }
1107
+ function assignBooleanKey(value, label, id, assignValue, priority) {
1108
+ const bool = parseBooleanValue(extractKeyValue(value, label));
1109
+ if (typeof bool === 'boolean') {
1110
+ assignValue(id, bool, 'Tech Stack & CESS Onboarding section', priority);
1111
+ }
1112
+ }
1113
+ function normalizeChoiceLabel(value, choices) {
1114
+ const normalized = normalizeText(value)?.toLowerCase().replace(/[`]/gu, '').trim();
1115
+ if (!normalized || !isConcreteChoiceValue(normalized)) {
1116
+ return undefined;
1117
+ }
1118
+ return choices.find((choice) => choice.toLowerCase() === normalized);
1119
+ }
1120
+ function isConcreteChoiceValue(value) {
1121
+ return !/\s\/\s/u.test(value);
1122
+ }
880
1123
  function inferEasUses(value) {
881
1124
  const normalized = value.toLowerCase();
882
- return EAS_USE_OPTIONS.filter((item) => normalized.includes(item.toLowerCase()));
1125
+ const selected = EAS_USE_OPTIONS.filter((item) => normalized.includes(item.toLowerCase()));
1126
+ if (/\bbuilding mobile apps?\b/u.test(normalized) && !selected.includes('building mobile applications')) {
1127
+ selected.push('building mobile applications');
1128
+ }
1129
+ return selected;
883
1130
  }
884
1131
  function parsePlatformList(value) {
885
1132
  const normalized = value
@@ -934,17 +1181,17 @@ function normalizeExtractedAnswerValue(id, value) {
934
1181
  case 'reactNavigationLayout':
935
1182
  return normalizeEnum(value, ['stack', 'tabs', 'drawer']);
936
1183
  case 'stylingSystem':
937
- return normalizeEnum(value, ['uniwind', 'nativewind', 'tamagui', 'restyle', 'stylesheet']);
1184
+ return normalizeEnum(value, ['uniwind', 'nativewind', 'nativewindui', 'tamagui', 'restyle', 'stylesheet']);
938
1185
  case 'stateManagement':
939
1186
  return normalizeEnum(value, ['zustand', 'none']);
940
1187
  case 'authBackend':
941
1188
  return normalizeEnum(value, ['none', 'supabase', 'firebase']);
942
1189
  case 'platformStrategy':
943
- return normalizeEnum(value, ['folders', 'files-only']);
1190
+ return normalizePlatformStrategyValue(value);
944
1191
  case 'appDirectory':
945
- return normalizeEnum(value, ['src', 'root']);
1192
+ return normalizeAppDirectoryValue(value);
946
1193
  case 'platformLayouts':
947
- return normalizeEnum(value, ['shared', 'platform-specific']);
1194
+ return normalizePlatformLayoutsValue(value);
948
1195
  case 'webOutput':
949
1196
  return normalizeEnum(value, ['static', 'server', 'spa', 'none']);
950
1197
  case 'expoServerAdapter':
@@ -957,6 +1204,41 @@ function normalizeExtractedAnswerValue(id, value) {
957
1204
  return normalizeText(value);
958
1205
  }
959
1206
  }
1207
+ function isGenericExtractedAnswerValue(id, value) {
1208
+ if (id === 'displayAppName') {
1209
+ return isGenericTextValue(value);
1210
+ }
1211
+ if (id === 'audience') {
1212
+ return normalizeText(value)?.toLowerCase() === 'expo app users';
1213
+ }
1214
+ if (id === 'coreFlows') {
1215
+ const normalized = normalizeText(value)?.toLowerCase();
1216
+ return (!normalized ||
1217
+ normalized === AGENT_DERIVED_CORE_FLOWS.toLowerCase() ||
1218
+ normalized.includes('let the agent derive') ||
1219
+ normalized.startsWith('# todoforcontext'));
1220
+ }
1221
+ if (id === 'screens') {
1222
+ const normalized = normalizeText(value)?.toLowerCase();
1223
+ return !normalized || normalized === 'defer' || normalized.startsWith('# todoforcontext');
1224
+ }
1225
+ if (id === 'targetPlatforms') {
1226
+ const platforms = normalizeStringArray(value);
1227
+ return (platforms?.length === PLATFORM_OPTIONS.length &&
1228
+ PLATFORM_OPTIONS.every((platform) => platforms.includes(platform)));
1229
+ }
1230
+ if (id === 'deploymentTarget') {
1231
+ return normalizeText(value)?.toLowerCase() === 'expo web/native deployment';
1232
+ }
1233
+ return false;
1234
+ }
1235
+ function isGenericTextValue(value) {
1236
+ const normalized = normalizeText(value)
1237
+ ?.toLowerCase()
1238
+ .replace(/\s+/gu, ' ')
1239
+ .trim();
1240
+ return !normalized || normalized === 'template' || normalized === 'my expo app' || normalized === 'my-expo-app';
1241
+ }
960
1242
  function areAnswerValuesEquivalent(id, left, right) {
961
1243
  if (Array.isArray(left) || Array.isArray(right)) {
962
1244
  return JSON.stringify(normalizeStringArray(left) ?? []) === JSON.stringify(normalizeStringArray(right) ?? []);
@@ -1146,6 +1428,7 @@ export function normalizeCessIntakeAnswers(answers) {
1146
1428
  normalized.stylingSystem = normalizeEnum(answers.stylingSystem, [
1147
1429
  'uniwind',
1148
1430
  'nativewind',
1431
+ 'nativewindui',
1149
1432
  'tamagui',
1150
1433
  'restyle',
1151
1434
  'stylesheet',
@@ -1161,9 +1444,9 @@ export function normalizeCessIntakeAnswers(answers) {
1161
1444
  normalized.dataNeedsOther = normalizeText(answers.dataNeedsOther);
1162
1445
  normalized.targetPlatforms = normalizePlatforms(answers.targetPlatforms);
1163
1446
  normalized.firstTargetPlatform = normalizeText(answers.firstTargetPlatform);
1164
- normalized.platformStrategy = normalizeEnum(answers.platformStrategy, ['folders', 'files-only']);
1165
- normalized.appDirectory = normalizeEnum(answers.appDirectory, ['src', 'root']);
1166
- normalized.platformLayouts = normalizeEnum(answers.platformLayouts, ['shared', 'platform-specific']);
1447
+ normalized.platformStrategy = normalizePlatformStrategyValue(answers.platformStrategy);
1448
+ normalized.appDirectory = normalizeAppDirectoryValue(answers.appDirectory);
1449
+ normalized.platformLayouts = normalizePlatformLayoutsValue(answers.platformLayouts);
1167
1450
  normalized.webOutput = normalizeEnum(answers.webOutput, ['static', 'server', 'spa', 'none']);
1168
1451
  normalized.expoServerAdapter = normalizeEnum(answers.expoServerAdapter, [
1169
1452
  'eas',
@@ -1206,6 +1489,9 @@ export function buildCreateExpoStackFlags(answers) {
1206
1489
  case 'nativewind':
1207
1490
  flags.push('--nativewind');
1208
1491
  break;
1492
+ case 'nativewindui':
1493
+ flags.push('--nativewindui');
1494
+ break;
1209
1495
  case 'tamagui':
1210
1496
  flags.push('--tamagui');
1211
1497
  break;
@@ -1421,8 +1707,40 @@ function buildOnboardArgvFromCess(parentDir, appName, answers) {
1421
1707
  dataStart: answers.dataStart,
1422
1708
  testToMain: answers.testToMainSafeguards,
1423
1709
  saveDefaults: answers.saveDefaults,
1710
+ defaults: buildOnboardDefaultsFromCessAnswers(answers),
1424
1711
  };
1425
1712
  }
1713
+ function buildOnboardDefaultsFromCessAnswers(answers) {
1714
+ const defaults = new Set(['project-docs', 'guidelines']);
1715
+ switch (answers.stylingSystem) {
1716
+ case 'nativewindui':
1717
+ defaults.add('nativewindui');
1718
+ break;
1719
+ case 'nativewind':
1720
+ defaults.add('nativewind');
1721
+ break;
1722
+ case 'tamagui':
1723
+ defaults.add('tamagui');
1724
+ break;
1725
+ case 'restyle':
1726
+ defaults.add('restyle');
1727
+ break;
1728
+ case 'stylesheet':
1729
+ break;
1730
+ case 'uniwind':
1731
+ default:
1732
+ defaults.add('uniwind');
1733
+ break;
1734
+ }
1735
+ if (answers.dataStart === 'supabase' || answers.authBackend === 'supabase') {
1736
+ defaults.add('supabase');
1737
+ }
1738
+ defaults.add('doctor');
1739
+ if (answers.testToMainSafeguards ?? true) {
1740
+ defaults.add('test-to-main');
1741
+ }
1742
+ return [...defaults].join(',');
1743
+ }
1426
1744
  function materializeQuestion(definition, context) {
1427
1745
  return {
1428
1746
  id: definition.id,
@@ -1476,6 +1794,45 @@ function normalizeEnum(value, choices) {
1476
1794
  function normalizeChoice(value, choices) {
1477
1795
  return normalizeEnum(value, choices);
1478
1796
  }
1797
+ function normalizePlatformStrategyValue(value) {
1798
+ const normalized = normalizeText(value)?.toLowerCase();
1799
+ if (!normalized) {
1800
+ return undefined;
1801
+ }
1802
+ if (normalized === 'folders' || normalized.includes('folder')) {
1803
+ return 'folders';
1804
+ }
1805
+ if (normalized === 'files-only' || normalized.includes('file')) {
1806
+ return 'files-only';
1807
+ }
1808
+ return undefined;
1809
+ }
1810
+ function normalizeAppDirectoryValue(value) {
1811
+ const normalized = normalizeText(value)?.toLowerCase().replace(/[`]/gu, '').trim();
1812
+ if (!normalized) {
1813
+ return undefined;
1814
+ }
1815
+ if (normalized === 'src' || normalized === 'src/app') {
1816
+ return 'src';
1817
+ }
1818
+ if (normalized === 'root' || normalized === 'app') {
1819
+ return 'root';
1820
+ }
1821
+ return undefined;
1822
+ }
1823
+ function normalizePlatformLayoutsValue(value) {
1824
+ const normalized = normalizeText(value)?.toLowerCase();
1825
+ if (!normalized) {
1826
+ return undefined;
1827
+ }
1828
+ if (normalized === 'shared' || normalized.includes('shared')) {
1829
+ return 'shared';
1830
+ }
1831
+ if (normalized === 'platform-specific' || normalized.includes('platform-specific')) {
1832
+ return 'platform-specific';
1833
+ }
1834
+ return undefined;
1835
+ }
1479
1836
  function normalizeText(value) {
1480
1837
  if (typeof value !== 'string') {
1481
1838
  return undefined;
@@ -1532,6 +1889,8 @@ function formatStylingLabel(value) {
1532
1889
  return 'Uniwind';
1533
1890
  case 'nativewind':
1534
1891
  return 'NativeWind';
1892
+ case 'nativewindui':
1893
+ return 'NativeWindUI';
1535
1894
  case 'tamagui':
1536
1895
  return 'Tamagui';
1537
1896
  case 'restyle':