@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.
- 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 -35
- package/bundles/claude-code/commands/create-expo-super-stack.md +32 -30
- package/bundles/claude-code/skills/create-expo-super-stack/SKILL.md +17 -15
- 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 -48
- package/bundles/codex/commands/create-expo-super-stack.md +32 -30
- package/bundles/codex/skills/super-stack-startup/SKILL.md +34 -34
- package/bundles/codex/skills/workflow-continue-development/SKILL.md +48 -47
- package/bundles/codex/skills/workflow-create-expo-super-stack/SKILL.md +45 -42
- package/bundles/codex/skills/workflow-fix-seo/SKILL.md +42 -41
- package/bundles/codex/skills/workflow-prepare-deploy/SKILL.md +42 -41
- package/bundles/codex/skills/workflow-project-research-plan/SKILL.md +42 -41
- package/bundles/codex/skills/workflow-push-merge-loop/SKILL.md +37 -36
- package/bundles/codex/skills/workflow-review-expo-project/SKILL.md +42 -41
- package/bundles/codex/skills/workflow-run-doctor/SKILL.md +51 -50
- package/bundles/codex/skills/workflow-wrap-up/SKILL.md +80 -79
- 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 +37 -35
- 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 +37 -35
- package/dist/cess-intake.d.ts +106 -0
- package/dist/cess-intake.d.ts.map +1 -0
- package/dist/cess-intake.js +898 -0
- package/dist/cess-intake.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +3 -1
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/mcp-install.d.ts +3 -2
- package/dist/commands/mcp-install.d.ts.map +1 -1
- package/dist/commands/mcp-install.js +17 -44
- package/dist/commands/mcp-install.js.map +1 -1
- package/dist/commands/onboard.d.ts +10 -3
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +25 -15
- package/dist/commands/onboard.js.map +1 -1
- package/dist/commands/roadmap.d.ts +6 -0
- package/dist/commands/roadmap.d.ts.map +1 -0
- package/dist/commands/roadmap.js +54 -0
- package/dist/commands/roadmap.js.map +1 -0
- package/dist/project-memory.d.ts +0 -1
- package/dist/project-memory.d.ts.map +1 -1
- package/dist/project-memory.js +101 -89
- package/dist/project-memory.js.map +1 -1
- package/dist/roadmap.d.ts +71 -0
- package/dist/roadmap.d.ts.map +1 -0
- package/dist/roadmap.js +865 -0
- package/dist/roadmap.js.map +1 -0
- package/dist/stylist-theme.d.ts.map +1 -1
- package/dist/stylist-theme.js +1 -20
- package/dist/stylist-theme.js.map +1 -1
- package/package.json +11 -3
- package/templates/embedded-fonts.template.ts +72 -72
- package/templates/expo-sdk-56-screen-universal.template.tsx +709 -709
- package/templates/project/guidelines.md +4 -5
- package/templates/stylist-screen.template.tsx +3456 -3446
package/dist/project-memory.js
CHANGED
|
@@ -2,6 +2,7 @@ import { access, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { DEFAULT_STYLIST_THEME, renderGlobalCssThemeBlock, renderThemeTokensFile, } from './stylist-theme.js';
|
|
5
|
+
import { generateProjectRoadmap } from './roadmap.js';
|
|
5
6
|
const SOFTWARE_MANSION_CORE_DEPENDENCIES = {
|
|
6
7
|
'react-native-gesture-handler': '~2.30.0',
|
|
7
8
|
'react-native-reanimated': '4.3.1',
|
|
@@ -24,6 +25,10 @@ const STYLIST_DEPENDENCIES = {
|
|
|
24
25
|
'@react-native-async-storage/async-storage': '2.2.0',
|
|
25
26
|
'reanimated-color-picker': '^4.2.0',
|
|
26
27
|
};
|
|
28
|
+
const STYLIST_DEV_DEPENDENCIES = {
|
|
29
|
+
'@types/node': '^25.9.1',
|
|
30
|
+
tailwindcss: '^4.2.4',
|
|
31
|
+
};
|
|
27
32
|
const EXPO_UI_DEPENDENCIES = {
|
|
28
33
|
'@expo/ui': '~56.0.14',
|
|
29
34
|
};
|
|
@@ -34,7 +39,7 @@ const UNIWIND_DEV_DEPENDENCIES = {
|
|
|
34
39
|
tailwindcss: '^4.2.4',
|
|
35
40
|
};
|
|
36
41
|
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
37
|
-
const MDS_CLI_VERSION = '0.1.
|
|
42
|
+
const MDS_CLI_VERSION = '0.1.12';
|
|
38
43
|
const MDS_NPX_COMMAND = 'npx mds';
|
|
39
44
|
const DEFAULT_GUIDELINES_TEMPLATE_PATH = path.join(PACKAGE_ROOT, 'templates', 'project', 'guidelines.md');
|
|
40
45
|
const STYLIST_SCREEN_TEMPLATE_PATH = path.join(PACKAGE_ROOT, 'templates', 'stylist-screen.template.tsx');
|
|
@@ -75,18 +80,39 @@ export async function scaffoldProjectMemory(projectPath, answers, options = {})
|
|
|
75
80
|
await mkdir(projectDir, { recursive: true });
|
|
76
81
|
const force = Boolean(options.force);
|
|
77
82
|
const infoPath = path.join(projectDir, 'info.md');
|
|
83
|
+
const todoPath = path.join(projectDir, 'todo.md');
|
|
78
84
|
const stylePath = path.join(projectDir, 'style.md');
|
|
79
85
|
const existingInfo = await readOptionalText(infoPath);
|
|
80
86
|
const existingStyle = await readOptionalText(stylePath);
|
|
81
87
|
const guidelines = await resolveGuidelines(answers, options);
|
|
82
88
|
const results = await Promise.all([
|
|
83
89
|
writeProjectMemoryFile(infoPath, renderInfo(projectPath, answers, existingInfo), force, true),
|
|
84
|
-
writeIfAllowed(
|
|
90
|
+
writeIfAllowed(todoPath, renderTodo(answers), force),
|
|
85
91
|
writeProjectMemoryFile(stylePath, renderStyle(answers, existingStyle), force, true),
|
|
86
92
|
writeIfAllowed(path.join(projectDir, 'guidelines.md'), guidelines, force),
|
|
87
93
|
writeIfAllowed(path.join(projectPath, 'AGENTS.md'), renderAgentInstructions(answers), force),
|
|
88
94
|
writeIfAllowed(path.join(projectPath, 'CLAUDE.md'), renderClaudeMd(answers), force),
|
|
89
95
|
]);
|
|
96
|
+
const roadmapResult = await generateProjectRoadmap(projectPath, {
|
|
97
|
+
write: true,
|
|
98
|
+
preserveStatus: true,
|
|
99
|
+
});
|
|
100
|
+
const todoResultIndex = results.findIndex((result) => result.filePath === todoPath);
|
|
101
|
+
if (todoResultIndex >= 0) {
|
|
102
|
+
const todoResult = results[todoResultIndex];
|
|
103
|
+
if (todoResult) {
|
|
104
|
+
results[todoResultIndex] = {
|
|
105
|
+
filePath: todoResult.filePath,
|
|
106
|
+
wrote: todoResult.wrote || roadmapResult.wrote,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
results.push({
|
|
112
|
+
filePath: todoPath,
|
|
113
|
+
wrote: roadmapResult.wrote,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
90
116
|
if (shouldGenerateIntakeAgentHandoff(answers, existingInfo, existingStyle)) {
|
|
91
117
|
results.push(await writeIfAllowed(path.join(projectDir, 'intake-agent.md'), renderIntakeAgentHandoff(answers), force));
|
|
92
118
|
}
|
|
@@ -188,6 +214,7 @@ export async function scaffoldRichBoilerplate(projectPath, answers, force, optio
|
|
|
188
214
|
}
|
|
189
215
|
export function renderInfo(projectPath, answers, existingInfo) {
|
|
190
216
|
const importedNotes = renderImportedNotes(existingInfo, INFO_HEADINGS);
|
|
217
|
+
const hasConcreteCoreFlows = !isGenericCoreFlowsText(answers.coreFlows);
|
|
191
218
|
return [
|
|
192
219
|
`# ${answers.appName} Project Info`,
|
|
193
220
|
'',
|
|
@@ -209,11 +236,15 @@ export function renderInfo(projectPath, answers, existingInfo) {
|
|
|
209
236
|
'',
|
|
210
237
|
'## Core Features',
|
|
211
238
|
'',
|
|
212
|
-
|
|
239
|
+
hasConcreteCoreFlows
|
|
240
|
+
? `Derived from the first planned flows: ${answers.coreFlows}`
|
|
241
|
+
: '# TodoForContext(optional): List the first core features the MVP should deliver.',
|
|
213
242
|
'',
|
|
214
243
|
'## Core User Flows',
|
|
215
244
|
'',
|
|
216
|
-
|
|
245
|
+
hasConcreteCoreFlows
|
|
246
|
+
? answers.coreFlows
|
|
247
|
+
: '# TodoForContext(optional): Describe the first real end-to-end user flow the MVP should support.',
|
|
217
248
|
'',
|
|
218
249
|
'## Must-Include Screens Or Flows',
|
|
219
250
|
'',
|
|
@@ -263,12 +294,6 @@ export function renderInfo(projectPath, answers, existingInfo) {
|
|
|
263
294
|
'',
|
|
264
295
|
'## Questions To Revisit',
|
|
265
296
|
'',
|
|
266
|
-
...(hasThinOnboardingAnswers(answers)
|
|
267
|
-
? [
|
|
268
|
-
'- Replace generic onboarding defaults with app-specific decisions.',
|
|
269
|
-
'- Confirm the exact first user flow before production buildout starts.',
|
|
270
|
-
]
|
|
271
|
-
: []),
|
|
272
297
|
'',
|
|
273
298
|
'## Resources',
|
|
274
299
|
'',
|
|
@@ -299,7 +324,6 @@ export function renderInfo(projectPath, answers, existingInfo) {
|
|
|
299
324
|
'',
|
|
300
325
|
`- Advanced package setup: ${formatBoolean(answers.advancedPackageSetup)}`,
|
|
301
326
|
`- Create Expo starter components: ${formatBoolean(answers.includeCreateExpoComponents)}`,
|
|
302
|
-
`- Latest Expo SDK preference: ${formatBoolean(answers.useLatestExpoSdk)}`,
|
|
303
327
|
`- MDS guidelines template: yes`,
|
|
304
328
|
`- Expo UI: ${formatBoolean(answers.usesExpoUi)}`,
|
|
305
329
|
`- Expo UI Universal components: ${formatBoolean(answers.usesExpoUiUniversalComponents)}`,
|
|
@@ -311,7 +335,6 @@ export function renderInfo(projectPath, answers, existingInfo) {
|
|
|
311
335
|
].join('\n');
|
|
312
336
|
}
|
|
313
337
|
export function renderTodo(answers) {
|
|
314
|
-
const needsReview = hasThinOnboardingAnswers(answers);
|
|
315
338
|
return [
|
|
316
339
|
`# ${answers.appName} TODO`,
|
|
317
340
|
'',
|
|
@@ -320,55 +343,31 @@ export function renderTodo(answers) {
|
|
|
320
343
|
'- [ ] Browse exposition pages to understand included base packages.',
|
|
321
344
|
"- [ ] Review styling in the 'Stylist' page.",
|
|
322
345
|
'- [ ] Review `project/` files for accuracy and planning adjustments.',
|
|
323
|
-
'- [ ]
|
|
346
|
+
'- [ ] Run or defer `eject-stylist`; mark this todo done after ejection or deciding to defer (if you want to keep the stylist around for tinkering).',
|
|
324
347
|
'- [ ] Run `mds eject exposition` and keep only the generated sections you want to retain.',
|
|
325
|
-
'- [ ] Resolve every `# TodoForContext(optional):` marker by filling the section underneath or deleting the marker line to acknowledge no extra context is needed.
|
|
326
|
-
'',
|
|
327
|
-
'- [x] Confirm app purpose, audience, and primary flows in `project/info.md`.',
|
|
348
|
+
'- [ ] Resolve every `# TodoForContext(optional):` marker in `project/info.md` by filling the section underneath or deleting the marker line to acknowledge no extra context is needed.',
|
|
328
349
|
'- [ ] Confirm visual direction in `project/style.md` after using the Stylist page.',
|
|
350
|
+
'- [ ] After the `project/info.md` markers are resolved, refresh the agent-derived roadmap from `project/info.md` and review it for accuracy.',
|
|
329
351
|
'- [ ] Keep or prune included package examples after reviewing `/exposition`.',
|
|
330
352
|
'- [ ] Remove exposition pages before production once their lessons are absorbed.',
|
|
331
|
-
...(needsReview
|
|
332
|
-
? [
|
|
333
|
-
'- [ ] Replace generic onboarding placeholders with real app decisions before full implementation.',
|
|
334
|
-
]
|
|
335
|
-
: []),
|
|
336
353
|
'',
|
|
337
354
|
'## Phase 1: App Shell And First Flow',
|
|
338
355
|
'',
|
|
339
|
-
`- [ ]
|
|
340
|
-
|
|
341
|
-
`- [ ] Use ${formatPlatformLayoutMode(answers.platformLayoutMode)} unless project memory is updated.`,
|
|
342
|
-
`- [ ] Implement the first core flow from project info: ${answers.coreFlows}.`,
|
|
343
|
-
'- [ ] Keep route files thin and move real UI into feature screens.',
|
|
344
|
-
'- [ ] Apply Stylist synced theme tokens to production UI components and screens.',
|
|
356
|
+
`- [ ] Establish the app shell and first implementation-ready route in ${formatAppDirectory(answers.appDirectory)}.`,
|
|
357
|
+
'- [ ] Implement the first concrete product flow from `project/info.md` and the roadmap.',
|
|
345
358
|
'',
|
|
346
359
|
'## Phase 2: Data Layer',
|
|
347
360
|
'',
|
|
348
|
-
`- [ ]
|
|
349
|
-
...(answers.dataStart === '
|
|
350
|
-
? [
|
|
351
|
-
|
|
352
|
-
'- [ ] Replace the local adapter with Supabase when the product needs synced/authenticated data.',
|
|
353
|
-
]
|
|
354
|
-
: [
|
|
355
|
-
'- [ ] Create separate Supabase projects for test/staging and production.',
|
|
356
|
-
'- [ ] Wire publishable client keys through environment files, never service-role keys.',
|
|
357
|
-
]),
|
|
358
|
-
'- [ ] Verify data requirements against `project/info.md` before adding tables or auth.',
|
|
361
|
+
`- [ ] Implement the initial data layer using ${formatDataStart(answers.dataStart)}.`,
|
|
362
|
+
...(answers.dataStart === 'supabase'
|
|
363
|
+
? ['- [ ] Create separate Supabase projects for test/staging and production.']
|
|
364
|
+
: []),
|
|
359
365
|
'',
|
|
360
366
|
'## Phase 3: Complete Product Flows',
|
|
361
367
|
'',
|
|
362
368
|
'- [ ] Build the remaining core flows from `project/info.md` phase by phase.',
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
...answers.targetPlatforms.map((platform) => `- [ ] Verify ${platform} behavior.`),
|
|
366
|
-
...(answers.usesExpoUi ? ['- [ ] Add Expo UI examples where they improve native feel.'] : []),
|
|
367
|
-
...(answers.usesExpoUiUniversalComponents
|
|
368
|
-
? ['- [ ] Review the Expo UI Universal examples before replacing generated exposition code.']
|
|
369
|
-
: []),
|
|
370
|
-
...(answers.usesExpoNativeTabs
|
|
371
|
-
? ['- [ ] Prototype Expo Native Tabs for mobile navigation.']
|
|
369
|
+
...(answers.targetPlatforms.length > 1
|
|
370
|
+
? ['- [ ] Adapt the working MVP flow for the remaining target platforms after the primary flow is stable.']
|
|
372
371
|
: []),
|
|
373
372
|
...(answers.easUses.length > 0
|
|
374
373
|
? answers.easUses.map((item) => `- [ ] Configure EAS for ${item}.`)
|
|
@@ -376,11 +375,11 @@ export function renderTodo(answers) {
|
|
|
376
375
|
'',
|
|
377
376
|
'## Phase 4: Polish, Safeguards, And Release',
|
|
378
377
|
'',
|
|
379
|
-
'- [ ] Prune unused Software Mansion examples and remove unneeded packages.',
|
|
380
378
|
'- [ ] Run `mds doctor --ci` and address errors.',
|
|
381
379
|
...(answers.testToMainSafeguards
|
|
382
380
|
? [
|
|
383
381
|
'- [ ] Follow `project/release-flow.md` for test-to-main development.',
|
|
382
|
+
'- [ ] Complete the one-time GitHub repo setup from `project/release-flow.md` so `test` and `main` are protected correctly.',
|
|
384
383
|
'- [ ] Add GitHub branch protection so PR checks pass before merging into `test` or `main`.',
|
|
385
384
|
]
|
|
386
385
|
: ['- [ ] Decide on release safeguards before production work begins.']),
|
|
@@ -390,7 +389,6 @@ export function renderTodo(answers) {
|
|
|
390
389
|
...(answers.deployedServer !== 'none'
|
|
391
390
|
? [`- [ ] Plan deployed server work: ${formatServerChoice(answers.deployedServer)}.`]
|
|
392
391
|
: []),
|
|
393
|
-
'- [ ] Add monorepo support after the MVP is stable.',
|
|
394
392
|
'',
|
|
395
393
|
].join('\n');
|
|
396
394
|
}
|
|
@@ -454,7 +452,7 @@ function renderThemeProvider() {
|
|
|
454
452
|
return [
|
|
455
453
|
"import { createContext, useContext, useMemo, useState, type Dispatch, type ReactNode, type SetStateAction } from 'react';",
|
|
456
454
|
'',
|
|
457
|
-
"import
|
|
455
|
+
"import defaultThemeTokens, { type StylistColorPalette, type StylistColorScheme, type StylistThemeTokens } from './tokens';",
|
|
458
456
|
'',
|
|
459
457
|
'export type AppThemeValue = StylistThemeTokens & {',
|
|
460
458
|
' activeScheme: StylistColorScheme;',
|
|
@@ -462,14 +460,14 @@ function renderThemeProvider() {
|
|
|
462
460
|
'};',
|
|
463
461
|
'',
|
|
464
462
|
'const AppThemeContext = createContext<AppThemeValue>({',
|
|
465
|
-
' ...
|
|
466
|
-
' activeScheme:
|
|
467
|
-
' activeColors:
|
|
463
|
+
' ...defaultThemeTokens,',
|
|
464
|
+
' activeScheme: defaultThemeTokens.colorSystem.previewScheme,',
|
|
465
|
+
' activeColors: defaultThemeTokens.colors[defaultThemeTokens.colorSystem.previewScheme],',
|
|
468
466
|
'});',
|
|
469
467
|
'const AppThemeSetterContext = createContext<Dispatch<SetStateAction<StylistThemeTokens>> | null>(null);',
|
|
470
468
|
'',
|
|
471
469
|
'export function AppThemeProvider({ children }: { children: ReactNode }) {',
|
|
472
|
-
' const [theme, setTheme] = useState<StylistThemeTokens>(
|
|
470
|
+
' const [theme, setTheme] = useState<StylistThemeTokens>(defaultThemeTokens);',
|
|
473
471
|
' const value = useMemo<AppThemeValue>(() => {',
|
|
474
472
|
' const activeScheme = theme.colorSystem.previewScheme;',
|
|
475
473
|
' return {',
|
|
@@ -570,7 +568,6 @@ export function renderGuidelines(answers) {
|
|
|
570
568
|
'- Develop through feature branches into `test`, then promote validated work from `test` to `main`.',
|
|
571
569
|
]
|
|
572
570
|
: []),
|
|
573
|
-
`- Latest Expo SDK preference captured during onboarding: ${formatBoolean(answers.useLatestExpoSdk)}.`,
|
|
574
571
|
`- Expo UI Universal components preference captured during onboarding: ${formatBoolean(answers.usesExpoUiUniversalComponents)}.`,
|
|
575
572
|
'- Treat monorepo scaffolding as future work until the single-app MVP is stable.',
|
|
576
573
|
'',
|
|
@@ -595,7 +592,7 @@ export function renderAgentInstructions(answers) {
|
|
|
595
592
|
'',
|
|
596
593
|
'If the user says `mds continue` or `MDS Continue`, first run `mds continue` from the app root if available. Use the MDS Continue brief to propose the next plan and wait for approval before editing files. If the command is unavailable, manually inspect markers, Doctor status, git status, and `project/todo.md` in that order.',
|
|
597
594
|
'',
|
|
598
|
-
'Before any intake, planning, scaffolding, or phase work, scan
|
|
595
|
+
'Before any intake, planning, scaffolding, or phase work, scan `project/info.md` for the marker `# TodoForContext(optional):`. If any remain, stop and tell the user to fill the section underneath OR delete the marker line to acknowledge they do not want to add that context. Only proceed when zero `project/info.md` markers remain.',
|
|
599
596
|
'',
|
|
600
597
|
'Then build from `project/todo.md` in phase order. Do not make changes that conflict with project memory. If the files are unclear or generic, update the project memory first or ask the user.',
|
|
601
598
|
'',
|
|
@@ -733,6 +730,7 @@ async function ensurePackageJson(projectPath, answers, manageUniwind) {
|
|
|
733
730
|
'expo-doctor': packageJson.scripts?.['expo-doctor'] ?? 'npx expo-doctor',
|
|
734
731
|
'post-create-check': packageJson.scripts?.['post-create-check'] ?? 'npx expo install --fix && npx expo-doctor',
|
|
735
732
|
'ci:verify': packageJson.scripts?.['ci:verify'] ?? `${MDS_NPX_COMMAND} doctor --ci`,
|
|
733
|
+
test: packageJson.scripts?.test ?? 'npm run lint && npm run typecheck',
|
|
736
734
|
};
|
|
737
735
|
if (!manageUniwind) {
|
|
738
736
|
packageJson.scripts['patch:nativewind-metro'] =
|
|
@@ -786,12 +784,10 @@ async function ensurePackageJson(projectPath, answers, manageUniwind) {
|
|
|
786
784
|
...packageJson.dependencies,
|
|
787
785
|
};
|
|
788
786
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
};
|
|
794
|
-
}
|
|
787
|
+
packageJson.dependencies = {
|
|
788
|
+
...ANDROID_NAVIGATION_BAR_DEPENDENCIES,
|
|
789
|
+
...packageJson.dependencies,
|
|
790
|
+
};
|
|
795
791
|
if (manageUniwind) {
|
|
796
792
|
packageJson.dependencies = {
|
|
797
793
|
...UNIWIND_DEPENDENCIES,
|
|
@@ -807,6 +803,7 @@ async function ensurePackageJson(projectPath, answers, manageUniwind) {
|
|
|
807
803
|
delete packageJson.devDependencies['prettier-plugin-tailwindcss'];
|
|
808
804
|
}
|
|
809
805
|
packageJson.devDependencies = {
|
|
806
|
+
...STYLIST_DEV_DEPENDENCIES,
|
|
810
807
|
...packageJson.devDependencies,
|
|
811
808
|
'@mr.dj2u/cli': packageJson.devDependencies?.['@mr.dj2u/cli'] ?? `^${MDS_CLI_VERSION}`,
|
|
812
809
|
};
|
|
@@ -828,7 +825,6 @@ function applyGuidelinesTemplate(template, answers) {
|
|
|
828
825
|
deploymentTarget: answers.deploymentTarget,
|
|
829
826
|
advancedPackageSetup: formatBoolean(answers.advancedPackageSetup),
|
|
830
827
|
includeCreateExpoComponents: formatBoolean(answers.includeCreateExpoComponents),
|
|
831
|
-
useLatestExpoSdk: formatBoolean(answers.useLatestExpoSdk),
|
|
832
828
|
targetPlatforms: answers.targetPlatforms.map((item) => `- ${item}`).join('\n'),
|
|
833
829
|
firstTargetPlatform: answers.firstTargetPlatform,
|
|
834
830
|
appDirectory: formatAppDirectory(answers.appDirectory),
|
|
@@ -948,14 +944,20 @@ function hasThinOnboardingAnswers(answers) {
|
|
|
948
944
|
const genericValues = new Set([
|
|
949
945
|
'Expo app users',
|
|
950
946
|
'Onboarding, primary app workflow, settings',
|
|
951
|
-
'Agent should derive the first core user flows from project/info.md during intake.',
|
|
952
947
|
'Local state first; add backend only when needed',
|
|
953
948
|
'Expo web/native deployment',
|
|
954
949
|
]);
|
|
955
950
|
if (!answers.screens?.trim()) {
|
|
956
951
|
return true;
|
|
957
952
|
}
|
|
958
|
-
return [answers.audience, answers.
|
|
953
|
+
return ([answers.audience, answers.dataNeeds, answers.deploymentTarget].some((value) => genericValues.has(value.trim())) || isGenericCoreFlowsText(answers.coreFlows));
|
|
954
|
+
}
|
|
955
|
+
function isGenericCoreFlowsText(value) {
|
|
956
|
+
const trimmed = value.trim();
|
|
957
|
+
return (trimmed.length === 0 ||
|
|
958
|
+
trimmed ===
|
|
959
|
+
'Let the agent derive the first real core user flows later from the fully clarified `project/info.md`.' ||
|
|
960
|
+
trimmed === 'Agent should derive the first core user flows from project/info.md during intake.');
|
|
959
961
|
}
|
|
960
962
|
async function ensureUniwindMetroConfig(projectPath) {
|
|
961
963
|
const metroPath = path.join(projectPath, 'metro.config.js');
|
|
@@ -1033,20 +1035,35 @@ async function removeTailwindPrettierPluginConfig(filePath) {
|
|
|
1033
1035
|
async function ensureGlobalCssImport(projectPath, appDirectory) {
|
|
1034
1036
|
const layoutPath = path.join(getExpoRouterAppDir(projectPath, appDirectory), '_layout.tsx');
|
|
1035
1037
|
const appPath = path.join(projectPath, 'App.tsx');
|
|
1038
|
+
const globalCssPath = path.join(projectPath, 'global.css');
|
|
1039
|
+
const hasGlobalCss = await pathExists(globalCssPath);
|
|
1036
1040
|
const layout = await readOptionalText(layoutPath);
|
|
1037
1041
|
if (layout) {
|
|
1038
|
-
const
|
|
1039
|
-
const updated =
|
|
1040
|
-
? layout.
|
|
1041
|
-
|
|
1042
|
+
const globalCssImportPattern = /^\s*import\s+['"][^'"]*global\.css['"];?\r?\n/m;
|
|
1043
|
+
const updated = hasGlobalCss
|
|
1044
|
+
? layout.match(globalCssImportPattern)
|
|
1045
|
+
? layout.replace(globalCssImportPattern, `${renderGlobalCssImport(layoutPath, projectPath)}\n`)
|
|
1046
|
+
: `${renderGlobalCssImport(layoutPath, projectPath)}\n${layout}`
|
|
1047
|
+
: layout.replace(globalCssImportPattern, '');
|
|
1042
1048
|
if (updated !== layout) {
|
|
1043
1049
|
await writeFile(layoutPath, updated, 'utf8');
|
|
1044
1050
|
}
|
|
1045
1051
|
return;
|
|
1046
1052
|
}
|
|
1047
1053
|
const app = await readOptionalText(appPath);
|
|
1048
|
-
if (
|
|
1049
|
-
|
|
1054
|
+
if (!app) {
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
const globalCssImportPattern = /^\s*import\s+['"][^'"]*global\.css['"];?\r?\n/m;
|
|
1058
|
+
if (hasGlobalCss) {
|
|
1059
|
+
if (!app.match(globalCssImportPattern)) {
|
|
1060
|
+
await writeFile(appPath, `import './global.css';\n${app}`, 'utf8');
|
|
1061
|
+
}
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
const updated = app.replace(globalCssImportPattern, '');
|
|
1065
|
+
if (updated !== app) {
|
|
1066
|
+
await writeFile(appPath, updated, 'utf8');
|
|
1050
1067
|
}
|
|
1051
1068
|
}
|
|
1052
1069
|
function getExpoRouterAppDir(projectPath, appDirectory) {
|
|
@@ -1307,7 +1324,7 @@ function renderStylistSyncApiRoute() {
|
|
|
1307
1324
|
"import { access, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';",
|
|
1308
1325
|
"import path from 'node:path';",
|
|
1309
1326
|
'',
|
|
1310
|
-
"import
|
|
1327
|
+
"import defaultThemeTokens from '../../theme/tokens';",
|
|
1311
1328
|
'',
|
|
1312
1329
|
'interface SyncResponse {',
|
|
1313
1330
|
' projectPath: string;',
|
|
@@ -1359,7 +1376,7 @@ function renderStylistSyncApiRoute() {
|
|
|
1359
1376
|
'',
|
|
1360
1377
|
' const themeFromJson = await readThemeJson(themePath);',
|
|
1361
1378
|
' const themeFromStyle = await readThemeFromStyleMarkdown(stylePath);',
|
|
1362
|
-
' const resolvedTheme = themeFromStyle ?? themeFromJson ??
|
|
1379
|
+
' const resolvedTheme = themeFromStyle ?? themeFromJson ?? defaultThemeTokens;',
|
|
1363
1380
|
" const themeSource = themeFromStyle ? 'style.md' : themeFromJson ? 'theme.json' : 'default';",
|
|
1364
1381
|
' const mismatchDetected =',
|
|
1365
1382
|
' Boolean(themeFromJson) &&',
|
|
@@ -1883,7 +1900,7 @@ function renderRichRootLayout(projectPath, appDir, navigationShell, answers) {
|
|
|
1883
1900
|
"import { GestureHandlerRootView } from 'react-native-gesture-handler';",
|
|
1884
1901
|
"import { KeyboardProvider } from 'react-native-keyboard-controller';",
|
|
1885
1902
|
"import { SafeAreaProvider } from 'react-native-safe-area-context';",
|
|
1886
|
-
`import
|
|
1903
|
+
`import themeFontAssets from '${themeFontAssetsImport}';`,
|
|
1887
1904
|
`import { AppThemeProvider, useAppTheme } from '${themeProviderImport}';`,
|
|
1888
1905
|
'',
|
|
1889
1906
|
'function RouterThemeBridge({ children }: { children: ReactNode }) {',
|
|
@@ -1965,8 +1982,8 @@ function renderRichRootLayout(projectPath, appDir, navigationShell, answers) {
|
|
|
1965
1982
|
'}',
|
|
1966
1983
|
'',
|
|
1967
1984
|
'export default function Layout() {',
|
|
1968
|
-
' const hasFontAssets = Object.keys(
|
|
1969
|
-
' const [fontsLoaded, fontsError] = useFonts(
|
|
1985
|
+
' const hasFontAssets = Object.keys(themeFontAssets).length > 0;',
|
|
1986
|
+
' const [fontsLoaded, fontsError] = useFonts(themeFontAssets);',
|
|
1970
1987
|
'',
|
|
1971
1988
|
' if (hasFontAssets && !fontsLoaded && !fontsError) {',
|
|
1972
1989
|
' return null;',
|
|
@@ -2063,8 +2080,10 @@ function renderReleaseFlow(answers) {
|
|
|
2063
2080
|
'## GitHub Setup The User Still Needs To Do',
|
|
2064
2081
|
'',
|
|
2065
2082
|
'- Create `test` and `main` branches.',
|
|
2083
|
+
'- Confirm GitHub Actions is enabled for the repo and that the generated workflow is allowed to run.',
|
|
2066
2084
|
'- In GitHub branch protection, require pull requests and status checks for `test` and `main`.',
|
|
2067
2085
|
'- Require the generated `MDS PR Checks` workflow before merge.',
|
|
2086
|
+
'- If the agent has GitHub access with enough permissions, let it apply these repo settings for you; otherwise do this one-time setup in the GitHub UI.',
|
|
2068
2087
|
'',
|
|
2069
2088
|
].join('\n');
|
|
2070
2089
|
}
|
|
@@ -2190,6 +2209,7 @@ function renderGestureCard() {
|
|
|
2190
2209
|
function renderKeyboardForm() {
|
|
2191
2210
|
return [
|
|
2192
2211
|
"import { Keyboard, Platform, ScrollView, StyleSheet, TextInput } from 'react-native';",
|
|
2212
|
+
"import { KeyboardAwareScrollView, KeyboardToolbar } from 'react-native-keyboard-controller';",
|
|
2193
2213
|
'',
|
|
2194
2214
|
'export function KeyboardForm() {',
|
|
2195
2215
|
' if (Platform.OS === "web") {',
|
|
@@ -2200,14 +2220,6 @@ function renderKeyboardForm() {
|
|
|
2200
2220
|
' </ScrollView>',
|
|
2201
2221
|
' );',
|
|
2202
2222
|
' }',
|
|
2203
|
-
'',
|
|
2204
|
-
" const keyboardController = require('react-native-keyboard-controller') as {",
|
|
2205
|
-
' KeyboardAwareScrollView: any;',
|
|
2206
|
-
' KeyboardToolbar: any;',
|
|
2207
|
-
' };',
|
|
2208
|
-
' const KeyboardAwareScrollView = keyboardController.KeyboardAwareScrollView;',
|
|
2209
|
-
' const KeyboardToolbar = keyboardController.KeyboardToolbar;',
|
|
2210
|
-
'',
|
|
2211
2223
|
' return (',
|
|
2212
2224
|
' <>',
|
|
2213
2225
|
' <KeyboardAwareScrollView bottomOffset={72} contentContainerStyle={styles.form} style={styles.scroller}>',
|
|
@@ -3008,7 +3020,7 @@ function renderHomeScreen(answers, navigationShell) {
|
|
|
3008
3020
|
"import { appSnapshot } from '../../data/mock-app';",
|
|
3009
3021
|
"import { useAppTheme } from '../../theme/provider';",
|
|
3010
3022
|
'',
|
|
3011
|
-
'const expositionLinks:
|
|
3023
|
+
'const expositionLinks: { href: Href; title: string; body: string }[] = [',
|
|
3012
3024
|
...expositionLinks,
|
|
3013
3025
|
'];',
|
|
3014
3026
|
'',
|
|
@@ -4303,9 +4315,9 @@ function renderStylistScreen(answers) {
|
|
|
4303
4315
|
"import ColorPicker, { HueSlider, Panel1, Preview, Swatches } from 'reanimated-color-picker';",
|
|
4304
4316
|
'',
|
|
4305
4317
|
"import { AnimatedPressable, ExpositionNotice } from '../../components/exposition';",
|
|
4306
|
-
"import
|
|
4318
|
+
"import defaultThemeTokens from '../../theme/tokens';",
|
|
4307
4319
|
'',
|
|
4308
|
-
'type StylistTheme = typeof
|
|
4320
|
+
'type StylistTheme = typeof defaultThemeTokens;',
|
|
4309
4321
|
"type ColorKey = keyof StylistTheme['colors'];",
|
|
4310
4322
|
'',
|
|
4311
4323
|
'const colorKeys: ColorKey[] = [',
|
|
@@ -4317,11 +4329,11 @@ function renderStylistScreen(answers) {
|
|
|
4317
4329
|
" 'warning',",
|
|
4318
4330
|
'];',
|
|
4319
4331
|
'',
|
|
4320
|
-
"const spacingKeys:
|
|
4332
|
+
"const spacingKeys: (keyof StylistTheme['layout']['spacing'])[] = ['xs', 'sm', 'md', 'lg', 'xl'];",
|
|
4321
4333
|
"const NATIVE_SAVE_COMMAND = 'npm run stylist:sync:android';",
|
|
4322
4334
|
'',
|
|
4323
4335
|
'export default function StylistScreen() {',
|
|
4324
|
-
' const [theme, setTheme] = useState<StylistTheme>(
|
|
4336
|
+
' const [theme, setTheme] = useState<StylistTheme>(defaultThemeTokens);',
|
|
4325
4337
|
" const [selectedColor, setSelectedColor] = useState<ColorKey>('primary');",
|
|
4326
4338
|
" const [saveMessage, setSaveMessage] = useState('');",
|
|
4327
4339
|
" const [nativeDraft, setNativeDraft] = useState('');",
|