@mr.dj2u/cli 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/bundles/claude-code/.claude-plugin/plugin.json +20 -20
  2. package/bundles/claude-code/.mcp.json +11 -11
  3. package/bundles/claude-code/agents/mds.md +36 -35
  4. package/bundles/claude-code/commands/create-expo-super-stack.md +32 -30
  5. package/bundles/claude-code/skills/create-expo-super-stack/SKILL.md +17 -15
  6. package/bundles/claude-code/skills/super-stack-startup/SKILL.md +4 -4
  7. package/bundles/codex/.codex-plugin/plugin.json +42 -42
  8. package/bundles/codex/.mcp.json +11 -11
  9. package/bundles/codex/README.md +51 -48
  10. package/bundles/codex/commands/create-expo-super-stack.md +32 -30
  11. package/bundles/codex/skills/super-stack-startup/SKILL.md +34 -34
  12. package/bundles/codex/skills/workflow-continue-development/SKILL.md +48 -47
  13. package/bundles/codex/skills/workflow-create-expo-super-stack/SKILL.md +45 -42
  14. package/bundles/codex/skills/workflow-fix-seo/SKILL.md +42 -41
  15. package/bundles/codex/skills/workflow-prepare-deploy/SKILL.md +42 -41
  16. package/bundles/codex/skills/workflow-project-research-plan/SKILL.md +42 -41
  17. package/bundles/codex/skills/workflow-push-merge-loop/SKILL.md +37 -36
  18. package/bundles/codex/skills/workflow-review-expo-project/SKILL.md +42 -41
  19. package/bundles/codex/skills/workflow-run-doctor/SKILL.md +51 -50
  20. package/bundles/codex/skills/workflow-wrap-up/SKILL.md +80 -79
  21. package/bundles/vscode-copilot/.github/agents/mds.agent.md +22 -22
  22. package/bundles/vscode-copilot/.github/copilot-instructions.md +8 -8
  23. package/bundles/vscode-copilot/.github/prompts/continue-development.prompt.md +40 -40
  24. package/bundles/vscode-copilot/.github/prompts/create-expo-super-stack.prompt.md +37 -35
  25. package/bundles/vscode-copilot/.github/prompts/fix-seo.prompt.md +34 -34
  26. package/bundles/vscode-copilot/.github/prompts/prepare-deploy.prompt.md +34 -34
  27. package/bundles/vscode-copilot/.github/prompts/project-research-plan.prompt.md +34 -34
  28. package/bundles/vscode-copilot/.github/prompts/push-merge-loop.prompt.md +30 -30
  29. package/bundles/vscode-copilot/.github/prompts/review-expo-project.prompt.md +34 -34
  30. package/bundles/vscode-copilot/.github/prompts/run-doctor.prompt.md +43 -43
  31. package/bundles/vscode-copilot/.github/prompts/wrap-up.prompt.md +72 -72
  32. package/bundles/vscode-copilot/.github/skills/super-stack-startup/SKILL.md +39 -39
  33. package/bundles/vscode-copilot/.vscode/mcp.json +11 -11
  34. package/bundles/vscode-copilot/user/.copilot/agents/mds.agent.md +22 -22
  35. package/bundles/vscode-copilot/user/.copilot/instructions.md +8 -8
  36. package/bundles/vscode-copilot/user/.copilot/skills/super-stack-startup/SKILL.md +39 -39
  37. package/bundles/vscode-copilot/user/.copilot/skills/workflow-create-expo-super-stack/SKILL.md +37 -35
  38. package/dist/cess-intake.d.ts +106 -0
  39. package/dist/cess-intake.d.ts.map +1 -0
  40. package/dist/cess-intake.js +898 -0
  41. package/dist/cess-intake.js.map +1 -0
  42. package/dist/cli.d.ts.map +1 -1
  43. package/dist/cli.js +14 -4
  44. package/dist/cli.js.map +1 -1
  45. package/dist/commands/agent.d.ts.map +1 -1
  46. package/dist/commands/agent.js +3 -1
  47. package/dist/commands/agent.js.map +1 -1
  48. package/dist/commands/mcp-install.d.ts +3 -2
  49. package/dist/commands/mcp-install.d.ts.map +1 -1
  50. package/dist/commands/mcp-install.js +17 -44
  51. package/dist/commands/mcp-install.js.map +1 -1
  52. package/dist/commands/onboard.d.ts +10 -3
  53. package/dist/commands/onboard.d.ts.map +1 -1
  54. package/dist/commands/onboard.js +25 -15
  55. package/dist/commands/onboard.js.map +1 -1
  56. package/dist/commands/roadmap.d.ts +6 -0
  57. package/dist/commands/roadmap.d.ts.map +1 -0
  58. package/dist/commands/roadmap.js +54 -0
  59. package/dist/commands/roadmap.js.map +1 -0
  60. package/dist/project-memory.d.ts +0 -1
  61. package/dist/project-memory.d.ts.map +1 -1
  62. package/dist/project-memory.js +101 -89
  63. package/dist/project-memory.js.map +1 -1
  64. package/dist/roadmap.d.ts +71 -0
  65. package/dist/roadmap.d.ts.map +1 -0
  66. package/dist/roadmap.js +865 -0
  67. package/dist/roadmap.js.map +1 -0
  68. package/dist/stylist-theme.d.ts.map +1 -1
  69. package/dist/stylist-theme.js +1 -20
  70. package/dist/stylist-theme.js.map +1 -1
  71. package/package.json +11 -3
  72. package/templates/embedded-fonts.template.ts +72 -72
  73. package/templates/expo-sdk-56-screen-universal.template.tsx +709 -709
  74. package/templates/project/guidelines.md +4 -5
  75. package/templates/stylist-screen.template.tsx +3456 -3446
@@ -0,0 +1,898 @@
1
+ import path from 'node:path';
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
+ const DEFAULT_PROJECT_NAME = 'my-expo-app';
4
+ const STACK_DEFAULTS = {
5
+ scriptLanguage: 'typescript',
6
+ packageManager: 'npm',
7
+ navigationLibrary: 'expo-router',
8
+ reactNavigationLayout: 'stack',
9
+ stylingSystem: 'uniwind',
10
+ stateManagement: 'zustand',
11
+ authBackend: 'none',
12
+ easSetup: false,
13
+ };
14
+ const CESS_QUESTIONS = [
15
+ {
16
+ id: 'parentDir',
17
+ prompt: 'Which parent folder should contain the new app folder?',
18
+ kind: 'text',
19
+ defaultValue: (context) => context.parentDir,
20
+ },
21
+ {
22
+ id: 'appName',
23
+ prompt: 'What should the new Expo app folder be named?',
24
+ kind: 'text',
25
+ defaultValue: (context) => context.appName,
26
+ },
27
+ {
28
+ id: 'scriptLanguage',
29
+ prompt: 'TypeScript or JavaScript?',
30
+ kind: 'single-select',
31
+ options: () => [
32
+ { value: 'typescript', label: 'TypeScript', hint: 'Default and strongly recommended' },
33
+ { value: 'javascript', label: 'JavaScript' },
34
+ ],
35
+ defaultValue: () => STACK_DEFAULTS.scriptLanguage,
36
+ },
37
+ {
38
+ id: 'packageManager',
39
+ prompt: 'Which package manager should the app use?',
40
+ kind: 'single-select',
41
+ options: () => [
42
+ { value: 'npm', label: 'npm' },
43
+ { value: 'pnpm', label: 'pnpm' },
44
+ { value: 'yarn', label: 'yarn' },
45
+ { value: 'bun', label: 'bun' },
46
+ ],
47
+ defaultValue: () => STACK_DEFAULTS.packageManager,
48
+ },
49
+ {
50
+ id: 'navigationLibrary',
51
+ prompt: 'Which navigation library should the app use?',
52
+ kind: 'single-select',
53
+ options: () => [
54
+ { value: 'expo-router', label: 'Expo Router', hint: 'Default and recommended' },
55
+ { value: 'react-navigation', label: 'React Navigation' },
56
+ ],
57
+ defaultValue: () => STACK_DEFAULTS.navigationLibrary,
58
+ },
59
+ {
60
+ id: 'reactNavigationLayout',
61
+ prompt: 'Which React Navigation layout should the starter use?',
62
+ kind: 'single-select',
63
+ options: () => [
64
+ { value: 'stack', label: 'Stack' },
65
+ { value: 'tabs', label: 'Tabs' },
66
+ { value: 'drawer', label: 'Drawer + Tabs' },
67
+ ],
68
+ defaultValue: () => STACK_DEFAULTS.reactNavigationLayout,
69
+ shouldAsk: (context) => context.resolvedAnswers.navigationLibrary === 'react-navigation',
70
+ },
71
+ {
72
+ id: 'stylingSystem',
73
+ prompt: 'Which styling system should the starter use?',
74
+ kind: 'single-select',
75
+ options: () => [
76
+ { value: 'uniwind', label: 'Uniwind', hint: 'Default and MDS preference' },
77
+ { value: 'nativewind', label: 'NativeWind' },
78
+ { value: 'tamagui', label: 'Tamagui' },
79
+ { value: 'restyle', label: 'Restyle' },
80
+ { value: 'stylesheet', label: 'StyleSheet only' },
81
+ ],
82
+ defaultValue: () => STACK_DEFAULTS.stylingSystem,
83
+ },
84
+ {
85
+ id: 'stateManagement',
86
+ prompt: 'What shared state approach should the starter use?',
87
+ kind: 'single-select',
88
+ options: () => [
89
+ { value: 'zustand', label: 'Zustand', hint: 'Default' },
90
+ { value: 'none', label: 'None / decide later' },
91
+ ],
92
+ defaultValue: () => STACK_DEFAULTS.stateManagement,
93
+ },
94
+ {
95
+ id: 'authBackend',
96
+ prompt: 'Which auth/backend starter should the app use?',
97
+ kind: 'single-select',
98
+ options: () => [
99
+ { value: 'none', label: 'None', hint: 'Default' },
100
+ { value: 'supabase', label: 'Supabase' },
101
+ { value: 'firebase', label: 'Firebase' },
102
+ ],
103
+ defaultValue: () => STACK_DEFAULTS.authBackend,
104
+ },
105
+ {
106
+ id: 'easSetup',
107
+ prompt: 'Set up EAS in the generated starter now?',
108
+ kind: 'single-select',
109
+ options: () => [
110
+ { value: false, label: 'No', hint: 'Default' },
111
+ { value: true, label: 'Yes' },
112
+ ],
113
+ defaultValue: () => STACK_DEFAULTS.easSetup,
114
+ },
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
+ {
122
+ id: 'audience',
123
+ prompt: 'Who is this app for?',
124
+ kind: 'text',
125
+ defaultValue: (context) => context.onboardAnswers.audience,
126
+ },
127
+ {
128
+ id: 'coreFlows',
129
+ prompt: 'What should users be able to do first?',
130
+ kind: 'text',
131
+ defaultValue: (context) => context.onboardAnswers.coreFlows || AGENT_DERIVED_CORE_FLOWS,
132
+ },
133
+ {
134
+ id: 'screens',
135
+ prompt: 'What screens do you already know the app needs? Reply with "defer" to leave this for later.',
136
+ kind: 'text',
137
+ defaultValue: () => 'defer',
138
+ },
139
+ {
140
+ id: 'dataNeedSelections',
141
+ prompt: 'Which data categories does the app need?',
142
+ kind: 'multi-select',
143
+ options: () => [
144
+ ...DATA_NEED_OPTIONS.map((item) => ({ value: item, label: item })),
145
+ { value: OTHER_DATA_NEEDS, label: 'Other / custom notes' },
146
+ ],
147
+ defaultValue: () => ['Local UI/app state'],
148
+ },
149
+ {
150
+ id: 'dataNeedsOther',
151
+ prompt: 'What other data needs should MDS remember?',
152
+ kind: 'text',
153
+ shouldAsk: (context) => Array.isArray(context.currentAnswers.dataNeedSelections) &&
154
+ context.currentAnswers.dataNeedSelections.includes(OTHER_DATA_NEEDS),
155
+ },
156
+ {
157
+ id: 'targetPlatforms',
158
+ prompt: 'Which platforms will this app target?',
159
+ kind: 'multi-select',
160
+ options: () => PLATFORM_OPTIONS.map((item) => ({ value: item, label: formatPlatformLabel(item) })),
161
+ defaultValue: () => ['web', 'ios', 'android'],
162
+ },
163
+ {
164
+ id: 'firstTargetPlatform',
165
+ prompt: 'Which selected platform should be the first MVP target?',
166
+ kind: 'single-select',
167
+ options: (context) => (context.resolvedAnswers.targetPlatforms ?? []).map((item) => ({
168
+ value: item,
169
+ label: formatPlatformLabel(item),
170
+ })),
171
+ defaultValue: (context) => context.onboardAnswers.firstTargetPlatform,
172
+ shouldAsk: (context) => (context.resolvedAnswers.targetPlatforms?.length ?? 0) > 1,
173
+ },
174
+ {
175
+ id: 'platformStrategy',
176
+ prompt: 'When platforms diverge, how should platform-specific code be organized?',
177
+ kind: 'single-select',
178
+ options: () => [
179
+ { value: 'files-only', label: 'File suffixes only', hint: 'Default' },
180
+ { value: 'folders', label: 'Platform folders' },
181
+ ],
182
+ defaultValue: (context) => context.onboardAnswers.platformFileStrategy,
183
+ shouldAsk: (context) => (context.resolvedAnswers.targetPlatforms?.length ?? 0) > 1,
184
+ },
185
+ {
186
+ id: 'appDirectory',
187
+ prompt: 'Where should Expo Router route files live?',
188
+ kind: 'single-select',
189
+ options: () => [
190
+ { value: 'src', label: 'src/app', hint: 'Default for new Super Stack apps' },
191
+ { value: 'root', label: 'app' },
192
+ ],
193
+ defaultValue: (context) => context.onboardAnswers.appDirectory,
194
+ },
195
+ {
196
+ id: 'platformLayouts',
197
+ prompt: 'Do selected platforms need their own layouts?',
198
+ kind: 'single-select',
199
+ options: () => [
200
+ { value: 'shared', label: 'Shared layouts', hint: 'Default' },
201
+ { value: 'platform-specific', label: 'Platform-specific layouts' },
202
+ ],
203
+ defaultValue: (context) => context.onboardAnswers.platformLayoutMode,
204
+ shouldAsk: (context) => (context.resolvedAnswers.targetPlatforms?.length ?? 0) > 1,
205
+ },
206
+ {
207
+ id: 'webOutput',
208
+ prompt: 'Which Expo web output mode fits the app?',
209
+ kind: 'single-select',
210
+ options: () => [
211
+ { value: 'static', label: 'Static', hint: 'Default' },
212
+ { value: 'server', label: 'Server' },
213
+ { value: 'spa', label: 'SPA' },
214
+ ],
215
+ defaultValue: (context) => context.onboardAnswers.webOutput === 'none' ? 'static' : context.onboardAnswers.webOutput,
216
+ shouldAsk: (context) => (context.resolvedAnswers.targetPlatforms ?? []).includes('web'),
217
+ },
218
+ {
219
+ id: 'expoServerAdapter',
220
+ prompt: 'How will the Expo Router server be hosted in production?',
221
+ kind: 'single-select',
222
+ options: () => [
223
+ { value: 'eas', label: 'EAS hosting', hint: 'Default for server output' },
224
+ { value: 'express', label: 'Express adapter' },
225
+ { value: 'bun', label: 'Bun adapter' },
226
+ { value: 'other', label: 'Other / not sure yet' },
227
+ ],
228
+ defaultValue: (context) => context.onboardAnswers.expoServerAdapter === 'none'
229
+ ? 'eas'
230
+ : context.onboardAnswers.expoServerAdapter,
231
+ explanation: EXPO_SERVER_ADAPTER_EXPLANATION,
232
+ shouldAsk: (context) => context.resolvedAnswers.webOutput === 'server',
233
+ },
234
+ {
235
+ id: 'customBackend',
236
+ prompt: 'Does this project need a separate backend API server running alongside Expo?',
237
+ kind: 'single-select',
238
+ options: () => [
239
+ { value: false, label: 'No', hint: 'Default' },
240
+ { value: true, label: 'Yes' },
241
+ ],
242
+ defaultValue: (context) => context.onboardAnswers.customBackend,
243
+ explanation: CUSTOM_BACKEND_EXPLANATION,
244
+ shouldAsk: (context) => context.resolvedAnswers.webOutput !== 'none' ||
245
+ (context.resolvedAnswers.targetPlatforms ?? []).some((item) => item !== 'web'),
246
+ },
247
+ {
248
+ id: 'customBackendEntry',
249
+ prompt: 'What is the backend server entry point?',
250
+ kind: 'text',
251
+ defaultValue: (context) => context.onboardAnswers.customBackendEntry,
252
+ shouldAsk: (context) => context.resolvedAnswers.customBackend === true,
253
+ },
254
+ {
255
+ id: 'deploymentTarget',
256
+ prompt: 'How will the first version reach its users?',
257
+ kind: 'text',
258
+ defaultValue: (context) => context.onboardAnswers.deploymentTarget,
259
+ },
260
+ {
261
+ id: 'includeCreateExpoComponents',
262
+ prompt: 'Keep or generate the starter components that come with create-expo-app?',
263
+ kind: 'single-select',
264
+ options: () => [
265
+ { value: false, label: 'No', hint: 'Default' },
266
+ { value: true, label: 'Yes' },
267
+ ],
268
+ defaultValue: (context) => context.onboardAnswers.includeCreateExpoComponents,
269
+ },
270
+ {
271
+ id: 'usesExpoUi',
272
+ prompt: 'Use Expo UI for native-feeling screens?',
273
+ kind: 'single-select',
274
+ options: () => [
275
+ { value: true, label: 'Yes', hint: 'Default' },
276
+ { value: false, label: 'No' },
277
+ ],
278
+ defaultValue: (context) => context.onboardAnswers.usesExpoUi,
279
+ shouldAsk: (context) => hasMobileTarget(context.resolvedAnswers.targetPlatforms),
280
+ },
281
+ {
282
+ id: 'usesExpoUiUniversalComponents',
283
+ prompt: 'Use Expo UI Universal components?',
284
+ kind: 'single-select',
285
+ options: () => [
286
+ { value: true, label: 'Yes', hint: 'Default' },
287
+ { value: false, label: 'No' },
288
+ ],
289
+ defaultValue: (context) => context.onboardAnswers.usesExpoUiUniversalComponents,
290
+ shouldAsk: (context) => hasMobileTarget(context.resolvedAnswers.targetPlatforms) &&
291
+ context.resolvedAnswers.usesExpoUi === true,
292
+ },
293
+ {
294
+ id: 'usesExpoNativeTabs',
295
+ prompt: 'Use Expo Native Tabs?',
296
+ kind: 'single-select',
297
+ options: () => [
298
+ { value: true, label: 'Yes', hint: 'Default' },
299
+ { value: false, label: 'No' },
300
+ ],
301
+ defaultValue: (context) => context.onboardAnswers.usesExpoNativeTabs,
302
+ shouldAsk: (context) => hasMobileTarget(context.resolvedAnswers.targetPlatforms),
303
+ },
304
+ {
305
+ id: 'easUses',
306
+ prompt: 'Which EAS uses should the roadmap remember?',
307
+ kind: 'multi-select',
308
+ options: () => EAS_USE_OPTIONS.map((item) => ({ value: item, label: item })),
309
+ defaultValue: (context) => context.onboardAnswers.easUses,
310
+ explanation: EAS_EXPLANATION,
311
+ shouldAsk: (context) => context.resolvedAnswers.easSetup === true,
312
+ },
313
+ {
314
+ id: 'guidelinesTemplate',
315
+ prompt: 'Use the bundled MDS project/guidelines.md template?',
316
+ kind: 'single-select',
317
+ options: () => [
318
+ { value: true, label: 'Yes', hint: 'Default and recommended' },
319
+ { value: false, label: 'No' },
320
+ ],
321
+ defaultValue: () => true,
322
+ },
323
+ {
324
+ id: 'dataStart',
325
+ prompt: 'Would you like to start with local dummy data or go straight to Supabase?',
326
+ kind: 'single-select',
327
+ options: () => [
328
+ { value: 'local', label: 'Local dummy data', hint: 'Default' },
329
+ { value: 'supabase', label: 'Supabase' },
330
+ ],
331
+ defaultValue: (context) => context.onboardAnswers.dataStart,
332
+ explanation: DATA_START_EXPLANATION,
333
+ },
334
+ {
335
+ id: 'testToMainSafeguards',
336
+ prompt: 'Use test-to-main safeguards for this project?',
337
+ kind: 'single-select',
338
+ options: () => [
339
+ { value: true, label: 'Yes', hint: 'Default' },
340
+ { value: false, label: 'No' },
341
+ ],
342
+ defaultValue: (context) => context.onboardAnswers.testToMainSafeguards,
343
+ explanation: TEST_TO_MAIN_EXPLANATION,
344
+ },
345
+ {
346
+ id: 'saveDefaults',
347
+ prompt: 'Save this configuration as your personal default for future app generation?',
348
+ kind: 'single-select',
349
+ options: () => [
350
+ { value: false, label: 'No', hint: 'Default' },
351
+ { value: true, label: 'Yes' },
352
+ ],
353
+ defaultValue: () => false,
354
+ },
355
+ ];
356
+ export function buildCessIntakeStep(input) {
357
+ const cwd = input.cwd ? path.resolve(input.cwd) : process.cwd();
358
+ const parentDirProvided = normalizeText(input.parentDir);
359
+ const appNameProvided = normalizeText(input.appName);
360
+ const parentDir = normalizeParentDir(input.parentDir, cwd);
361
+ const appName = normalizeProjectName(input.appName);
362
+ const currentAnswers = normalizeCessIntakeAnswers(input.answers);
363
+ const resolvedPlan = resolveCessPlan({
364
+ parentDir,
365
+ appName,
366
+ answers: currentAnswers,
367
+ cwd,
368
+ });
369
+ const context = {
370
+ parentDir,
371
+ appName,
372
+ currentAnswers,
373
+ resolvedAnswers: resolvedPlan.answers,
374
+ onboardAnswers: resolvedPlan.onboardAnswers,
375
+ };
376
+ const projectPath = resolvedPlan.projectPath;
377
+ const nextQuestion = findNextQuestion(context, currentAnswers, {
378
+ parentDirProvided,
379
+ appNameProvided,
380
+ });
381
+ const missingRequirements = nextQuestion ? [nextQuestion.id] : [];
382
+ if (nextQuestion) {
383
+ return {
384
+ status: 'question',
385
+ nextQuestion,
386
+ options: nextQuestion.options,
387
+ defaultValue: nextQuestion.defaultValue,
388
+ updatedAnswers: currentAnswers,
389
+ missingRequirements,
390
+ projectPath,
391
+ parentDir,
392
+ appName,
393
+ };
394
+ }
395
+ if (missingRequirements.length > 0) {
396
+ return {
397
+ status: 'blocked',
398
+ updatedAnswers: currentAnswers,
399
+ missingRequirements,
400
+ summaryLines: resolvedPlan.summaryLines,
401
+ projectPath,
402
+ parentDir,
403
+ appName,
404
+ };
405
+ }
406
+ if (currentAnswers.confirmed === true) {
407
+ return {
408
+ status: 'ready',
409
+ updatedAnswers: currentAnswers,
410
+ missingRequirements: [],
411
+ summaryLines: resolvedPlan.summaryLines,
412
+ projectPath,
413
+ parentDir,
414
+ appName,
415
+ };
416
+ }
417
+ return {
418
+ status: 'confirm',
419
+ updatedAnswers: currentAnswers,
420
+ missingRequirements: [],
421
+ summaryLines: resolvedPlan.summaryLines,
422
+ projectPath,
423
+ parentDir,
424
+ appName,
425
+ };
426
+ }
427
+ export function resolveCessPlan(input) {
428
+ const cwd = input.cwd ? path.resolve(input.cwd) : process.cwd();
429
+ const parentDir = normalizeParentDir(input.parentDir, cwd);
430
+ const appName = normalizeProjectName(input.appName);
431
+ const projectPath = path.resolve(parentDir, appName);
432
+ const currentAnswers = normalizeCessIntakeAnswers(input.answers);
433
+ const onboardArgv = buildOnboardArgvFromCess(parentDir, appName, currentAnswers);
434
+ const onboardPlan = defaultOnboardPlan(onboardArgv, projectPath);
435
+ const answers = buildResolvedCessAnswers(currentAnswers, parentDir, appName, onboardPlan.answers);
436
+ const finalOnboardArgv = buildOnboardArgvFromCess(parentDir, appName, answers);
437
+ const finalOnboardPlan = defaultOnboardPlan(finalOnboardArgv, projectPath);
438
+ const createExpoStackFlags = buildCreateExpoStackFlags(answers);
439
+ const mdsFlags = buildMdsFlags(appName, finalOnboardPlan.answers, answers);
440
+ return {
441
+ projectPath,
442
+ parentDir,
443
+ appName,
444
+ answers,
445
+ onboardArgv: finalOnboardArgv,
446
+ onboardAnswers: finalOnboardPlan.answers,
447
+ createExpoStackFlags,
448
+ mdsFlags,
449
+ summaryLines: buildCessSummaryLines(parentDir, appName, answers, finalOnboardPlan.answers),
450
+ };
451
+ }
452
+ export function validateCessGenerationReadiness(input) {
453
+ const cwd = input.cwd ? path.resolve(input.cwd) : process.cwd();
454
+ const parentDir = normalizeParentDir(input.parentDir, cwd);
455
+ const appName = normalizeProjectName(input.appName);
456
+ const currentAnswers = normalizeCessIntakeAnswers({
457
+ ...input.answers,
458
+ confirmed: undefined,
459
+ });
460
+ const resolvedPlan = resolveCessPlan({
461
+ parentDir,
462
+ appName,
463
+ answers: currentAnswers,
464
+ cwd,
465
+ });
466
+ const nextQuestion = findNextQuestion({
467
+ parentDir,
468
+ appName,
469
+ currentAnswers,
470
+ resolvedAnswers: resolvedPlan.answers,
471
+ onboardAnswers: resolvedPlan.onboardAnswers,
472
+ }, currentAnswers, {
473
+ parentDirProvided: normalizeText(input.parentDir),
474
+ appNameProvided: normalizeText(input.appName),
475
+ });
476
+ return nextQuestion ? [nextQuestion.id] : [];
477
+ }
478
+ export function normalizeCessIntakeAnswers(answers) {
479
+ if (!answers) {
480
+ return {};
481
+ }
482
+ const normalized = {};
483
+ normalized.confirmed = normalizeBoolean(answers.confirmed);
484
+ normalized.scriptLanguage = normalizeEnum(answers.scriptLanguage, ['typescript', 'javascript']);
485
+ normalized.packageManager = normalizeEnum(answers.packageManager, ['npm', 'pnpm', 'yarn', 'bun']);
486
+ normalized.navigationLibrary = normalizeEnum(answers.navigationLibrary, [
487
+ 'expo-router',
488
+ 'react-navigation',
489
+ ]);
490
+ normalized.reactNavigationLayout = normalizeEnum(answers.reactNavigationLayout, [
491
+ 'stack',
492
+ 'tabs',
493
+ 'drawer',
494
+ ]);
495
+ normalized.stylingSystem = normalizeEnum(answers.stylingSystem, [
496
+ 'uniwind',
497
+ 'nativewind',
498
+ 'tamagui',
499
+ 'restyle',
500
+ 'stylesheet',
501
+ ]);
502
+ normalized.stateManagement = normalizeEnum(answers.stateManagement, ['zustand', 'none']);
503
+ normalized.authBackend = normalizeEnum(answers.authBackend, ['none', 'supabase', 'firebase']);
504
+ normalized.easSetup = normalizeBoolean(answers.easSetup);
505
+ normalized.displayAppName = normalizeText(answers.displayAppName);
506
+ normalized.audience = normalizeText(answers.audience);
507
+ normalized.coreFlows = normalizeText(answers.coreFlows);
508
+ normalized.screens = normalizeOptionalDeferText(answers.screens);
509
+ normalized.dataNeedSelections = normalizeStringArray(answers.dataNeedSelections);
510
+ normalized.dataNeedsOther = normalizeText(answers.dataNeedsOther);
511
+ normalized.targetPlatforms = normalizePlatforms(answers.targetPlatforms);
512
+ normalized.firstTargetPlatform = normalizeText(answers.firstTargetPlatform);
513
+ normalized.platformStrategy = normalizeEnum(answers.platformStrategy, ['folders', 'files-only']);
514
+ normalized.appDirectory = normalizeEnum(answers.appDirectory, ['src', 'root']);
515
+ normalized.platformLayouts = normalizeEnum(answers.platformLayouts, ['shared', 'platform-specific']);
516
+ normalized.webOutput = normalizeEnum(answers.webOutput, ['static', 'server', 'spa', 'none']);
517
+ normalized.expoServerAdapter = normalizeEnum(answers.expoServerAdapter, [
518
+ 'eas',
519
+ 'express',
520
+ 'bun',
521
+ 'other',
522
+ 'none',
523
+ ]);
524
+ normalized.customBackend = normalizeBoolean(answers.customBackend);
525
+ normalized.customBackendEntry = normalizeText(answers.customBackendEntry);
526
+ normalized.deploymentTarget = normalizeText(answers.deploymentTarget);
527
+ normalized.includeCreateExpoComponents = normalizeBoolean(answers.includeCreateExpoComponents);
528
+ normalized.usesExpoUi = normalizeBoolean(answers.usesExpoUi);
529
+ normalized.usesExpoUiUniversalComponents = normalizeBoolean(answers.usesExpoUiUniversalComponents);
530
+ normalized.usesExpoNativeTabs = normalizeBoolean(answers.usesExpoNativeTabs);
531
+ normalized.easUses = normalizeStringArray(answers.easUses);
532
+ normalized.guidelinesTemplate = normalizeBoolean(answers.guidelinesTemplate);
533
+ normalized.dataStart = normalizeEnum(answers.dataStart, ['local', 'supabase']);
534
+ normalized.testToMainSafeguards = normalizeBoolean(answers.testToMainSafeguards);
535
+ normalized.saveDefaults = normalizeBoolean(answers.saveDefaults);
536
+ return Object.fromEntries(Object.entries(normalized).filter(([, value]) => value !== undefined));
537
+ }
538
+ export function buildCreateExpoStackFlags(answers) {
539
+ const flags = [];
540
+ flags.push(answers.scriptLanguage === 'javascript' ? '--javascript' : '--typescript');
541
+ flags.push(`--${answers.packageManager}`);
542
+ flags.push(answers.navigationLibrary === 'react-navigation' ? '--react-navigation' : '--expo-router');
543
+ if (answers.navigationLibrary === 'react-navigation') {
544
+ if (answers.reactNavigationLayout === 'tabs') {
545
+ flags.push('--tabs');
546
+ }
547
+ else if (answers.reactNavigationLayout === 'drawer') {
548
+ flags.push('--drawer+tabs');
549
+ }
550
+ }
551
+ switch (answers.stylingSystem) {
552
+ case 'uniwind':
553
+ flags.push('--uniwind');
554
+ break;
555
+ case 'nativewind':
556
+ flags.push('--nativewind');
557
+ break;
558
+ case 'tamagui':
559
+ flags.push('--tamagui');
560
+ break;
561
+ case 'restyle':
562
+ flags.push('--restyle');
563
+ break;
564
+ case 'stylesheet':
565
+ break;
566
+ }
567
+ if (answers.stateManagement === 'zustand') {
568
+ flags.push('--zustand');
569
+ }
570
+ if (answers.authBackend === 'supabase') {
571
+ flags.push('--supabase');
572
+ }
573
+ else if (answers.authBackend === 'firebase') {
574
+ flags.push('--firebase');
575
+ }
576
+ if (answers.easSetup) {
577
+ flags.push('--eas');
578
+ }
579
+ return flags;
580
+ }
581
+ export function buildMdsFlags(appName, onboardAnswers, intakeAnswers) {
582
+ const flags = [
583
+ `--mds-app-name=${quoteFlagValue(onboardAnswers.appName)}`,
584
+ `--mds-audience=${quoteFlagValue(onboardAnswers.audience)}`,
585
+ `--mds-core-flows=${quoteFlagValue(onboardAnswers.coreFlows)}`,
586
+ `--mds-data-needs=${quoteFlagValue(buildDataNeedsFlagValue(intakeAnswers))}`,
587
+ `--mds-platforms=${onboardAnswers.targetPlatforms.join(',')}`,
588
+ `--mds-first-platform=${onboardAnswers.firstTargetPlatform}`,
589
+ `--mds-platform-strategy=${onboardAnswers.platformFileStrategy}`,
590
+ `--mds-app-directory=${onboardAnswers.appDirectory}`,
591
+ `--mds-platform-layouts=${onboardAnswers.platformLayoutMode}`,
592
+ `--mds-web-output=${onboardAnswers.webOutput}`,
593
+ `--mds-deployed-server=${onboardAnswers.deployedServer}`,
594
+ `--mds-data-start=${onboardAnswers.dataStart}`,
595
+ '--mds-yes',
596
+ ];
597
+ if (intakeAnswers.screens && intakeAnswers.screens !== 'defer') {
598
+ flags.push(`--mds-screens=${quoteFlagValue(intakeAnswers.screens)}`);
599
+ }
600
+ if (onboardAnswers.includeCreateExpoComponents) {
601
+ flags.push('--mds-create-expo-components');
602
+ }
603
+ else {
604
+ flags.push('--mds-no-create-expo-components');
605
+ }
606
+ if (onboardAnswers.usesExpoUi) {
607
+ flags.push('--mds-expo-ui');
608
+ }
609
+ else {
610
+ flags.push('--mds-no-expo-ui');
611
+ }
612
+ if (onboardAnswers.usesExpoUiUniversalComponents) {
613
+ flags.push('--mds-expo-ui-universal');
614
+ }
615
+ else {
616
+ flags.push('--mds-no-expo-ui-universal');
617
+ }
618
+ if (onboardAnswers.usesExpoNativeTabs) {
619
+ flags.push('--mds-expo-native-tabs');
620
+ }
621
+ else {
622
+ flags.push('--mds-no-expo-native-tabs');
623
+ }
624
+ if (onboardAnswers.easUses.length > 0) {
625
+ flags.push(`--mds-eas-uses=${quoteFlagValue(onboardAnswers.easUses.join(','))}`);
626
+ }
627
+ if (intakeAnswers.guidelinesTemplate === false) {
628
+ flags.push('--mds-no-guidelines-template');
629
+ }
630
+ else {
631
+ flags.push('--mds-guidelines-template');
632
+ }
633
+ if (onboardAnswers.testToMainSafeguards) {
634
+ flags.push('--mds-test-to-main');
635
+ }
636
+ else {
637
+ flags.push('--mds-no-test-to-main');
638
+ }
639
+ if (intakeAnswers.saveDefaults) {
640
+ flags.push('--mds-save-defaults');
641
+ }
642
+ else {
643
+ flags.push('--mds-no-save-defaults');
644
+ }
645
+ if (path.basename(appName) !== onboardAnswers.appName) {
646
+ flags.push(`--mds-app-name=${quoteFlagValue(onboardAnswers.appName)}`);
647
+ }
648
+ return dedupe(flags);
649
+ }
650
+ export function buildCreateExpoSuperStackArgv(plan) {
651
+ return [plan.appName, ...plan.createExpoStackFlags, ...plan.mdsFlags];
652
+ }
653
+ export function buildCessSummaryLines(parentDir, appName, answers, onboardAnswers) {
654
+ const stackLine = [
655
+ answers.scriptLanguage === 'javascript' ? 'JavaScript' : 'TypeScript',
656
+ answers.packageManager,
657
+ answers.navigationLibrary === 'react-navigation'
658
+ ? `React Navigation${answers.reactNavigationLayout === 'tabs' ? ' Tabs' : answers.reactNavigationLayout === 'drawer' ? ' Drawer + Tabs' : ' Stack'}`
659
+ : 'Expo Router',
660
+ formatStylingLabel(answers.stylingSystem ?? STACK_DEFAULTS.stylingSystem),
661
+ answers.stateManagement === 'zustand' ? 'Zustand' : 'No shared state starter',
662
+ answers.authBackend === 'none'
663
+ ? 'No auth starter'
664
+ : formatTitle(answers.authBackend ?? STACK_DEFAULTS.authBackend),
665
+ answers.easSetup ? 'EAS starter enabled' : 'No EAS starter',
666
+ ].join(', ');
667
+ const platformLine = [
668
+ `platforms: ${onboardAnswers.targetPlatforms.map(formatPlatformLabel).join(', ')}`,
669
+ `first MVP: ${formatPlatformLabel(onboardAnswers.firstTargetPlatform)}`,
670
+ `routes: ${onboardAnswers.appDirectory === 'src' ? 'src/app' : 'app'}`,
671
+ `platform strategy: ${onboardAnswers.platformFileStrategy}`,
672
+ `layouts: ${onboardAnswers.platformLayoutMode}`,
673
+ ].join(', ');
674
+ const serverLine = onboardAnswers.webOutput === 'none'
675
+ ? 'web output: none'
676
+ : `web output: ${onboardAnswers.webOutput}, deployed server: ${onboardAnswers.deployedServer}`;
677
+ return [
678
+ `app: ${appName} at ${parentDir}`,
679
+ `stack: ${stackLine}`,
680
+ `audience: ${onboardAnswers.audience}`,
681
+ `core flows: ${onboardAnswers.coreFlows}`,
682
+ platformLine,
683
+ serverLine,
684
+ `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
+ ];
686
+ }
687
+ function buildResolvedCessAnswers(currentAnswers, parentDir, appName, onboardAnswers) {
688
+ const targetPlatforms = currentAnswers.targetPlatforms ?? onboardAnswers.targetPlatforms;
689
+ const usesExpoUi = currentAnswers.usesExpoUi ?? onboardAnswers.usesExpoUi;
690
+ const screens = currentAnswers.screens === 'defer' ? '' : currentAnswers.screens;
691
+ return {
692
+ confirmed: currentAnswers.confirmed === true,
693
+ scriptLanguage: currentAnswers.scriptLanguage ?? STACK_DEFAULTS.scriptLanguage,
694
+ packageManager: currentAnswers.packageManager ?? STACK_DEFAULTS.packageManager,
695
+ navigationLibrary: currentAnswers.navigationLibrary ?? STACK_DEFAULTS.navigationLibrary,
696
+ reactNavigationLayout: currentAnswers.reactNavigationLayout ?? STACK_DEFAULTS.reactNavigationLayout,
697
+ stylingSystem: currentAnswers.stylingSystem ?? STACK_DEFAULTS.stylingSystem,
698
+ stateManagement: currentAnswers.stateManagement ?? STACK_DEFAULTS.stateManagement,
699
+ authBackend: currentAnswers.authBackend ?? STACK_DEFAULTS.authBackend,
700
+ easSetup: currentAnswers.easSetup ?? STACK_DEFAULTS.easSetup,
701
+ displayAppName: currentAnswers.displayAppName ?? onboardAnswers.appName ?? appName,
702
+ audience: currentAnswers.audience ?? onboardAnswers.audience,
703
+ coreFlows: currentAnswers.coreFlows ?? onboardAnswers.coreFlows ?? AGENT_DERIVED_CORE_FLOWS,
704
+ screens,
705
+ dataNeedSelections: normalizeStringArray(currentAnswers.dataNeedSelections) ?? ['Local UI/app state'],
706
+ dataNeedsOther: currentAnswers.dataNeedsOther,
707
+ targetPlatforms,
708
+ firstTargetPlatform: currentAnswers.firstTargetPlatform ?? onboardAnswers.firstTargetPlatform ?? targetPlatforms[0] ?? 'web',
709
+ platformStrategy: currentAnswers.platformStrategy ?? onboardAnswers.platformFileStrategy,
710
+ appDirectory: currentAnswers.appDirectory ?? onboardAnswers.appDirectory,
711
+ platformLayouts: currentAnswers.platformLayouts ?? onboardAnswers.platformLayoutMode,
712
+ webOutput: currentAnswers.webOutput ?? onboardAnswers.webOutput,
713
+ expoServerAdapter: currentAnswers.expoServerAdapter ?? onboardAnswers.expoServerAdapter,
714
+ customBackend: currentAnswers.customBackend ?? onboardAnswers.customBackend,
715
+ customBackendEntry: currentAnswers.customBackendEntry ?? onboardAnswers.customBackendEntry,
716
+ deploymentTarget: currentAnswers.deploymentTarget ?? onboardAnswers.deploymentTarget,
717
+ includeCreateExpoComponents: currentAnswers.includeCreateExpoComponents ?? onboardAnswers.includeCreateExpoComponents,
718
+ usesExpoUi,
719
+ usesExpoUiUniversalComponents: currentAnswers.usesExpoUiUniversalComponents ??
720
+ (usesExpoUi ? onboardAnswers.usesExpoUiUniversalComponents : false),
721
+ usesExpoNativeTabs: currentAnswers.usesExpoNativeTabs ?? onboardAnswers.usesExpoNativeTabs,
722
+ easUses: currentAnswers.easUses ?? onboardAnswers.easUses,
723
+ guidelinesTemplate: currentAnswers.guidelinesTemplate ?? true,
724
+ dataStart: currentAnswers.dataStart ?? onboardAnswers.dataStart,
725
+ testToMainSafeguards: currentAnswers.testToMainSafeguards ?? onboardAnswers.testToMainSafeguards,
726
+ saveDefaults: currentAnswers.saveDefaults ?? false,
727
+ };
728
+ }
729
+ function buildOnboardArgvFromCess(parentDir, appName, answers) {
730
+ const screens = normalizeOptionalDeferText(answers.screens);
731
+ const dataNeeds = formatDataNeedsSelection(answers.dataNeedSelections ?? ['Local UI/app state'], normalizeText(answers.dataNeedsOther));
732
+ const targetPlatforms = normalizePlatforms(answers.targetPlatforms) ?? ['web', 'ios', 'android'];
733
+ const webOutput = answers.webOutput ?? (targetPlatforms.includes('web') ? 'static' : 'none');
734
+ const expoServerAdapter = answers.expoServerAdapter ?? 'none';
735
+ const customBackend = answers.customBackend ?? false;
736
+ return {
737
+ project: path.resolve(parentDir, appName),
738
+ yes: true,
739
+ appName: normalizeText(answers.displayAppName) ?? appName,
740
+ audience: normalizeText(answers.audience),
741
+ coreFlows: normalizeText(answers.coreFlows),
742
+ screens,
743
+ dataNeeds,
744
+ deploymentTarget: normalizeText(answers.deploymentTarget),
745
+ createExpoComponents: answers.includeCreateExpoComponents,
746
+ platforms: targetPlatforms,
747
+ firstPlatform: normalizeText(answers.firstTargetPlatform),
748
+ platformStrategy: answers.platformStrategy,
749
+ appDirectory: answers.appDirectory,
750
+ platformLayouts: answers.platformLayouts,
751
+ webOutput,
752
+ expoServerAdapter,
753
+ customBackend,
754
+ customBackendEntry: normalizeText(answers.customBackendEntry),
755
+ deployedServer: deriveDeployedServer(webOutput, expoServerAdapter, customBackend, undefined),
756
+ expoUi: answers.usesExpoUi,
757
+ expoUiUniversal: answers.usesExpoUiUniversalComponents,
758
+ expoNativeTabs: answers.usesExpoNativeTabs,
759
+ easSelected: answers.easSetup,
760
+ easUses: answers.easUses,
761
+ guidelinesTemplate: answers.guidelinesTemplate,
762
+ dataStart: answers.dataStart,
763
+ testToMain: answers.testToMainSafeguards,
764
+ saveDefaults: answers.saveDefaults,
765
+ };
766
+ }
767
+ function materializeQuestion(definition, context) {
768
+ return {
769
+ id: definition.id,
770
+ prompt: definition.prompt,
771
+ kind: definition.kind,
772
+ options: definition.options?.(context),
773
+ defaultValue: definition.defaultValue?.(context),
774
+ explanation: definition.explanation,
775
+ };
776
+ }
777
+ function findNextQuestion(context, answers, root) {
778
+ for (const definition of CESS_QUESTIONS) {
779
+ if (definition.shouldAsk && !definition.shouldAsk(context)) {
780
+ continue;
781
+ }
782
+ if (!isQuestionMissing(definition.id, answers, root)) {
783
+ continue;
784
+ }
785
+ return materializeQuestion(definition, context);
786
+ }
787
+ return null;
788
+ }
789
+ function isQuestionMissing(id, answers, root) {
790
+ if (id === 'parentDir') {
791
+ return !root.parentDirProvided;
792
+ }
793
+ if (id === 'appName') {
794
+ return !root.appNameProvided;
795
+ }
796
+ const value = answers[id];
797
+ if (typeof value === 'boolean') {
798
+ return false;
799
+ }
800
+ if (Array.isArray(value)) {
801
+ return value.length === 0;
802
+ }
803
+ return typeof value !== 'string' || value.trim().length === 0;
804
+ }
805
+ function normalizeParentDir(parentDir, cwd) {
806
+ return path.resolve(cwd, normalizeText(parentDir) ?? '.');
807
+ }
808
+ function normalizeProjectName(appName) {
809
+ return normalizeText(appName) ?? DEFAULT_PROJECT_NAME;
810
+ }
811
+ function normalizeBoolean(value) {
812
+ return typeof value === 'boolean' ? value : undefined;
813
+ }
814
+ function normalizeEnum(value, choices) {
815
+ return typeof value === 'string' && choices.includes(value) ? value : undefined;
816
+ }
817
+ function normalizeText(value) {
818
+ if (typeof value !== 'string') {
819
+ return undefined;
820
+ }
821
+ const trimmed = value.trim();
822
+ return trimmed.length > 0 ? trimmed : undefined;
823
+ }
824
+ function normalizeOptionalDeferText(value) {
825
+ const normalized = normalizeText(value);
826
+ if (!normalized || normalized.toLowerCase() === 'defer') {
827
+ return undefined;
828
+ }
829
+ return normalized;
830
+ }
831
+ function normalizeStringArray(value) {
832
+ if (!Array.isArray(value)) {
833
+ return undefined;
834
+ }
835
+ const normalized = value
836
+ .filter((item) => typeof item === 'string')
837
+ .map((item) => item.trim())
838
+ .filter(Boolean);
839
+ return normalized.length > 0 ? normalized : undefined;
840
+ }
841
+ function normalizePlatforms(value) {
842
+ const platforms = normalizeStringArray(value);
843
+ if (!platforms) {
844
+ return undefined;
845
+ }
846
+ const normalized = platforms.filter((item) => PLATFORM_OPTIONS.includes(item));
847
+ return normalized.length > 0 ? normalized : undefined;
848
+ }
849
+ function formatPlatformLabel(value) {
850
+ if (value === 'ios') {
851
+ return 'iOS';
852
+ }
853
+ if (value === 'apple-tv') {
854
+ return 'Apple TV';
855
+ }
856
+ if (value === 'android-tv') {
857
+ return 'Android TV';
858
+ }
859
+ return formatTitle(value);
860
+ }
861
+ function formatTitle(value) {
862
+ return value
863
+ .split(/[\s-]+/u)
864
+ .map((item) => item.charAt(0).toUpperCase() + item.slice(1))
865
+ .join(' ');
866
+ }
867
+ function formatStylingLabel(value) {
868
+ switch (value) {
869
+ case 'uniwind':
870
+ return 'Uniwind';
871
+ case 'nativewind':
872
+ return 'NativeWind';
873
+ case 'tamagui':
874
+ return 'Tamagui';
875
+ case 'restyle':
876
+ return 'Restyle';
877
+ case 'stylesheet':
878
+ return 'StyleSheet only';
879
+ }
880
+ }
881
+ function quoteFlagValue(value) {
882
+ return value;
883
+ }
884
+ function dedupe(values) {
885
+ return [...new Set(values)];
886
+ }
887
+ function hasMobileTarget(platforms) {
888
+ return (platforms ?? []).some((platform) => platform === 'ios' || platform === 'android');
889
+ }
890
+ function buildDataNeedsFlagValue(answers) {
891
+ const selections = (answers.dataNeedSelections ?? ['Local UI/app state']).filter((item) => item !== OTHER_DATA_NEEDS);
892
+ const custom = normalizeText(answers.dataNeedsOther);
893
+ if (custom) {
894
+ return [...selections, custom].join(', ');
895
+ }
896
+ return selections.join(', ');
897
+ }
898
+ //# sourceMappingURL=cess-intake.js.map