@mr.dj2u/cli 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundles/claude-code/.claude-plugin/plugin.json +20 -20
- package/bundles/claude-code/.mcp.json +11 -11
- package/bundles/claude-code/agents/mds.md +36 -36
- package/bundles/claude-code/commands/create-expo-super-stack.md +34 -32
- package/bundles/claude-code/skills/create-expo-super-stack/SKILL.md +9 -7
- package/bundles/claude-code/skills/super-stack-startup/SKILL.md +4 -4
- package/bundles/codex/.codex-plugin/plugin.json +42 -42
- package/bundles/codex/.mcp.json +11 -11
- package/bundles/codex/README.md +51 -51
- package/bundles/codex/commands/create-expo-super-stack.md +34 -32
- package/bundles/codex/skills/super-stack-startup/SKILL.md +34 -34
- package/bundles/codex/skills/workflow-continue-development/SKILL.md +48 -48
- package/bundles/codex/skills/workflow-create-expo-super-stack/SKILL.md +47 -45
- package/bundles/codex/skills/workflow-fix-seo/SKILL.md +42 -42
- package/bundles/codex/skills/workflow-prepare-deploy/SKILL.md +42 -42
- package/bundles/codex/skills/workflow-project-research-plan/SKILL.md +42 -42
- package/bundles/codex/skills/workflow-push-merge-loop/SKILL.md +37 -37
- package/bundles/codex/skills/workflow-review-expo-project/SKILL.md +42 -42
- package/bundles/codex/skills/workflow-run-doctor/SKILL.md +51 -51
- package/bundles/codex/skills/workflow-wrap-up/SKILL.md +80 -80
- package/bundles/vscode-copilot/.github/agents/mds.agent.md +22 -22
- package/bundles/vscode-copilot/.github/copilot-instructions.md +8 -8
- package/bundles/vscode-copilot/.github/prompts/continue-development.prompt.md +40 -40
- package/bundles/vscode-copilot/.github/prompts/create-expo-super-stack.prompt.md +39 -37
- package/bundles/vscode-copilot/.github/prompts/fix-seo.prompt.md +34 -34
- package/bundles/vscode-copilot/.github/prompts/prepare-deploy.prompt.md +34 -34
- package/bundles/vscode-copilot/.github/prompts/project-research-plan.prompt.md +34 -34
- package/bundles/vscode-copilot/.github/prompts/push-merge-loop.prompt.md +30 -30
- package/bundles/vscode-copilot/.github/prompts/review-expo-project.prompt.md +34 -34
- package/bundles/vscode-copilot/.github/prompts/run-doctor.prompt.md +43 -43
- package/bundles/vscode-copilot/.github/prompts/wrap-up.prompt.md +72 -72
- package/bundles/vscode-copilot/.github/skills/super-stack-startup/SKILL.md +39 -39
- package/bundles/vscode-copilot/.vscode/mcp.json +11 -11
- package/bundles/vscode-copilot/user/.copilot/agents/mds.agent.md +22 -22
- package/bundles/vscode-copilot/user/.copilot/instructions.md +8 -8
- package/bundles/vscode-copilot/user/.copilot/skills/super-stack-startup/SKILL.md +39 -39
- package/bundles/vscode-copilot/user/.copilot/skills/workflow-create-expo-super-stack/SKILL.md +39 -37
- package/dist/cess-intake.d.ts +16 -0
- package/dist/cess-intake.d.ts.map +1 -1
- package/dist/cess-intake.js +677 -15
- package/dist/cess-intake.js.map +1 -1
- package/dist/commands/mcp-install.d.ts +2 -2
- package/dist/commands/mcp-install.js +1 -1
- package/dist/commands/onboard.d.ts +8 -0
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +8 -0
- package/dist/commands/onboard.js.map +1 -1
- package/dist/commands/stylist.d.ts.map +1 -1
- package/dist/commands/stylist.js +5 -1
- package/dist/commands/stylist.js.map +1 -1
- package/dist/project-memory.d.ts +8 -0
- package/dist/project-memory.d.ts.map +1 -1
- package/dist/project-memory.js +89 -5
- package/dist/project-memory.js.map +1 -1
- package/package.json +3 -3
package/dist/cess-intake.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { AGENT_DERIVED_CORE_FLOWS, CUSTOM_BACKEND_EXPLANATION, DATA_NEED_OPTIONS, DATA_START_EXPLANATION, EAS_EXPLANATION, EAS_USE_OPTIONS, EXPO_SERVER_ADAPTER_EXPLANATION, OTHER_DATA_NEEDS, PLATFORM_OPTIONS, TEST_TO_MAIN_EXPLANATION, defaultOnboardPlan, deriveDeployedServer, formatDataNeedsSelection, } from './commands/onboard.js';
|
|
3
3
|
const DEFAULT_PROJECT_NAME = 'my-expo-app';
|
|
4
|
+
const DEFAULT_DISPLAY_APP_NAME = 'My Expo App';
|
|
4
5
|
const STACK_DEFAULTS = {
|
|
5
6
|
scriptLanguage: 'typescript',
|
|
6
7
|
packageManager: 'npm',
|
|
@@ -20,9 +21,9 @@ const CESS_QUESTIONS = [
|
|
|
20
21
|
},
|
|
21
22
|
{
|
|
22
23
|
id: 'appName',
|
|
23
|
-
prompt: 'What
|
|
24
|
+
prompt: 'What is the name of your app?',
|
|
24
25
|
kind: 'text',
|
|
25
|
-
defaultValue: (context) => context.
|
|
26
|
+
defaultValue: (context) => context.appDisplayName,
|
|
26
27
|
},
|
|
27
28
|
{
|
|
28
29
|
id: 'scriptLanguage',
|
|
@@ -112,12 +113,6 @@ const CESS_QUESTIONS = [
|
|
|
112
113
|
],
|
|
113
114
|
defaultValue: () => STACK_DEFAULTS.easSetup,
|
|
114
115
|
},
|
|
115
|
-
{
|
|
116
|
-
id: 'displayAppName',
|
|
117
|
-
prompt: 'What display app name should MDS use in project memory?',
|
|
118
|
-
kind: 'text',
|
|
119
|
-
defaultValue: (context) => context.onboardAnswers.appName,
|
|
120
|
-
},
|
|
121
116
|
{
|
|
122
117
|
id: 'audience',
|
|
123
118
|
prompt: 'Who is this app for?',
|
|
@@ -353,22 +348,676 @@ const CESS_QUESTIONS = [
|
|
|
353
348
|
defaultValue: () => false,
|
|
354
349
|
},
|
|
355
350
|
];
|
|
351
|
+
const CESS_SNAPSHOT_START = '<!-- MDS_CESS_SNAPSHOT_START -->';
|
|
352
|
+
const CESS_SNAPSHOT_END = '<!-- MDS_CESS_SNAPSHOT_END -->';
|
|
353
|
+
export function extractCessInfoFromMarkdown(input) {
|
|
354
|
+
const infoMarkdown = normalizeLineEndings(input.infoMarkdown);
|
|
355
|
+
const sections = parseMarkdownSections(infoMarkdown);
|
|
356
|
+
const evidence = {};
|
|
357
|
+
const prefilledAnswers = {};
|
|
358
|
+
const ambiguousQuestionIds = new Set();
|
|
359
|
+
const usedSections = new Set();
|
|
360
|
+
const explicitAppName = normalizeText(input.appName);
|
|
361
|
+
let derivedDisplayName = explicitAppName;
|
|
362
|
+
let derivedFolderSlug = explicitAppName ? slugifyAppName(explicitAppName) : undefined;
|
|
363
|
+
const recordEvidence = (key, note) => {
|
|
364
|
+
evidence[key] = [...(evidence[key] ?? []), note];
|
|
365
|
+
};
|
|
366
|
+
const assignValue = (id, value, note) => {
|
|
367
|
+
const normalized = normalizeExtractedAnswerValue(id, value);
|
|
368
|
+
if (normalized === undefined) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (ambiguousQuestionIds.has(id)) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const current = prefilledAnswers[id];
|
|
375
|
+
if (current !== undefined && !areAnswerValuesEquivalent(id, current, normalized)) {
|
|
376
|
+
delete prefilledAnswers[id];
|
|
377
|
+
ambiguousQuestionIds.add(id);
|
|
378
|
+
recordEvidence(id, `${note} (conflicts with earlier extracted value)`);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
prefilledAnswers[id] = normalized;
|
|
382
|
+
recordEvidence(id, note);
|
|
383
|
+
};
|
|
384
|
+
const snapshot = parseCessSnapshot(infoMarkdown);
|
|
385
|
+
if (snapshot) {
|
|
386
|
+
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');
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const title = extractProjectTitle(infoMarkdown);
|
|
399
|
+
if (!derivedDisplayName && title) {
|
|
400
|
+
derivedDisplayName = title;
|
|
401
|
+
recordEvidence('appName', `Derived app name from title: ${title}`);
|
|
402
|
+
}
|
|
403
|
+
if (!derivedFolderSlug && derivedDisplayName) {
|
|
404
|
+
derivedFolderSlug = slugifyAppName(derivedDisplayName);
|
|
405
|
+
}
|
|
406
|
+
const targetUsers = sections.get('Target Users');
|
|
407
|
+
if (targetUsers) {
|
|
408
|
+
usedSections.add('Target Users');
|
|
409
|
+
assignValue('audience', normalizeSectionText(targetUsers), 'Target Users section');
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
const overview = sections.get('Overview');
|
|
413
|
+
const overviewAudience = extractAudienceFromOverview(overview);
|
|
414
|
+
if (overviewAudience) {
|
|
415
|
+
usedSections.add('Overview');
|
|
416
|
+
assignValue('audience', overviewAudience, 'Overview section');
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const coreUserFlows = sections.get('Core User Flows');
|
|
420
|
+
if (coreUserFlows) {
|
|
421
|
+
usedSections.add('Core User Flows');
|
|
422
|
+
assignValue('coreFlows', normalizeListSection(coreUserFlows), 'Core User Flows section');
|
|
423
|
+
}
|
|
424
|
+
const mustIncludeScreens = sections.get('Must-Include Screens Or Flows');
|
|
425
|
+
if (mustIncludeScreens) {
|
|
426
|
+
usedSections.add('Must-Include Screens Or Flows');
|
|
427
|
+
assignValue('screens', normalizeListSection(mustIncludeScreens), 'Must-Include Screens Or Flows section');
|
|
428
|
+
}
|
|
429
|
+
const dataAndBackend = sections.get('Data And Backend');
|
|
430
|
+
if (dataAndBackend) {
|
|
431
|
+
usedSections.add('Data And Backend');
|
|
432
|
+
const inferredDataNeeds = inferDataNeedSelections(dataAndBackend);
|
|
433
|
+
if (inferredDataNeeds.length > 0) {
|
|
434
|
+
assignValue('dataNeedSelections', inferredDataNeeds, 'Data And Backend section');
|
|
435
|
+
}
|
|
436
|
+
const dataStart = inferDataStart(dataAndBackend);
|
|
437
|
+
if (dataStart) {
|
|
438
|
+
assignValue('dataStart', dataStart, 'Data And Backend section');
|
|
439
|
+
}
|
|
440
|
+
const authBackend = inferAuthBackend(dataAndBackend);
|
|
441
|
+
if (authBackend) {
|
|
442
|
+
assignValue('authBackend', authBackend, 'Data And Backend section');
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const platforms = sections.get('Platforms');
|
|
446
|
+
if (platforms) {
|
|
447
|
+
usedSections.add('Platforms');
|
|
448
|
+
extractPlatformDecisions(platforms, assignValue);
|
|
449
|
+
}
|
|
450
|
+
const packageChoices = sections.get('Package Choices');
|
|
451
|
+
if (packageChoices) {
|
|
452
|
+
usedSections.add('Package Choices');
|
|
453
|
+
extractPackageChoices(packageChoices, assignValue);
|
|
454
|
+
}
|
|
455
|
+
const releaseStrategy = sections.get('Release Strategy');
|
|
456
|
+
if (releaseStrategy) {
|
|
457
|
+
usedSections.add('Release Strategy');
|
|
458
|
+
const deploymentTarget = extractBulletValue(releaseStrategy, 'Deployment plan');
|
|
459
|
+
if (deploymentTarget) {
|
|
460
|
+
assignValue('deploymentTarget', deploymentTarget, 'Release Strategy section');
|
|
461
|
+
}
|
|
462
|
+
const easUses = inferEasUses(extractBulletValue(releaseStrategy, 'EAS usage') ?? releaseStrategy);
|
|
463
|
+
if (easUses.length > 0) {
|
|
464
|
+
assignValue('easUses', easUses, 'Release Strategy section');
|
|
465
|
+
assignValue('easSetup', true, 'Release Strategy section');
|
|
466
|
+
}
|
|
467
|
+
const testToMain = parseBooleanValue(extractBulletValue(releaseStrategy, 'Test-to-main safeguards'));
|
|
468
|
+
if (typeof testToMain === 'boolean') {
|
|
469
|
+
assignValue('testToMainSafeguards', testToMain, 'Release Strategy section');
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const techStackSection = sections.get('Tech Stack & MDS Onboarding');
|
|
473
|
+
if (techStackSection) {
|
|
474
|
+
usedSections.add('Tech Stack & MDS Onboarding');
|
|
475
|
+
extractTechStackDecisions(techStackSection, assignValue);
|
|
476
|
+
}
|
|
477
|
+
const onboardingDecisionsSection = sections.get('Onboarding Decisions');
|
|
478
|
+
if (onboardingDecisionsSection) {
|
|
479
|
+
usedSections.add('Onboarding Decisions');
|
|
480
|
+
extractOnboardingDecisionLines(onboardingDecisionsSection, assignValue);
|
|
481
|
+
}
|
|
482
|
+
if (derivedDisplayName) {
|
|
483
|
+
assignValue('displayAppName', derivedDisplayName, 'Derived app name');
|
|
484
|
+
}
|
|
485
|
+
const preservedNotes = [...sections.entries()]
|
|
486
|
+
.filter(([heading, body]) => !usedSections.has(heading) && normalizeSectionText(body))
|
|
487
|
+
.map(([heading, body]) => `## ${heading}\n\n${normalizeSectionText(body)}`);
|
|
488
|
+
const missingQuestionIds = validateCessGenerationReadiness({
|
|
489
|
+
parentDir: input.parentDir,
|
|
490
|
+
appName: derivedFolderSlug,
|
|
491
|
+
answers: prefilledAnswers,
|
|
492
|
+
cwd: input.cwd,
|
|
493
|
+
});
|
|
494
|
+
return {
|
|
495
|
+
prefilledAnswers,
|
|
496
|
+
derivedDisplayName,
|
|
497
|
+
derivedFolderSlug,
|
|
498
|
+
missingQuestionIds,
|
|
499
|
+
ambiguousQuestionIds: Array.from(ambiguousQuestionIds),
|
|
500
|
+
evidence,
|
|
501
|
+
preservedNotes,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function parseCessSnapshot(infoMarkdown) {
|
|
505
|
+
const match = new RegExp(`${escapeRegExp(CESS_SNAPSHOT_START)}([\\s\\S]*?)${escapeRegExp(CESS_SNAPSHOT_END)}`, 'u').exec(infoMarkdown);
|
|
506
|
+
if (!match?.[1]) {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
const rawBlock = match[1].trim();
|
|
510
|
+
const jsonMatch = /```json\s*([\s\S]*?)```/u.exec(rawBlock);
|
|
511
|
+
const jsonText = (jsonMatch?.[1] ?? rawBlock).trim();
|
|
512
|
+
if (!jsonText) {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
try {
|
|
516
|
+
const parsed = JSON.parse(jsonText);
|
|
517
|
+
const answers = typeof parsed.answers === 'object' && parsed.answers && !Array.isArray(parsed.answers)
|
|
518
|
+
? parsed.answers
|
|
519
|
+
: undefined;
|
|
520
|
+
return {
|
|
521
|
+
displayAppName: normalizeText(parsed.displayAppName),
|
|
522
|
+
folderSlug: normalizeText(parsed.folderSlug),
|
|
523
|
+
answers,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function extractProjectTitle(infoMarkdown) {
|
|
531
|
+
const match = /^#\s+(.+?)\s*$/mu.exec(infoMarkdown);
|
|
532
|
+
if (!match?.[1]) {
|
|
533
|
+
return undefined;
|
|
534
|
+
}
|
|
535
|
+
return normalizeProjectTitle(match[1]);
|
|
536
|
+
}
|
|
537
|
+
function parseMarkdownSections(markdown) {
|
|
538
|
+
const sections = new Map();
|
|
539
|
+
const matches = [...markdown.matchAll(/^##\s+(.+?)\s*$/gmu)];
|
|
540
|
+
for (let index = 0; index < matches.length; index += 1) {
|
|
541
|
+
const match = matches[index];
|
|
542
|
+
const next = matches[index + 1];
|
|
543
|
+
if (!match) {
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
const heading = match?.[1]?.trim();
|
|
547
|
+
if (!heading || match.index === undefined) {
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
const start = match.index + match[0].length;
|
|
551
|
+
const end = next?.index ?? markdown.length;
|
|
552
|
+
sections.set(heading, markdown.slice(start, end).trim());
|
|
553
|
+
}
|
|
554
|
+
return sections;
|
|
555
|
+
}
|
|
556
|
+
function extractAudienceFromOverview(overview) {
|
|
557
|
+
const text = normalizeSectionText(overview);
|
|
558
|
+
if (!text) {
|
|
559
|
+
return undefined;
|
|
560
|
+
}
|
|
561
|
+
const match = /^Build an Expo app for\s+(.+?)[.]\s*$/iu.exec(text);
|
|
562
|
+
return normalizeText(match?.[1] ?? text);
|
|
563
|
+
}
|
|
564
|
+
function normalizeSectionText(value) {
|
|
565
|
+
const normalized = normalizeLineEndings(value ?? '')
|
|
566
|
+
.replace(/<!--[\s\S]*?-->/gu, '')
|
|
567
|
+
.replace(/^\s*>/gmu, '')
|
|
568
|
+
.trim();
|
|
569
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
570
|
+
}
|
|
571
|
+
function normalizeListSection(value) {
|
|
572
|
+
const text = normalizeSectionText(value);
|
|
573
|
+
if (!text) {
|
|
574
|
+
return undefined;
|
|
575
|
+
}
|
|
576
|
+
const bulletLines = text
|
|
577
|
+
.split('\n')
|
|
578
|
+
.map((line) => line.trim())
|
|
579
|
+
.filter(Boolean)
|
|
580
|
+
.map((line) => line.replace(/^[-*]\s+/u, ''))
|
|
581
|
+
.filter((line) => !line.startsWith('# TodoForContext'));
|
|
582
|
+
return bulletLines.length > 0 ? bulletLines.join('\n') : text;
|
|
583
|
+
}
|
|
584
|
+
function inferDataNeedSelections(value) {
|
|
585
|
+
const normalized = value.toLowerCase();
|
|
586
|
+
const selected = new Set();
|
|
587
|
+
for (const option of DATA_NEED_OPTIONS) {
|
|
588
|
+
if (normalized.includes(option.toLowerCase())) {
|
|
589
|
+
selected.add(option);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (/\blocal\b|\bsqlite\b/u.test(normalized)) {
|
|
593
|
+
selected.add('Local UI/app state');
|
|
594
|
+
}
|
|
595
|
+
if (/\bauth\b|\buser account\b|\bsign in\b|\bsign-up\b|\bsign up\b/u.test(normalized)) {
|
|
596
|
+
selected.add('User accounts/authentication');
|
|
597
|
+
}
|
|
598
|
+
if (/\bdatabase\b|\bsupabase\b|\brecords\b/u.test(normalized)) {
|
|
599
|
+
selected.add('Backend database records');
|
|
600
|
+
}
|
|
601
|
+
if (/\bfile\b|\bimage\b|\bphoto\b|\bupload\b|\bstorage\b/u.test(normalized)) {
|
|
602
|
+
selected.add('File/image uploads or storage');
|
|
603
|
+
}
|
|
604
|
+
if (/\bapi\b|\bintegration\b/u.test(normalized)) {
|
|
605
|
+
selected.add('External APIs/integrations');
|
|
606
|
+
}
|
|
607
|
+
if (/\banalytics\b|\bevents\b/u.test(normalized)) {
|
|
608
|
+
selected.add('Analytics/events');
|
|
609
|
+
}
|
|
610
|
+
if (/\bpayments?\b|\bsubscription\b/u.test(normalized)) {
|
|
611
|
+
selected.add('Payments/subscriptions');
|
|
612
|
+
}
|
|
613
|
+
if (/\brealtime\b|\bcollaboration\b/u.test(normalized)) {
|
|
614
|
+
selected.add('Realtime/collaboration');
|
|
615
|
+
}
|
|
616
|
+
if (/\bpush\b|\bemail\b|\bnotification\b/u.test(normalized)) {
|
|
617
|
+
selected.add('Push/email notifications');
|
|
618
|
+
}
|
|
619
|
+
if (/\boffline\b|\bcache\b|\bsync\b/u.test(normalized)) {
|
|
620
|
+
selected.add('Offline sync/cache');
|
|
621
|
+
}
|
|
622
|
+
if (/\badmin\b|\bmoderation\b/u.test(normalized)) {
|
|
623
|
+
selected.add('Admin/moderation tools');
|
|
624
|
+
}
|
|
625
|
+
return Array.from(selected);
|
|
626
|
+
}
|
|
627
|
+
function inferDataStart(value) {
|
|
628
|
+
const normalized = value.toLowerCase();
|
|
629
|
+
if (normalized.includes('supabase')) {
|
|
630
|
+
return 'supabase';
|
|
631
|
+
}
|
|
632
|
+
if (normalized.includes('local dummy data') || normalized.includes('sqlite') || normalized.includes('local ui/app state')) {
|
|
633
|
+
return 'local';
|
|
634
|
+
}
|
|
635
|
+
return undefined;
|
|
636
|
+
}
|
|
637
|
+
function inferAuthBackend(value) {
|
|
638
|
+
const normalized = value.toLowerCase();
|
|
639
|
+
if (normalized.includes('supabase')) {
|
|
640
|
+
return 'supabase';
|
|
641
|
+
}
|
|
642
|
+
if (normalized.includes('firebase')) {
|
|
643
|
+
return 'firebase';
|
|
644
|
+
}
|
|
645
|
+
return undefined;
|
|
646
|
+
}
|
|
647
|
+
function extractPlatformDecisions(value, assignValue) {
|
|
648
|
+
const targetPlatforms = parsePlatformList(extractBulletValue(value, 'Target platforms'));
|
|
649
|
+
if (targetPlatforms.length > 0) {
|
|
650
|
+
assignValue('targetPlatforms', targetPlatforms, 'Platforms section');
|
|
651
|
+
}
|
|
652
|
+
const firstTargetPlatform = normalizeChoice(extractBulletValue(value, 'First MVP platform'), PLATFORM_OPTIONS);
|
|
653
|
+
if (firstTargetPlatform) {
|
|
654
|
+
assignValue('firstTargetPlatform', firstTargetPlatform, 'Platforms section');
|
|
655
|
+
}
|
|
656
|
+
const appDirectoryValue = extractBulletValue(value, 'Expo Router app directory');
|
|
657
|
+
if (appDirectoryValue?.includes('src/app')) {
|
|
658
|
+
assignValue('appDirectory', 'src', 'Platforms section');
|
|
659
|
+
}
|
|
660
|
+
else if (appDirectoryValue?.includes('app')) {
|
|
661
|
+
assignValue('appDirectory', 'root', 'Platforms section');
|
|
662
|
+
}
|
|
663
|
+
const organization = extractBulletValue(value, 'Platform-specific organization') ?? '';
|
|
664
|
+
if (organization.toLowerCase().includes('folder')) {
|
|
665
|
+
assignValue('platformStrategy', 'folders', 'Platforms section');
|
|
666
|
+
}
|
|
667
|
+
else if (organization.toLowerCase().includes('file')) {
|
|
668
|
+
assignValue('platformStrategy', 'files-only', 'Platforms section');
|
|
669
|
+
}
|
|
670
|
+
const layoutMode = extractBulletValue(value, 'Platform layout mode');
|
|
671
|
+
if (layoutMode?.toLowerCase().includes('platform-specific')) {
|
|
672
|
+
assignValue('platformLayouts', 'platform-specific', 'Platforms section');
|
|
673
|
+
}
|
|
674
|
+
else if (layoutMode?.toLowerCase().includes('shared')) {
|
|
675
|
+
assignValue('platformLayouts', 'shared', 'Platforms section');
|
|
676
|
+
}
|
|
677
|
+
const webOutput = normalizeChoice(extractBulletValue(value, 'Web output'), ['static', 'server', 'spa', 'none']);
|
|
678
|
+
if (webOutput) {
|
|
679
|
+
assignValue('webOutput', webOutput, 'Platforms section');
|
|
680
|
+
}
|
|
681
|
+
const deployedServer = (extractBulletValue(value, 'Deployed server') ?? '').toLowerCase();
|
|
682
|
+
if (deployedServer.includes('no deployed server') || deployedServer === 'none') {
|
|
683
|
+
assignValue('expoServerAdapter', 'none', 'Platforms section');
|
|
684
|
+
assignValue('customBackend', false, 'Platforms section');
|
|
685
|
+
}
|
|
686
|
+
else if (deployedServer.includes('eas')) {
|
|
687
|
+
assignValue('expoServerAdapter', 'eas', 'Platforms section');
|
|
688
|
+
}
|
|
689
|
+
else if (deployedServer.includes('express')) {
|
|
690
|
+
assignValue('expoServerAdapter', 'express', 'Platforms section');
|
|
691
|
+
}
|
|
692
|
+
else if (deployedServer.includes('bun')) {
|
|
693
|
+
assignValue('expoServerAdapter', 'bun', 'Platforms section');
|
|
694
|
+
}
|
|
695
|
+
else if (deployedServer.includes('custom')) {
|
|
696
|
+
assignValue('expoServerAdapter', 'other', 'Platforms section');
|
|
697
|
+
assignValue('customBackend', true, 'Platforms section');
|
|
698
|
+
}
|
|
699
|
+
const expoUi = parseBooleanValue(extractBulletValue(value, 'Expo UI'));
|
|
700
|
+
if (typeof expoUi === 'boolean') {
|
|
701
|
+
assignValue('usesExpoUi', expoUi, 'Platforms section');
|
|
702
|
+
}
|
|
703
|
+
const expoUiUniversal = parseBooleanValue(extractBulletValue(value, 'Expo UI Universal components'));
|
|
704
|
+
if (typeof expoUiUniversal === 'boolean') {
|
|
705
|
+
assignValue('usesExpoUiUniversalComponents', expoUiUniversal, 'Platforms section');
|
|
706
|
+
}
|
|
707
|
+
const expoNativeTabs = parseBooleanValue(extractBulletValue(value, 'Expo Native Tabs'));
|
|
708
|
+
if (typeof expoNativeTabs === 'boolean') {
|
|
709
|
+
assignValue('usesExpoNativeTabs', expoNativeTabs, 'Platforms section');
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function extractPackageChoices(value, assignValue) {
|
|
713
|
+
const entries = normalizeListSection(value)
|
|
714
|
+
?.split('\n')
|
|
715
|
+
.map((item) => item.trim().toLowerCase())
|
|
716
|
+
.filter(Boolean) ?? [];
|
|
717
|
+
for (const entry of entries) {
|
|
718
|
+
if (entry === 'uniwind') {
|
|
719
|
+
assignValue('stylingSystem', 'uniwind', 'Package Choices section');
|
|
720
|
+
}
|
|
721
|
+
else if (entry === 'nativewind') {
|
|
722
|
+
assignValue('stylingSystem', 'nativewind', 'Package Choices section');
|
|
723
|
+
}
|
|
724
|
+
else if (entry === 'tamagui') {
|
|
725
|
+
assignValue('stylingSystem', 'tamagui', 'Package Choices section');
|
|
726
|
+
}
|
|
727
|
+
else if (entry === 'restyle') {
|
|
728
|
+
assignValue('stylingSystem', 'restyle', 'Package Choices section');
|
|
729
|
+
}
|
|
730
|
+
else if (entry === 'supabase') {
|
|
731
|
+
assignValue('authBackend', 'supabase', 'Package Choices section');
|
|
732
|
+
}
|
|
733
|
+
else if (entry === 'firebase') {
|
|
734
|
+
assignValue('authBackend', 'firebase', 'Package Choices section');
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function extractTechStackDecisions(value, assignValue) {
|
|
739
|
+
const language = normalizeChoice(extractKeyValue(value, 'Language'), ['typescript', 'javascript']);
|
|
740
|
+
if (language) {
|
|
741
|
+
assignValue('scriptLanguage', language, 'Tech Stack & MDS Onboarding section');
|
|
742
|
+
}
|
|
743
|
+
const packageManager = normalizeChoice(extractKeyValue(value, 'Package manager'), ['npm', 'pnpm', 'yarn', 'bun']);
|
|
744
|
+
if (packageManager) {
|
|
745
|
+
assignValue('packageManager', packageManager, 'Tech Stack & MDS Onboarding section');
|
|
746
|
+
}
|
|
747
|
+
const routing = (extractKeyValue(value, 'Routing') ?? '').toLowerCase();
|
|
748
|
+
if (routing.includes('react navigation')) {
|
|
749
|
+
assignValue('navigationLibrary', 'react-navigation', 'Tech Stack & MDS Onboarding section');
|
|
750
|
+
if (routing.includes('tabs')) {
|
|
751
|
+
assignValue('reactNavigationLayout', 'tabs', 'Tech Stack & MDS Onboarding section');
|
|
752
|
+
}
|
|
753
|
+
else if (routing.includes('drawer')) {
|
|
754
|
+
assignValue('reactNavigationLayout', 'drawer', 'Tech Stack & MDS Onboarding section');
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
assignValue('reactNavigationLayout', 'stack', 'Tech Stack & MDS Onboarding section');
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
else if (routing.includes('expo router')) {
|
|
761
|
+
assignValue('navigationLibrary', 'expo-router', 'Tech Stack & MDS Onboarding section');
|
|
762
|
+
}
|
|
763
|
+
const styling = (extractKeyValue(value, 'Styling') ?? '').toLowerCase();
|
|
764
|
+
if (styling.includes('uniwind')) {
|
|
765
|
+
assignValue('stylingSystem', 'uniwind', 'Tech Stack & MDS Onboarding section');
|
|
766
|
+
}
|
|
767
|
+
else if (styling.includes('nativewind')) {
|
|
768
|
+
assignValue('stylingSystem', 'nativewind', 'Tech Stack & MDS Onboarding section');
|
|
769
|
+
}
|
|
770
|
+
else if (styling.includes('tamagui')) {
|
|
771
|
+
assignValue('stylingSystem', 'tamagui', 'Tech Stack & MDS Onboarding section');
|
|
772
|
+
}
|
|
773
|
+
else if (styling.includes('restyle')) {
|
|
774
|
+
assignValue('stylingSystem', 'restyle', 'Tech Stack & MDS Onboarding section');
|
|
775
|
+
}
|
|
776
|
+
else if (styling.includes('stylesheet')) {
|
|
777
|
+
assignValue('stylingSystem', 'stylesheet', 'Tech Stack & MDS Onboarding section');
|
|
778
|
+
}
|
|
779
|
+
const stateManagement = (extractKeyValue(value, 'State management') ?? '').toLowerCase();
|
|
780
|
+
if (stateManagement.includes('zustand')) {
|
|
781
|
+
assignValue('stateManagement', 'zustand', 'Tech Stack & MDS Onboarding section');
|
|
782
|
+
}
|
|
783
|
+
else if (stateManagement.includes('none')) {
|
|
784
|
+
assignValue('stateManagement', 'none', 'Tech Stack & MDS Onboarding section');
|
|
785
|
+
}
|
|
786
|
+
const auth = inferAuthBackend(extractKeyValue(value, 'Auth') ?? '');
|
|
787
|
+
if (auth) {
|
|
788
|
+
assignValue('authBackend', auth, 'Tech Stack & MDS Onboarding section');
|
|
789
|
+
}
|
|
790
|
+
const distribution = extractKeyValue(value, 'Distribution');
|
|
791
|
+
if (distribution) {
|
|
792
|
+
assignValue('deploymentTarget', distribution, 'Tech Stack & MDS Onboarding section');
|
|
793
|
+
}
|
|
794
|
+
const easUses = inferEasUses(extractKeyValue(value, 'EAS') ?? '');
|
|
795
|
+
if (easUses.length > 0) {
|
|
796
|
+
assignValue('easUses', easUses, 'Tech Stack & MDS Onboarding section');
|
|
797
|
+
assignValue('easSetup', true, 'Tech Stack & MDS Onboarding section');
|
|
798
|
+
}
|
|
799
|
+
extractOnboardingDecisionLines(value, assignValue);
|
|
800
|
+
}
|
|
801
|
+
function extractOnboardingDecisionLines(value, assignValue) {
|
|
802
|
+
const lines = normalizeLineEndings(value)
|
|
803
|
+
.split('\n')
|
|
804
|
+
.map((line) => line.trim())
|
|
805
|
+
.filter(Boolean);
|
|
806
|
+
for (const line of lines) {
|
|
807
|
+
const rawKeyValue = /^-\s+(.+?):\s+(.+)$/u.exec(line);
|
|
808
|
+
const key = normalizeText(rawKeyValue?.[1]);
|
|
809
|
+
const rawValue = normalizeText(rawKeyValue?.[2]);
|
|
810
|
+
if (!key || !rawValue) {
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
813
|
+
const loweredKey = key.toLowerCase();
|
|
814
|
+
if (loweredKey === 'advanced package setup') {
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
if (loweredKey === 'create expo starter components') {
|
|
818
|
+
assignValue('includeCreateExpoComponents', parseBooleanValue(rawValue), 'Onboarding decisions');
|
|
819
|
+
}
|
|
820
|
+
else if (loweredKey === 'latest expo sdk preference') {
|
|
821
|
+
// Captured for human context only; SDK targeting is enforced by the generator.
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
else if (loweredKey === 'expo ui') {
|
|
825
|
+
assignValue('usesExpoUi', parseBooleanValue(rawValue), 'Onboarding decisions');
|
|
826
|
+
}
|
|
827
|
+
else if (loweredKey === 'expo ui universal components') {
|
|
828
|
+
assignValue('usesExpoUiUniversalComponents', parseBooleanValue(rawValue), 'Onboarding decisions');
|
|
829
|
+
}
|
|
830
|
+
else if (loweredKey === 'expo native tabs') {
|
|
831
|
+
assignValue('usesExpoNativeTabs', parseBooleanValue(rawValue), 'Onboarding decisions');
|
|
832
|
+
}
|
|
833
|
+
else if (loweredKey === 'target platforms') {
|
|
834
|
+
const targetPlatforms = parsePlatformList(rawValue);
|
|
835
|
+
if (targetPlatforms.length > 0) {
|
|
836
|
+
assignValue('targetPlatforms', targetPlatforms, 'Onboarding decisions');
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
else if (loweredKey === 'first mvp platform') {
|
|
840
|
+
assignValue('firstTargetPlatform', normalizeChoice(rawValue, PLATFORM_OPTIONS), 'Onboarding decisions');
|
|
841
|
+
}
|
|
842
|
+
else if (loweredKey === 'expo router app directory') {
|
|
843
|
+
assignValue('appDirectory', rawValue.includes('src/app') ? 'src' : 'root', 'Onboarding decisions');
|
|
844
|
+
}
|
|
845
|
+
else if (loweredKey === 'platform-specific organization') {
|
|
846
|
+
assignValue('platformStrategy', rawValue.toLowerCase().includes('folder') ? 'folders' : 'files-only', 'Onboarding decisions');
|
|
847
|
+
}
|
|
848
|
+
else if (loweredKey === 'platform layout mode') {
|
|
849
|
+
assignValue('platformLayouts', rawValue.toLowerCase().includes('platform-specific') ? 'platform-specific' : 'shared', 'Onboarding decisions');
|
|
850
|
+
}
|
|
851
|
+
else if (loweredKey === 'web output') {
|
|
852
|
+
assignValue('webOutput', normalizeChoice(rawValue, ['static', 'server', 'spa', 'none']), 'Onboarding decisions');
|
|
853
|
+
}
|
|
854
|
+
else if (loweredKey === 'deployed server') {
|
|
855
|
+
extractPlatformDecisions(`- Deployed server: ${rawValue}`, assignValue);
|
|
856
|
+
}
|
|
857
|
+
else if (loweredKey === 'eas usage') {
|
|
858
|
+
const easUses = inferEasUses(rawValue);
|
|
859
|
+
if (easUses.length > 0) {
|
|
860
|
+
assignValue('easUses', easUses, 'Onboarding decisions');
|
|
861
|
+
assignValue('easSetup', true, 'Onboarding decisions');
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
else if (loweredKey === 'data start') {
|
|
865
|
+
assignValue('dataStart', inferDataStart(rawValue), 'Onboarding decisions');
|
|
866
|
+
}
|
|
867
|
+
else if (loweredKey === 'test-to-main safeguards') {
|
|
868
|
+
assignValue('testToMainSafeguards', parseBooleanValue(rawValue), 'Onboarding decisions');
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
function extractBulletValue(value, label) {
|
|
873
|
+
const match = new RegExp(`^-\\s+${escapeRegExp(label)}:\\s+(.+)$`, 'imu').exec(value);
|
|
874
|
+
return normalizeText(match?.[1]);
|
|
875
|
+
}
|
|
876
|
+
function extractKeyValue(value, label) {
|
|
877
|
+
const match = new RegExp(`^-\\s+(?:\\*\\*)?${escapeRegExp(label)}(?:\\*\\*)?:\\s+(.+)$`, 'imu').exec(value);
|
|
878
|
+
return normalizeText(match?.[1]);
|
|
879
|
+
}
|
|
880
|
+
function inferEasUses(value) {
|
|
881
|
+
const normalized = value.toLowerCase();
|
|
882
|
+
return EAS_USE_OPTIONS.filter((item) => normalized.includes(item.toLowerCase()));
|
|
883
|
+
}
|
|
884
|
+
function parsePlatformList(value) {
|
|
885
|
+
const normalized = value
|
|
886
|
+
?.split(',')
|
|
887
|
+
.map((item) => item.replace(/[`]/gu, '').trim().toLowerCase())
|
|
888
|
+
.filter(Boolean) ?? [];
|
|
889
|
+
return normalized
|
|
890
|
+
.map((item) => {
|
|
891
|
+
if (item === 'ios' || item === 'android' || item === 'web' || item === 'apple-tv' || item === 'android-tv') {
|
|
892
|
+
return item;
|
|
893
|
+
}
|
|
894
|
+
return item.replace(/\s+/gu, '-');
|
|
895
|
+
})
|
|
896
|
+
.filter((item) => PLATFORM_OPTIONS.includes(item));
|
|
897
|
+
}
|
|
898
|
+
function parseBooleanValue(value) {
|
|
899
|
+
const normalized = value?.trim().toLowerCase();
|
|
900
|
+
if (!normalized) {
|
|
901
|
+
return undefined;
|
|
902
|
+
}
|
|
903
|
+
if (['yes', 'true', 'on'].includes(normalized)) {
|
|
904
|
+
return true;
|
|
905
|
+
}
|
|
906
|
+
if (['no', 'false', 'off'].includes(normalized)) {
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
909
|
+
return undefined;
|
|
910
|
+
}
|
|
911
|
+
function normalizeExtractedAnswerValue(id, value) {
|
|
912
|
+
switch (id) {
|
|
913
|
+
case 'confirmed':
|
|
914
|
+
case 'easSetup':
|
|
915
|
+
case 'customBackend':
|
|
916
|
+
case 'includeCreateExpoComponents':
|
|
917
|
+
case 'usesExpoUi':
|
|
918
|
+
case 'usesExpoUiUniversalComponents':
|
|
919
|
+
case 'usesExpoNativeTabs':
|
|
920
|
+
case 'guidelinesTemplate':
|
|
921
|
+
case 'testToMainSafeguards':
|
|
922
|
+
case 'saveDefaults':
|
|
923
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
924
|
+
case 'dataNeedSelections':
|
|
925
|
+
case 'targetPlatforms':
|
|
926
|
+
case 'easUses':
|
|
927
|
+
return normalizeStringArray(value) ?? undefined;
|
|
928
|
+
case 'scriptLanguage':
|
|
929
|
+
return normalizeEnum(value, ['typescript', 'javascript']);
|
|
930
|
+
case 'packageManager':
|
|
931
|
+
return normalizeEnum(value, ['npm', 'pnpm', 'yarn', 'bun']);
|
|
932
|
+
case 'navigationLibrary':
|
|
933
|
+
return normalizeEnum(value, ['expo-router', 'react-navigation']);
|
|
934
|
+
case 'reactNavigationLayout':
|
|
935
|
+
return normalizeEnum(value, ['stack', 'tabs', 'drawer']);
|
|
936
|
+
case 'stylingSystem':
|
|
937
|
+
return normalizeEnum(value, ['uniwind', 'nativewind', 'tamagui', 'restyle', 'stylesheet']);
|
|
938
|
+
case 'stateManagement':
|
|
939
|
+
return normalizeEnum(value, ['zustand', 'none']);
|
|
940
|
+
case 'authBackend':
|
|
941
|
+
return normalizeEnum(value, ['none', 'supabase', 'firebase']);
|
|
942
|
+
case 'platformStrategy':
|
|
943
|
+
return normalizeEnum(value, ['folders', 'files-only']);
|
|
944
|
+
case 'appDirectory':
|
|
945
|
+
return normalizeEnum(value, ['src', 'root']);
|
|
946
|
+
case 'platformLayouts':
|
|
947
|
+
return normalizeEnum(value, ['shared', 'platform-specific']);
|
|
948
|
+
case 'webOutput':
|
|
949
|
+
return normalizeEnum(value, ['static', 'server', 'spa', 'none']);
|
|
950
|
+
case 'expoServerAdapter':
|
|
951
|
+
return normalizeEnum(value, ['eas', 'express', 'bun', 'other', 'none']);
|
|
952
|
+
case 'dataStart':
|
|
953
|
+
return normalizeEnum(value, ['local', 'supabase']);
|
|
954
|
+
case 'screens':
|
|
955
|
+
return normalizeOptionalDeferText(value);
|
|
956
|
+
default:
|
|
957
|
+
return normalizeText(value);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function areAnswerValuesEquivalent(id, left, right) {
|
|
961
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
962
|
+
return JSON.stringify(normalizeStringArray(left) ?? []) === JSON.stringify(normalizeStringArray(right) ?? []);
|
|
963
|
+
}
|
|
964
|
+
if (typeof left === 'boolean' || typeof right === 'boolean') {
|
|
965
|
+
return left === right;
|
|
966
|
+
}
|
|
967
|
+
return normalizeExtractedAnswerValue(id, left) === normalizeExtractedAnswerValue(id, right);
|
|
968
|
+
}
|
|
969
|
+
function normalizeProjectTitle(value) {
|
|
970
|
+
const trimmed = value.replace(/\s+project info$/iu, '').trim();
|
|
971
|
+
const normalized = normalizeText(trimmed);
|
|
972
|
+
return normalized ? displayNameFromProjectName(normalized) : undefined;
|
|
973
|
+
}
|
|
974
|
+
function normalizeLineEndings(value) {
|
|
975
|
+
return value.replace(/\r\n/gu, '\n');
|
|
976
|
+
}
|
|
977
|
+
function slugifyAppName(value) {
|
|
978
|
+
const normalized = normalizeText(value);
|
|
979
|
+
if (!normalized) {
|
|
980
|
+
return undefined;
|
|
981
|
+
}
|
|
982
|
+
const slug = normalized
|
|
983
|
+
.toLowerCase()
|
|
984
|
+
.replace(/[^a-z0-9]+/gu, '-')
|
|
985
|
+
.replace(/^-+|-+$/gu, '');
|
|
986
|
+
return slug || DEFAULT_PROJECT_NAME;
|
|
987
|
+
}
|
|
988
|
+
function displayNameFromProjectName(value) {
|
|
989
|
+
const normalized = normalizeText(value);
|
|
990
|
+
if (!normalized) {
|
|
991
|
+
return DEFAULT_DISPLAY_APP_NAME;
|
|
992
|
+
}
|
|
993
|
+
if (normalized.includes(' ')) {
|
|
994
|
+
return normalized;
|
|
995
|
+
}
|
|
996
|
+
return normalized
|
|
997
|
+
.replace(/[-_]+/gu, ' ')
|
|
998
|
+
.replace(/\b\w/gu, (match) => match.toUpperCase());
|
|
999
|
+
}
|
|
1000
|
+
function escapeRegExp(value) {
|
|
1001
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
1002
|
+
}
|
|
356
1003
|
export function buildCessIntakeStep(input) {
|
|
357
1004
|
const cwd = input.cwd ? path.resolve(input.cwd) : process.cwd();
|
|
358
1005
|
const parentDirProvided = normalizeText(input.parentDir);
|
|
359
1006
|
const appNameProvided = normalizeText(input.appName);
|
|
360
1007
|
const parentDir = normalizeParentDir(input.parentDir, cwd);
|
|
361
1008
|
const appName = normalizeProjectName(input.appName);
|
|
1009
|
+
const appDisplayName = displayNameFromProjectName(input.appName);
|
|
362
1010
|
const currentAnswers = normalizeCessIntakeAnswers(input.answers);
|
|
363
1011
|
const resolvedPlan = resolveCessPlan({
|
|
364
1012
|
parentDir,
|
|
365
|
-
appName,
|
|
1013
|
+
appName: input.appName,
|
|
366
1014
|
answers: currentAnswers,
|
|
367
1015
|
cwd,
|
|
368
1016
|
});
|
|
369
1017
|
const context = {
|
|
370
1018
|
parentDir,
|
|
371
1019
|
appName,
|
|
1020
|
+
appDisplayName,
|
|
372
1021
|
currentAnswers,
|
|
373
1022
|
resolvedAnswers: resolvedPlan.answers,
|
|
374
1023
|
onboardAnswers: resolvedPlan.onboardAnswers,
|
|
@@ -428,11 +1077,12 @@ export function resolveCessPlan(input) {
|
|
|
428
1077
|
const cwd = input.cwd ? path.resolve(input.cwd) : process.cwd();
|
|
429
1078
|
const parentDir = normalizeParentDir(input.parentDir, cwd);
|
|
430
1079
|
const appName = normalizeProjectName(input.appName);
|
|
1080
|
+
const appDisplayName = displayNameFromProjectName(input.appName);
|
|
431
1081
|
const projectPath = path.resolve(parentDir, appName);
|
|
432
1082
|
const currentAnswers = normalizeCessIntakeAnswers(input.answers);
|
|
433
1083
|
const onboardArgv = buildOnboardArgvFromCess(parentDir, appName, currentAnswers);
|
|
434
1084
|
const onboardPlan = defaultOnboardPlan(onboardArgv, projectPath);
|
|
435
|
-
const answers = buildResolvedCessAnswers(currentAnswers, parentDir, appName, onboardPlan.answers);
|
|
1085
|
+
const answers = buildResolvedCessAnswers(currentAnswers, parentDir, appName, appDisplayName, onboardPlan.answers);
|
|
436
1086
|
const finalOnboardArgv = buildOnboardArgvFromCess(parentDir, appName, answers);
|
|
437
1087
|
const finalOnboardPlan = defaultOnboardPlan(finalOnboardArgv, projectPath);
|
|
438
1088
|
const createExpoStackFlags = buildCreateExpoStackFlags(answers);
|
|
@@ -466,6 +1116,7 @@ export function validateCessGenerationReadiness(input) {
|
|
|
466
1116
|
const nextQuestion = findNextQuestion({
|
|
467
1117
|
parentDir,
|
|
468
1118
|
appName,
|
|
1119
|
+
appDisplayName: displayNameFromProjectName(input.appName),
|
|
469
1120
|
currentAnswers,
|
|
470
1121
|
resolvedAnswers: resolvedPlan.answers,
|
|
471
1122
|
onboardAnswers: resolvedPlan.onboardAnswers,
|
|
@@ -675,7 +1326,7 @@ export function buildCessSummaryLines(parentDir, appName, answers, onboardAnswer
|
|
|
675
1326
|
? 'web output: none'
|
|
676
1327
|
: `web output: ${onboardAnswers.webOutput}, deployed server: ${onboardAnswers.deployedServer}`;
|
|
677
1328
|
return [
|
|
678
|
-
`app: ${appName} at ${parentDir}`,
|
|
1329
|
+
`app: ${onboardAnswers.appName} (folder: ${appName}) at ${parentDir}`,
|
|
679
1330
|
`stack: ${stackLine}`,
|
|
680
1331
|
`audience: ${onboardAnswers.audience}`,
|
|
681
1332
|
`core flows: ${onboardAnswers.coreFlows}`,
|
|
@@ -684,7 +1335,7 @@ export function buildCessSummaryLines(parentDir, appName, answers, onboardAnswer
|
|
|
684
1335
|
`data start: ${onboardAnswers.dataStart}, test-to-main: ${onboardAnswers.testToMainSafeguards ? 'on' : 'off'}, guidelines template: ${answers.guidelinesTemplate === false ? 'off' : 'on'}, save defaults: ${answers.saveDefaults ? 'on' : 'off'}`,
|
|
685
1336
|
];
|
|
686
1337
|
}
|
|
687
|
-
function buildResolvedCessAnswers(currentAnswers, parentDir, appName, onboardAnswers) {
|
|
1338
|
+
function buildResolvedCessAnswers(currentAnswers, parentDir, appName, appDisplayName, onboardAnswers) {
|
|
688
1339
|
const targetPlatforms = currentAnswers.targetPlatforms ?? onboardAnswers.targetPlatforms;
|
|
689
1340
|
const usesExpoUi = currentAnswers.usesExpoUi ?? onboardAnswers.usesExpoUi;
|
|
690
1341
|
const screens = currentAnswers.screens === 'defer' ? '' : currentAnswers.screens;
|
|
@@ -698,7 +1349,7 @@ function buildResolvedCessAnswers(currentAnswers, parentDir, appName, onboardAns
|
|
|
698
1349
|
stateManagement: currentAnswers.stateManagement ?? STACK_DEFAULTS.stateManagement,
|
|
699
1350
|
authBackend: currentAnswers.authBackend ?? STACK_DEFAULTS.authBackend,
|
|
700
1351
|
easSetup: currentAnswers.easSetup ?? STACK_DEFAULTS.easSetup,
|
|
701
|
-
displayAppName: currentAnswers.displayAppName ?? onboardAnswers.appName ?? appName,
|
|
1352
|
+
displayAppName: currentAnswers.displayAppName ?? appDisplayName ?? onboardAnswers.appName ?? appName,
|
|
702
1353
|
audience: currentAnswers.audience ?? onboardAnswers.audience,
|
|
703
1354
|
coreFlows: currentAnswers.coreFlows ?? onboardAnswers.coreFlows ?? AGENT_DERIVED_CORE_FLOWS,
|
|
704
1355
|
screens,
|
|
@@ -736,7 +1387,15 @@ function buildOnboardArgvFromCess(parentDir, appName, answers) {
|
|
|
736
1387
|
return {
|
|
737
1388
|
project: path.resolve(parentDir, appName),
|
|
738
1389
|
yes: true,
|
|
739
|
-
appName: normalizeText(answers.displayAppName) ?? appName,
|
|
1390
|
+
appName: normalizeText(answers.displayAppName) ?? displayNameFromProjectName(appName),
|
|
1391
|
+
generatorScriptLanguage: answers.scriptLanguage,
|
|
1392
|
+
generatorPackageManager: answers.packageManager,
|
|
1393
|
+
generatorNavigationLibrary: answers.navigationLibrary,
|
|
1394
|
+
generatorReactNavigationLayout: answers.reactNavigationLayout,
|
|
1395
|
+
generatorStylingSystem: answers.stylingSystem,
|
|
1396
|
+
generatorStateManagement: answers.stateManagement,
|
|
1397
|
+
generatorAuthBackend: answers.authBackend,
|
|
1398
|
+
generatorEasSetup: answers.easSetup,
|
|
740
1399
|
audience: normalizeText(answers.audience),
|
|
741
1400
|
coreFlows: normalizeText(answers.coreFlows),
|
|
742
1401
|
screens,
|
|
@@ -806,7 +1465,7 @@ function normalizeParentDir(parentDir, cwd) {
|
|
|
806
1465
|
return path.resolve(cwd, normalizeText(parentDir) ?? '.');
|
|
807
1466
|
}
|
|
808
1467
|
function normalizeProjectName(appName) {
|
|
809
|
-
return
|
|
1468
|
+
return slugifyAppName(appName) ?? DEFAULT_PROJECT_NAME;
|
|
810
1469
|
}
|
|
811
1470
|
function normalizeBoolean(value) {
|
|
812
1471
|
return typeof value === 'boolean' ? value : undefined;
|
|
@@ -814,6 +1473,9 @@ function normalizeBoolean(value) {
|
|
|
814
1473
|
function normalizeEnum(value, choices) {
|
|
815
1474
|
return typeof value === 'string' && choices.includes(value) ? value : undefined;
|
|
816
1475
|
}
|
|
1476
|
+
function normalizeChoice(value, choices) {
|
|
1477
|
+
return normalizeEnum(value, choices);
|
|
1478
|
+
}
|
|
817
1479
|
function normalizeText(value) {
|
|
818
1480
|
if (typeof value !== 'string') {
|
|
819
1481
|
return undefined;
|